Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

pthread condition variable deadlock?

12 views
Skip to first unread message

Andy Barclay

unread,
Sep 12, 2001, 4:09:38 PM9/12/01
to
I am writing some course material on multi-threading using posix
threads. I seem to be having some difficulty with posix condition
variables.

The problem is that the program deadlocks. On a single processor, it
will run for quite a while (several hundred thousand itterations)
before deadlocking. On a multi-processor, it deadlocks much more
quickly.

I've been programming long enough that I'm 99% sure its a bug in my
code and not a problem on either of the operating systems that I have
tried it on (Linux and Solaris).

Anyway, if anyone can help me, it would be appreciated.

/* solution to producer/consumer problem */
#include <stdio.h>
#include <pthread.h>

#define BUFFERSIZE 5

int buffer[BUFFERSIZE];
int produced=0;
int consumed=0;
volatile int nitems=0;
pthread_cond_t buffernotfull,buffernotempty;
pthread_mutex_t buffernotfullmutex,buffernotemptymutex;

void *producer(void *tmp)
{
while(1)
{
pthread_mutex_lock(&buffernotfullmutex);
while (nitems>=BUFFERSIZE)
pthread_cond_wait(&buffernotfull,&buffernotfullmutex);
buffer[produced]++;
produced=(produced+1)%BUFFERSIZE;
nitems++;
pthread_cond_signal(&buffernotempty);
pthread_cond_signal(&buffernotfull);
pthread_mutex_unlock(&buffernotfullmutex);
}
}

void *consumer(void *tmp)
{
while(1)
{
pthread_mutex_lock(&buffernotemptymutex);
while (nitems<=0)
pthread_cond_wait(&buffernotempty,&buffernotemptymutex);
printf("%d\n",buffer[consumed]);
consumed=(consumed+1)%BUFFERSIZE;
nitems--;
pthread_cond_signal(&buffernotfull);
pthread_cond_signal(&buffernotempty);
pthread_mutex_unlock(&buffernotemptymutex);
}
}

int main()
{
pthread_t prodtid,contid;
int i;

/* initialize the buffer to 0 */
for (i=0; i<BUFFERSIZE; i++)
buffer[i]=0;

/* initialize the condition variables and mutexes*/
pthread_cond_init(&buffernotempty,NULL);
pthread_cond_init(&buffernotfull,NULL);
pthread_mutex_init(&buffernotemptymutex,NULL);
pthread_mutex_init(&buffernotfullmutex,NULL);

pthread_create(&prodtid,NULL,producer,NULL);
pthread_create(&contid,NULL,consumer,NULL);

pthread_join(prodtid,NULL);
pthread_join(contid,NULL);
}

David Schwartz

unread,
Sep 12, 2001, 4:33:02 PM9/12/01
to
Andy Barclay wrote:

> void *producer(void *tmp)
> {
> while(1)
> {
> pthread_mutex_lock(&buffernotfullmutex);
> while (nitems>=BUFFERSIZE)
> pthread_cond_wait(&buffernotfull,&buffernotfullmutex);
> buffer[produced]++;
> produced=(produced+1)%BUFFERSIZE;
> nitems++;
> pthread_cond_signal(&buffernotempty);
> pthread_cond_signal(&buffernotfull);
> pthread_mutex_unlock(&buffernotfullmutex);
> }
> }

> void *consumer(void *tmp)
> {
> while(1)
> {
> pthread_mutex_lock(&buffernotemptymutex);
> while (nitems<=0)
> pthread_cond_wait(&buffernotempty,&buffernotemptymutex);
> printf("%d\n",buffer[consumed]);
> consumed=(consumed+1)%BUFFERSIZE;
> nitems--;
> pthread_cond_signal(&buffernotfull);
> pthread_cond_signal(&buffernotempty);
> pthread_mutex_unlock(&buffernotemptymutex);
> }
> }

What mutex protects 'nitems'?

DS

Kaz Kylheku

unread,
Sep 12, 2001, 4:56:27 PM9/12/01
to
In article <3ebd7d0b.01091...@posting.google.com>, Andy

Barclay wrote:
>Anyway, if anyone can help me, it would be appreciated.
>
>/* solution to producer/consumer problem */
>#include <stdio.h>
>#include <pthread.h>
>
>#define BUFFERSIZE 5
>
>int buffer[BUFFERSIZE];
>int produced=0;
>int consumed=0;
>volatile int nitems=0;
>pthread_cond_t buffernotfull,buffernotempty;
>pthread_mutex_t buffernotfullmutex,buffernotemptymutex;

