diff --git a/src/compat/process.c b/src/compat/process.c index 0839299..ba7055c 100644 --- a/src/compat/process.c +++ b/src/compat/process.c @@ -88,6 +88,8 @@ static int get_win_status(HANDLE handle) { } return sig ? sig : exit_code << 8; } +#else +# include #endif struct worker_context g_worker_context = {.test = NULL}; @@ -115,9 +117,7 @@ static struct full_context local_ctx; # error Unsupported compiler. Use GCC or Clang under *nixes. # endif -static void handle_sigchld(CR_UNUSED int sig) { - assert(sig == SIGCHLD); - +static void handle_sigchld_pump(void) { int fd = g_worker_pipe->fds[1]; pid_t pid; int status; @@ -142,8 +142,49 @@ static void handle_sigchld(CR_UNUSED int sig) { } } } + +/* + * This child reaping logic is a dirty hack to prevent deadlocks + * when the pipe is overflowing and a child terminates. + * + * (Windows doesn't have this issue as the waitpid logic is threaded by + * RegisterWaitForSingleObject) + * + * REMOVE WHEN REFACTORING I/O LAYER + */ +static pthread_t child_pump; +static bool child_pump_running; + +static void *chld_pump_thread_main(void *nil) { + child_pump_running = true; + + do { + handle_sigchld_pump(); + usleep(1000); + } while (child_pump_running); + + return nil; +} #endif +void init_proc_compat(void) { +#ifndef VANILLA_WIN32 + pthread_attr_t attr; + int err = pthread_attr_init(&attr) + || pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) + || pthread_create(&child_pump, &attr, chld_pump_thread_main, NULL) + || pthread_attr_destroy(&attr); + if (err) { + perror(0); + exit(1); + } +#endif +} + +void free_proc_compat(void) { + child_pump_running = false; +} + #ifdef VANILLA_WIN32 struct wait_context { HANDLE wait_handle; @@ -251,16 +292,6 @@ int resume_child(void) { free(param); return 1; #else -# if defined(__unix__) || defined(__APPLE__) - struct sigaction sa; - sa.sa_handler = &handle_sigchld; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; - if (sigaction(SIGCHLD, &sa, 0) == -1) { - perror(0); - exit(1); - } -# endif return 0; #endif } diff --git a/src/compat/process.h b/src/compat/process.h index 1259e7e..193c12b 100644 --- a/src/compat/process.h +++ b/src/compat/process.h @@ -60,4 +60,7 @@ bool is_current_process(s_proc_handle *proc); unsigned long long get_process_id(void); unsigned long long get_process_id_of(s_proc_handle *proc); +void init_proc_compat(void); +void free_proc_compat(void); + #endif /* !COMPAT_PROCESS_H_ */ diff --git a/src/core/runner.c b/src/core/runner.c index 091a7e4..53238db 100644 --- a/src/core/runner.c +++ b/src/core/runner.c @@ -452,6 +452,7 @@ static int criterion_run_all_tests_impl(struct criterion_test_set *set) { strerror(errno)); abort(); } + init_proc_compat(); struct criterion_global_stats *stats = stats_init(); run_tests_async(set, stats); @@ -466,6 +467,7 @@ static int criterion_run_all_tests_impl(struct criterion_test_set *set) { log(post_all, stats); cleanup: + free_proc_compat(); sfree(g_worker_pipe); sfree(stats); return result;