Skip to content

Instantly share code, notes, and snippets.

@b4284
Last active January 5, 2019 07:25
Show Gist options
  • Save b4284/9d3888d522f420f268aff1fccf99dc82 to your computer and use it in GitHub Desktop.
Save b4284/9d3888d522f420f268aff1fccf99dc82 to your computer and use it in GitHub Desktop.
Simulating corotines with POSIX threads

Simulating coroutines with POSIX threads

  • message.scm -- guile implementation
  • message.c -- c implementation

Run test.sh to run both implementations.

If "Aborted..." shows up means there has been inconsistent results.

#include <stdio.h>
#include <pthread.h>
pthread_mutex_t mtx1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mtx2 = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv1;
pthread_cond_t cv2;
pthread_cond_t cv3;
pthread_cond_t cv4;
int v = 0;
void BA(void) {
pthread_cond_signal(&cv1);
pthread_cond_wait(&cv3, &mtx1);
}
void AB(void) {
pthread_cond_signal(&cv3);
pthread_cond_wait(&cv1, &mtx1);
}
void BC(void) {
pthread_cond_signal(&cv2);
pthread_cond_wait(&cv4, &mtx2);
}
void CB(void) {
pthread_cond_signal(&cv4);
pthread_cond_wait(&cv2, &mtx2);
}
void *A(void *args) {
pthread_mutex_lock(&mtx1);
for (;;) {
AB();
v += 1;
printf("A: v=%d\n", v);
}
}
void *C(void *args) {
pthread_mutex_lock(&mtx2);
for (;;) {
CB();
v += 1;
printf("C: v=%d\n", v);
}
}
int main() {
pthread_t t1, t2;
pthread_mutex_lock(&mtx1);
pthread_mutex_lock(&mtx2);
pthread_create(&t1, NULL, A, NULL);
pthread_create(&t2, NULL, C, NULL);
pthread_cond_wait(&cv3, &mtx1);
pthread_cond_wait(&cv4, &mtx2);
for (;;) {
v += 1;
printf("B: v=%d\n", v);
BA();
BC();
}
return 0;
}
(use-modules (ice-9 threads))
(define mtx1 (make-mutex))
(define mtx2 (make-mutex))
(define cv1 (make-condition-variable)) ;; cv1: B -> A
(define cv2 (make-condition-variable)) ;; cv2: B -> C
(define cv3 (make-condition-variable)) ;; cv3: A -> B
(define cv4 (make-condition-variable)) ;; cv4: C -> B
(define v 0)
(lock-mutex mtx1) ;; block t1
(lock-mutex mtx2) ;; block t2
(define (B->A)
(signal-condition-variable cv1) ;; signal B -> A is going to happen
(wait-condition-variable cv3 mtx1)) ;; release mtx1 and wait for A -> B
(define (B->C)
(signal-condition-variable cv2) ;; signal B -> C is going to happen
(wait-condition-variable cv4 mtx2)) ;; release mtx2 and wait for C -> B
(define (A->B)
(signal-condition-variable cv3) ;; signal A -> B is going to happen
(wait-condition-variable cv1 mtx1)) ;; release mtx1 and wait for B -> A
(define (C->B)
(signal-condition-variable cv4) ;; signal C -> B is going to happen
(wait-condition-variable cv2 mtx2)) ;; release mtx2 and wait for B -> C
(call-with-new-thread
(lambda ()
(lock-mutex mtx1) ;; wait for B release mtx1
(let A ()
(A->B)
(set! v (+ v 1))
(format #t "A: v=~a~%" v)
(A))))
(call-with-new-thread
(lambda ()
(lock-mutex mtx2) ;; wait for B to release mtx2
(let C ()
(C->B)
(set! v (+ v 1))
(format #t "C: v=~a~%" v)
(C))))
(wait-condition-variable cv3 mtx1) ;; trigger first execution of A, resume by A->B
(wait-condition-variable cv4 mtx2) ;; trigger first execution of C, resume by C->B
(let B ()
(set! v (+ v 1))
(format #t "B: v=~a~%" v)
(B->A)
(B->C)
(B))
#!/bin/sh
# Guile run
echo "========== guile run =========="
for (( i=1 ; i<100 ; i+=1 )) do
echo "=== Run $i ==="
MD5_1=$(guile message.scm |head -10000 |md5sum)
if [[ $i -gt 1 && "$MD5_2" != "$MD5_1" ]]; then
echo "Aborted..."
break
fi
MD5_2="$MD5_1"
done
# C run
echo "========== C run =========="
gcc -pthread -o message message.c
for (( i=1 ; i<100 ; i+=1 )) do
echo "=== Run $i ==="
MD5_1=$(./message |head -10000 |md5sum)
if [[ $i -gt 1 && "$MD5_2" != "$MD5_1" ]]; then
echo "Aborted..."
break
fi
MD5_2="$MD5_1"
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment