Friday, July 31, 2015

Condition variables and the wait() and signal() calls

Here's a little example of how to use condition variables and the wait() and signal() calls. It creates one thread that waits on 'cv' and another that signals it every second. This second thread just sleeps between signals, but in a real use case it would be called by some other routine to signal the sleeper thread (instead of just sleeping).

As a bonus, both threads check if they slept for over a millisecond more than they should.
/*
 * cc -mt -lpthread timer.c -o timer -O3
 */
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <err.h>
#include <strings.h>
#include <sys/time.h>
#include <sys/types.h>

#define SECOND         (1000000000)
#define MILISEC        (1000000)

pthread_cond_t         cv;
pthread_mutex_t        mutex;
struct timespec        ts = { 1 ,  0 };

static void
check_hrt(hrtime_t hrt, char *str)
{
    hrtime_t delta = gethrtime() - hrt;

    if ((delta - SECOND) >= MILISEC) {
        (void) fprintf(stderr, "[%s] %lu ns (!!!)\n", str, delta);
    } else {
        (void) fprintf(stderr, "[%s] %lu ns\n", str, delta);
    }
}

void *
wait_thread(void *arg)
{
    hrtime_t hrt, delta;
    int      ret;

    for (;;) {
        pthread_mutex_lock(&mutex);
        hrt = gethrtime();

        ret = pthread_cond_wait(&cv, &mutex);
        if (ret != 0 && ret != ETIMEDOUT) {
            errno = ret;
            perror("[wait] condwait timed out");
        }

        pthread_mutex_unlock(&mutex);
        check_hrt(hrt, "wait");
    }
}

void *
signal_thread(void *arg)
{
    hrtime_t hrt, delta;

    for (;;) { 
        hrt = gethrtime();
        nanosleep(&ts, NULL);

        pthread_mutex_lock(&mutex);
        pthread_cond_signal(&cv);
        pthread_mutex_unlock(&mutex);

        check_hrt(hrt, "sign");
    }
}

int
main(int argc, char **argv)
{
    pthread_t waiter, signaler;

    pthread_cond_init(&cv, NULL);
    pthread_mutex_init(&mutex, NULL);

    if (pthread_create(&waiter, NULL, wait_thread, NULL) != 0) {
        perror("failed to create waiter thread");
        return (1);
    }

    if (pthread_create(&signaler, NULL, signal_thread, NULL) != 0) {
        perror("failed to create signaler thread");
        return (1);
    }

    pthread_join(waiter, NULL);
    pthread_join(signaler, NULL);

    return (0);
}

No comments:

Post a Comment