mirror of
https://github.com/TomHarte/CLK.git
synced 2024-08-24 23:29:00 +00:00
Improvements as a result: audio works in a second machine started in an existing window; there is no audio thread footprint if there is no audio.
92 lines
2.4 KiB
C++
92 lines
2.4 KiB
C++
#ifndef FUNCTIONTHREAD_H
|
|
#define FUNCTIONTHREAD_H
|
|
|
|
#include <atomic>
|
|
#include <QApplication>
|
|
#include <QDebug>
|
|
#include <QEvent>
|
|
#include <QThread>
|
|
|
|
/*!
|
|
* \brief The LambdaThread class
|
|
*
|
|
* Provides a QThread to which lambdas can be posted.
|
|
*
|
|
* Disclaimer: this might be a crutch that reveals a misunderstanding of the Qt
|
|
* threading infrastructure. We'll see.
|
|
*/
|
|
class FunctionThread: public QThread {
|
|
public:
|
|
~FunctionThread() {
|
|
stop();
|
|
}
|
|
|
|
void run() override {
|
|
// Gymnastics here: events posted directly to the QThread will occur on the thread
|
|
// that created the QThread. To have events occur within a QThread, they have to be
|
|
// posted to an object created on that thread. FunctionPerformer fills that role.
|
|
if(!performer) performer = std::make_unique<FunctionPerformer>();
|
|
performerFlag.clear();
|
|
exec();
|
|
}
|
|
|
|
void stop() {
|
|
if(isRunning()) {
|
|
performAsync([this] {
|
|
this->quit();
|
|
});
|
|
}
|
|
wait();
|
|
}
|
|
|
|
void start() {
|
|
if(isRunning()) {
|
|
return;
|
|
}
|
|
|
|
// TODO: I've assumed a race condition here with the creation of performer; if QThread
|
|
// blocks on completion of `run` when starting then this is redundant.
|
|
performerFlag.test_and_set();
|
|
QThread::start();
|
|
while(performerFlag.test_and_set());
|
|
}
|
|
|
|
/*!
|
|
* \brief Schedules a function to be performed on this thread. Control
|
|
* must return to the main event loop for the function to be performed;
|
|
* use QCoreApplication::sendPostedEvents() to ensure the function is
|
|
* performed before then, if required.
|
|
*
|
|
* \param function The function to perform.
|
|
*/
|
|
void performAsync(const std::function<void(void)> &function) {
|
|
QApplication::instance()->postEvent(performer.get(), new FunctionEvent(function));
|
|
QCoreApplication::sendPostedEvents();
|
|
}
|
|
|
|
private:
|
|
struct FunctionEvent: public QEvent {
|
|
FunctionEvent(const std::function<void(void)> &function) : QEvent(QEvent::Type::User), function(function) {}
|
|
std::function<void(void)> function;
|
|
};
|
|
|
|
struct FunctionPerformer: public QObject {
|
|
FunctionPerformer(): QObject() {}
|
|
|
|
bool event(QEvent *event) override {
|
|
if(event->type() == QEvent::Type::User) {
|
|
const auto functionEvent = dynamic_cast<FunctionEvent *>(event);
|
|
if(functionEvent) {
|
|
functionEvent->function();
|
|
return true;
|
|
}
|
|
}
|
|
return QObject::event(event);
|
|
}
|
|
};
|
|
std::unique_ptr<FunctionPerformer> performer;
|
|
std::atomic_flag performerFlag;
|
|
};
|
|
|
|
#endif // FUNCTIONTHREAD_H
|