To understand the code of pthread condition variables, I have written my own version. Does it look correct? I am using it in a program, its working, but working surprisingly much faster. Originally the program takes around 2.5 seconds and with my version of condition variables it takes only 0.8 seconds, and the output of the program is correct too. However, I'm not sure, if my implementation is correct.
struct cond_node_t
{
sem_t s;
cond_node_t * next;
};
struct cond_t
{
cond_node_t * q; // Linked List
pthread_mutex_t qm; // Lock for the Linked List
};
int my_pthread_cond_init( cond_t * cond )
{
cond->q = NULL;
pthread_mutex_init( &(cond->qm), NULL );
}
int my_pthread_cond_wait( cond_t* cond, pthread_mutex_t* mutex )
{
cond_node_t * self;
pthread_mutex_lock(&(cond->qm));
self = (cond_node_t*)calloc( 1, sizeof(cond_node_t) );
self->next = cond->q;
cond->q = self;
sem_init( &self->s, 0, 0 );
pthread_mutex_unlock(&(cond->qm));
pthread_mutex_unlock(mutex);
sem_wait( &self->s );
free( self ); // Free the node
pthread_mutex_lock(mutex);
}
int my_pthread_cond_signal( cond_t * cond )
{
pthread_mutex_lock(&(cond->qm));
if (cond->q != NULL)
{
sem_post(&(cond->q->s));
cond->q = cond->q->next;
}
pthread_mutex_unlock(&(cond->qm));
}
int my_pthread_cond_broadcast( cond_t * cond )
{
pthread_mutex_lock(&(cond->qm));
while ( cond->q != NULL)
{
sem_post( &(cond->q->s) );
cond->q = cond->q->next;
}
pthread_mutex_unlock(&(cond->qm));
}
You don't seem to respect this requirement:
These functions atomically release mutex and cause the calling thread to block on the condition variable cond; atomically here means "atomically with respect to access by another thread to the mutex and then the condition variable". That is, if another thread is able to acquire the mutex after the about-to-block thread has released it, then a subsequent call to pthread_cond_broadcast() or pthread_cond_signal() in that thread shall behave as if it were issued after the about-to-block thread has blocked.
You unlock and then wait. Another thread can do lots of things between these operations.
P.S. I'm not sure myself if I'm interpreting this paragraph correctly, feel free to point out my error.
Apart from the missing return value checks, there are some more issues that should be fixable:
sem_destroy
is not called.cond_node_t
after waking the target thread, potentially resulting in a use-after-free.Further comments:
pthread_cond_timedwait
timeouts) may lead to complications.Basically your strategy looks ok, but you have one major danger, some undefined behavior, and a nit pick:
sem_wait
is interruptible so under heavy load or bad luck your thread will be woken up spuriously. You'd have to carefully catch all thatmalloc
or calloc
Edit: Actually, you don't need malloc
/free
at all. A local variable would do as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With