Skip to content

Commit

Permalink
Use safepoint to deliver SIGINT
Browse files Browse the repository at this point in the history
* Remove unnecessary sigatomic
* Make flisp calls sigatomic
* Make type inference calls sigatomic
* Refactor interthread communication through signal
* Make sure `sleep` is aborted on `SIGINT` on Linux to deliver the exception faster
* Implement force signal throwing when `SIGINT` arrives too frequently
* Hack to abort io syscall on `SIGINT`

Fix #1468; Fix #2622; Towards #14675
  • Loading branch information
yuyichao committed May 6, 2016
1 parent 1022fe2 commit 10321f5
Show file tree
Hide file tree
Showing 19 changed files with 356 additions and 139 deletions.
2 changes: 2 additions & 0 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1562,6 +1562,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
Expand Down Expand Up @@ -1608,6 +1609,7 @@ function typeinf_loop(frame)
println(ex)
ccall(:jlbacktrace, Void, ())
end
ccall(:jl_sigatomic_end, Void, ())
nothing
end

Expand Down
6 changes: 6 additions & 0 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}

Expand Down Expand Up @@ -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)
Expand All @@ -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);
}
}
Expand Down
5 changes: 1 addition & 4 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,17 +194,14 @@ 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
eh->gc_state = jl_gc_state();
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)
Expand Down
2 changes: 0 additions & 2 deletions src/debuginfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion src/jlapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
25 changes: 10 additions & 15 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,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*
Expand Down Expand Up @@ -1392,20 +1393,16 @@ JL_DLLEXPORT void jl_yield(void);

// async signal handling ------------------------------------------------------

#include <signal.h>

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);
Expand Down Expand Up @@ -1511,7 +1508,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
Expand All @@ -1525,7 +1521,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);
Expand Down
14 changes: 14 additions & 0 deletions src/julia_threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#ifndef _OS_WINDOWS_
# include <pthread.h>
#endif
#include <signal.h>

// This includes all the thread local states we care about for a thread.
#define JL_MAX_BT_SIZE 80000
Expand All @@ -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;
Expand All @@ -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__
Expand Down Expand Up @@ -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)
Expand Down
63 changes: 59 additions & 4 deletions src/safepoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}
Expand Down
32 changes: 28 additions & 4 deletions src/signal-handling.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 10321f5

Please sign in to comment.