condition variable - why calling pthread_cond_signal() before calling pthread_cond_wait() is a logical error?
Asked Answered
K

5

29

It's written in POSIX threads tutorial https://computing.llnl.gov/tutorials/pthreads/ that it is a logical error.

my question is why it is a logical error?

In my program i need to use these signals, however i cannot guarantee that there will be a thread that will be in _cond_wait state. I tried to test it and nothing happens. Is this can cause unexpected behavior or worse?

thank you!

Kevin answered 4/4, 2011 at 9:35 Comment(0)
V
40

The answer of blaze comes closest, but is not totally clear:
conditional variables should only be used to signal a change in a condition.

Thread 1 checks a condition. If the condition doesn't meet, he waits on the condition variable until the condition meets. Because the condition is checked first, he shouldn't care whether the condition variable was signaled:

pthread_mutex_lock(&mutex); 
while (!condition)
    pthread_cond_wait(&cond, &mutex); 
pthread_mutex_unlock(&mutex);

Thread 2 changes the condition and signals the change via the condition variable. He doesn't care whether threads are waiting or not:

pthread_mutex_lock(&mutex); 
changeCondition(); 
pthread_mutex_unlock(&mutex); 
pthread_cond_signal(&cond)

The bottom line is: the communication is done via some condition. A condition variable only wakes up waiting threads so they can check the condition.

Examples for conditions:

  • Queue is not empty, so an entry can be taken from the queue
  • A boolean flag is set, so the thread wait s until the other thread signal it's okay to continue
  • some bits in a bitset are set, so the waiting thread can handle the corresponding events

see also pthread example

