Skip to content

Instantly share code, notes, and snippets.

@rtv

rtv/cond.c

Created Feb 19, 2013
Embed
What would you like to do?
An example of using pthread's condition variables, showing how to block a main thread while waiting for worker threads to finish their work, without joining the threads. This could be useful if you want to loop the threads, for example.
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
/* Compile like this:
gcc --std=c99 -lpthread cond.c -o cond
*/
const size_t NUMTHREADS = 20;
/* a global count of the number of threads finished working. It will
be protected by mutex and changes to it will be signalled to the
main thread via cond */
int done = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
/* Note: error checking on pthread_X calls ommitted for clarity - you
should always check the return values in real code. */
/* Note: passing the thread id via a void pointer is cheap and easy,
* but the code assumes pointers and long ints are the same size
* (probably 64bits), which is a little hacky. */
void* ThreadEntry( void* id )
{
const int myid = (long)id; // force the pointer to be a 64bit integer
const int workloops = 5;
for( int i=0; i<workloops; i++ )
{
printf( "[thread %d] working (%d/%d)\n", myid, i, workloops );
sleep(1); // simulate doing some costly work
}
// we're going to manipulate done and use the cond, so we need the mutex
pthread_mutex_lock( &mutex );
// increase the count of threads that have finished their work.
done++;
printf( "[thread %d] done is now %d. Signalling cond.\n", myid, done );
// wait up the main thread (if it is sleeping) to test the value of done
pthread_cond_signal( &cond );
pthread_mutex_unlock( & mutex );
return NULL;
}
int main( int argc, char** argv )
{
puts( "[thread main] starting" );
pthread_t threads[NUMTHREADS];
for( int t=0; t<NUMTHREADS; t++ )
pthread_create( &threads[t], NULL, ThreadEntry, (void*)(long)t );
// we're going to test "done" so we need the mutex for safety
pthread_mutex_lock( &mutex );
// are the other threads still busy?
while( done < NUMTHREADS )
{
printf( "[thread main] done is %d which is < %d so waiting on cond\n",
done, (int)NUMTHREADS );
/* block this thread until another thread signals cond. While
blocked, the mutex is released, then re-aquired before this
thread is woken up and the call returns. */
pthread_cond_wait( & cond, & mutex );
puts( "[thread main] wake - cond was signalled." );
/* we go around the loop with the lock held */
}
printf( "[thread main] done == %d so everyone is done\n", (int)NUMTHREADS );
pthread_mutex_unlock( & mutex );
return 0;
}
@KristupasSavickas

This comment has been minimized.

Copy link

@KristupasSavickas KristupasSavickas commented Nov 17, 2017

Nice example, but what's the point of keeping done global? Couldn't you keep it in the loop and increment it in the main loop when a condition occurs? You would need mutexes that way.

@vpatov

This comment has been minimized.

Copy link

@vpatov vpatov commented Nov 23, 2017

@KristupasSavickas the point of done being global is to create a necessity for mutexes. This is a demonstration of thread conditions after all.

@AirborneLizard

This comment has been minimized.

Copy link

@AirborneLizard AirborneLizard commented Nov 30, 2017

Should done be volatile?

@driskell

This comment has been minimized.

Copy link

@driskell driskell commented Dec 12, 2017

Sorry if I misunderstanding. Wouldn’t this deadlock if the main thread entered the loop before the workers finished? It looks like it holds the mutex lock during cond_wait.

@driskell

This comment has been minimized.

Copy link

@driskell driskell commented Dec 12, 2017

Aha I read it again I misunderstood! :) thanks for the example!

@vineetgarc

This comment has been minimized.

Copy link

@vineetgarc vineetgarc commented Feb 2, 2018

@driskell, indeed it seems it would deadlock as u mentioned - what am I missing ?

@js0701

This comment has been minimized.

Copy link

@js0701 js0701 commented Feb 18, 2018

It seems will deadlock?

@icthieves

This comment has been minimized.

Copy link

@icthieves icthieves commented Feb 28, 2018

@vineetgarc @js0701
pthread_cond_wait() implicitly releases the mutex it is passed when it is called, and implicitly re-aqcuires the mutex when it is returned.
That's why this function takes a condition and a mutex.
So what is happening is that main() acquires the lock, then immediately checks the condition (which hasn't been signaled yet).
Since it hasn't been signaled, pthread_cond_wait() releases the mutex and blocks the main() thread until the condition variable is signaled by another thread.

@o2gy84

This comment has been minimized.

Copy link

@o2gy84 o2gy84 commented Jan 16, 2019

Is it OK in this example to put pthread_mutex_unlock( & mutex ) before pthread_cond_signal( &cond ); ?

@zyxxel

This comment has been minimized.

Copy link

@zyxxel zyxxel commented Apr 28, 2019

No, bad things can happen if you call pthread_cond_signal() without holding the same mutex that waiting threads specifies in their pthread_cond_wait() call.

If pthread_cond_signal() is called without holding the mutex, then the waiting thread can get into an infinite wait because the thread signalling the condition might do it in-between the waiting thread decides if it needs to wait and blocking in pthread_cond_wait().

The pthread_cond_signal() will only wake a waiting thread. If no thread was waiting, then the signal condition was lost and a thread that later starts to wait may wait forever. The above code doesn't suffer from this because inside the mutex-protected critical section, it checks the state of 'done' before deciding if it should wait. With 'done' less than NUMTHREADS, the waiting thread knows that at least one thread have still not signalled - so it's safe to wait on the condition variable.

@ratin3

This comment has been minimized.

Copy link

@ratin3 ratin3 commented May 30, 2019

Zyxxel: If you use the same mutex before a pthread_cond_wait() call and pthread_cond_signal(), then they will be mutually exclusive, meaning you won't need to call pthread_cond_wait anyway, just use a flag.

@paul-cybercitizen

This comment has been minimized.

Copy link

@paul-cybercitizen paul-cybercitizen commented Jun 9, 2020

Is the below message correct? I thought I read that threads can wake from a waiting on a condition for spurious reasons, so we can't assume a signal was received when waking

puts( "[thread main] wake - cond was signalled." );

@paul-cybercitizen

This comment has been minimized.

Copy link

@paul-cybercitizen paul-cybercitizen commented Jun 9, 2020

Is the below message correct? I thought I read that threads can wake from a waiting on a condition for spurious reasons, so we can't assume a signal was received when waking

puts( "[thread main] wake - cond was signalled." );

The other thread signals before releasing the lock, maybe that guarantees to the waked thread that the signal was received?

@rtv

This comment has been minimized.

Copy link
Owner Author

@rtv rtv commented Jun 9, 2020

@rtv

This comment has been minimized.

Copy link
Owner Author

@rtv rtv commented Jun 9, 2020

Note that the condition is checked after cond returns. The worst that can happen is that the puts() is slightly misleading.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.