Is it safe to use std::mutex::try_lock or ScopedTryLock on the realtime thread?

Short question, does it take a predictable amount of time for a try lock call on some mutex to return? I’m lacking a bit of understanding of the level at which mutexes are managed, will this cause some system call with unpredictable return time depending on the system load or will this return immediately in any case as this is no real system call?

Background: I have some buffers which are filled with audio samples that might get reallocated because of external events at any time. I simply want to ignore samples that are coming in while reallocating so I’m wrapping the access in a try lock on the audio thread side and use a normal lock on the side that initiates the reallocation from another thread. This works like a charm on my computer but I just want to be sure if this should be safe in every case?

(Sorry it is a 3 years old thread, but i have exactly the same question.)

In the Timur Doumler’s blog it is said that it is NOT safe due to mutex_unlock. But i read a paper about that approach few time ago that doesn’t refer to this problem (sorry in french) < https://hal.archives-ouvertes.fr/hal-01791422/document >.

I would like to get POV about that:

  1. Is it a theoretical case, or did people really experienced such blocking while unlocking the mutex?
  2. When replacing the mutex by a spin lock, what’s happen if the DSP thread is preempted with the lock aquired (especially with ONE core)?

FYI : few more thoughts in another Timur Doumler’s talk (at 10 minutes / https://www.youtube.com/watch?v=zrWYJ6FdOFQ).

Future reader should read that also: :grinning:

Timur has shown that std::mutex::try_lock is not safe on the audio thread, but the OP had also asked about juce::ScopedTryLock. It seems to me that this one might be safe if you use it in conjunction with juce::SpinLock, because there is no possibility of it notifying another thread. Would something like this be safe to do on the audio thread?

juce::SpinLock::ScopedTryLockType l (mySpinLock);

    if (l.isLocked())
        // do stuff...

If the lock isn’t acquired, it just does nothing and tries again next time.

Is this approach safe for the audio thread?

SpinLock is based on a simple Atomic. The ScopedTryLock is lock-free, since it will only call to a simple atomic function on a single variable. So I assume its 100% safe to use it in the audio thread.

AFAIK such trylock approach with a spinlock is safe. But is it good? For very low contention, and for short code to protect i guess that it is.

But with more contention it could be a very bad solution (CPU/battery cost, risk of starvation). Frankly, more i learn about that, less i know what to use.

I suppose that thread preemption (i was caring above in the topic) never happens, since i never heard people talking about it! Probably that the OS will never preempt a high priority thread before the low priority threads.

In my code i kept the std::mutex_try_lock for now. The Timur Doumler’s trick is not very portable (and not fully tested). I’ll probably make my own version with nano_sleep instead of __asm__ __volatile__ ("pause" ::: "memory") and such. I’m afraid to make experiments with ARM instructions and i really want the low priority thread to wait (instead of polling and consuming CPU/battery power).

I would like to know how it is done in production codes (real softwares not youtube/forums talks). :grin:

Well, usually in my real software I do atomic exchanges and other operations instead using try_lock(s), but I always have it if for some reason I cannot achieve what I want with atomic operations

1 Like

I found this article helpful when designing lock free mechanisms for plugins:
https://preshing.com/20120913/acquire-and-release-semantics/

Moreover JUCE itself contains severals SpinLock! :grinning_face_with_smiling_eyes:

But It seems that just one SpinLock::ScopedTryLockType is used (in juce_convolution.cpp).

@dave-elton Yes. I learned a lot reading Preshing blog. I read it again and again (each time before to go back into RT/lock-free problems).

2 Likes

The kind of SpinLock exposed in Timur Doumler’s blog (exponential back-off) is interesting. It would be awesome to have one in JUCE (or elsewhere) fully tested on various platform. Isn’t it something that JUCE community could do?

I tried to make one but TBH i’m not an expert in assembly (i just stoled things from various projects), so i don’t want to publish code that would burn the computer of volunteers. :sweat_smile:

Pretty astonished that after so much time searching i’m not able to get a definitive answer.

The link (provided in related thread below) is pretty interesting.

https://www.realworldtech.com/forum/?threadid=189711&curpostid=189723

(Grumble mode activated) :weary:

Frankly, what’s the meaning of all the C++ evolution, if we can not have guarantee about such thing?

Don’t use locks in a realtime context.

Is that definitive enough? :wink:

Paraphrasing Timur (i think it was) said at ADC 21, that spinlocks may be real-time safe, but they EAT up CPU (and battery) while spinning. In general this strategy of burning CPU results in poor performance of the DAW in general because you are needlessly consuming CPU cycles that the DAW could be using more productively (e.g. repainting the UI at a high framerate).

Secondly, considering that lock-free mechanisms are available and widely used (e.g. message-passing), my opinion is that we could all save a lot of wasted time and effort if we stopped reaching for locks as the default ‘go to’ for solving our concurrency challenges.

full disclaimer: I did use a std:condition_variable in a streaming disk sampler, that is in use in a major companies products, and it seems to work OK. (condition variables take a lock for a very short time).

Frankly, what’s the meaning of all the C++ evolution, if we can not have guarantee about such thing?

C++ does not dictate what the OS can or cannot provide. If the OS can’t provide any guarantee in regards to locking, you can’t blame C++ for that.

5 Likes

After reading few papers (e.g. this) i would say: “Don’t use trylocks in a realtime context”.
Thanks @JeffMcClintock and others!
Of course my 2 cents.

For future readers:

  1. Link i posted above is dead / a new one is:

< https://kola.opus.hbz-nrw.de/frontdoor/deliver/index/docId/2140/file/Zuepke_thesis_pdfa-1.pdf >

  1. Timur Doumler published the spin lock implementation there:
  1. Spin locks / preemption:
1 Like