Skip to content

Instantly share code, notes, and snippets.

@skeeto
Created July 30, 2025 16:13
Show Gist options
  • Save skeeto/9309c63ffe7c34ff790a0a47bc7e40ed to your computer and use it in GitHub Desktop.
Save skeeto/9309c63ffe7c34ff790a0a47bc7e40ed to your computer and use it in GitHub Desktop.
// $ 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