This is the mail archive of the libc-help@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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;
}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]