Vibrant answered 4/4, 2011 at 12:28 Comment(16)
In the second code, I think pthread_cond_signal(&cond) should be between the mutex locksMesocarp
@Duke: no it shouldn't. I used to think this too, but there is no reason why it should and when the receiving side has same or higher priority, you have 2 extra context switches because the receiving side has to block until the sender unlocks the mutex.Vibrant
@Duke: see also this article from Anthony Williams: justsoftwaresolutions.co.uk/threading/…Vibrant
@stefaanv, your explanation is the shortest and clearest I have found on the Internet!Retroaction
@Retroaction Thanks, mind you that more modern constructs (e.g. c++11 threading) help you with using condition variables this way.Vibrant
@Vibrant Your example does not make sense to me. If thread 1 happens to lock the mutex first, then thread 2 wont be able to progress and will cause a deadlock. The problem is thread 1 is waiting for thread 2 to call changeCondition() and thread 2 is being blocked and the only way for thread 1 to unlock the mutex is if the condition is true.Underhanded
@LuisAverhoff: thread 1 will almost always be in the pthread_cond_wait() call where the mutex is unlocked. That's why the mutex has to be passed to this call. So thread 2 only needs to wait when thread 1 is actually checking the condition. This is what the mutex is for: protecting the resources that make up the condition..Vibrant
@LuisAverhoff: By the way: this is not an example, this should be the default of how to work with condition variables in pthread.Vibrant
Thanks, this explains the behavior I was debugging in my app: my startThread() function exited immediately when the thread was started, but had not yet entered pthread_cond_wait. If I called stopThread() immediately and inside it I set predicate=true; pthread_cond_signal(), then my thread did not wake up because the signal came too early. Now I will know that it's by design and I should not set cond_signal when I'm not sure if a thread is waiting for it. A workaround was simple - check for the predicate once in the thread loop before entering pthread_cond_wait.Bull
Thanks! I was wondering what the recommended way was to ensure that the signal wan't sent too early and missed. This is it! I wonder why the signal is so ephemeral... Why doesn't "cond" have some state that can be checked at anytime?Artless
@Vibrant How to understand "conditional variables should only be used to signal a change in a condition."? What's in a condition?Urogenous
@john: specific to this context, the condition is what the receiving thread is waiting for to be fulfilled, hence in the "template"-code the while (!condition). A nice example is the thread-shared queue where the receiving thread waits for the queue to be non-empty so it can consume the next entry in the queue.Vibrant
BY the way, the information given by @Vibrant is not correct. Many people thoguht it was back in 2013. You should always signal the condition variable before unlocking the mutex for better performance. If you unlock and then signal, either the unlock or the signal could wake a thread, resulting in extra work. If you signal while still holding the lock, the signal cannot possibly make any thread ready-to-run (since they need the mutex to make forward progress) and so an expensive check is avoided by good implementations.Lavinalavine
@DavidSchwartz The information is based on what we found on a setup where the waiting thread had higher priority and we found out that the waiting thread was woken up by the signal only to sleep again on the mutex locked, so that gave extra work. When threads don't have higher priority, unlocking doesn't have to mean waking up the other threads. So it was not just that people thought that in 2013. Things might have changed in the mean time.Vibrant
@Vibrant Why would the implementation wake up a thread that's not ready-to-run? It sounds like a poor implementation. It knows that any thread blocked on a condition variable cannot make forward progress until it can acquire the associated mutex and it knows the mutex is locked, so why would it wake a thread it knows cannot make forward progress? I'd say to look for better implementations. (But definitely don't design your code around limitations of poor implementations.)Lavinalavine
It might have been a poor implementation (pthread, gcc), but that's what we saw, it's not just what the people thought.Vibrant
A
8

My 2 cents: I do not know the side effects of calling *pthread_cond_signal()* when no thread has been blocked calling *pthread_cond_wait()*. This is really an implementation detail What I think is that, if your threading/timimg model do not guarantee the rigth order between wait and signal, probably you should consider a different sync mechanism [like a simple semaphore, for example] when you can signal the semaphore from thread B even if the thread A has nor reached the sync point. When thread A will reach the sync point, it will find the semaphore incremented and will enter the critical session.

Appaloosa answered 4/4, 2011 at 9:58 Comment(2)
+1 for suggesting semaphores, which are often easier to use correctly.Monoploid
I'm really concerned about the timing model. It seems that the programmer having to know about the ordering of actions in autonomous threads is not "the right level of abstraction". I don't like it and I've been avoiding using the signal and wait commands because it seems like its bound to cause an error sooner or later.Artless
H
5

I write my answer because I do not see the one that will calm people. I also stumbled upon that bizarre disturbing warning about “logical error” in that tutorial. Note that there is nothing about this “error” in the POSIX documentation article on pthread_cond_signal. I am sure that this is an unfortunate choice of the term or a plain mistake on part of the author of the tutorial. Their claim may be interpreted as if a process will terminate with an error in this situation or that any program permitting this situation is incorrect. Nothing of the sort is true. Such situations are common. The documentation says that

The pthread_cond_signal() and pthread_cond_broadcast() functions have no effect if there are no threads currently blocked on cond.

So don't worry and be happy.

Hennebery answered 7/12, 2019 at 8:11 Comment(2)
By far the best answer, I think. First, it really answers the "why...error" question, while the accepted and most up voted one does not. Second it is simple and easy to understand.Sabec
@RenaudPacalet very late reply to your comment. My answer indeed doesn't answer the question directly but it refers to https://mcmap.net/q/481182/-condition-variable-why-calling-pthread_cond_signal-before-calling-pthread_cond_wait-is-a-logical-error (answer by blaze) that already answers the question. I think my 'clarification' had some upvotes at the time because it wasn't that obvious how cv's should be used. It was sometimes used as simple barriers where the receiver was just waiting for the signal to continue, with confusing errors as a result. By the way, the tutorial seems to imply that the mutex has to be locked when calling pthread_cond_signal which is also not true.Vibrant
S
3

A condition variable allows one thread to wake another up from a wait. They work only if there is a thread waiting at the moment when you trigger the condition. The way to ensure that this is the case is for the waiting thread to lock a mutex which is linked to the condition, and for the signalling thread to lock that mutex before triggering the condition. In other words, the signalling thread can only lock the mutex and trigger the condition if the other thread had the mutex locked but is now waiting.

I'm most familiar with boost, so I'll use that in this example:

// A shared mutex, global in this case.
boost::mutex myMutex;

// Condition variable
boost::condition_variable myCondition;

void threadProc()
{
    // Lock the mutex while the thread is running.
    boost::mutex::scoped_lock guard( myMutex );

    while( true )
    {
        // Do stuff, then...

        myCondition.wait( guard ); // Unlocks the mutex and waits for a notification.
    }
}

void func()
{
    // Function wants to trigger the other thread. Locks the mutex...
    boost::mutex::scoped_lock guard( myMutex );

    // Since the mutex is locked, we know that the other thread is
    // waiting on the condition variable...
    myCondition.notify_all();
}

To signal a condition variable when there is no corresponding wait is a logical error because nothing will ever receive the signal. Condition variables don't remain in a signalled state.

Shook answered 4/4, 2011 at 9:52 Comment(0)
A
3

If you do not care that this signal will be lost - there is no error. It is only an error if you expect later coming waiting thread to wake from cond_wait() immediately.

Since this is usual use case for pthread_cond, tutorial calls this logical error. But nothing will crash and no unexpected behavior will occur. In normal execution flow cond_signal() still may be issued when there is no threads in cond_wait(): f.e., all readers may be just doing message processing when writer adds another data piece in queue.

Amateurish answered 4/4, 2011 at 10:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.