/* * Illustration of FreeBSD pthread_cond_wait() bug * * This program sets up a conditional wait and fires off a dozen threads * that simply wait for the condition. Once the threads are started, * the main thread loops signalling the condition once a second. * * Normally, this should result in "Signalling" and "Got Condition" * being printed once a second. However, because of some bugs in * FreeBSD, the pthread_cond_wait() spins the CPU and no progress is * made. * * gcc -o condWaitBug -g -Wall -pthread condWaitBug.c */ #include #include #include #include #include #include pthread_mutex_t lock; pthread_cond_t condition; static void * condThread(void *arg) { /* Wait until we are signalled, then print. */ while (1) { /* Be sure to do proper locking and unlocking */ assert(!pthread_mutex_lock(&lock)); assert(!pthread_cond_wait(&condition, &lock)); printf("Got Condition!\n"); assert(!pthread_mutex_unlock(&lock)); } return (NULL); } static void * condtimedThread(void *arg) { struct timespec ts; int count = 0; int rval; /* Wait until we are signalled, then print. */ while (1) { clock_gettime(CLOCK_REALTIME, &ts); if ((count & 0x1) == 0) ts.tv_sec = ts.tv_sec + 1; else ts.tv_sec = ts.tv_sec + 2; /* Be sure to do proper locking and unlocking */ assert(!pthread_mutex_lock(&lock)); rval = pthread_cond_timedwait(&condition, &lock, &ts); pthread_mutex_unlock(&lock); if (rval == ETIMEDOUT) printf("Thread timedout\n"); else printf("Got signal\n"); } return (NULL); } int main(int argc, char **argv) { /* Initialize Lock */ pthread_mutexattr_t lock_attr; pthread_condattr_t cond_attr; pthread_t tid; pthread_attr_t attr; struct timeval timeout; int j, k; assert(!pthread_mutexattr_init(&lock_attr)); assert(!pthread_mutex_init(&lock, &lock_attr)); assert(!pthread_mutexattr_destroy(&lock_attr)); /* Initialize Condition */ assert(!pthread_condattr_init(&cond_attr)); assert(!pthread_cond_init(&condition, &cond_attr)); assert(!pthread_condattr_destroy(&cond_attr)); assert(!pthread_attr_init(&attr)); /* Spawn off a dozen threads to get signalled */ for (j = 0; j < 12; ++j) { if ((j & 0x1) == 0) assert(!pthread_create(&tid, &attr, condThread, 0)); else assert(!pthread_create(&tid, &attr, condtimedThread, 0)); pthread_yield(); } assert(!pthread_attr_destroy(&attr)); /* Sleep for 3 seconds to make sure the threads started up. */ timeout.tv_sec = 3; timeout.tv_usec = 0; select(0, 0, 0, 0, &timeout); for (k = 0; k < 60; ++k) { /* Signal while locked */ assert(!pthread_mutex_lock(&lock)); printf("Signalling\n"); assert(!pthread_cond_signal(&condition)); assert(!pthread_mutex_unlock(&lock)); /* Sleep for 1 second */ timeout.tv_sec = 1; timeout.tv_usec = 0; select(0, 0, 0, 0, &timeout); assert(!pthread_mutex_lock(&lock)); assert(!pthread_cond_broadcast(&condition)); printf("Signalled again\n"); assert(!pthread_mutex_unlock(&lock)); printf("Mutex unlocked\n"); /* Sleep for 1 second */ timeout.tv_sec = 1; timeout.tv_usec = 0; select(0, 0, 0, 0, &timeout); } return EXIT_SUCCESS; }