MeVisLab Toolbox Reference


Introduction

Calling specific methods from a background thread is not allowed for methods that interact with UI elements or implictly invoke field listeners. The solution is to schedule the method invocation with an event to the main thread. This helper utility should make this a no-brainer and protects from the usual foot gun scenario regarding module destruction with unprocessed events.

The safest way of using it if you don't have control over the thread object is to grab a protected handle from MainThreadCommunicator that can be used to access the call functionality. This way, the module's destructor will wait until the access scope is over.

Synopsis:

class MyModule : public Module
{
// ...
protected:
void handleNotification(Field* field) override
{
if (field == inMessageChannelFld) {
subscription.reset();
auto channel = inMessageChannelFld->getTypedValue<MessageChannel*>();
if (channel) {
// grab a handle with a weak reference to the communicator which is captured
// in the lambda that is potentially called by an externally controlled thread
handle = communicator.handle();
subscription = channel.subscribe([this, handle](const std::string& message) {
// when control reaches this point with a different thread there are two options
// 1. "this" (the MyModule instance) is gone
// 2. "this" is still alive
// with instantiating auch an access object, we can check whether "this" is alive
// and lock it to be alive until we are done if applicable
auto main_thread_access = handle.access();
if (main_thread_access) {
main_thread_access->call([this, message] {
// when control reaches this point, we can be sure it's the main thread
outMessageFld->setValue(message);
});
}
});
}
}
}
BaseField* inMessageChannelFld;
StringField* outMessageFld;
ChannelSubscription subscription;
MainThreadCommunicator communicator;
};

A simpler way, which can only (!) be used if the module instance itself has control over the thread object, is noted down here:

class ThreadControllingModule : public Module
{
public:
~ThreadControllingModule()
{
thread.join();
}
protected:
void handleNotification(Field* field) override
{
if (field == updateFld) {
// wait in case there is already a computation ongoing
thread.join();
// run computation in background thread
auto parameter = inParameterFld->getValue();
thread = std::thread([this, parameter] {
auto result = parameter + 42; // heavy computation
// it can not happen that 'this' is gone since
// its destructor waits for this thread to finish.
communicator.call([this, result] {
outResultFld->setValue(result);
});
});
}
}
IntField* inParameterFld;
IntField* outResultFld;
NotifyField* updateFld;
std::thread thread;
UnprotectedMainThreadCommunicator communicator;
};