The problem is that you have two locks trying to protect the same
buffer. Each thread locks a different mutex when accessing the buffer.
So the buffer is protected against concurrent or interleaved access by
these two threads. To do that, you must use a single mutex (in conjunction
with both condition variables).

Don't take this as some kind of personal flame, but are you sure you are
ready to teach multithreading? Seriously consider taking some time to
beef up a little bit before training others, or writing training material.

Don't take this the wrong way; I don't have any reason to doubt that you
are a fine instructor or course material writer, or that with a little
more concurrent programming experience, you will make a good instructor
in that subject. (Or maybe you already have a lot of concurrent programming
experience, but not with mutex-like objects; I have no way of knowing).

As a teacher, you have to be able to ask yourself the question, for
any given subject, ``do I currently have enough mastery to teach this
to others?'' and come up with an answer that follows from a honest
self-assessment.

Andy Barclay

unread,
Sep 13, 2001, 12:15:21 AM9/13/01
to
k...@ashi.footprints.net (Kaz Kylheku) wrote in message
>
> The problem is that you have two locks trying to protect the same
> buffer. Each thread locks a different mutex when accessing the buffer.
> So the buffer is protected against concurrent or interleaved access by
> these two threads. To do that, you must use a single mutex (in conjunction
> with both condition variables).
>
> Don't take this as some kind of personal flame, but are you sure you are
> ready to teach multithreading? Seriously consider taking some time to
> beef up a little bit before training others, or writing training material.

No offence taken.

I have quite a bit of experience with concurrent programming including
synchronization using semaphores and signals.

The table of contents that I received from the customer requested that
I cover mutex's and condition variables as well, and I have no
experience with these two techniques. I have been trying to piece
together appropriate usage using only the man pages and posts on deja.

>
> Don't take this the wrong way; I don't have any reason to doubt that you
> are a fine instructor or course material writer, or that with a little
> more concurrent programming experience, you will make a good instructor
> in that subject. (Or maybe you already have a lot of concurrent programming
> experience, but not with mutex-like objects; I have no way of knowing).

In this case, I am only writing the material. I will not be the
instructor, however, I feel I could probably deal with it since the
condition variable section is only a small part of the material.

>
> As a teacher, you have to be able to ask yourself the question, for
> any given subject, ``do I currently have enough mastery to teach this
> to others?'' and come up with an answer that follows from a honest
> self-assessment.

I fully agree with this statement. This week, I am here in Atlanta
teaching a class for Networld+Interop on DNS. As a trainer, I need to
be very comfortable with all the material (and even material outide
the scope of the course), however, I am not sure that this is as
important when only writing the material.

Thanks very much for your help. I will implement your suggestion.

Kaz Kylheku

unread,
Sep 13, 2001, 12:57:49 AM9/13/01
to
In article <3ebd7d0b.01091...@posting.google.com>, Andy
Barclay wrote:
>I have quite a bit of experience with concurrent programming including
>synchronization using semaphores and signals.

I see; that was a suspicion that I had, and expressed:

>> in that subject. (Or maybe you already have a lot of concurrent programming
>> experience, but not with mutex-like objects; I have no way of knowing).

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

It appeared to me that you were effectively trying to construct a pair
of semaphores, using a pair of mutexes and condition variables. This is
a workable idea: a semaphore can be made using a counter protected
by a mutex, and a condition variable.

A useful exercise for students is to ask them to implement one kind
of synchronization object using another: e.g. ``make mutexes
and conditions using a send/receive/reply messaging discipline''.

Alexander Terekhov

unread,
Sep 13, 2001, 4:01:32 AM9/13/01
to

Andy Barclay wrote:
>
> I am writing some course material on multi-threading using posix
> threads. I seem to be having some difficulty with posix condition
> variables.
>
> The problem is that the program deadlocks. On a single processor, it
> will run for quite a while (several hundred thousand itterations)
> before deadlocking. On a multi-processor, it deadlocks much more
> quickly.
>
> I've been programming long enough that I'm 99% sure its a bug in my
> code and not a problem on either of the operating systems that I have
> tried it on (Linux and Solaris).
>
> Anyway, if anyone can help me, it would be appreciated.
>
> /* solution to producer/consumer problem */
> #include ?stdio.h?
> #include ?pthread.h?

>
> #define BUFFERSIZE 5
>
> int buffer[BUFFERSIZE];
> int produced=0;
> int consumed=0;
> volatile int nitems=0;
> pthread_cond_t buffernotfull,buffernotempty;
> pthread_mutex_t buffernotfullmutex,buffernotemptymutex;
>
> void *producer(void *tmp)
> {
> while(1)
> {
> pthread_mutex_lock(?buffernotfullmutex);
> while (nitems?=BUFFERSIZE)
> pthread_cond_wait(?buffernotfull,?buffernotfullmutex);

> buffer[produced]++;
> produced=(produced+1)%BUFFERSIZE;
> nitems++;
> pthread_cond_signal(?buffernotempty);
> pthread_cond_signal(?buffernotfull);
> pthread_mutex_unlock(?buffernotfullmutex);
> }
> }
>
> void *consumer(void *tmp)
> {
> while(1)
> {
> pthread_mutex_lock(?buffernotemptymutex);
> while (nitems?=0)
> pthread_cond_wait(?buffernotempty,?buffernotemptymutex);

> printf("%d\n",buffer[consumed]);
> consumed=(consumed+1)%BUFFERSIZE;
> nitems--;
> pthread_cond_signal(?buffernotfull);
> pthread_cond_signal(?buffernotempty);
> pthread_mutex_unlock(?buffernotemptymutex);

> }
> }
>
> int main()
> {
> pthread_t prodtid,contid;
> int i;
>
> /* initialize the buffer to 0 */
> for (i=0; i?BUFFERSIZE; i++)

> buffer[i]=0;
>
> /* initialize the condition variables and mutexes*/
> pthread_cond_init(?buffernotempty,NULL);
> pthread_cond_init(?buffernotfull,NULL);
> pthread_mutex_init(?buffernotemptymutex,NULL);
> pthread_mutex_init(?buffernotfullmutex,NULL);
>
> pthread_create(?prodtid,NULL,producer,NULL);
> pthread_create(?contid,NULL,consumer,NULL);
>
> pthread_join(prodtid,NULL);
> pthread_join(contid,NULL);
> }

you do not need volatiles, two mutexes and
double signaling (notempty&notfull) in both
producer and consumer routines..

/* solution to producer/consumer problem */
#include <stdio.h>
#include <pthread.h>

pthread_mutex_t mutex;
pthread_cond_t notfull,notempty;

#define BUFFERSIZE 5

int buffer[BUFFERSIZE];
const char* buffer2[BUFFERSIZE];
int produced=0;
int consumed=0;
int nitems=0;
int finish=0;

void* producer(void* name)
{
while(1)
{
pthread_mutex_lock(&mutex);
while (nitems == BUFFERSIZE && !finish)
pthread_cond_wait(&notfull,&mutex);
if (finish)
break;
buffer[produced]++;
buffer2[produced] = (const char*)name;
nitems++;
produced=(produced+1)%BUFFERSIZE;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&notempty);
}
pthread_mutex_unlock(&mutex);
return NULL;
}

void* consumer(void* name)
{
while(1)
{
pthread_mutex_lock(&mutex);
while (nitems == 0 && !finish)
pthread_cond_wait(&notempty,&mutex);
if (finish && nitems == 0)
break;
printf("%d %s %s\n",buffer[consumed],
buffer2[consumed],
(const char*)name);
consumed=(consumed+1)%BUFFERSIZE;
nitems--;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&notfull);
}
pthread_mutex_unlock(&mutex);
return NULL;
}

int main()
{
pthread_t prodtid[2],constid[3];
int i;

/* initialize the buffer to 0 */
for (i=0; i < BUFFERSIZE; i++)
buffer[i]=0;

/* initialize mutex and condition variables */
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&notempty,NULL);
pthread_cond_init(&notfull,NULL);

pthread_create(prodtid+0,NULL,producer,"PROD-1");
pthread_create(prodtid+1,NULL,producer,"PROD-2");
pthread_create(constid+0,NULL,consumer,"CONS-1");
pthread_create(constid+1,NULL,consumer,"CONS-2");
pthread_create(constid+2,NULL,consumer,"CONS-3");

Sleep( 1000 ); // 1 sec

pthread_mutex_lock(&mutex);
finish = 1;
pthread_mutex_unlock(&mutex);

pthread_cond_broadcast(&notfull);
pthread_cond_broadcast(&notempty);

pthread_join(prodtid[0],NULL);
pthread_join(prodtid[1],NULL);
pthread_join(constid[0],NULL);
pthread_join(constid[1],NULL);
pthread_join(constid[2],NULL);

pthread_cond_destroy(&notempty);
pthread_cond_destroy(&notfull);
pthread_mutex_destroy(&mutex);

return 0;
}

regards,
alexander.

0 new messages