Is it safe to call getActiveEditor() in processBlock?

Hello,
is it safe to check in processBlock:
if (getActiveEditor() != nullptr)

Please notice that if it is true then I use pointer to object which is defined in my AudioProcessorEditor constructor. (Don’t worry, I don’t use any message thread in processBlock, I’ve just have FFT analyser component in editor, and in processBlock I only need to feed it with the buffer).

Of course I also could check if( myFftComponent != nullptr ) but I am still afraid if it’s safe, and what happen if during processBlock myFftComponent would be deleted by closing editor window?

That’s why I wonder maybe I should define myFftComponent in AudioProcessor constructor, and then use it inside processBlock only if getActiveEditor() is not nullptr. But there is still question if it’s safe?

For any help great thanks in advance.

Best Regards.

I find it’s best for the audio processor to have no knowledge of the UI, given that it’s never guaranteed to exist. So instead of the processor sending data to the editor, the editor should instead ask the processor for the data it needs as-and-when it needs it.

Put simply, in your process block you’d simply write the data to some buffer in the processor and then provide a getter for that data for the editor to call. You’ll need to be careful with threading, since the processor will likely be writing on the audio thread while the editor is reading from the message thread.

There’s various ways of making that process realtime-thread-safe depending on the data you need to share. Check out @dave96 and @fr810’s Real-time 101 talk.

To handle that, what I usually do is have two buffers, whose pointers are stored in an array, and have an atomic int index into that array in the processor component, with getters to fetch the pointer to either the buffer that the processor can work on or the buffer that the editor can access. Then, to swap which is which, I just subtract the processor’s index from 1 and set it atomically, so that any future accesses now apply to the other buffer.

That’s pretty much the right approach but you may also need to handle cases where you get multiple read operations between each write operation, in which case you’ll likely be reading old data and in the wrong order.

A solution to this is given in the Real-time 101 talk, under Double Buffering:

https://drowaudio.github.io/presentations/ADC%202019%20-%20Real-time%20101/Real-time%20101/assets/player/KeynoteDHTMLPlayer.html#158

Indeed what you do is to add a FIFO (that’s what it’s called, first in - first out queue) to your processor and you copy all samples out of processBlock.
Your editor can set an atomic flag in the processor if it is visible or if the data can be discarded (i.e. not needed to copy). Accessing that atomic flag from both threads is safe.
You also need a way to skip if the FIFO is full (e.g. it is not fast enough consumed) that way you don’t block the audio thread.

Accessing getActiveEditor() is never safe. The best you can do is checking if it is nullptr or not (even that has a risk, but a low one). But you must not use the pointer, as it can be freed during your operation.

Great thanks for all your answers.

I also wonder maybe I could solve my issues with lambda (for example std::function<void()>).

I wonder if it is safe to call such lambda inside the processBlock. What happen if I change my lambda to other function while the old one is executing inside processBlock. Is it safe or not?

Best Regards

This is a great example of a “use after free” error.

When you overwrite an std::function, the captured context will be destructed. Not only is that not real time safe (if the context is larger than a pointer, it will be on the heap) but it also means the executing function logic will be referring to data that has been freed.

In general, it is never safe to read data that is being mutated elsewhere. The cases where it is safe either guarantee the reader or writer have unique access to the shared data (like a lock/mutex) or guaranteeing that your reader cannot access data that is being overwritten (like a fifo, where the reader should never have the same index/pointer to written data as the writer).