Locking, threads, notification and race conditions


#1

I’m now wondering if there’s a race condition intrinsic to the thread model as it stands now.

My threaded code has actually been working perfectly well :smiley: but I believe my worrying about these minor details before they happen will make my final code reliable.

Correct me if I’m wrong, but if you wait() and you have a lock, you keep that lock…? (So you probably shouldn’t do that.)

Given this, I don’t see a thread-safe way to wait until a flag turns true.

My best try would be:

  1. Take the lock.
  2. Check if the flag is true.
  3. Drop the lock.
  4. If the flag was false, wait(-1) until I get notified, then go to 1.

and in the other thread:

  1. Take the lock.
  2. Set the flag to true.
  3. Drop the lock.
  4. Call notify().

This almost works… unfortunately, there’s a race condition between 3 and 4, where occasionally notify() might get called from another thread before I wait() - resulting in loss-of-liveness for that thread.

Java, for example, handles this by automatically relieving you of your locks as you sleep or wait.

Such a change is clearly impossible in JUCE as you’d potentially break a lot of clients, and in subtle ways to boot; but is there some way to use any of the primitives to get a truly thread-safe “wait for flag” code fragment? I’m looking at the “threads” subdirectory but nothing seems to do it.

(In case you’re thinking that “this would never happen” - if you run your code with a race condition enough, it will crop up! And in this application, the loop occurs during the production of digital audio, which means it will be run millions of times…)

The workaround is that your loop spins instead of waiting - you call wait(10), say, instead of wait(-1). For all sorts of reasons, however, it’s bad practice to spin your threads this way (for example, because it can result in disk thrashing because the thread’s memory space keeps getting brought back into real memory, not to mention potential hiccups from the occasional random 10ms delay thrown into your execution wall clock…)

My thinking on this is that there is no way to do it with what you have there - but that adding a single class to do just this, waiting for a locked condition flag, would fix a lot of possible cases neatly.


#2

True, doing a WaitableEvent::wait() has no connection with any locks that you may be holding (and has no way of finding out what they are, so couldn’t possibly do so in its current form).

I guess to What you’d need would be something like WaitableEvent::wait (CriticalSection& criticalSectionToReleaseWhileWaiting), which would release and re-gain the lock safely…

Maybe what you need could be done by:

[code]ScopedLock sl (lock);

…do stuff while locked…

for (;:wink:
{
lock.unlock();
someEvent.wait (-1);

if (lock.tryEnter())
    break;

}

…do more stuff while locked…
[/code]

?


#3

I think it suffers from the same race condition between the time you drop the lock and the time you wait… :frowning:


#4

Hmm, yes. I guess it’d need to use some native OS calls to get the same functionality then.


#5

Yes.

This stuff is tricky to get right - it’s the sort of thing where you take hours to write half-a-dozen lines and then worry about it late at night. And I already have my workaround (I prefer never to whine without having a solution ready to go).

That said, the interface to this would be tiny. You’d need two classes, one class to keep the flag in, and another to behave like a scoped lock, and you’d write:

[code]AtomicFlag atomicFlag(false);

while (!threadShouldExit()) {
ScopedAtomicFlagLock l(&atomicFlag, true, timeToWait);
if (l.success()) {
// Do stuff, knowing that the atomicFlag is true and you have a lock on it.
break;
} else {
// Either timeToWait expired, or we got notified. We know that the flag was false when we left
// the constructor of the Lock but we don’t have a lock on atomicFlag.
}
}[/code]
As I said, I don’t urgently need this, but I’d certainly be willing to help with e.g. spec’ing it out or reviewing the code if you wanted to give it a try at some point in the future - and figured I should post this so it was “on the record” for the next guy.

Thanks as always for your hard work!


#6

Thanks Tom, that sounds like a neat class.


#7

Aren’t you talking about Condition (and the Windows’s equivalent Event) ?
Don’t re-invent the wheel, there is already WaitableEvent in juce.

Your first thread should do:
if (event1.wait())
{
take_lock
// process (whatever you want)
event2.signal();
event1.reset();
release_lock
}

The second/other thread should do
while (1) {
if (lock_tryenter)
{
event1.signal();
event2.reset();
// process
release_lock
event2.wait(infinite) // if you care about result of thread 1
}
else
{
event2.wait() // It’s set by thread 1 when it’s done processing
}
}