Condition Variable - Why Calling Pthread_Cond_Signal() Before Calling Pthread_Cond_Wait() Is a Logical Error

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



Leave a reply



Submit