This is the mail archive of the
libc-help@sourceware.org
mailing list for the glibc project.
pthread_cond_wait spurious wakeups
- From: Michael Braun <Michael dot Braun at zoran dot com>
- To: libc-help at sourceware dot org
- Date: Mon, 14 Jun 2010 16:55:32 +0200
- Subject: pthread_cond_wait spurious wakeups
According to the documentation of the function spurious wakeups are to be expected,
however I'm wondering if the numbers I'm seeing are still considered spurious.
The following source starts a few threads (currently 5) and has each of them
try to access a shared resource locked by a variable using pthread_cond_wait/_signal
with a variable duty cycle of locked unlocked periods implemented as local spins.
The loop counts the number of completed loop iterations. A alarm signal will print the
counts of the current settings and then update the duty cycle and total count.
The following is a sample output:
Starting up 5 threads...
locked 0 unlocked 100000 loop: 648 iterations 0 spurious
locked 25000 unlocked 75000 loop: 581 iterations 12 spurious
locked 50000 unlocked 50000 loop: 572 iterations 177 spurious
locked 75000 unlocked 25000 loop: 424 iterations 212 spurious
locked 100000 unlocked 0 loop: 386 iterations 363 spurious
locked 0 unlocked 10000 loop: 6665 iterations 0 spurious
locked 2500 unlocked 7500 loop: 5968 iterations 21 spurious
locked 5000 unlocked 5000 loop: 4910 iterations 1727 spurious
locked 7500 unlocked 2500 loop: 3838 iterations 2481 spurious
locked 10000 unlocked 0 loop: 3806 iterations 1318 spurious
locked 0 unlocked 1000 loop: 57040 iterations 0 spurious
locked 250 unlocked 750 loop: 54908 iterations 12 spurious
locked 500 unlocked 500 loop: 27375 iterations 2101 spurious
locked 750 unlocked 250 loop: 23718 iterations 23384 spurious
locked 1000 unlocked 0 loop: 24193 iterations 23430 spurious
As you can see the number of spurious wakeups increases with the contention
and shorter overall cycles, with almost completely locked 1000 count spins
I see almost as many spurious as correct wakeups. Is this expected behaviour?
Michael
Sample code: please note that this is not 100% clean code, but AFAICS
there should not be a significant quantitative difference caused as
a result of the inaccuracies. Also the code in question obviously doesn't
make sense as it is - it's only intended to illustrate the question.
Compile using: gcc -O2 -Wall -Werror spurious.c -o spurious -lpthread
spurious.c:
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/time.h>
#include <signal.h>
#include <sched.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
volatile int available = 1;
unsigned spinLocked;
unsigned spinUnlocked;
volatile unsigned iterations;
volatile unsigned spurious;
volatile int stop = 0;
void *ThreadProc(void *arg)
{
volatile unsigned count;
/* wait until all threads are started and we're ready to go */
while(!spinLocked && !spinUnlocked)
sched_yield();
/* basically a endless loop */
while(!stop){
/* aquire the lock */
pthread_mutex_lock(&mutex);
while(!available){
/* use conditional wait */
pthread_cond_wait(&cond, &mutex);
/* if the resource is not available the wakeup was spurious */
if(!available)
++spurious;
}
available--;
iterations++;
pthread_mutex_unlock(&mutex);
count = spinLocked;
while(count)
--count;
pthread_mutex_lock(&mutex);
available++;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
count = spinUnlocked;
while(count)
--count;
}
return NULL;
}
/* alarm signal handler - this is where the count and duty cycle is updated */
void SignalSIGALRM(int signum)
{
static unsigned count = 100000;
signal(SIGALRM, SignalSIGALRM);
if(spinLocked || spinUnlocked){
printf("%6u iterations %6u spurious\n", iterations, spurious);
if(spinLocked == count){
count /= 10;
if(count < 1000)
exit(0);
spinLocked = 0;
}
else
}
else
spinLocked += count * 25 / 100;
spinUnlocked = count - spinLocked;
}
else{
spinUnlocked = count;
}
iterations = 0;
spurious = 0;
printf("locked %6d unlocked %6d loop: ", spinLocked, spinUnlocked);
}
int main(int argn, char *argv[], char *envp[])
{
pthread_t threads[5];
int i;
struct itimerval itimervalue;
int error;
void (*savedSIGALRM)(int);
/* disable stdout buffering */
setvbuf(stdout, NULL, _IONBF, 0);
savedSIGALRM = signal(SIGALRM, SignalSIGALRM);
if(savedSIGALRM == SIG_ERR)
perror("signal");
/* initialize to zero to have threads wait after startup */
spinLocked = 0;
spinUnlocked = 0;
printf("Starting up %d threads...\n", (int)(sizeof(threads)/sizeof(threads[0])));
for(i=0; i<sizeof(threads)/sizeof(threads[0]); i++){
error = pthread_create(threads+i, NULL, ThreadProc, NULL);
if(error)
printf("pthread_create returned %d\n", error);
}
itimervalue.it_interval.tv_sec = 0;
itimervalue.it_interval.tv_usec = 100000;
itimervalue.it_value.tv_sec = 0;
itimervalue.it_value.tv_usec = 1;
error = setitimer(ITIMER_REAL, &itimervalue, NULL);
if(error)
perror("setitimer");
/* main thread will basically wait here in a yield loop for simplicity */
while(1)
sched_yield();
for(i=0; i<sizeof(threads)/sizeof(threads[0]); i++)
pthread_cancel(threads[i]);
return EXIT_SUCCESS;
}