AudioProcessor::processBlock() and Thread::notify() - is it lock-free?

Simple question, can be used Thread::notify() (to trigger an external thread) inside the AudioProcessor::processBlock()? Or does it perform any lock and it should be forbidden?

Sure, you’re almost certainly safe to call that.

1 Like

Thank you, really appreciated

I’m on macOS - Thread::notify() calls WaitableEvent::signal(), and on a Posix system, this function calls pthread_mutex_lock (&mutex).

Unless macOS supports priority inheritance from realtime threads to normal Posix threads (does it? I don’t know…), I’d rather avoid calling Thread::notify() on a realtime thread/audio callback.

3 Likes

Are there perhaps any good alternatives available that are guaranteed to be lock-free? I’d also like to wake up other threads from audio callback context, but seems dangerous with juce::thread::notify() at least with the posix implementation that contains locks.

I think it does, the mutex is configured with the following attributes:

    pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT);

@myhrman do you know that this is actually a problem? A mutex lock/unlock might take slightly more time to execute than a lock-free implementation but as long as it’s not guarding anything that is unbounded the time is deterministic (resource contention considered).

If I understand correctly, the mutex inside a WaitableEvent::signal() is only locked between entry in to wait/signal and the condition broadcast/wait calls.

If you have a low-priority thread calling wait that happens to sleep between the mutex lock and condition wait, when it is signaled, that thread will wake up as soon as possible to complete.

The main thing to be worried of here is gettimeofday if it results in a system call. However I have a feeling that these have mapped user-space versions in a lot of Linux OSes (and maybe macOS, it would be good to check).

Is there some other performance hit you’re seeing? I’d be interested to know as well how this kind of thing manifests on different OSes.

This quite likely only works within the Posix priority band. It‘s unclear and undocumented whether regular Posix threads will be temporarily elevated to a time-constrained priority.

Cf. https://stackoverflow.com/questions/22634514/os-x-pthread-prio-inherit-mutexes-between-normal-and-real-time-threads

Interesting. It looks like wait_queues might be a better API to use on macOs? https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/KernelProgramming/scheduler/scheduler.html

Or maybe GCD has a slightly higher level API.


Also, if we’re talking about JUCE Audio callbacks, don’t they run on their own thread which I don’t think gets created with THREAD_TIME_CONSTRAINT_POLICY anyway?

I’d like to get to the bottom of this tangent but back to the OP, what’s the use case? How quickly do you need to wake up other threads from within the audio thread and are you then waiting on them to complete or can you just carry on?

@dave96 I don’t know if there is an actual problem with notify() taking too long time to execute. I so far only have a suspicion about this being the case. We did mitigate a similar problem by getting rid of notify(), but it could also be that the root cause was something else in this situation, and that the observed result was just a side-effect.

My use-case is this BTW: on each audio frame, a thread needs to wake up and start asynchronous processing of some data that is passed via lock-free queues from each audio callback. Without the notify(), the thread needs to poll very often for new data, causing quite some CPU load when it is done basically each millisecond. The audio thread does not wait for the external thread to finish or anything, it just triggers it in order to minimize the latency.

In these situations I’m wondering if you could have something like a timer or third “service” thread that simply checks some atomics and then calls notify on the processing thread.
That way, the audio callback would just set one of these atomic flags to mark it as “needing a notification call”.

It’s a bit convoluted though and as you say, will add some extra overhead as you’d need something to poll these flags but it would be lock-free.

1 Like