typestar

The race you cannot see in C

The same increment with and without a lock, run enough times to lose counts.

#include <pthread.h>
#include <stdio.h>

#define ROUNDS 200000

static long unsafe_total = 0;
static long safe_total = 0;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

static void *bump(void *arg) {
    (void) arg;
    for (int i = 0; i < ROUNDS; i++) {
        unsafe_total++;
        pthread_mutex_lock(&lock);
        safe_total++;
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}

int main(void) {
    pthread_t a, b;
    pthread_create(&a, NULL, bump, NULL);
    pthread_create(&b, NULL, bump, NULL);
    pthread_join(a, NULL);
    pthread_join(b, NULL);

    printf("expected %d\n", 2 * ROUNDS);
    printf("unsafe   %ld\n", unsafe_total);
    printf("safe     %ld\n", safe_total);
    return 0;
}

How it works

  1. total++ is three operations, not one.
  2. Two threads interleaving them lose updates.
  3. The unlocked total comes out lower, and differently each run.

Keywords and builtins used here

The run, in numbers

Lines
32
Characters to type
644
Tokens
167
Three-star pace
105 tpm

At the three-star pace of 105 tokens a minute, this run takes about 95 seconds.

Type this snippet

Step 2 of 4 in Locking, step 5 of 10 in Threads & concurrency.

← Previous Next →