Wait(-1) and notify


#1

Hello, Juce-y people. I’m bringing up a tiny worry I’ve had since I started using Juce.

Most of my “polling” threads are in fact event-driven, and what I’d really love to do is this:

[code]// In my worker thread:
while (!dataAvailable())
wait(-1);

// In another thread.
workerThread->notify();[/code]

The trouble is that there’s a built-in race condition in this construct - because the notify might occur between checking the condition and the wait(-1).

Java gets around this problem by requiring you to lock the item (in this case, it would be the thread) before you wait for notification or notify. Behind the scenes, the JVM atomically removes the lock and puts you into the wait state, so there’s no possibility of a race condition.

It seems to be that this could be done with Juce by having, ugh, a single global “lock lock”. (I’m not proposing changing the existing wait, of course, but creating a new “lockedWait”.) While the idea of another global lock is somewhat repugnant, since it’s always the last lock to be taken it can’t ever be responsible for a deadlock.

So behind the scenes, when you get a “lockedWait”, you take the “lock lock”, unlock the locked object, call wait, and then release the “lock lock”. This prevents the race condition, but the one thing that can’t be done with current juce is releasing the lock AFTER the wait…

It’d be really nice to have this. As it is, I have all my threads spinning (usually wait(40)) even though I’m not sure if I’ve ever even seen this race condition once.


#2

Why not do it like this instead (in the consumer thread):

while (shouldLoop)
{
    if (wait(100))
    {
        consumeData();
    }
}

There’s no race condition then. When notify is called, wait will exit with true and data is consumed.


#3

That’s what I’m in fact doing, sorry if I wasn’t clear!

But why should I have to spin or poll? Many other languages and locking systems, certainly Java, allow you to wait without this race condition. I have no idea how much crap is paged back into active memory when my inactive thread wakes up.

And consider that I have to test to see if anything has really happened - and in one case that test is somewhat expensive and might wake up your CD player (though I think I fixed that problem).

It’d be so much nicer if the thread was only woken up by an actual notification, or shutdown.

By the way, there’s an issue with your code snippet: when you exit the wait, it might be that you were notified because shouldLoop has become false - probably it’s time to shut your thread down! - so you shouldn’t try to consume data in that case. Better might be:

while (shouldLoop()) { if (hasData()) consumeData(); else wait(100); }which guarantees that you check shouldLoop() after each significant operation.


#4

What race condition ? The wait is stopped as soon as you have data, and then it is consumed ? Please explain wherein the race condition occurs ? Bear in mind that a race condition involves that something can go wrong. I don’t see it here.

Ok, I fail to see why it would be so much nicer. The expiration of the wait is only there to allow the thread to shutdown. But Ok, if there was a way to wait for multiple events with wait (like WaitForMultipleObjects on Windows) it would be a bit cleaner, but not much.

Yes, true, but normally that would be handled in the consumeData function which should of course not consume anything if there is nothing to consume :slight_smile:


#5

What race condition ? The wait is stopped as soon as you have data, and then it is consumed ? Please explain wherein the race condition occurs ? Bear in mind that a race condition involves that something can go wrong. I don’t see it here.

There is no race condition in the code with wait(100) - that code works fine and I use something like that.

And I don’t need to wait for multiple events in a thread - that’s not my issue. My threads are more or less classic consumer/producer threads, very uniform.

What I do want to do is to wait without spinning - as you can do in most other language systems. I’d like this code snippet to be correct:while (shouldLoop()) { if (hasData()) consumeData(); else wait(-1); } where the producer thread notifies the consumer thread in order to terminate the wait(-1).

Unfortunately, that code snippet does have a race condition in it - if the producer thread happens to call notify() between the time that hasData() returns false and the time you finish the wait(-1) then you might wait forever. When I first started this, and was admittedly new to Juce, it seemed to me that wait(-1)/notify constructs were not, in fact, 100% reliable, though I haven’t gone back to it now I know what I’m doing.

This is a well-known issue, important enough that Java, e.g., deals with this as part of the fundamental language - you literally can’t wait or notify on a Java object (in Java, all objects are also locks) unless you’ve locked it first.

Yes, calling wait(SPIN_PERIOD) works, but it’s suboptimal. I have no idea what the cost of waking a thread is, but it can’t be zero, and perhaps it forces a lot of memory to page in. And now I have to decide what to set SPIN_PERIOD to be - if too long, I might occasionally encounter this problem, if too short it might really consume something.

It’s more hair, more to think about, I just want to say wait(-1) and only wake my thread when it’s either really got data, or it’s shutting down.


#6

Ah, that’s what I thought you were thinking. But btw what exactly do you mean by “finish the wait(-1)”?? You won’t finish the wait(-1) unless something singnals it ?

Anyway it is incorrect. No matter where you call notify() (i.e. in the producer thread) it will set the event to signaled state, which means that wait(-1) will return true as soon as you call it (and reset the event). There is no race condition.