/* race.c -- two defects a compiler will not diagnose, and a tool will.
 *
 *     cc -O1 -g -std=c11 -pthread -fsanitize=thread -o race data/sync/race.c
 *     ./race race       # unsynchronised access to a shared object
 *     ./race deadlock   # two mutexes acquired in opposite orders
 *
 * Both programs run to completion and print the right answer often enough to
 * pass a test suite. That is the point: neither failure is reliably
 * observable, so the tool has to reason about the schedule rather than watch
 * one execution.
 */
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <string.h>

static long counter;                       /* deliberately not _Atomic */
static pthread_mutex_t a = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t b = PTHREAD_MUTEX_INITIALIZER;

static void *racer(void *unused)
{
    (void)unused;
    for (int i = 0; i < 10000; i++)
        counter++;                          /* read-modify-write, unguarded */
    return NULL;
}

static void *ab_order(void *unused)
{
    (void)unused;
    pthread_mutex_lock(&a);
    pthread_mutex_lock(&b);                 /* A then B */
    pthread_mutex_unlock(&b);
    pthread_mutex_unlock(&a);
    return NULL;
}

static void *ba_order(void *unused)
{
    (void)unused;
    pthread_mutex_lock(&b);
    pthread_mutex_lock(&a);                 /* B then A -- the inversion */
    pthread_mutex_unlock(&a);
    pthread_mutex_unlock(&b);
    return NULL;
}

int main(int argc, char **argv)
{
    const char *mode = argc > 1 ? argv[1] : "race";
    pthread_t t1, t2;

    if (strcmp(mode, "deadlock") == 0) {
        pthread_create(&t1, NULL, ab_order, NULL);
        pthread_join(t1, NULL);             /* serialised: no hang, still wrong */
        pthread_create(&t2, NULL, ba_order, NULL);
        pthread_join(t2, NULL);
        printf("both orders completed\n");
        return 0;
    }

    pthread_create(&t1, NULL, racer, NULL);
    pthread_create(&t2, NULL, racer, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("counter = %ld (expected 20000)\n", counter);
    return 0;
}
