condition variable - why calling pthread_cond_signal() before calling pthread_cond_wait() is a logical error?
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
What happens if no threads are waiting and condition signal was sent?
Nothing. The signal disappears.
How to set pthread_cond_signal so that the program doesn't hang?
This code is broken.
Firstly, you are modifying variables inside checkServerExists
without locking the mutex. This is undefined behaviour.
If you fix that, then you are also not signalling your condition variable outside the printHello
function. Consequently, once the thread has blocked in the pthread_cond_wait
call, it will only wake due to spurious wakes, and when another printHello
thread signals it. You should call pthread_cond_signal
at the point where you set the condition
flag, rather than in printHello
.
A condition variable is just a notification mechanism. You need to associate a predicate with it, which is the condition being waited for (in your case, condition!=0
). You must ensure that the variables accessed when setting and testing the condition are protected by a mutex, and that this mutex is the one passed to pthread_cond_wait
in order to avoid potential race conditions. When you set the variables to indicate that the sleeping thread should wake, then you call pthread_cond_signal
.
I've modified your code slightly to make it work. In particular, I've put the loop round the pthread_cond_wait
call, and unlocked the mutex before calling pthread_join
so that the printHello
thread can acquire the mutex and proceed. You should never hold a mutex lock across a thread join. This code could still be improved greatly --- amongst other things, it is not exception safe.
#include <pthread.h>
#include <stdio.h>
#include <vector>
#include <string>
FILE *fp;
pthread_mutex_t demoMutex;
pthread_cond_t demoConditionVar;
unsigned short globalThreadIndex = 0;
struct serverInfo
{
unsigned int serverId;
pthread_t threadId;
std :: vector <pthread_t> queue;
};
std :: vector <serverInfo> serverInfoVector;
void * printHello (void* threadId)
{
pthread_t *my_tid = (pthread_t *)threadId;
printf ("\nIn `printHello ()`: thread id %ld\n", pthread_self ());
pthread_mutex_lock (&demoMutex);
unsigned int i = 0;
bool found = false;
while (serverInfoVector.empty())
pthread_cond_wait (&demoConditionVar, &demoMutex);
while ((i < serverInfoVector.size ()) && !found)
{
if (*my_tid == serverInfoVector [i].threadId)
{
found = true;
break;
}
else
i++;
}
if (found)
{
pthread_t writeToFile = pthread_self ();
unsigned short iterate;
for (iterate = 0; iterate < 10000; iterate++)
{
fprintf (fp, " %d %d", iterate, 4);
fprintf (fp, " %lu %lu", writeToFile, sizeof (pthread_t));
if (!serverInfoVector [i].queue.empty ())
{
fprintf (fp, " %c %u", 'A', 1);
fprintf (fp, " %lu %lu", serverInfoVector [i].queue.front (), sizeof (pthread_t));
serverInfoVector [i].queue.pop_back ();
}
fprintf (fp, "\n %lu %u", writeToFile, 1);
}
}
pthread_mutex_unlock (&demoMutex);
pthread_exit (NULL);
}
void checkServerExists (unsigned int serverNumber, std :: string message)
{
unsigned int i = 0;
bool found = false;
pthread_mutex_lock (&demoMutex);
if (serverInfoVector.size () > 0)
{
while ((i <= serverInfoVector.size ()) && (found == false))
{
if (serverNumber == serverInfoVector [i].threadId)
{
found = true;
break;
}
else
i++;
}
}
if (!found)
{
pthread_t newThread [2];
int returnValue;
if ((returnValue = pthread_create (&newThread [globalThreadIndex], NULL, printHello, (void*) &newThread [globalThreadIndex])) != 0)
{
printf ("\nerror: pthread_create failed with error number %d", returnValue);
}
printf ("\nIn checkServerExists ()`: thread id %ld\n", newThread [globalThreadIndex]);
serverInfo obj;
obj.serverId = serverNumber;
obj.threadId = newThread [globalThreadIndex];
obj.queue.push_back (newThread [globalThreadIndex]);
serverInfoVector.push_back (obj);
// Now, since something has been pushed in the vector, it makes sense to wake up the sleeping thread.
// Now, since something has been pushed in the vector, it makes sense to wake up the sleeping thread.
pthread_cond_signal (&demoConditionVar);
pthread_mutex_unlock(&demoMutex);
pthread_join (newThread [globalThreadIndex], NULL);
}
else
{
pthread_mutex_unlock(&demoMutex);
}
}
int main ()
{
fp = fopen ("xyz", "w");
pthread_mutex_init (&demoMutex, NULL);
pthread_cond_init (&demoConditionVar, NULL);
checkServerExists (1, "anisha");
globalThreadIndex++;
checkServerExists (2, "anisha");
return 0;
}
pthread_cond_wait does not unlock the mutex
After reading about pthread_setcancelstate, I found that pthread_cond_wait
is a cancellation point of the thread. If cancel is enabled for the thread and is deferred, then cancellation point will test for cancel. If any cancel is pending, thread will exit.
So, in my case, thread exits leaving mutex_ locked. Therefore signalling thread blocks.
But there is one more doubt. All the threads are being created from the same function using Thread class. Why only this thread has got such behaviour of cancellation?
Guaranteeing execution of pthread_cond_wait before pthread_wait_signal
It does not matter whether pthread_cond_wait
happens before or after pthread_cond_signal
, because you only call pthread_cond_wait
while the predicate is false, and the state on which the predicate depends cannot change while the mutex associated with the condition variable is held.
Suppose the waiter is doing something like:
pthread_mutex_lock(mutex);
while (!predicate(state)) pthread_cond_wait(cond, mutex);
pthread_mutex_unlock(mutex);
In order to have a reason to signal the condition variable, the thread doing the signaling must have made some change to state
. In order to do this without invoking undefined behavior, it must hold mutex
, which protects state
. However, this means either the change to state
completed before the above code snippet (in which case pthread_cond_wait
is never called because predicate(state)
is now true), during the call to pthread_cond_wait
(while the mutex is unlocked; in this case, the signal after changing state
will unblock the wait), or after the end of the above code snippet (in which case it's irrelevant).
Related Topics
How to Declare Constexpr Extern
Why Cannot a Non-Member Function Be Used for Overloading the Assignment Operator
Loop Unrolling to Achieve Maximum Throughput with Ivy Bridge and Haswell
C++ Issue with Function Overloading in an Inherited Class
Use Wm_Copydata to Send Data Between Processes
Linker Error While Linking Boost Log Tutorial (Undefined References)
Determining If a Number Is Prime
Is There Ever a Need for a "Do {...} While ( )" Loop
Why Is 'Char' Signed by Default in C++
Std::Map, Pointer to Map Key Value, Is This Possible
How to Implement 2D Vector Array
Does a C++ Struct Have a Default Constructor
How Does the Compilation of Templates Work