Created
July 30, 2025 16:13
-
-
Save skeeto/9309c63ffe7c34ff790a0a47bc7e40ed to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// $ cc -g3 -fsanitize=thread,undefined test.c | |
// | |
// No data races detected because the mutex is unnecessary. To verify TSan | |
// is indeed working, modify newqueue to initialize improperly, such as | |
// setting a zero field to non-zero. This will cause a data race. | |
#define lenof(a) (int)(sizeof(a) / sizeof(*(a))) | |
typedef struct { | |
int count; | |
} Sem; | |
static void post(Sem *s) | |
{ | |
__atomic_add_fetch(&s->count, 1, __ATOMIC_RELEASE); | |
} | |
static void wait(Sem *s) | |
{ | |
for (;;) { | |
int old = __atomic_load_n(&s->count, __ATOMIC_ACQUIRE); | |
while (old) { | |
bool r = __atomic_compare_exchange_n( | |
&s->count, &old, old-1, false, | |
__ATOMIC_ACQUIRE, | |
__ATOMIC_RELAXED | |
); | |
if (r) return; | |
} | |
} | |
} | |
typedef struct { | |
Sem filled; | |
Sem empty; | |
int read; | |
int write; | |
long slots[1<<5]; | |
} Queue; | |
static Queue newqueue() | |
{ | |
Queue q = {}; | |
q.empty = (Sem){lenof(q.slots)}; | |
return q; | |
} | |
static void push(Queue *q, long v) | |
{ | |
int mask = lenof(q->slots) - 1; | |
wait(&q->empty); | |
q->slots[(q->write += 1u)&mask] = v; | |
post(&q->filled); | |
} | |
static long pop(Queue *q) | |
{ | |
int mask = lenof(q->slots) - 1; | |
wait(&q->filled); | |
long v = q->slots[(q->read += 1u)&mask]; | |
post(&q->empty); | |
return v; | |
} | |
// Test | |
#include <assert.h> | |
#include <pthread.h> | |
enum { N = 1'000'000 }; | |
static void *producer(void *arg) | |
{ | |
Queue *q = arg; | |
for (int i = 1; i <= N; i++) { | |
push(q, i); | |
} | |
push(q, 0); | |
return 0; | |
} | |
int main() | |
{ | |
Queue q = newqueue(); | |
pthread_t th; | |
pthread_create(&th, 0, producer, &q); | |
pthread_detach(th); | |
long long sum = 0; | |
for (long v; (v = pop(&q));) { | |
sum += v; | |
} | |
assert(sum == (N * (N + 1LL))/2); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment