Thead-safe Signals

I remember some time ago on another thread Paul mentioned that Cinder’s Signal class is not thread-safe. If that’s still the case, how would one go forward with emitting events from another thread?

My case is that I have a thread running a loop in which a hardware device (NFC Reader) is constantly checked and when a new tag is found, I want to emit a signal on the main thread for the user to work with. Something like what ofNotifyEvent does on Openframeworks by using the Poco lib under the hood.

If that’s not doable with Cinder’s signals, I guess what I can do is to have an update method in my main thread in which an atomic<bool> is checked and if it was triggered on the secondary thread, then I emit a signal in update on the main thread. Does that sound OK or is there another way to achieve this?

You might be able to use the main thread’s io_service for things like this.

void YourClass::YourThreadedFunction()
{
    if ( someCondition )
    {
        app::App::get()->dispatchAsync ( [=] { yourSignal.emit(); } );
    }
}

This will cause your signal to be emitted on the next App::update() on the main thread. You may still need to be careful about when adding new connections to the signal but these tend to happen only once at startup so you can just make sure everybody’s listening before you kick off the thread.

Thanks a bunch @lithium. I was aware of the io_service in Cinder through using asio but had no idea about dispatchAsync(). I searched the discourse and found this other thread with you yourself explaining it which helped a lot.

To resolve the issue of adding connections mid-program, can I maybe use an std::atomic<Signal> or something similar? or a mutex? or will that not work?

Also do you know if I connect events to Cinder’s main update signal, do they happen before each update or after the update before the draw? Is there a way to set the order of how these events are called? Sorry this is not a threading question per se but since the workaround is to have an update event in my class that is connected to Cinder’s, I thought I’d ask. Thanks!

To resolve the issue of adding connections mid-program, can I maybe use an std::atomic<Signal> or something similar? or a mutex ? or will that not work?

That won’t fix the thread safety, but since you’re only ever using the signal on the main thread now you can just use it like normal and there shouldn’t be a problem. I’m not sure if constantly adding and removing signals in general is a good idea though, maybe there’s another way to attack your problem that kills both birds with one stone?

Also do you know if I connect events to Cinder’s main update signal, do they happen before each update or after the update before the draw ? Is there a way to set the order of how these events are called? Sorry this is not a threading question per se but since the workaround is to have an update event in my class that is connected to Cinder’s, I thought I’d ask. Thanks!

Looking at AppBase.cpp:216 it looks like the signals are fired immediately prior to calling the App's overriden update method. To handle ordering, the Signal::connect method accepts a priority, but i can never remember if higher or lower means first or last.

void YourApp::setup()
{
    static auto s0 = getSignalUpdate().connect ( [=] { std::cout << "First, (or maybe last?)\n"; }, 0 );
    static auto s1 = getSignalUpdate().connect ( [=] { std::cout << "Last, (or maybe first?)\n"; }, 1 );
}

void YourApp::update()
{
    std::cout << "I'm definitely last\n";
}
1 Like

That won’t fix the thread safety, but since you’re only ever using the signal on the main thread now you can just use it like normal and there shouldn’t be a problem.

That’s true, thanks for the clarification.

I’m not sure if constantly adding and removing signals in general is a good idea though, maybe there’s another way to attack your problem that kills both birds with one stone?

I really don’t think the case of constantly connecting and disconnecting signals will happen often in my case. I was just looking to make the block safer and more accessible I guess :slight_smile:

Looking at AppBase.cpp:216 it looks like the signals are fired immediately prior to calling the App 's overriden update method. To handle ordering, the Signal::connect method accepts a priority, but i can never remember if higher or lower means first or last.

Thanks for the tip. It’s exactly what I was looking for. In regards to how the priority parameter affects the order’s chain, I found this helpful note in Signals.h which explains that in specific:

You can control the order in which callbacks are connected by providing a priority int as the first argument to connect(). A higher priority means the callback will happen sooner in the emission chain. Callbacks within the same emission group will be called according to the order they were connected, first in first out. By default all connected slots will be assigned to the priority group 0, e.g. you call connect() with no priority argument. This also means by default, when no priority groups are used then whatever callback is connected first to a signal will be called first.`