- 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 |