Async signalling from Audio Processor to FIFO reader (Editor) (Probably a question for TheVinn and co)


#1

Hi All, 

 

I'm wondering if I can start of a bit of a discussion. 

 

I'm in the process of writing a little example plugin which I think will be useful for a few folks using JUCE for drawing Filter Displays utilising Vitrual Analogue filter transfer functions. 

Whilst doing this I've thought about adding metering etc. I understand the basic concepts behind lock-free FIFO's (single reader and writer) and am slowly building on my knowledge of lock free concurrent data structures and message passing.

However the only method that I have been capable of so far is updating VU style meters with an FIFO which is polled / read by the editor within a timer call back. Various forum threads and sources have stated what a mess polling is and people like TheVinn have explained the proper approach of an aynchronous singalling technique along with a lock free queue for data tansfer. 

My question is can anyone shed some light on an approch for sending an asynchronous signal / update message between threads ? This will most likely need to be done from the processBlock in some manner. I know that the AsyncUpdater class invoked malloc so this is a no go for correct lock-free code(VFLib does deal with this issue I believe). Basically I need to send a change message of some description (Asynchronously without any memory allocation) to wake up a function in my Editor which will read data from the lock-free FIFO being filled by the processBlock and use this data to update meters etc.

My only approach may well be to go with VFLib and use the thread queues but I dont fully understand the library yet and would like to get my head around different techniques for inter thread comms / async signalling before using any libraries blindly. 

 

Any help / words of wisdom on this topic woud be greatly appreciated. I appreciate the key word of wisdom may well be "Use a library, don't even go there because correct lock-free concurrency is a climb down the coding rabbit hole". Herb Stutter and many others have hinted at this....

 

Cheers 


#2

One problem with plugin programming is that hosts can call callbacks from any thread they want, so if you use single-writer, single reader Fifo this is might be a problem. Also if you use the processBlock() as single point where you process the queue, what do you do if the hosts isn't calling it, for example if the playback is stopped, but you need to update you internal data-structure which is important for your GUI representation. 

For easy plugins you might just choose a very flat data-structure which atomic values that doesn't need to be consistent, but what you do with complex data. I always come to the point, where i need to use CriticalSections, to handle all possible circumstances host behavior can produce. 

 


#3

Hi Chkn, 

 

Thanks for the response. 

 

Im not sure I fully understand the implications of the host not calling processBlock/play back as you have explained it. If the FIFO is used for the purposes of VU meter for example and the processBlock function is the only method "writing" into the FIFO how would it produce incorrect behaviour when play back had stopped ? Would the FIFO just not stop being filled / written into and thus there is no sample data at the read end regardless of what thread processBlock() is being called on? (resulting in a 0 level/display on a vu meter as nothing is read to populate). 

Would you suggest setting up a worker thread of some description which deals with the FIFO ? 

 

My real question is how to handle async updates / notifications. 

 

In other words telling my GUI / Worker thread that something needs to be done asynchornously from or right after the processBlock call without incurring malloc , waking up the thread that will deal with consuming the data from the FIFO or whatever other source / method I should end up choosing as an alternative to just polling some buffer with a timer or something. 

 

I've been looking into the use of futures and lambda's etc as an approach to this but I'm still very much in the learning stage with these concepts. 

 

Any further discussions / help from yourself and others more learned in multi-threading would be most appreciated. 

 

Regards


#4

Hey chkn, can you clarify your first sentence and describe the scenario you think might be a problem?


#5

Any chance any of the JUCE team can offer words of wisdom on this topic? 

 

Am I attempting a mammoth task where  JUCE 4 is about to unleash a undoubtedly far better and much more mighty solution to async/cross thread message passing that is lock and malloc free ? (I have no idea whether this is in the works or not...)

 

Failing that does anyone have any useful resources they can point to / words of advice ? 

 

 

Cheers


#6

Well, JUCE 4 is indeed going to provide some really good utility classes for this.. We needed a good solution at ROLI for Equator, so that's what we'll be releasing with JUCE. Whether it'll be exactly right for you or not, I don't know.


#7

Thanks Jules, 

 

I thought this may well be the case.

Perhaps it'll be better to devote my time to other bits of development and hold out for JUCE 4 so I can study your approach. Thanks for the heads up. 

That being said if anyone does have any further advice / thoughts on this topic I'd be interested to hear them and keep the thread / discussion going as I think it's an important topic for plugin developers and JUCE users in general. 

 

Josh 


#8

Bumping up this thread one last time incase any other threading guru's have something to add in regarde to async message passing options. 

 

Cheers


#9

I’m exactly encountering the same use-case. Nothing big actually, I just want to have asynchronous updates of noteOns noteOffs and some info on the playback’s current state triggered from the processing thread to something else (could be GUI or not, just something else).

Is there anything new I could use to solve my problem?

Thanks!


#10

If memory serves me rightly I think it might me worth looking at the WaitableEvent class.

Jules confirmed some stuff about the class being fully asynch in an old thread. Maybe you could have a worker thread or something that gets triggered ?

I’m skim reading this on my lunch though so I may be well off.

Josh


#11

WaitableEvent.Signal( ) malloc?

Yes, it’s safe, it won’t block at all.

(The other thread says I’ll bump it, so I’ll just post it here…)

This is not necessarily true; it’s an implementation detail of the operating system. For a thread to signal others, it (probably) has to enter kernel mode and schedule threads. This is probably not an atomic operation, so I would expect a mutex somewhere along the line.

On POSIX (OS X & linux) WaitableEvent::signal() acquires a mutex as well, protecting the condition variable. As we can see from this glibc implementation, signaling a pthread_cond_t acquires another mutex to protect the routine, as well as some other dirty stuff (futexes) for signaling the kernel:
https://fossies.org/dox/glibc-2.23/nptl_2pthread__cond__signal_8c_source.html
So thats 3 nested potentially blocking routines.

Semaphores also use a mutex under the hood when you signal them, in every unix-y os I’ve checked out. For Windows, we obviously cannot know.

I spend some time looking at this, and as far as I can see, there’s no existing primitive for signaling another thread in any kind of way (as in scheduling), that promises not to block the signalee, in any operating system I’ve seen.

The only safe system is the Timer / background thread polling and posting notifications.


#12

Hi Mayae,

I did wonder…

I remember hints of atomic singling being mentioned for C++ 17 standard etc. If I remember rightly when I was looking into fully asynchronous signalling I pretty much came to the conclusion that it is the bane of lock free concurrency…

Polling may well be the safest option as you say.