Skip to content

Instantly share code, notes, and snippets.

@skeeto
Last active April 17, 2022 15:40
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save skeeto/c63b9ddf2c599eeca86356325b93f3a7 to your computer and use it in GitHub Desktop.
Save skeeto/c63b9ddf2c599eeca86356325b93f3a7 to your computer and use it in GitHub Desktop.
Observe memory reordering
#include <pthread.h>
#include <stdatomic.h>
#include <stdio.h>
#ifndef N
# define N 10000000
#endif
// Spin-lock barrier for two threads. Initialize to zero.
static void
barrier(_Atomic unsigned *barrier)
{
unsigned v = ++*barrier;
if (v & 1) {
for (v &= 2; (*barrier&2) == v;);
}
}
struct vars {
_Atomic unsigned barrier;
int w0, w1;
int r0, r1;
};
static int
experiment(int *w0, int *w1)
{
int r1;
#if __amd64__
__asm volatile (
"movl $1, %1\n"
"movl %2, %0\n"
: "=r"(r1), "=m"(*w0)
: "m"(*w1)
);
#elif __aarch64__
r1 = 1;
__asm volatile (
"str %w0, %1\n"
"ldr %w0, %2\n"
: "+r"(r1), "=m"(*w0)
: "m"(*w1)
);
#else
atomic_store_explicit((atomic_int *)w0, 1, memory_order_relaxed);
r1 = atomic_load_explicit((atomic_int *)w1, memory_order_relaxed);
#endif
return r1;
}
static void *
thread(void *arg)
{
struct vars *t = arg;
for (int i = 0; i < N; i++) {
barrier(&t->barrier);
t->r0 = experiment(&t->w1, &t->w0);
barrier(&t->barrier);
}
return 0;
}
int
main(void)
{
int count = 0;
struct vars t[1] = {{0}};
pthread_t thr;
pthread_create(&thr, 0, thread, t);
for (int i = 0; i < N; i++) {
t->w0 = t->w1 = 0;
barrier(&t->barrier);
t->r1 = experiment(&t->w0, &t->w1);
barrier(&t->barrier);
count += !t->r0 && !t->r1;
}
pthread_join(thr, 0);
printf("%d / %d (%.9g%%)\n", count, N, 100.0*count/N);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment