diff --git a/base/inference.jl b/base/inference.jl index 4815b38a6fc76..981f47cb7d71c 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -1560,6 +1560,7 @@ function typeinf_loop(frame) frame.inworkq || typeinf_frame(frame) return end + ccall(:jl_sigatomic_begin, Void, ()) try in_typeinf_loop = true # the core type-inference algorithm @@ -1606,6 +1607,7 @@ function typeinf_loop(frame) println(ex) ccall(:jlbacktrace, Void, ()) end + ccall(:jl_sigatomic_end, Void, ()) nothing end diff --git a/src/ast.c b/src/ast.c index f68ee35f730fa..a22e0b3a74c2f 100644 --- a/src/ast.c +++ b/src/ast.c @@ -230,6 +230,7 @@ static jl_ast_context_list_t *jl_ast_ctx_freed = NULL; static jl_ast_context_t *jl_ast_ctx_enter(void) { + JL_SIGATOMIC_BEGIN(); JL_LOCK_NOGC(&flisp_lock); jl_ast_context_list_t *node; jl_ast_context_t *ctx; @@ -267,6 +268,7 @@ static jl_ast_context_t *jl_ast_ctx_enter(void) static void jl_ast_ctx_leave(jl_ast_context_t *ctx) { + JL_SIGATOMIC_END(); if (--ctx->ref) return; JL_LOCK_NOGC(&flisp_lock); @@ -285,6 +287,8 @@ void jl_init_frontend(void) jl_ast_main_ctx.task = jl_current_task; jl_ast_context_list_insert(&jl_ast_ctx_using, &jl_ast_main_ctx.list); jl_init_ast_ctx(&jl_ast_main_ctx); + // To match the one in jl_ast_ctx_leave + JL_SIGATOMIC_BEGIN(); jl_ast_ctx_leave(&jl_ast_main_ctx); } @@ -758,6 +762,7 @@ jl_value_t *jl_parse_eval_all(const char *fname, size_t len, form = scm_to_julia(fl_ctx, expansion, 0); jl_sym_t *head = NULL; if (jl_is_expr(form)) head = ((jl_expr_t*)form)->head; + JL_SIGATOMIC_END(); if (head == jl_incomplete_sym) jl_errorf("syntax: %s", jl_string_data(jl_exprarg(form,0))); else if (head == error_sym) @@ -766,6 +771,7 @@ jl_value_t *jl_parse_eval_all(const char *fname, size_t len, jl_lineno = jl_unbox_long(jl_exprarg(form,0)); else result = jl_toplevel_eval_flex(form, 1); + JL_SIGATOMIC_BEGIN(); ast = cdr_(ast); } } diff --git a/src/builtins.c b/src/builtins.c index 542cd80e2a365..a175bff4077a8 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -194,7 +194,7 @@ JL_CALLABLE(jl_f_throw) JL_DLLEXPORT void jl_enter_handler(jl_handler_t *eh) { - JL_SIGATOMIC_BEGIN(); + // Must have no safepoint eh->prev = jl_current_task->eh; eh->gcstack = jl_pgcstack; #ifdef JULIA_ENABLE_THREADING @@ -202,9 +202,6 @@ JL_DLLEXPORT void jl_enter_handler(jl_handler_t *eh) eh->locks_len = jl_current_task->locks.len; #endif jl_current_task->eh = eh; - // TODO: this should really go after setjmp(). see comment in - // ctx_switch in task.c. - JL_SIGATOMIC_END(); } JL_DLLEXPORT void jl_pop_handler(int n) diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index e0a74a3a4b2e1..a4e957bdfe93f 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -1674,9 +1674,7 @@ void RTDyldMemoryManagerUnix::registerEHFrames(uint8_t *Addr, di->start_ip = start_ip; di->end_ip = end_ip; - JL_SIGATOMIC_BEGIN(); _U_dyn_register(di); - JL_SIGATOMIC_END(); } void RTDyldMemoryManagerUnix::deregisterEHFrames(uint8_t *Addr, diff --git a/src/init.c b/src/init.c index c16f821cdfa2c..9c75389616658 100644 --- a/src/init.c +++ b/src/init.c @@ -528,6 +528,11 @@ static void jl_resolve_sysimg_location(JL_IMAGE_SEARCH rel) jl_options.load = abspath(jl_options.load); } +static void jl_set_io_wait(int v) +{ + jl_get_ptls_states()->io_wait = v; +} + void _julia_init(JL_IMAGE_SEARCH rel) { #ifdef JULIA_ENABLE_THREADING @@ -536,6 +541,7 @@ void _julia_init(JL_IMAGE_SEARCH rel) #endif jl_safepoint_init(); libsupport_init(); + ios_set_io_wait_func = jl_set_io_wait; jl_io_loop = uv_default_loop(); // this loop will internal events (spawning process etc.), // best to call this first, since it also initializes libuv restore_signals(); diff --git a/src/jlapi.c b/src/jlapi.c index 479132d996c59..4f4fcdf79ce55 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -229,7 +229,7 @@ JL_DLLEXPORT void jl_sigatomic_begin(void) JL_DLLEXPORT void jl_sigatomic_end(void) { - if (jl_defer_signal == 0) + if (jl_get_ptls_states()->defer_signal == 0) jl_error("sigatomic_end called in non-sigatomic region"); JL_SIGATOMIC_END(); } diff --git a/src/julia.h b/src/julia.h index 07287c141167b..2fc907bec75e6 100644 --- a/src/julia.h +++ b/src/julia.h @@ -637,6 +637,7 @@ JL_DLLEXPORT void jl_clear_malloc_data(void); // GC write barriers JL_DLLEXPORT void jl_gc_queue_root(jl_value_t *root); // root isa jl_value_t* +// Do NOT put a safepoint here STATIC_INLINE void jl_gc_wb(void *parent, void *ptr) { // parent and ptr isa jl_value_t* @@ -1366,20 +1367,16 @@ JL_DLLEXPORT void jl_yield(void); // async signal handling ------------------------------------------------------ -#include - -JL_DLLEXPORT extern volatile sig_atomic_t jl_signal_pending; -JL_DLLEXPORT extern volatile sig_atomic_t jl_defer_signal; - -#define JL_SIGATOMIC_BEGIN() jl_atomic_fetch_add(&jl_defer_signal, 1) -#define JL_SIGATOMIC_END() \ - do { \ - if (jl_atomic_fetch_add(&jl_defer_signal, -1) == 1 \ - && jl_signal_pending != 0) { \ - jl_signal_pending = 0; \ - jl_sigint_action(); \ +#define JL_SIGATOMIC_BEGIN() do { \ + jl_get_ptls_states()->defer_signal++; \ + jl_signal_fence(); \ + } while (0) +#define JL_SIGATOMIC_END() do { \ + jl_signal_fence(); \ + if (--jl_get_ptls_states()->defer_signal == 0) { \ + jl_sigint_safepoint(); \ } \ - } while(0) + } while (0) JL_DLLEXPORT void jl_sigint_action(void); JL_DLLEXPORT void jl_install_sigint_handler(void); @@ -1485,7 +1482,6 @@ STATIC_INLINE void jl_eh_restore_state(jl_handler_t *eh) { // This function should **NOT** have any safepoint before restoring // the GC state at the end. - JL_SIGATOMIC_BEGIN(); jl_current_task->eh = eh->prev; jl_pgcstack = eh->gcstack; #ifdef JULIA_ENABLE_THREADING @@ -1499,7 +1495,6 @@ STATIC_INLINE void jl_eh_restore_state(jl_handler_t *eh) // This should be the last since this can trigger a safepoint // that throws a SIGINT. jl_gc_state_save_and_set(eh->gc_state); - JL_SIGATOMIC_END(); } JL_DLLEXPORT void jl_enter_handler(jl_handler_t *eh); diff --git a/src/julia_threads.h b/src/julia_threads.h index a64fb1f43fd3a..0179f4a898c8c 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -26,6 +26,7 @@ #ifndef _OS_WINDOWS_ # include #endif +#include // This includes all the thread local states we care about for a thread. #define JL_MAX_BT_SIZE 80000 @@ -43,6 +44,7 @@ typedef struct _jl_tls_states_t { volatile int8_t gc_state; volatile int8_t in_finalizer; int8_t disable_gc; + volatile sig_atomic_t defer_signal; struct _jl_thread_heap_t *heap; struct _jl_module_t *current_module; struct _jl_task_t *volatile current_task; @@ -58,6 +60,12 @@ typedef struct _jl_tls_states_t { size_t bt_size; // JL_MAX_BT_SIZE + 1 elements long uintptr_t *bt_data; + // Atomically set by the sender, reset by the handler. + volatile sig_atomic_t signal_request; + // Allow the sigint to be raised asynchronously + // this is limited to the few places we do synchronous IO + // we can make this more general (similar to defer_signal) if necessary + volatile sig_atomic_t io_wait; } jl_tls_states_t; #ifdef __MIC__ @@ -295,6 +303,12 @@ JL_DLLEXPORT JL_CONST_FUNC jl_tls_states_t *(jl_get_ptls_states)(void); jl_signal_fence(); \ (void)safepoint_load; \ } while (0) +#define jl_sigint_safepoint() do { \ + jl_signal_fence(); \ + size_t safepoint_load = jl_get_ptls_states()->safepoint[-1]; \ + jl_signal_fence(); \ + (void)safepoint_load; \ + } while (0) #ifndef JULIA_ENABLE_THREADING extern JL_DLLEXPORT jl_tls_states_t jl_tls_states; #define jl_get_ptls_states() (&jl_tls_states) diff --git a/src/safepoint.c b/src/safepoint.c index 6d94895414e66..6644f32b63c61 100644 --- a/src/safepoint.c +++ b/src/safepoint.c @@ -14,7 +14,10 @@ extern "C" { #endif -/* static uint32_t jl_signal_pending = 0; */ +// 0: no sigint is pending +// 1: at least one sigint is pending, only the sigint page is enabled. +// 2: at least one sigint is pending, both safepoint pages are enabled. +JL_DLLEXPORT sig_atomic_t jl_signal_pending = 0; volatile uint32_t jl_gc_running = 0; char *jl_safepoint_pages = NULL; // The number of safepoints enabled on the three pages. @@ -168,9 +171,61 @@ void jl_safepoint_wait_gc(void) #endif } -void jl_safepoint_enable_sigint(void); -void jl_safepoint_defer_sigint(void); -int jl_safepoint_consume_sigint(void); +void jl_safepoint_enable_sigint(void) +{ + jl_mutex_lock_nogc(&safepoint_lock); + // Make sure both safepoints are enabled exactly once for SIGINT. + switch (jl_signal_pending) { + default: + assert(0 && "Shouldn't happen."); + case 0: + // Enable SIGINT page + jl_safepoint_enable(0); + // fall through + case 1: + // SIGINT page is enabled, enable GC page + jl_safepoint_enable(1); + // fall through + case 2: + jl_signal_pending = 2; + } + jl_mutex_unlock_nogc(&safepoint_lock); +} + +void jl_safepoint_defer_sigint(void) +{ + jl_mutex_lock_nogc(&safepoint_lock); + // Make sure the GC safepoint is disabled for SIGINT. + if (jl_signal_pending == 2) { + jl_safepoint_disable(1); + jl_signal_pending = 1; + } + jl_mutex_unlock_nogc(&safepoint_lock); +} + +int jl_safepoint_consume_sigint(void) +{ + int has_signal = 0; + jl_mutex_lock_nogc(&safepoint_lock); + // Make sure both safepoints are disabled for SIGINT. + switch (jl_signal_pending) { + default: + assert(0 && "Shouldn't happen."); + case 2: + // Disable gc page + jl_safepoint_disable(1); + // fall through + case 1: + // GC page is disabled, disable SIGINT page + jl_safepoint_disable(0); + has_signal = 1; + // fall through + case 0: + jl_signal_pending = 0; + } + jl_mutex_unlock_nogc(&safepoint_lock); + return has_signal; +} #ifdef __cplusplus } diff --git a/src/signal-handling.c b/src/signal-handling.c index cf92b43681fc9..3c5a97cc1a673 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -23,11 +23,35 @@ static const uint64_t GIGA = 1000000000ULL; JL_DLLEXPORT void jl_profile_stop_timer(void); JL_DLLEXPORT int jl_profile_start_timer(void); -volatile sig_atomic_t jl_signal_pending = 0; -volatile sig_atomic_t jl_defer_signal = 0; +static uint64_t jl_last_sigint_trigger = 0; +static void jl_clear_force_sigint(void) +{ + jl_last_sigint_trigger = 0; +} -int exit_on_sigint = 0; -JL_DLLEXPORT void jl_exit_on_sigint(int on) {exit_on_sigint = on;} +static int jl_check_force_sigint(void) +{ + static double accum_weight = 0; + uint64_t cur_time = uv_hrtime(); + uint64_t dt = cur_time - jl_last_sigint_trigger; + uint64_t last_t = jl_last_sigint_trigger; + jl_last_sigint_trigger = cur_time; + if (last_t == 0) { + accum_weight = 0; + return 0; + } + double new_weight = accum_weight * exp(-(dt / 1e9)) + 0.3; + if (!isnormal(new_weight)) + new_weight = 0; + accum_weight = new_weight; + return new_weight > 1; +} + +static int exit_on_sigint = 0; +JL_DLLEXPORT void jl_exit_on_sigint(int on) +{ + exit_on_sigint = on; +} // what to do on SIGINT JL_DLLEXPORT void jl_sigint_action(void) diff --git a/src/signals-mach.c b/src/signals-mach.c index de3e7513261f1..cd49a18aa88c6 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -33,6 +33,28 @@ void jl_mach_gc_end(void) } suspended_threads.len = 0; } + +// Suspend the thread and return `1` if the GC is running. +// Otherwise return `0` +static int jl_mach_gc_wait(jl_tls_states_t *ptls, + mach_port_t thread, int16_t tid) +{ + jl_mutex_lock_nogc(&safepoint_lock); + if (!jl_gc_running) { + // GC is done before we get the message or the safepoint is enabled + // for SIGINT. + jl_mutex_unlock_nogc(&safepoint_lock); + return 0; + } + // Otherwise, set the gc state of the thread, suspend and record it + int8_t gc_state = ptls->gc_state; + jl_atomic_store_release(&ptls->gc_state, JL_GC_STATE_WAITING); + uintptr_t item = tid | (((uintptr_t)gc_state) << 16); + arraylist_push(&suspended_threads, (void*)item); + thread_suspend(thread); + jl_mutex_unlock_nogc(&safepoint_lock); + return 1; +} #endif static mach_port_t segv_port = 0; @@ -165,24 +187,22 @@ kern_return_t catch_exception_raise(mach_port_t exception_port, kern_return_t ret = thread_get_state(thread, x86_EXCEPTION_STATE64, (thread_state_t)&exc_state, &exc_count); HANDLE_MACH_ERROR("thread_get_state", ret); uint64_t fault_addr = exc_state.__faultvaddr; -#ifdef JULIA_ENABLE_THREADING if (jl_addr_is_safepoint(fault_addr)) { - jl_mutex_lock_nogc(&safepoint_lock); - if (!jl_gc_running) { - // GC is done before we get the message, do nothing and return - jl_mutex_unlock_nogc(&safepoint_lock); +#ifdef JULIA_ENABLE_THREADING + if (jl_mach_gc_wait(ptls, thread, tid)) + return KERN_SUCCESS; + if (ptls->tid != 0) return KERN_SUCCESS; +#endif + if (ptls->defer_signal) { + jl_safepoint_defer_sigint(); + } + else if (jl_safepoint_consume_sigint()) { + jl_clear_force_sigint(); + jl_throw_in_thread(tid, thread, jl_interrupt_exception); } - // Otherwise, set the gc state of the thread, suspend and record it - int8_t gc_state = ptls->gc_state; - jl_atomic_store_release(ptls->gc_state, JL_GC_STATE_WAITING); - uintptr_t item = tid | (((uintptr_t)gc_state) << 16); - arraylist_push(&suspended_threads, (void*)item); - thread_suspend(thread); - jl_mutex_unlock_nogc(&safepoint_lock); return KERN_SUCCESS; } -#endif #ifdef SEGV_EXCEPTION if (1) { #else @@ -224,9 +244,8 @@ static void attach_exception_port(thread_port_t thread) HANDLE_MACH_ERROR("thread_set_exception_ports", ret); } -static void jl_thread_suspend_and_get_state(int tid, unw_context_t **ctx, int sig) +static void jl_thread_suspend_and_get_state(int tid, unw_context_t **ctx) { - (void)sig; mach_port_t tid_port = pthread_mach_thread_np(jl_all_task_states[tid].system_id); kern_return_t ret = thread_suspend(tid_port); @@ -247,19 +266,36 @@ static void jl_thread_suspend_and_get_state(int tid, unw_context_t **ctx, int si static void jl_thread_resume(int tid, int sig) { mach_port_t thread = pthread_mach_thread_np(jl_all_task_states[tid].system_id); + kern_return_t ret = thread_resume(thread); + HANDLE_MACH_ERROR("thread_resume", ret); +} - if (tid == 0 && sig == SIGINT) { - if (jl_defer_signal) { - jl_signal_pending = sig; - } - else { - jl_signal_pending = 0; - jl_throw_in_thread(tid, thread, jl_interrupt_exception); - } +// Throw jl_interrupt_exception if the master thread is in a signal async region +// or if SIGINT happens too often. +static void jl_try_deliver_sigint(void) +{ + mach_port_t thread = pthread_mach_thread_np(jl_all_task_states[0].system_id); + jl_tls_states_t *ptls = jl_all_task_states[0].ptls; + + kern_return_t ret = thread_suspend(thread); + HANDLE_MACH_ERROR("thread_suspend", ret); + + // This abort `sleep` and other syscall. + ret = thread_abort(thread); + HANDLE_MACH_ERROR("thread_abort", ret); + + jl_safepoint_enable_sigint(); + int force = jl_check_force_sigint(); + if (force || (!ptls->defer_signal && ptls->io_wait)) { + jl_safepoint_consume_sigint(); + if (force) + jl_safe_printf("WARNING: Force throwing a SIGINT\n"); + jl_clear_force_sigint(); + jl_throw_in_thread(0, thread, jl_interrupt_exception); } - kern_return_t ret = thread_resume(thread); - HANDLE_MACH_ERROR("thread_resume", ret) + ret = thread_resume(thread); + HANDLE_MACH_ERROR("thread_resume", ret); } static int profile_started = 0; @@ -339,7 +375,7 @@ void *mach_profile_listener(void *arg) break; unw_context_t *uc; - jl_thread_suspend_and_get_state(i, &uc, -1); + jl_thread_suspend_and_get_state(i, &uc); #ifdef LIBOSXUNWIND /* diff --git a/src/signals-unix.c b/src/signals-unix.c index a2c5f62d59bb8..faca18ad15537 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -38,7 +38,6 @@ unsigned sig_stack_size = SIGSTKSZ; #endif static pthread_t signals_thread; -static volatile int remote_sig; static int is_addr_on_stack(jl_tls_states_t *ptls, void *addr) { @@ -89,13 +88,23 @@ static void segv_handler(int sig, siginfo_t *info, void *context) { assert(sig == SIGSEGV || sig == SIGBUS); -#ifdef JULIA_ENABLE_THREADING if (jl_addr_is_safepoint((uintptr_t)info->si_addr)) { jl_unblock_signal(sig); +#ifdef JULIA_ENABLE_THREADING jl_set_gc_and_wait(); + // Do not raise sigint on worker thread + if (ti_tid != 0) + return; +#endif + if (jl_get_ptls_states()->defer_signal) { + jl_safepoint_defer_sigint(); + } + else if (jl_safepoint_consume_sigint()) { + jl_clear_force_sigint(); + jl_throw(jl_interrupt_exception); + } return; } -#endif if (jl_safe_restore || is_addr_on_stack(jl_get_ptls_states(), info->si_addr)) { // stack overflow, or restarting jl_ jl_unblock_signal(sig); jl_throw(jl_stackovf_exception); @@ -131,51 +140,54 @@ static void allocate_segv_handler(void) } static unw_context_t *volatile signal_context; -static volatile int waiting_for; static pthread_mutex_t in_signal_lock; static pthread_cond_t exit_signal_cond; static pthread_cond_t signal_caught_cond; -static void jl_thread_suspend_and_get_state(int tid, unw_context_t **ctx, int sig) +static void jl_thread_suspend_and_get_state(int tid, unw_context_t **ctx) { pthread_mutex_lock(&in_signal_lock); - remote_sig = sig; - waiting_for = tid; + jl_tls_states_t *ptls = jl_all_task_states[tid].ptls; + jl_atomic_store_release(&ptls->signal_request, 1); pthread_kill(jl_all_task_states[tid].system_id, SIGUSR2); pthread_cond_wait(&signal_caught_cond, &in_signal_lock); // wait for thread to acknowledge - assert(waiting_for == 0); + assert(jl_atomic_load_acquire(&ptls->signal_request) == 0); *ctx = signal_context; } static void jl_thread_resume(int tid, int sig) { (void)sig; - remote_sig = 0; - waiting_for = tid; + jl_tls_states_t *ptls = jl_all_task_states[tid].ptls; + jl_atomic_store_release(&ptls->signal_request, 1); pthread_cond_broadcast(&exit_signal_cond); pthread_cond_wait(&signal_caught_cond, &in_signal_lock); // wait for thread to acknowledge - assert(waiting_for == 0); + assert(jl_atomic_load_acquire(&ptls->signal_request) == 0); pthread_mutex_unlock(&in_signal_lock); } - -static inline void wait_barrier(void) +// Throw jl_interrupt_exception if the master thread is in a signal async region +// or if SIGINT happens too often. +static void jl_try_deliver_sigint(void) { - if (waiting_for < 0) { - if (jl_atomic_fetch_add(&waiting_for, 1) == -1) { - pthread_cond_broadcast(&signal_caught_cond); - } - } - else { - pthread_cond_broadcast(&signal_caught_cond); - waiting_for = 0; - } + jl_tls_states_t *ptls = jl_all_task_states[0].ptls; + jl_safepoint_enable_sigint(); + jl_atomic_store_release(&ptls->signal_request, 2); + // This also makes sure `sleep` is aborted. + pthread_kill(jl_all_task_states[0].system_id, SIGUSR2); } + +// request: +// 0: nothing +// 1: get state +// 3: throw sigint if `!defer_signal && io_wait` or if force throw threshold +// is reached void usr2_handler(int sig, siginfo_t *info, void *ctx) { ucontext_t *context = (ucontext_t*)ctx; - if ((remote_sig > 0 && waiting_for < 0) || waiting_for == ti_tid) { - int realsig = remote_sig; + jl_tls_states_t *ptls = jl_get_ptls_states(); + sig_atomic_t request = jl_atomic_exchange(&ptls->signal_request, 0); + if (request == 1) { #ifdef __APPLE__ signal_context = (unw_context_t*)&context->uc_mcontext->__ss; #else @@ -183,20 +195,25 @@ void usr2_handler(int sig, siginfo_t *info, void *ctx) #endif pthread_mutex_lock(&in_signal_lock); - wait_barrier(); + pthread_cond_broadcast(&signal_caught_cond); pthread_cond_wait(&exit_signal_cond, &in_signal_lock); - wait_barrier(); + request = jl_atomic_exchange(&ptls->signal_request, 0); + assert(request == 1); + (void)request; + pthread_cond_broadcast(&signal_caught_cond); pthread_mutex_unlock(&in_signal_lock); - - if (ti_tid == 0 && realsig == SIGINT) { - if (jl_defer_signal) { - jl_signal_pending = realsig; - } - else { - jl_signal_pending = 0; - jl_unblock_signal(sig); - jl_throw(jl_interrupt_exception); - } + } + else if (request == 2) { + jl_unblock_signal(sig); + int force = jl_check_force_sigint(); + if (force || (!ptls->defer_signal && ptls->io_wait)) { + jl_safepoint_consume_sigint(); + if (force) + jl_safe_printf("WARNING: Force throwing a SIGINT\n"); + // Force a throw + jl_clear_force_sigint(); + // TODO: implement `jl_throw_in_ctx` -- Jameson + jl_throw(jl_interrupt_exception); } } } @@ -351,8 +368,19 @@ static void *signal_listener(void *arg) profile = (sig == SIGUSR1); # endif #endif + if (sig == SIGINT) { + if (exit_on_sigint) { + critical = 1; + } + else { + jl_try_deliver_sigint(); + continue; + } + } + else { + critical = 0; + } - critical = (sig == SIGINT && exit_on_sigint); critical |= (sig == SIGTERM); critical |= (sig == SIGABRT); critical |= (sig == SIGQUIT); @@ -367,7 +395,7 @@ static void *signal_listener(void *arg) // (so that thread zero gets notified last) for (i = jl_n_threads; i-- > 0; ) { // notify thread to stop - jl_thread_suspend_and_get_state(i, &signal_context, sig); + jl_thread_suspend_and_get_state(i, &signal_context); // do backtrace on thread contexts for critical signals // this part must be signal-handler safe diff --git a/src/signals-win.c b/src/signals-win.c index 79f2c119bef85..5660d3a459931 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -41,6 +41,21 @@ static char *strsignal(int sig) return "?"; } +static void jl_try_throw_sigint(void) +{ + jl_tls_states_t *ptls = jl_get_ptls_states(); + jl_safepoint_enable_sigint(); + int force = jl_check_force_sigint(); + if (force || (!ptls->defer_signal && ptls->io_wait)) { + jl_safepoint_consume_sigint(); + if (force) + jl_safe_printf("WARNING: Force throwing a SIGINT\n"); + // Force a throw + jl_clear_force_sigint(); + jl_throw(jl_interrupt_exception); + } +} + void __cdecl crt_sig_handler(int sig, int num) { CONTEXT Context; @@ -62,13 +77,9 @@ void __cdecl crt_sig_handler(int sig, int num) break; case SIGINT: signal(SIGINT, (void (__cdecl *)(int))crt_sig_handler); - if (jl_defer_signal) { - jl_signal_pending = sig; - } - else { - jl_signal_pending = 0; - jl_sigint_action(); - } + if (exit_on_sigint) + jl_exit(130); // 128 + SIGINT + jl_try_throw_sigint(); break; default: // SIGSEGV, (SSIGTERM, IGILL) memset(&Context, 0, sizeof(Context)); @@ -111,48 +122,59 @@ void jl_throw_in_ctx(jl_value_t *excpt, CONTEXT *ctxThread, int bt) HANDLE hMainThread = INVALID_HANDLE_VALUE; -static BOOL WINAPI sigint_handler(DWORD wsig) //This needs winapi types to guarantee __stdcall +// Try to throw the exception in the master thread. +static void jl_try_deliver_sigint(void) { - int sig; - //windows signals use different numbers from unix (raise) - switch(wsig) { - case CTRL_C_EVENT: sig = SIGINT; break; - //case CTRL_BREAK_EVENT: sig = SIGTERM; break; - // etc. - default: sig = SIGTERM; break; - } - if (jl_defer_signal) { - jl_signal_pending = sig; + jl_tls_states_t *ptls = jl_all_task_states[0].ptls; + jl_safepoint_enable_sigint(); + if ((DWORD)-1 == SuspendThread(hMainThread)) { + // error + jl_safe_printf("error: SuspendThread failed\n"); + return; } - else { - jl_signal_pending = 0; - if (exit_on_sigint) jl_exit(130); - if ((DWORD)-1 == SuspendThread(hMainThread)) { - //error - jl_safe_printf("error: SuspendThread failed\n"); - return 0; - } + int force = jl_check_force_sigint(); + if (force || (!ptls->defer_signal && ptls->io_wait)) { + jl_safepoint_consume_sigint(); + if (force) + jl_safe_printf("WARNING: Force throwing a SIGINT\n"); + // Force a throw + jl_clear_force_sigint(); CONTEXT ctxThread; - memset(&ctxThread,0,sizeof(CONTEXT)); + memset(&ctxThread, 0, sizeof(CONTEXT)); ctxThread.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; if (!GetThreadContext(hMainThread, &ctxThread)) { - //error + // error jl_safe_printf("error: GetThreadContext failed\n"); - return 0; + return; } jl_throw_in_ctx(jl_interrupt_exception, &ctxThread, 1); ctxThread.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - if (!SetThreadContext(hMainThread,&ctxThread)) { + if (!SetThreadContext(hMainThread, &ctxThread)) { jl_safe_printf("error: SetThreadContext failed\n"); - //error - return 0; - } - if ((DWORD)-1 == ResumeThread(hMainThread)) { - jl_safe_printf("error: ResumeThread failed\n"); - //error - return 0; + // error + return; } } + if ((DWORD)-1 == ResumeThread(hMainThread)) { + jl_safe_printf("error: ResumeThread failed\n"); + // error + return; + } +} + +static BOOL WINAPI sigint_handler(DWORD wsig) //This needs winapi types to guarantee __stdcall +{ + int sig; + //windows signals use different numbers from unix (raise) + switch(wsig) { + case CTRL_C_EVENT: sig = SIGINT; break; + //case CTRL_BREAK_EVENT: sig = SIGTERM; break; + // etc. + default: sig = SIGTERM; break; + } + if (exit_on_sigint) + jl_exit(128 + sig); // 128 + SIGINT + jl_try_deliver_sigint(); return 1; } @@ -168,12 +190,23 @@ static LONG WINAPI _exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo, jl_throw_in_ctx(jl_stackovf_exception, ExceptionInfo->ContextRecord,in_ctx&&pSetThreadStackGuarantee); return EXCEPTION_CONTINUE_EXECUTION; case EXCEPTION_ACCESS_VIOLATION: -#ifdef JULIA_ENABLE_THREADING if (jl_addr_is_safepoint(ExceptionInfo->ExceptionRecord->ExceptionInformation[1])) { +#ifdef JULIA_ENABLE_THREADING jl_set_gc_and_wait(); + // Do not raise sigint on worker thread + if (ti_tid != 0) + return EXCEPTION_CONTINUE_EXECUTION; +#endif + if (jl_get_ptls_states()->defer_signal) { + jl_safepoint_defer_sigint(); + } + else if (jl_safepoint_consume_sigint()) { + jl_clear_force_sigint(); + jl_throw_in_ctx(jl_interrupt_exception, + ExceptionInfo->ContextRecord, in_ctx); + } return EXCEPTION_CONTINUE_EXECUTION; } -#endif if (ExceptionInfo->ExceptionRecord->ExceptionInformation[0] == 1) { // writing to read-only memory (e.g. mmap) jl_throw_in_ctx(jl_readonlymemory_exception, ExceptionInfo->ContextRecord,in_ctx); return EXCEPTION_CONTINUE_EXECUTION; diff --git a/src/support/ios.c b/src/support/ios.c index e2ed4abc8a08a..537c73d3f4e0d 100644 --- a/src/support/ios.c +++ b/src/support/ios.c @@ -36,6 +36,14 @@ extern "C" { #endif +void (*ios_set_io_wait_func)(int) = NULL; +static void set_io_wait_begin(int v) +{ + if (__likely(ios_set_io_wait_func)) { + ios_set_io_wait_func(v); + } +} + /* OS-level primitive wrappers */ #if defined(__APPLE__) || defined(_OS_WINDOWS_) @@ -92,7 +100,9 @@ static int _os_read(long fd, void *buf, size_t n, size_t *nread) ssize_t r; while (1) { + set_io_wait_begin(1); r = read((int)fd, buf, LIMIT_IO_SIZE(n)); + set_io_wait_begin(0); if (r > -1) { *nread = (size_t)r; return 0; @@ -113,7 +123,9 @@ static int _os_read_all(long fd, void *buf, size_t n, size_t *nread) *nread = 0; while (n>0) { + set_io_wait_begin(1); int err = _os_read(fd, buf, n, &got); + set_io_wait_begin(0); n -= got; *nread += got; buf = (char *)buf + got; @@ -844,7 +856,9 @@ static int open_cloexec(const char *path, int flags, mode_t mode) static int no_cloexec=0; if (!no_cloexec) { + set_io_wait_begin(1); int fd = open(path, flags | O_CLOEXEC, mode); + set_io_wait_begin(0); if (fd != -1) return fd; if (errno != EINVAL) @@ -852,7 +866,10 @@ static int open_cloexec(const char *path, int flags, mode_t mode) no_cloexec = 1; } #endif - return open(path, flags, mode); + set_io_wait_begin(1); + int fd = open(path, flags, mode); + set_io_wait_begin(0); + return fd; } #endif @@ -871,7 +888,9 @@ ios_t *ios_file(ios_t *s, const char *fname, int rd, int wr, int create, int tru if (!wlen) goto open_file_err; wchar_t *fname_w = (wchar_t*)alloca(wlen*sizeof(wchar_t)); if (!MultiByteToWideChar(CP_UTF8, 0, fname, -1, fname_w, wlen)) goto open_file_err; + set_io_wait_begin(1); fd = _wopen(fname_w, flags | O_BINARY | O_NOINHERIT, _S_IREAD | _S_IWRITE); + set_io_wait_begin(0); #else fd = open_cloexec(fname, flags, S_IRUSR | S_IWUSR /* 0600 */ | S_IRGRP | S_IROTH /* 0644 */); #endif diff --git a/src/support/ios.h b/src/support/ios.h index 70e9353f62bda..7f86dcac4133e 100644 --- a/src/support/ios.h +++ b/src/support/ios.h @@ -70,6 +70,7 @@ typedef struct { char local[IOS_INLSIZE]; } ios_t; +extern void (*ios_set_io_wait_func)(int); /* low-level interface functions */ JL_DLLEXPORT size_t ios_read(ios_t *s, char *dest, size_t n); JL_DLLEXPORT size_t ios_readall(ios_t *s, char *dest, size_t n); diff --git a/src/task.c b/src/task.c index 077245a302235..00b24d04b5f4a 100644 --- a/src/task.c +++ b/src/task.c @@ -496,6 +496,7 @@ static void init_task(jl_task_t *t, char *stack) // yield to exception handler void JL_NORETURN throw_internal(jl_value_t *e) { + jl_get_ptls_states()->io_wait = 0; if (jl_safe_restore) jl_longjmp(*jl_safe_restore, 1); jl_gc_unsafe_enter(); diff --git a/src/threading.c b/src/threading.c index 0c1523fda3f2b..bfc47c8e3676f 100644 --- a/src/threading.c +++ b/src/threading.c @@ -113,6 +113,7 @@ static void ti_initthread(int16_t tid) ptls->safepoint = (size_t*)(jl_safepoint_pages + jl_page_size * 2 + sizeof(size_t)); } + ptls->defer_signal = 0; ptls->current_module = NULL; void *bt_data = malloc(sizeof(uintptr_t) * (JL_MAX_BT_SIZE + 1)); if (bt_data == NULL) { diff --git a/src/typemap.c b/src/typemap.c index 09cbc0258a895..7a61edf6486d3 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -834,7 +834,7 @@ jl_typemap_entry_t *jl_typemap_insert(union jl_typemap_t *cache, jl_value_t *par *overwritten = ml->func.value; if (newvalue == NULL) // don't overwrite with guard entries return ml; - JL_SIGATOMIC_BEGIN(); + // sigatomic begin ml->sig = type; jl_gc_wb(ml, ml->sig); ml->simplesig = simpletype; @@ -846,7 +846,7 @@ jl_typemap_entry_t *jl_typemap_insert(union jl_typemap_t *cache, jl_value_t *par ml->func.value = newvalue; if (newvalue) jl_gc_wb(ml, newvalue); - JL_SIGATOMIC_END(); + // sigatomic end return ml; } if (overwritten != NULL) diff --git a/test/core.jl b/test/core.jl index 4a1fb33e10f7e..9fc6d7cdeac35 100644 --- a/test/core.jl +++ b/test/core.jl @@ -2142,10 +2142,11 @@ end @unix_only begin ccall(:jl_exit_on_sigint, Void, (Cint,), 0) @test_throws InterruptException begin - #ccall(:raise, Void, (Cint,), 2) # llvm installs a custom version on Darwin that resolves to pthread_kill(pthread_self(), sig), which isn't what we want - Libc.systemsleep(0.1) ccall(:kill, Void, (Cint, Cint,), getpid(), 2) - Libc.systemsleep(0.2) # wait for SIGINT to arrive + for i in 1:10 + Libc.systemsleep(0.1) + ccall(:jl_gc_safepoint, Void, ()) # wait for SIGINT to arrive + end end ccall(:jl_exit_on_sigint, Void, (Cint,), 1) end