Expander - Thread safe?

Hi there,

I was reading this helpful post Module expanders tutorial? , following the example by @Vortico here https://github.com/VCVRack/Rack/issues/1204#issuecomment-469797678

But not sure if the whole is really thread safe. Edge case: what if I read from a different thread (which is “slow”):

float *message = (float*) leftExpander.consumerMessage;

and the swap (caused by messageFlipRequested later in the Engine, on the producer side) occurs twice before I currently read the whole *message on the “slow” thread?

Is it correct to state that in this case I could read parts of the *message written in different process()'s call?

Such as:

  1. audio thread write buffer A
  2. gui thread take the pointer of A (message)
  3. swap occurs (=>B)
  4. audio thread write to buffer B
  5. gui thread read 1/2 of the arrays of A and than will be stopped by other “main thread”
  6. swap happens again (=>A)
  7. audio thread write again to A
  8. gui thread will be reactivated, reading the other half of the array A with new values

Isn’t this a problem?

Thanks

You are guaranteed to have exclusive access to your expander message during Module::process() and nowhere else. If you want to use a thread to send expander messages, you’ll need to send data to Module in a thread-safe way, and then copy that data to the producerMessage in Module::process(). For receiving expander messages on another thread, do the reverse.

Ok, so the mechanism is not thread-safe itself.

I would like to replicate the same mechanism for sharing and sync data from audio to gui thread, but it will fail without an appropriate management of concurrency.

I need to investigate more, finding a proper way :wink:

Whenever I want to send „messages“ from the DSP thread to the GUI thread I use dsp::RingBuffer which is thread-safe for single producer and single consumer. It also helps for enqueuing multiple messages as the GUI runs „slower“.

2 Likes

That is a classic solution. Sounds like a very good idea.