From be5f5810f4257b15d330b7a61436f0aaad310868 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sat, 9 Jul 2011 20:30:30 -0400 Subject: [PATCH] adding a ctrl-C (SIGINT) handler, starting work on issue #26. a few more things probably need to be made signal safe, but it is usable now with the caveat that you shouldn't trust the system too much after hitting ctrl-C. --- src/alloc.c | 1 + src/boot.j | 1 + src/builtins.c | 4 ++++ src/codegen.cpp | 7 ++++++- src/dump.c | 2 ++ src/gc.c | 2 ++ src/gf.c | 8 +++++++- src/init.c | 30 ++++++++++++++++++++++++++++++ src/julia.h | 20 +++++++++++++++++++- src/task.c | 14 ++++++++++++++ 10 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index d0c0d528663b2..866f1169b321f 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -48,6 +48,7 @@ jl_value_t *jl_an_empty_cell=NULL; jl_value_t *jl_stackovf_exception; jl_value_t *jl_divbyzero_exception; jl_value_t *jl_undefref_exception; +jl_value_t *jl_interrupt_exception; jl_bits_type_t *jl_pointer_type; jl_bits_type_t *jl_pointer_void_type; diff --git a/src/boot.j b/src/boot.j index c2ea3102cb1bc..03f1d22399a53 100644 --- a/src/boot.j +++ b/src/boot.j @@ -187,6 +187,7 @@ type IOError <: Exception end type StackOverflowError <: Exception end type EOFError <: Exception end type UndefRefError <: Exception end +type InterruptException <: Exception end type UnionTooComplexError <: Exception types::Tuple diff --git a/src/builtins.c b/src/builtins.c index fb04d821d45e1..8c08b120de26a 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -87,6 +87,7 @@ JL_CALLABLE(jl_f_throw) void jl_enter_handler(jl_savestate_t *ss, jmp_buf *handlr) { + JL_SIGATOMIC_BEGIN(); ss->eh_task = jl_current_task->state.eh_task; ss->eh_ctx = jl_current_task->state.eh_ctx; ss->ostream_obj = jl_current_task->state.ostream_obj; @@ -99,6 +100,9 @@ void jl_enter_handler(jl_savestate_t *ss, jmp_buf *handlr) jl_current_task->state.prev = ss; jl_current_task->state.eh_task = jl_current_task; jl_current_task->state.eh_ctx = handlr; + // TODO: this should really go after setjmp(). see comment in + // ctx_switch in task.c. + JL_SIGATOMIC_END(); } void jl_pop_handler(int n) diff --git a/src/codegen.cpp b/src/codegen.cpp index c8f9d3e755260..0157b672941fc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -171,6 +171,7 @@ static void emit_function(jl_lambda_info_t *lam, Function *f); //static int n_compile=0; static Function *to_function(jl_lambda_info_t *li) { + JL_SIGATOMIC_BEGIN(); Function *f = Function::Create(jl_func_sig, Function::ExternalLinkage, li->name->name, jl_Module); assert(jl_is_expr(li->ast)); @@ -187,6 +188,7 @@ static Function *to_function(jl_lambda_info_t *li) //verifyFunction(*f); if (old != NULL) builder.SetInsertPoint(old); + JL_SIGATOMIC_END(); return f; } @@ -196,8 +198,11 @@ extern "C" void jl_generate_fptr(jl_function_t *f) jl_lambda_info_t *li = f->linfo; assert(li->functionObject); Function *llvmf = (Function*)li->functionObject; - if (li->fptr == NULL) + if (li->fptr == NULL) { + JL_SIGATOMIC_BEGIN(); li->fptr = (jl_fptr_t)jl_ExecutionEngine->getPointerToFunction(llvmf); + JL_SIGATOMIC_END(); + } assert(li->fptr != NULL); f->fptr = li->fptr; llvmf->deleteBody(); diff --git a/src/dump.c b/src/dump.c index 8b8508cddb0b1..41b2e23c21da7 100644 --- a/src/dump.c +++ b/src/dump.c @@ -803,6 +803,7 @@ void jl_save_system_image(char *fname, char *startscriptname) jl_serialize_value(&f, jl_stackovf_exception); jl_serialize_value(&f, jl_divbyzero_exception); jl_serialize_value(&f, jl_undefref_exception); + jl_serialize_value(&f, jl_interrupt_exception); jl_serialize_value(&f, jl_append_any_func); jl_serialize_value(&f, jl_method_missing_func); jl_serialize_value(&f, jl_get_global(jl_system_module, @@ -873,6 +874,7 @@ void jl_restore_system_image(char *fname) jl_stackovf_exception = jl_deserialize_value(&f); jl_divbyzero_exception = jl_deserialize_value(&f); jl_undefref_exception = jl_deserialize_value(&f); + jl_interrupt_exception = jl_deserialize_value(&f); jl_append_any_func = (jl_function_t*)jl_deserialize_value(&f); jl_method_missing_func = (jl_function_t*)jl_deserialize_value(&f); jl_idtable_type = (jl_struct_type_t*)jl_deserialize_value(&f); diff --git a/src/gc.c b/src/gc.c index f6e6cef70a847..9d9e4301ba05b 100644 --- a/src/gc.c +++ b/src/gc.c @@ -647,10 +647,12 @@ void jl_gc_collect() { allocd_bytes = 0; if (is_gc_enabled) { + JL_SIGATOMIC_BEGIN(); gc_mark(); sweep_weak_refs(); gc_sweep(); run_finalizers(); + JL_SIGATOMIC_END(); } } diff --git a/src/gf.c b/src/gf.c index 3e85b07ffb53a..8f917a95f67e1 100644 --- a/src/gf.c +++ b/src/gf.c @@ -839,6 +839,7 @@ jl_methlist_t *jl_method_list_insert(jl_methlist_t **pml, jl_tuple_t *type, while (l != NULL) { if (sigs_eq((jl_value_t*)type, (jl_value_t*)l->sig)) { // method overwritten + JL_SIGATOMIC_BEGIN(); l->sig = type; l->tvars = find_tvars((jl_value_t*)type, jl_null); l->has_tvars = (l->tvars != jl_null); @@ -846,6 +847,7 @@ jl_methlist_t *jl_method_list_insert(jl_methlist_t **pml, jl_tuple_t *type, jl_is_seq_type(jl_tupleref(type,type->length-1))); l->invokes = NULL; l->func = method; + JL_SIGATOMIC_END(); return l; } l = l->next; @@ -874,8 +876,8 @@ jl_methlist_t *jl_method_list_insert(jl_methlist_t **pml, jl_tuple_t *type, newrec->func = method; newrec->invokes = NULL; newrec->next = l; + JL_SIGATOMIC_BEGIN(); *pl = newrec; - JL_GC_POP(); // if this contains Union types, methods after it might actually be // more specific than it. we need to re-sort them. if (has_unions(type)) { @@ -903,12 +905,15 @@ jl_methlist_t *jl_method_list_insert(jl_methlist_t **pml, jl_tuple_t *type, pitem = pnext; } } + JL_SIGATOMIC_END(); + JL_GC_POP(); return newrec; } jl_methlist_t *jl_method_table_insert(jl_methtable_t *mt, jl_tuple_t *type, jl_function_t *method) { + JL_SIGATOMIC_BEGIN(); jl_methlist_t *ml = jl_method_list_insert(&mt->defs, type, method, 1); // invalidate cached methods that overlap this definition jl_methlist_t *l = mt->cache; @@ -934,6 +939,7 @@ jl_methlist_t *jl_method_table_insert(jl_methtable_t *mt, jl_tuple_t *type, if (na > mt->max_args) { mt->max_args = na; } + JL_SIGATOMIC_END(); return ml; } diff --git a/src/init.c b/src/init.c index bbed59f0479fd..5aa48c2f90ef8 100644 --- a/src/init.c +++ b/src/init.c @@ -77,6 +77,25 @@ void segv_handler(int sig, siginfo_t *info, void *context) } } +volatile sig_atomic_t signal_pending = 0; +volatile sig_atomic_t defer_signal = 0; + +void sigint_handler(int sig, siginfo_t *info, void *context) +{ + sigset_t sset; + sigemptyset(&sset); + sigaddset(&sset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sset, NULL); + + if (defer_signal) { + signal_pending = sig; + } + else { + signal_pending = 0; + jl_raise(jl_interrupt_exception); + } +} + static void jl_get_builtin_hooks(); void *jl_dl_handle; @@ -158,6 +177,15 @@ void julia_init(char *imageFile) exit(1); } + memset(&act, 0, sizeof(struct sigaction)); + sigemptyset(&act.sa_mask); + act.sa_sigaction = sigint_handler; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGINT, &act, NULL) < 0) { + ios_printf(ios_stderr, "sigaction: %s\n", strerror(errno)); + exit(1); + } + #ifdef JL_GC_MARKSWEEP jl_gc_enable(); #endif @@ -299,6 +327,8 @@ void jl_get_builtin_hooks() jl_apply((jl_function_t*)global("DivideByZeroError"), NULL, 0); jl_undefref_exception = jl_apply((jl_function_t*)global("UndefRefError"),NULL,0); + jl_interrupt_exception = + jl_apply((jl_function_t*)global("InterruptException"),NULL,0); jl_append_any_func = (jl_function_t*)global("append_any"); jl_method_missing_func = (jl_function_t*)global("method_missing"); diff --git a/src/julia.h b/src/julia.h index 485a4dc5dadd5..07ed27d321342 100644 --- a/src/julia.h +++ b/src/julia.h @@ -283,6 +283,7 @@ extern jl_struct_type_t *jl_uniontoocomplex_type; extern jl_value_t *jl_stackovf_exception; extern jl_value_t *jl_divbyzero_exception; extern jl_value_t *jl_undefref_exception; +extern jl_value_t *jl_interrupt_exception; extern jl_value_t *jl_an_empty_cell; extern jl_struct_type_t *jl_box_type; @@ -796,7 +797,22 @@ void *alloc_4w(); #define jl_gc_n_preserved_values() (0) #endif -// tasks +// asynch signal handling + +#include + +extern volatile sig_atomic_t signal_pending; +extern volatile sig_atomic_t defer_signal; + +#define JL_SIGATOMIC_BEGIN() (defer_signal++) +#define JL_SIGATOMIC_END() \ + do { \ + defer_signal--; \ + if (defer_signal == 0 && signal_pending != 0) \ + raise(signal_pending); \ + } while(0) + +// tasks and exceptions // context that needs to be restored around a try block typedef struct _jl_savestate_t { @@ -850,6 +866,7 @@ DLLEXPORT jl_array_t *jl_readuntil(ios_t *s, uint8_t delim); static inline void jl_eh_restore_state(jl_savestate_t *ss) { + JL_SIGATOMIC_BEGIN(); jl_current_task->state.eh_task = ss->eh_task; jl_current_task->state.eh_ctx = ss->eh_ctx; jl_current_task->state.ostream_obj = ss->ostream_obj; @@ -858,6 +875,7 @@ static inline void jl_eh_restore_state(jl_savestate_t *ss) #ifdef JL_GC_MARKSWEEP jl_current_task->state.gcstack = ss->gcstack; #endif + JL_SIGATOMIC_END(); } DLLEXPORT void jl_enter_handler(jl_savestate_t *ss, jmp_buf *handlr); diff --git a/src/task.c b/src/task.c index 4a2834924d575..72f3a00449807 100644 --- a/src/task.c +++ b/src/task.c @@ -235,6 +235,19 @@ static void ctx_switch(jl_task_t *t, jmp_buf *where) { if (t == jl_current_task) return; + /* + making task switching interrupt-safe is going to be challenging. + we need JL_SIGATOMIC_BEGIN in jl_enter_handler, and then + JL_SIGATOMIC_END after every JL_TRY setjmp that returns zero. + also protect jl_eh_restore_state. + then we need JL_SIGATOMIC_BEGIN at the top of this function (ctx_switch). + the JL_SIGATOMIC_END at the end of this function handles the case + of task switching with yieldto(). + then we need to handle the case of task switching via raise(). + to do that, the top of every catch block must do JL_SIGATOMIC_END + *IF AND ONLY IF* throwing the exception involved a task switch. + */ + //JL_SIGATOMIC_BEGIN(); if (!setjmp(jl_current_task->ctx)) { #ifdef COPY_STACKS jl_task_t *lastt = jl_current_task; @@ -254,6 +267,7 @@ static void ctx_switch(jl_task_t *t, jmp_buf *where) longjmp(*where, 1); #endif } + //JL_SIGATOMIC_END(); } static jl_value_t *switchto(jl_task_t *t)