Skip to content

Instantly share code, notes, and snippets.

@nicky-zs
Last active September 5, 2018 06:00
Show Gist options
  • Save nicky-zs/c5b3e3324658cd84e21a065ff670ebcf to your computer and use it in GitHub Desktop.
Save nicky-zs/c5b3e3324658cd84e21a065ff670ebcf to your computer and use it in GitHub Desktop.
The classic memory model problems on Intel x86-64 multi-core systems. Return values of system calls or library calls are NOT CHECKED because they are only demos.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
pthread_t t1, t2;
sem_t sem_start1, sem_start2, sem_end;
int A, B, X, Y;
void *f1(void *arg) {
for (;;) {
sem_wait(&sem_start1);
A = 1;
Y = B;
sem_post(&sem_end);
}
}
void *f2(void *arg) {
for (;;) {
sem_wait(&sem_start2);
B = 1;
X = A;
sem_post(&sem_end);
}
}
int main(int argc, char *argv[]) {
sem_init(&sem_start1, 0, 0);
sem_init(&sem_start2, 0, 0);
sem_init(&sem_end, 0, 0);
pthread_create(&t1, NULL, f1, NULL);
pthread_create(&t2, NULL, f2, NULL);
for (;;) {
A = B = X = Y = 0;
sem_post(&sem_start1);
sem_post(&sem_start2);
sem_wait(&sem_end);
sem_wait(&sem_end);
if (X == 0 && Y == 0) {
printf("%lu: X = %d, Y = %d\n", time(NULL), X, Y);
}
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
pthread_t t1, t2;
sem_t sem_start1, sem_start2, sem_end;
int A, B, X, Y;
void *f1(void *arg) {
for (;;) {
sem_wait(&sem_start1);
A = 1;
__sync_synchronize(); // or _mm_mfence() from emmintrin.h
Y = B;
sem_post(&sem_end);
}
}
void *f2(void *arg) {
for (;;) {
sem_wait(&sem_start2);
B = 1;
__sync_synchronize(); // or _mm_mfence() from emmintrin.h
X = A;
sem_post(&sem_end);
}
}
int main(int argc, char *argv[]) {
sem_init(&sem_start1, 0, 0);
sem_init(&sem_start2, 0, 0);
sem_init(&sem_end, 0, 0);
pthread_create(&t1, NULL, f1, NULL);
pthread_create(&t2, NULL, f2, NULL);
for (;;) {
A = B = X = Y = 0;
sem_post(&sem_start1);
sem_post(&sem_start2);
sem_wait(&sem_end);
sem_wait(&sem_end);
if (X == 0 && Y == 0) {
printf("%lu: X = %d, Y = %d\n", time(NULL), X, Y);
}
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
pthread_t t1, t2;
sem_t sem_start1, sem_start2, sem_end;
int A, B, X;
void *f1(void *arg) {
for (;;) {
sem_wait(&sem_start1);
A = 1;
X = B;
sem_post(&sem_end);
}
}
void *f2(void *arg) {
for (;;) {
sem_wait(&sem_start2);
B = 1;
X = A;
sem_post(&sem_end);
}
}
int main(int argc, char *argv[]) {
sem_init(&sem_start1, 0, 0);
sem_init(&sem_start2, 0, 0);
sem_init(&sem_end, 0, 0);
pthread_create(&t1, NULL, f1, NULL);
pthread_create(&t2, NULL, f2, NULL);
for (;;) {
A = B = X = 0;
sem_post(&sem_start1);
sem_post(&sem_start2);
sem_wait(&sem_end);
sem_wait(&sem_end);
if (X == 0) {
printf("%lu: X = %d\n", time(NULL), X);
}
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
pthread_t t1, t2;
sem_t sem_start1, sem_start2, sem_end;
int A, B;
volatile int X;
void *f1(void *arg) {
for (;;) {
sem_wait(&sem_start1);
A = 1;
__sync_synchronize(); // or _mm_mfence() from emmintrin.h
while (!__sync_bool_compare_and_swap(&X, X, B)) // CAS, no intrinsic replacement
;
sem_post(&sem_end);
}
}
void *f2(void *arg) {
for (;;) {
sem_wait(&sem_start2);
B = 1;
__sync_synchronize(); // or _mm_mfence() from emmintrin.h
while (!__sync_bool_compare_and_swap(&X, X, A)) // CAS, no intrinsic replacement
;
sem_post(&sem_end);
}
}
int main(int argc, char *argv[]) {
sem_init(&sem_start1, 0, 0);
sem_init(&sem_start2, 0, 0);
sem_init(&sem_end, 0, 0);
pthread_create(&t1, NULL, f1, NULL);
pthread_create(&t2, NULL, f2, NULL);
for (;;) {
A = B = X = 0;
sem_post(&sem_start1);
sem_post(&sem_start2);
sem_wait(&sem_end);
sem_wait(&sem_end);
if (X == 0) {
printf("%lu: X = %d\n", time(NULL), X);
}
}
return 0;
}
@nicky-zs
Copy link
Author

nicky-zs commented Aug 24, 2018

All the codes can be translated into Java and works on JVMs. However, the memory model of JVM seems not to be as strong as x86 platforms because it seems that JVMs allow reordering stores with stores or loads.
PS: On JVMs, a volatile write can act as a full memory fence for there will be a mfence instruction after any writes of a volatile variable. And AtomicXxx.set() has the memory effects of writing a volatile variable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment