On Mar 10, 2005, at 4:01 PM, Charles Lockhart wrote:
I just upgraded a RH9 box to FC1. I then rebuilt a multi-threaded
program and tried to run it, but found some behaviors that don't make
sense to me. One of the threads sleeps on a conditional wait (
pthread_cond_wait(&my_cond, &my_mutex)), and when a different thread
calls pthread_cond_broadcast(&my_cond), I kind of expect the first
thread to wake it's shiny smiling face and do some work. But it
doesn't. Shouldn't it? It used to.
I have found some references to bugs in RH9 of people running into
"stalls" with similar stuff, but that never happened to me on RH9, so
I figured it ought to work on FC1 as well.
Any info? Can anyone confirm or deny that I'm doing something goofy?
I didn't really change anything, so if there's some other library I'm
supposed to link to or something, please lemme know. Or for anything
else too.
Thanks in advance,
-Charles
pthread_cond_broadcast() will (attempt) to waken *ALL* threads waiting
on that condition variable. If there is more than one, they'll
race, and the first one that gets past the mutex 'wins'.
pthread_cond_signal() will attempt to waken *one* thread (typically the
first
thread waiting on the condition variable.)
Perhaps you're just running into side-effects of the scheduler. It
seems to work here (gentoo, 2.6.10 kernel). See the code and output
below.
(I'm not bragging on the code, it was quick-n-dirty.)
The Fedora Project officially ended support for Fedora Core 1 (FC1) on
September 20th, 2004. FC3 was released November 8, 2004.
Maybe you should run FC3 (which is current) .vs a release that is now
known as "Fedora Legacy". (http://fedora.redhat.com/)
jim
/home/jim> cat thread_signal.c
#include <pthread.h>
#include <stdio.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int workToDo = 0;
#define NTHREADS 5
void *
func(void *parm)
{
int rc;
int id = (int) parm;
while (1) {
/* Usually worker threads will loop on these operations */
if ((rc = pthread_mutex_lock(&mutex)) < 0) {
fprintf(stderr, "thread %d, pthread_mutex_lock(): %s\n",
id, strerror(rc)); /* strerror not thread-safe */
pthread_exit(NULL);
}
while (!workToDo) {
printf("\tThread %d blocked\n", id);
if ((rc = pthread_cond_wait(&cond, &mutex)) < 0) {
fprintf(stderr, "thread %d, pthread_cond_wait(): %s\n",
id, strerror(rc)); /* not thread-safe */
pthread_exit(NULL);
}
}
printf("\tThread %d awake, finish work!\n", id);
/* Under protection of the lock, complete or remove the work
*/
/* from whatever worker queue we have. Here it is simply a
flag */
workToDo = 0;
if ((rc = pthread_mutex_unlock(&mutex)) < 0) {
fprintf(stderr, "thread %d, pthread_mutex_unlock(): %s\n",
id, strerror(rc)); /* strerror not thread-safe */
pthread_exit(NULL);
}
}
return NULL;
}
int
main(int argc, char **argv)
{
pthread_t threadid[NTHREADS];
int rc, i;
printf("Create %d threads\n", NTHREADS);
for (i = 0; i < NTHREADS; i++) {
if ((rc = pthread_create(&threadid[i], NULL, func, (void *)i)) <
0) {
perror("pthread_create");
exit(1);
}
}
sleep(1); /* Sleep is not a robust way to serialize threads */
for (i = 0; i < 5; ++i) {
printf("Wake up a worker, work to do...\n");
if ((rc = pthread_mutex_lock(&mutex)) < 0) {
perror("pthread_mutex_lock()");
exit(1);
}
/* In the real world, all the threads might be busy, and
* we would add work to a queue instead of simply using a flag
* In that case the boolean predicate might be some boolean
* statement like: if (the-queue-contains-work)
*/
if (workToDo) {
printf("Work already present, likely threads are busy\n");
}
workToDo = 1;
#ifdef COND_SIGNAL
if ((rc = pthread_cond_signal(&cond)) < 0) {
perror("pthread_cond_signal()");
exit(1);
}
#else
if ((rc = pthread_cond_broadcast(&cond)) < 0) {
perror("pthread_cond_signal()");
exit(1);
}
#endif
if ((rc = pthread_mutex_unlock(&mutex)) < 0) {
perror("pthread_mutex_unlock()");
exit(1);
}
sleep(1); /* Sleep is not a robust way to serialize threads */
}
exit(0);
}
/home/jim> gcc thread_signal.c -o thread_signal -lpthread
/home/jim> gcc -DCOND_SIGNAL=1 thread_signal.c -o thread_cond_signal
-lpthread
/home/jim> gcc thread_signal.c -o thread_cond_broadcast -lpthread
/home/jim> ./thread_cond_signal
Create 5 threads
Thread 0 blocked
Thread 1 blocked
Thread 2 blocked
Thread 3 blocked
Thread 4 blocked
Wake up a worker, work to do...
Thread 0 awake, finish work!
Thread 0 blocked
Wake up a worker, work to do...
Thread 1 awake, finish work!
Thread 1 blocked
Wake up a worker, work to do...
Thread 2 awake, finish work!
Thread 2 blocked
Wake up a worker, work to do...
Thread 3 awake, finish work!
Thread 3 blocked
Wake up a worker, work to do...
Thread 4 awake, finish work!
Thread 4 blocked
/home/jim> ./thread_cond_broadcast
Create 5 threads
Thread 0 blocked
Thread 1 blocked
Thread 2 blocked
Thread 3 blocked
Thread 4 blocked
Wake up a worker, work to do...
Thread 0 awake, finish work!
Thread 0 blocked
Thread 1 blocked
Thread 2 blocked
Thread 3 blocked
Thread 4 blocked
Wake up a worker, work to do...
Thread 0 awake, finish work!
Thread 0 blocked
Thread 1 blocked
Thread 2 blocked
Thread 3 blocked
Thread 4 blocked
Wake up a worker, work to do...
Thread 0 awake, finish work!
Thread 0 blocked
Thread 1 blocked
Thread 2 blocked
Thread 3 blocked
Thread 4 blocked
Wake up a worker, work to do...
Thread 0 awake, finish work!
Thread 0 blocked
Thread 1 blocked
Thread 2 blocked
Thread 3 blocked
Thread 4 blocked
Wake up a worker, work to do...
Thread 0 awake, finish work!
Thread 0 blocked
Thread 1 blocked
Thread 2 blocked
Thread 3 blocked
Thread 4 blocked
/home/jim>