Wrap Gallant signals in Qt-compatible thread safe wrappers
ADD
src/gallantsignalwrapper.h
+#pragma once
+
+#include <QMetaObject>
+#include <functional>
+#include "Signals/Signal.h"
+
+namespace Ripes {
+/**
+ * class CrossThreadSignalWrapper
+ * This class acts as a wrapper for Gallant signals. @p fun will be executed within the event loop context of @p obj,
+ * upon signal @p sig being triggered. This serves the following purposes:
+ * - Gallant signals only accepts raw pointers as callback functions; we'd like to be able to pass a lambda function.
+ * - Gallant signals are not thread safe. The wrapper will ensure that signals go through the signal/slot system of Qt,
+ * which checks for, and handles, cross-thread signals.
+ */
+struct GallantSignalWrapperBase {};
+template <typename F, typename D, typename T>
+class GallantSignalWrapper : public GallantSignalWrapperBase {
+public:
+ GallantSignalWrapper(T* obj, F&& fun, D& sig, Qt::ConnectionType type = Qt::AutoConnection) {
+ wrapper = [=] { QMetaObject::invokeMethod(obj, fun, type); };
+ sig.Connect(this, &GallantSignalWrapper::ftunnel);
+ }
+
+private:
+ void ftunnel() { wrapper(); }
+ std::function<void()> wrapper;
+};
+} // namespace Ripes

MODIFY
src/mainwindow.cpp
src/mainwindow.cpp
@@ -97,10 +97,6 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), m_ui(new Ui::Main
// setup and connect widgets
connect(editTab, &EditTab::editorStateChanged, [=] { RipesSettings::setValue(RIPES_SETTING_HAS_SAVEFILE, false); });
-
- connect(ProcessorHandler::get(), &ProcessorHandler::exit, processorTab, &ProcessorTab::processorFinished);
- connect(ProcessorHandler::get(), &ProcessorHandler::runFinished, processorTab, &ProcessorTab::runFinished);
-
connect(&SystemIO::get(), &SystemIO::doPrint, processorTab, &ProcessorTab::printToLog);
// Setup status bar

MODIFY
src/processorhandler.cpp
src/processorhandler.cpp
@@ -247,28 +247,36 @@ void ProcessorHandler::_selectProcessor(const ProcessorID& id, const QStringList
emit programChanged();
}
- // Reconnect the wrappers for processor signal emission
- m_currentProcessor->processorWasClocked.Connect(this, &ProcessorHandler::processorWasClockedWrapper);
- m_currentProcessor->processorWasReset.Connect(this, &ProcessorHandler::processorResetWrapper);
- m_currentProcessor->processorWasReversed.Connect(this, &ProcessorHandler::processorReversedWrapper);
-
- emit processorChanged();
-}
-
-void ProcessorHandler::processorWasClockedWrapper() {
+ // Connect wrappers for making processor signal emissions thread safe.
+ m_signalWrappers.clear();
+ m_signalWrappers.push_back(std::unique_ptr<GallantSignalWrapperBase>(new GallantSignalWrapper(
+ this,
+ [=] {
emit processorClocked();
if (!_isRunning()) {
emit processorClockedNonRun();
_triggerProcStateChangeTimer();
}
-}
-void ProcessorHandler::processorResetWrapper() {
+ },
+ m_currentProcessor->processorWasClocked)));
+
+ m_signalWrappers.push_back(std::unique_ptr<GallantSignalWrapperBase>(new GallantSignalWrapper(
+ this,
+ [=] {
emit processorReset();
_triggerProcStateChangeTimer();
-}
-void ProcessorHandler::processorReversedWrapper() {
+ },
+ m_currentProcessor->processorWasReset)));
+
+ m_signalWrappers.push_back(std::unique_ptr<GallantSignalWrapperBase>(new GallantSignalWrapper(
+ this,
+ [=] {
emit processorReversed();
_triggerProcStateChangeTimer();
+ },
+ m_currentProcessor->processorWasReversed)));
+
+ emit processorChanged();
}
int ProcessorHandler::_getCurrentProgramSize() const {

MODIFY
src/processorhandler.h
src/processorhandler.h
#include <QFuture>
#include <QFutureWatcher>
#include <QObject>
+#include <memory>
#include "assembler/assembler.h"
#include "assembler/program.h"
+#include "gallantsignalwrapper.h"
#include "processorregistry.h"
#include "syscall/ripes_syscall.h"
@@ -224,14 +226,6 @@ private:
void _stopRun();
void _triggerProcStateChangeTimer();
- /**
- * @brief Wrapper functions for processor signal emissions. VSRTL's signal/slot library does not accept lambdas,
- * which is why we have to make these explicit member functions.
- */
- void processorWasClockedWrapper();
- void processorResetWrapper();
- void processorReversedWrapper();
-
void createAssemblerForCurrentISA();
void setStopRunFlag();
ProcessorHandler();
@@ -267,5 +261,6 @@ private:
* Semaphore handling locking simulator thread execution whilst trapping to the execution environment.
*/
QSemaphore m_sem;
+ std::vector<std::unique_ptr<GallantSignalWrapperBase>> m_signalWrappers;
};
} // namespace Ripes

MODIFY
src/processortab.cpp
src/processortab.cpp
@@ -114,6 +114,8 @@ ProcessorTab::ProcessorTab(QToolBar* controlToolbar, QToolBar* additionalToolbar
// Connect the global reset request signal to reset()
connect(ProcessorHandler::get(), &ProcessorHandler::processorReset, this, &ProcessorTab::reset);
+ connect(ProcessorHandler::get(), &ProcessorHandler::exit, this, &ProcessorTab::processorFinished);
+ connect(ProcessorHandler::get(), &ProcessorHandler::runFinished, this, &ProcessorTab::runFinished);
// Send input data from the console to the SystemIO stdin stream
connect(m_ui->console, &Console::sendData, &SystemIO::get(), &SystemIO::putStdInData);

Wrap Gallant signals in Qt-compatible thread safe wrappers