#include "moar.h" #include #ifdef _WIN32 /* Add signals used by libuv but not defined in the Windows signal.h */ #define SIGHUP 1 #define SIGKILL 9 #define SIGBREAK 21 #define SIGWINCH 28 #endif /* Info we convey about a signal handler. */ typedef struct { int signum; uv_signal_t handle; MVMThreadContext *tc; int work_idx; MVMObject *setup_notify_queue; MVMObject *setup_notify_schedulee; } SignalInfo; /* Signal callback; dispatches schedulee to the queue. */ static void signal_cb(uv_signal_t *handle, int sig_num) { SignalInfo *si = (SignalInfo *)handle->data; MVMThreadContext *tc = si->tc; MVMObject *arr = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray); MVMAsyncTask *t = MVM_io_eventloop_get_active_work(tc, si->work_idx); MVM_repr_push_o(tc, arr, t->body.schedulee); MVMROOT2(tc, t, arr, { MVMObject *sig_num_boxed = MVM_repr_box_int(tc, tc->instance->boot_types.BOOTInt, sig_num); MVM_repr_push_o(tc, arr, sig_num_boxed); }); MVM_repr_push_o(tc, t->body.queue, arr); } /* Sets the signal handler up on the event loop. */ static void setup(MVMThreadContext *tc, uv_loop_t *loop, MVMObject *async_task, void *data) { SignalInfo *si = (SignalInfo *)data; uv_signal_init(loop, &si->handle); si->work_idx = MVM_io_eventloop_add_active_work(tc, async_task); si->tc = tc; si->handle.data = si; uv_signal_start(&si->handle, signal_cb, si->signum); if (si->setup_notify_queue && si->setup_notify_schedulee) { MVMObject *arr; MVMROOT(tc, async_task, { arr = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray); }); MVM_repr_push_o(tc, arr, si->setup_notify_schedulee); MVM_repr_push_o(tc, si->setup_notify_queue, arr); } } static void free_on_close_cb(uv_handle_t *handle) { SignalInfo *si = (SignalInfo*)handle->data; MVM_io_eventloop_remove_active_work(si->tc, &(si->work_idx)); } static void cancel(MVMThreadContext *tc, uv_loop_t *loop, MVMObject *async_task, void *data) { SignalInfo *si = (SignalInfo *)data; if (si->work_idx >= 0) { if (!uv_is_closing((uv_handle_t *)&(si->handle))) uv_signal_stop(&si->handle); uv_close((uv_handle_t*)&si->handle, free_on_close_cb); } } static void gc_mark(MVMThreadContext *tc, void *data, MVMGCWorklist *worklist) { SignalInfo *si = (SignalInfo *)data; MVM_gc_worklist_add(tc, worklist, &si->setup_notify_queue); MVM_gc_worklist_add(tc, worklist, &si->setup_notify_schedulee); } /* Frees data associated with a signal async task. */ static void gc_free(MVMThreadContext *tc, MVMObject *t, void *data) { if (data) MVM_free(data); } /* Operations table for async signal task. */ static const MVMAsyncTaskOps op_table = { setup, NULL, cancel, gc_mark, gc_free }; #define NUM_SIG_WANTED 35 #define PROCESS_SIGS(X) \ X( MVM_SIGHUP ) \ X( MVM_SIGINT ) \ X( MVM_SIGQUIT ) \ X( MVM_SIGILL ) \ X( MVM_SIGTRAP ) \ X( MVM_SIGABRT ) \ X( MVM_SIGEMT ) \ X( MVM_SIGFPE ) \ X( MVM_SIGKILL ) \ X( MVM_SIGBUS ) \ X( MVM_SIGSEGV ) \ X( MVM_SIGSYS ) \ X( MVM_SIGPIPE ) \ X( MVM_SIGALRM ) \ X( MVM_SIGTERM ) \ X( MVM_SIGURG ) \ X( MVM_SIGSTOP ) \ X( MVM_SIGTSTP ) \ X( MVM_SIGCONT ) \ X( MVM_SIGCHLD ) \ X( MVM_SIGTTIN ) \ X( MVM_SIGTTOU ) \ X( MVM_SIGIO ) \ X( MVM_SIGXCPU ) \ X( MVM_SIGXFSZ ) \ X( MVM_SIGVTALRM ) \ X( MVM_SIGPROF ) \ X( MVM_SIGWINCH ) \ X( MVM_SIGINFO ) \ X( MVM_SIGUSR1 ) \ X( MVM_SIGUSR2 ) \ X( MVM_SIGTHR ) \ X( MVM_SIGSTKFLT ) \ X( MVM_SIGPWR ) \ X( MVM_SIGBREAK ) #define GEN_ENUMS(v) v, #define GEN_STRING(v) #v, #ifndef _MSC_VER #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" #endif static enum { PROCESS_SIGS(GEN_ENUMS) } MVM_sig_names; #ifndef _MSC_VER #pragma GCC diagnostic pop #endif static char const * const SIG_WANTED[NUM_SIG_WANTED] = { PROCESS_SIGS(GEN_STRING) }; static void populate_sig_values(MVMint8 *sig_vals) { MVMint8 i; for (i = 0; i < NUM_SIG_WANTED; i++) { sig_vals[i] = 0; } #ifdef SIGHUP sig_vals[MVM_SIGHUP] = SIGHUP; #endif #ifdef SIGINT sig_vals[MVM_SIGINT] = SIGINT; #endif #ifdef SIGQUIT sig_vals[MVM_SIGQUIT] = SIGQUIT; #endif #ifdef SIGILL sig_vals[MVM_SIGILL] = SIGILL; #endif #ifdef SIGTRAP sig_vals[MVM_SIGTRAP] = SIGTRAP; #endif #ifdef SIGABRT sig_vals[MVM_SIGABRT] = SIGABRT; #endif #ifdef SIGEMT sig_vals[MVM_SIGEMT] = SIGEMT; #endif #ifdef SIGFPE sig_vals[MVM_SIGFPE] = SIGFPE; #endif #ifdef SIGKILL sig_vals[MVM_SIGKILL] = SIGKILL; #endif #ifdef SIGBUS sig_vals[MVM_SIGBUS] = SIGBUS; #endif #ifdef SIGSEGV sig_vals[MVM_SIGSEGV] = SIGSEGV; #endif #ifdef SIGSYS sig_vals[MVM_SIGSYS] = SIGSYS; #endif #ifdef SIGPIPE sig_vals[MVM_SIGPIPE] = SIGPIPE; #endif #ifdef SIGALRM sig_vals[MVM_SIGALRM] = SIGALRM; #endif #ifdef SIGTERM sig_vals[MVM_SIGTERM] = SIGTERM; #endif #ifdef SIGURG sig_vals[MVM_SIGURG] = SIGURG; #endif #ifdef SIGSTOP sig_vals[MVM_SIGSTOP] = SIGSTOP; /* hammer time */ #endif #ifdef SIGTSTP sig_vals[MVM_SIGTSTP] = SIGTSTP; #endif #ifdef SIGCONT sig_vals[MVM_SIGCONT] = SIGCONT; #endif #ifdef SIGCHLD sig_vals[MVM_SIGCHLD] = SIGCHLD; #endif #ifdef SIGTTIN sig_vals[MVM_SIGTTIN] = SIGTTIN; #endif #ifdef SIGTTOU sig_vals[MVM_SIGTTOU] = SIGTTOU; #endif #ifdef SIGIO sig_vals[MVM_SIGIO] = SIGIO; #endif #ifdef SIGXCPU sig_vals[MVM_SIGXCPU] = SIGXCPU; #endif #ifdef SIGXFSZ sig_vals[MVM_SIGXFSZ] = SIGXFSZ; #endif #ifdef SIGVTALRM sig_vals[MVM_SIGVTALRM] = SIGVTALRM; #endif #ifdef SIGPROF sig_vals[MVM_SIGPROF] = SIGPROF; #endif #ifdef SIGWINCH sig_vals[MVM_SIGWINCH] = SIGWINCH; #endif #ifdef SIGINFO sig_vals[MVM_SIGINFO] = SIGINFO; #endif #ifdef SIGUSR1 sig_vals[MVM_SIGUSR1] = SIGUSR1; #endif #ifdef SIGUSR2 sig_vals[MVM_SIGUSR2] = SIGUSR2; #endif #ifdef SIGTHR sig_vals[MVM_SIGTHR] = SIGTHR; #endif #ifdef SIGSTKFLT sig_vals[MVM_SIGSTKFLT] = SIGSTKFLT; #endif #ifdef SIGPWR sig_vals[MVM_SIGPWR] = SIGPWR; #endif #ifdef SIGBREAK sig_vals[MVM_SIGBREAK] = SIGBREAK; #endif } #define SIG_SHIFT(s) (1 << ((s) - 1)) static void populate_instance_valid_sigs(MVMThreadContext *tc, MVMint8 *sig_vals) { MVMuint64 valid_sigs = 0; MVMint8 i; if ( tc->instance->valid_sigs ) return; for (i = 0; i < NUM_SIG_WANTED; i++) { if (sig_vals[i]) { valid_sigs |= SIG_SHIFT(sig_vals[i]); } } tc->instance->valid_sigs = valid_sigs; } MVMObject * MVM_io_get_signals(MVMThreadContext *tc) { MVMInstance * const instance = tc->instance; MVMHLLConfig * hll = MVM_hll_current(tc); MVMObject * sig_arr; MVMint8 sig_wanted_vals[NUM_SIG_WANTED]; populate_sig_values(sig_wanted_vals); if (instance->sig_arr) { return instance->sig_arr; } sig_arr = MVM_repr_alloc_init(tc, hll->slurpy_array_type); MVMROOT(tc, sig_arr, { MVMint8 i; for (i = 0; i < NUM_SIG_WANTED; i++) { MVMObject *key = NULL; MVMString *full_key = NULL; MVMObject *val = NULL; MVMROOT3(tc, key, full_key, val, { full_key = MVM_string_utf8_c8_decode( tc, instance->VMString, SIG_WANTED[i], strlen(SIG_WANTED[i]) ); key = MVM_repr_box_str(tc, hll->str_box_type, MVM_string_substring(tc, full_key, 4, -1) ); val = MVM_repr_box_int(tc, hll->int_box_type, sig_wanted_vals[i]); MVM_repr_push_o(tc, sig_arr, key); MVM_repr_push_o(tc, sig_arr, val); }); } populate_instance_valid_sigs(tc, sig_wanted_vals); instance->sig_arr = sig_arr; }); return sig_arr; } /* Register a new signal handler. */ MVMObject * MVM_io_signal_handle( MVMThreadContext *tc, MVMObject *setup_notify_queue, MVMObject *setup_notify_schedulee, MVMObject *queue, MVMObject *schedulee, MVMint64 signal, MVMObject *async_type ) { MVMAsyncTask *task; SignalInfo *signal_info; MVMInstance * const instance = tc->instance; if ( !instance->valid_sigs ) { MVMint8 sig_wanted_vals[NUM_SIG_WANTED]; populate_sig_values(sig_wanted_vals); populate_instance_valid_sigs(tc, sig_wanted_vals); } if ( signal <= 0 || !(instance->valid_sigs & SIG_SHIFT(signal) ) ) { MVM_exception_throw_adhoc(tc, "Unsupported signal handler %d", (int)signal); } /* Validate REPRs. */ if (REPR(queue)->ID != MVM_REPR_ID_ConcBlockingQueue) MVM_exception_throw_adhoc(tc, "signal target queue must have ConcBlockingQueue REPR"); if (setup_notify_queue && REPR(setup_notify_queue)->ID != MVM_REPR_ID_ConcBlockingQueue) MVM_exception_throw_adhoc(tc, "signal setup notify queue must have ConcBlockingQueue REPR"); if (REPR(async_type)->ID != MVM_REPR_ID_MVMAsyncTask) MVM_exception_throw_adhoc(tc, "signal result type must have REPR AsyncTask"); /* Create async task handle. */ MVMROOT4(tc, queue, schedulee, setup_notify_queue, setup_notify_schedulee, { task = (MVMAsyncTask *)MVM_repr_alloc_init(tc, async_type); }); MVM_ASSIGN_REF(tc, &(task->common.header), task->body.queue, queue); MVM_ASSIGN_REF(tc, &(task->common.header), task->body.schedulee, schedulee); task->body.ops = &op_table; signal_info = MVM_malloc(sizeof(SignalInfo)); signal_info->signum = signal; signal_info->setup_notify_queue = setup_notify_queue; signal_info->setup_notify_schedulee = setup_notify_schedulee; task->body.data = signal_info; /* Hand the task off to the event loop. */ MVMROOT(tc, task, { MVM_io_eventloop_queue_work(tc, (MVMObject *)task); }); return (MVMObject *)task; }