I’m fairly new to JUCE. I’ve implemented atomics for simple types like ints, floats and bools.
But I have a juce::String variable in the Processor that I want to read from the Editor.
I’ve read about Mutex, but apparently that might hold up the audio thread. Is there a safe way you would access?
(I’m just reading the string, not writing to it.)
Its presetName so its assigned a default value in the constructor, and its manipulated in setStateInformation() and then every time the user changes the preset.
So in theory not often, but I debugged it, and I found out that setStateInformation() which I thought was just called once when the DAW reloads the plugin, is actually being called every time I change a slider!
I went with buffering the presetName string into another string presetNameForEditor (both of these in the Processor) and then setting an atomic flag, then in the Editor timercallback() checking for that flag and then handling the presetNameForEditor.
a string is not in itself atomic. So the only safe method is to pass a copy to the editor.
One fairly easy method is to use a lock-free queue. The processor places the string on the queue, and the editor polls the queue via a timer, and removes the string from the queue.
One possible implementation is to treat the queue as simply a pipe that passes bytes safely from the processor to the editor. You can write some basic methods to push and pull values.
you end up with code that looks something like…
auto que = MessageQueToDsp();
// this statement wraps the queue as a c++ 'stream'. And pushes basic 'header' information on to it. (what type of message, what object it's going to)
my_msg_que_output_stream strm( que, parameterID, "ppc" ); // queue, parameter ID, type-of-message
strm << some_string;
This might sound like a bit of work, but once you have that type of mechanism, you can use it for all sorts of things.
like a thread-safe way to display a dialog box with some text to the user from the real-time thread…
int32_t ug_plugin3Base::sendMessageToGui( int32_t id, std::string message )
auto queue = AudioMaster()->getShell()->MessageQueToGui();
my_msg_que_output_stream strm( queue, Handle(), "dlg");
strm << message;
strm.Send(); // a kind of 'commit' that makes all the previous data 'visible' to the other thread at once.
That should be perfectly fine! I just want to raise a few security concerns, that are really a bit shooting into darkness, because I haven’t seen your code.
Are you sure that setStateInformation is called so regularly? It doesn’t seem to make much sense from the DAW to tell your plugin to play/process with new parameters/data, when you touch a Slider in your plugins UI. How would the DAW know about that anyway? Maybe you could elaborate on that a little more?
You won’t run in any hassle concerning the audio thread or mutex stuff or anything just by those preset-names, because the audio part of your application (hopefully) is not working with them/knowing about them. (Background: working with Strings in the audio thread is always a bit of a two edged sword. It might make it more easy every where else in your code, but 1) your audio thread doesn’t need to know a presets name to be able to use preset values and 2) it is very hard to work with strings without making system calls like malloc and free. If you can’t guarantee that the strings are small enough to fit into the small buffer each String object contains – which is not really documented, and you cannot test for it – you are better of to bring a lot of miles between strings and audio threads.)
There have been some discussion, that setStateInformation might not be called on the MessageThread (the one running your UI) but by some arbitrary thread the DAW decides on (not an audio thread of course!). What I would therefore recommend is defiantly adding the assert by @JussiNeuralDSP before you actually write the juce::String with the presetName from the MemoryBlock in what ever way you implement it, or (if you don’t want to have the hassle) copy the MemoryBlock into a lambda or other callable object and pass that off to the MessageQueue to be executed on the message thread. As that might be a costly operation, check if you are already on the message thread first, and if not, copy. If you are, perform your “load”-operation now.
Often memory location is mixed up with thread:
Just because the variable is a member of your AudioProcessor, it doesn’t mean it is accessed by a different thread.
ONLY the processBlock accesses data from the audio thread. If your variable is not directly or indirectly accessed from processBlock you are safe in terms of realtime constraints.
The setStateInformation and getStateInformation is most of the times accessed from the message thread, in which case you don’t have to do anything. But you are right to be careful, e.g.ProTools uses a dedicated thread for getStateInformation and setStateInformation. The good news is, this is not a realtime thread, so using mutex or other kind of locks are ok. Theoretically even MessageManagerLock, although I once had problems with that one.
The best way is to just swap pointers, which is impossible with plain members. In that case you need to use std::unique_ptr for the string. But you would even need to lock while you are using the name.
The most important: use by copy as much as you can, not by reference.
EDIT: re-reading Rincewinds answer, he said mostly the same… sorry