Last active
March 29, 2017 06:30
-
-
Save DaoWen/c2bb8c1b7ae75791255a46e2a0682d94 to your computer and use it in GitHub Desktop.
consistently segfaults in pthread_create on os x but not on linux
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <assert.h> | |
#include <pthread.h> | |
#include <inttypes.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdbool.h> | |
#include <unistd.h> | |
#define N 10 | |
#define RCHECK(expr) \ | |
do { \ | |
int _rcheck_expr_return_value = expr; \ | |
if (_rcheck_expr_return_value != 0) { \ | |
fprintf(stderr, "FAILED CALL: " #expr "\n"); \ | |
abort(); \ | |
} \ | |
} while (0); | |
typedef struct wait_state_st { | |
volatile intptr_t val; | |
pthread_t other; | |
pthread_mutex_t lock; | |
pthread_cond_t cond; | |
struct wait_state_st *next; | |
} wait_state; | |
pthread_mutex_t wait_list_lock = PTHREAD_MUTEX_INITIALIZER; | |
wait_state *wait_list_head = NULL; | |
volatile int active = 0; | |
static inline void push_thread(wait_state *ws) { | |
RCHECK(pthread_mutex_lock(&wait_list_lock)); | |
ws->next = wait_list_head; | |
wait_list_head = ws; | |
RCHECK(pthread_mutex_unlock(&wait_list_lock)); | |
} | |
static inline wait_state *pop_thread(void) { | |
wait_state *ws = NULL; | |
while (true) { | |
RCHECK(pthread_mutex_lock(&wait_list_lock)); | |
if (wait_list_head) { | |
ws = wait_list_head; | |
wait_list_head = ws->next; | |
} | |
RCHECK(pthread_mutex_unlock(&wait_list_lock)); | |
if (ws) return ws; | |
usleep(1000); | |
} | |
assert(!"UNREACHABLE"); | |
} | |
intptr_t thread_suspend(int count) { | |
intptr_t sum = 0; | |
// WAIT TO BE WOKEN UP "count" TIMES | |
for (int i = 0; i < count; i++) { | |
wait_state ws; | |
ws.val = -1; | |
ws.other = pthread_self(); | |
RCHECK(pthread_mutex_init(&ws.lock, NULL)); | |
RCHECK(pthread_cond_init(&ws.cond, NULL)); | |
RCHECK(pthread_mutex_lock(&ws.lock)); | |
push_thread(&ws); | |
while (ws.val < 0) { | |
RCHECK(pthread_cond_wait(&ws.cond, &ws.lock)); | |
} | |
assert(ws.other != pthread_self()); | |
pthread_join(ws.other, NULL); | |
sum += ws.val; | |
RCHECK(pthread_mutex_unlock(&ws.lock)); | |
} | |
return sum; | |
} | |
void thread_signal(intptr_t x) { | |
// wake up the suspended thread | |
__sync_fetch_and_add(&active, -1); | |
wait_state *ws = pop_thread(); | |
RCHECK(pthread_mutex_lock(&ws->lock)); | |
ws->val = x; | |
ws->other = pthread_self(); | |
RCHECK(pthread_cond_signal(&ws->cond)); | |
RCHECK(pthread_mutex_unlock(&ws->lock)); | |
} | |
void *fib(void *arg) { | |
intptr_t n = (intptr_t)arg; | |
if (n > 1) { | |
pthread_t t1, t2; | |
__sync_fetch_and_add(&active, 2); | |
RCHECK(pthread_create(&t1, NULL, fib, (void *)(n - 1))); | |
RCHECK(pthread_create(&t2, NULL, fib, (void *)(n - 2))); | |
intptr_t sum = thread_suspend(2); | |
thread_signal(sum); | |
} | |
else { | |
thread_signal(n); | |
} | |
return NULL; | |
} | |
intptr_t pure_fib(intptr_t n) { | |
if (n < 2) return n; | |
return pure_fib(n-1) + pure_fib(n-2); | |
} | |
int main(int argc, char *argv[]) { | |
printf("EXPECTED = %" PRIdPTR "\n", pure_fib(N)); | |
assert("START" && wait_list_head == NULL); | |
active = 1; | |
pthread_t t; | |
RCHECK(pthread_create(&t, NULL, fib, (void *)N)); | |
while (active > 0) { usleep(100000); } | |
intptr_t sum = thread_suspend(1); | |
printf("SUM = %" PRIdPTR "\n", sum); | |
printf("DONE %p\n", wait_list_head); | |
assert("END" && wait_list_head == NULL); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I wonder if the problem described in http://stackoverflow.com/a/36774524 is being triggered here...