diff options
author | Thiago de Arruda <tpadilha84@gmail.com> | 2014-04-01 08:54:31 -0300 |
---|---|---|
committer | Thiago de Arruda <tpadilha84@gmail.com> | 2014-04-01 09:46:29 -0300 |
commit | 774f668c43c14c6c6593264a1c8c9a0275b19037 (patch) | |
tree | 176395e37139e68c3a40f9271b9cfa04722c0ff1 /src/os_unix.c | |
parent | 40879af7bdd1633f3d22f0d1da9a24992cf3e2b2 (diff) | |
download | rneovim-774f668c43c14c6c6593264a1c8c9a0275b19037.tar.gz rneovim-774f668c43c14c6c6593264a1c8c9a0275b19037.tar.bz2 rneovim-774f668c43c14c6c6593264a1c8c9a0275b19037.zip |
Move signal handling to libuv event loop
This removes all signal handling code from os_unix.c to os/signal.c. Now signal
handling is done like this:
- Watchers for signals are registered with libuv default event loop
- `event_poll` continuously calls `poll_uv_loop` to produce events until it
receives user input, SIGINT or a timeout
- Any signals received in `poll_uv_loop` will push events to a queue that is
drained and processed by `event_poll`
Signals aren't handled directly in the libuv callback to avoid recursion in the
event loop(which isn't supported by libuv).
The same principle will apply to other events in the future: Push to a queue
from a libuv callback and drain it from `event_poll`
Diffstat (limited to 'src/os_unix.c')
-rw-r--r-- | src/os_unix.c | 590 |
1 files changed, 5 insertions, 585 deletions
diff --git a/src/os_unix.c b/src/os_unix.c index 091bf7999e..8da7c4701f 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -54,6 +54,7 @@ #include "os/event.h" #include "os/input.h" #include "os/shell.h" +#include "os/signal.h" #include "os_unixx.h" /* unix includes for os_unix.c only */ @@ -72,28 +73,6 @@ static int selinux_enabled = -1; extern int select(int, fd_set *, fd_set *, fd_set *, struct timeval *); #endif - - -/* - * end of autoconf section. To be extended... - */ - -/* Are the following #ifdefs still required? And why? Is that for X11? */ - -#if defined(ESIX) || defined(M_UNIX) && !defined(SCO) -# ifdef SIGWINCH -# undef SIGWINCH -# endif -# ifdef TIOCGWINSZ -# undef TIOCGWINSZ -# endif -#endif - -#if defined(SIGWINDOW) && !defined(SIGWINCH) /* hpux 9.01 has it */ -# define SIGWINCH SIGWINDOW -#endif - - static int get_x11_title(int); static int get_x11_icon(int); @@ -102,8 +81,6 @@ static int did_set_title = FALSE; static char_u *oldicon = NULL; static int did_set_icon = FALSE; -static void may_core_dump(void); - #ifdef HAVE_UNION_WAIT typedef union wait waitstatus; #else @@ -113,130 +90,12 @@ static pid_t wait4pid(pid_t, waitstatus *); static int RealWaitForChar(int, long, int *); - -static void handle_resize(void); - -#if defined(SIGWINCH) -static RETSIGTYPE sig_winch SIGPROTOARG; -#endif -#if defined(SIGINT) -static RETSIGTYPE catch_sigint SIGPROTOARG; -#endif -#if defined(SIGPWR) -static RETSIGTYPE catch_sigpwr SIGPROTOARG; -#endif -static RETSIGTYPE deathtrap SIGPROTOARG; - -static void catch_int_signal(void); -static void set_signals(void); -static void catch_signals - (RETSIGTYPE (*func_deadly)(), RETSIGTYPE (*func_other)()); static int have_wildcard(int, char_u **); static int have_dollars(int, char_u **); static int save_patterns(int num_pat, char_u **pat, int *num_file, char_u ***file); -#ifndef SIG_ERR -# define SIG_ERR ((RETSIGTYPE (*)())-1) -#endif - -/* volatile because it is used in signal handler sig_winch(). */ -static volatile int do_resize = FALSE; -/* volatile because it is used in signal handler deathtrap(). */ -static volatile int deadly_signal = 0; /* The signal we caught */ - - -#ifdef SYS_SIGLIST_DECLARED -/* - * I have seen - * extern char *_sys_siglist[NSIG]; - * on Irix, Linux, NetBSD and Solaris. It contains a nice list of strings - * that describe the signals. That is nearly what we want here. But - * autoconf does only check for sys_siglist (without the underscore), I - * do not want to change everything today.... jw. - * This is why AC_DECL_SYS_SIGLIST is commented out in configure.in - */ -#endif - -static struct signalinfo { - int sig; /* Signal number, eg. SIGSEGV etc */ - char *name; /* Signal name (not char_u!). */ - char deadly; /* Catch as a deadly signal? */ -} signal_info[] = -{ -#ifdef SIGHUP - {SIGHUP, "HUP", TRUE}, -#endif -#ifdef SIGQUIT - {SIGQUIT, "QUIT", TRUE}, -#endif -#ifdef SIGILL - {SIGILL, "ILL", TRUE}, -#endif -#ifdef SIGTRAP - {SIGTRAP, "TRAP", TRUE}, -#endif -#ifdef SIGABRT - {SIGABRT, "ABRT", TRUE}, -#endif -#ifdef SIGEMT - {SIGEMT, "EMT", TRUE}, -#endif -#ifdef SIGFPE - {SIGFPE, "FPE", TRUE}, -#endif -#ifdef SIGBUS - {SIGBUS, "BUS", TRUE}, -#endif -#if defined(SIGSEGV) - /* MzScheme uses SEGV in its garbage collector */ - {SIGSEGV, "SEGV", TRUE}, -#endif -#ifdef SIGSYS - {SIGSYS, "SYS", TRUE}, -#endif -#ifdef SIGALRM - {SIGALRM, "ALRM", FALSE}, /* Perl's alarm() can trigger it */ -#endif -#ifdef SIGTERM - {SIGTERM, "TERM", TRUE}, -#endif -#if defined(SIGVTALRM) - {SIGVTALRM, "VTALRM", TRUE}, -#endif -#if defined(SIGPROF) && !defined(WE_ARE_PROFILING) - /* MzScheme uses SIGPROF for its own needs; On Linux with profiling - * this makes Vim exit. WE_ARE_PROFILING is defined in Makefile. */ - {SIGPROF, "PROF", TRUE}, -#endif -#ifdef SIGXCPU - {SIGXCPU, "XCPU", TRUE}, -#endif -#ifdef SIGXFSZ - {SIGXFSZ, "XFSZ", TRUE}, -#endif -#ifdef SIGUSR1 - {SIGUSR1, "USR1", TRUE}, -#endif -#if defined(SIGUSR2) && !defined(FEAT_SYSMOUSE) - /* Used for sysmouse handling */ - {SIGUSR2, "USR2", TRUE}, -#endif -#ifdef SIGINT - {SIGINT, "INT", FALSE}, -#endif -#ifdef SIGWINCH - {SIGWINCH, "WINCH", FALSE}, -#endif -#ifdef SIGTSTP - {SIGTSTP, "TSTP", FALSE}, -#endif -#ifdef SIGPIPE - {SIGPIPE, "PIPE", FALSE}, -#endif - {-1, "Unknown!", FALSE} -}; /* * Write s[len] to the screen. @@ -248,99 +107,6 @@ void mch_write(char_u *s, int len) RealWaitForChar(read_cmd_fd, p_wd, NULL); } -static void handle_resize() -{ - do_resize = FALSE; - shell_resized(); -} - - -#if defined(HAVE_SIGALTSTACK) || defined(HAVE_SIGSTACK) -/* - * Support for using the signal stack. - * This helps when we run out of stack space, which causes a SIGSEGV. The - * signal handler then must run on another stack, since the normal stack is - * completely full. - */ - - -#ifndef SIGSTKSZ -# define SIGSTKSZ 8000 /* just a guess of how much stack is needed... */ -#endif - -# ifdef HAVE_SIGALTSTACK -static stack_t sigstk; /* for sigaltstack() */ -# else -static struct sigstack sigstk; /* for sigstack() */ -# endif - -static void init_signal_stack(void); -static char *signal_stack; - -static void init_signal_stack() -{ - if (signal_stack != NULL) { -# ifdef HAVE_SIGALTSTACK - sigstk.ss_sp = signal_stack; - sigstk.ss_size = SIGSTKSZ; - sigstk.ss_flags = 0; - (void)sigaltstack(&sigstk, NULL); -# else - sigstk.ss_sp = signal_stack; - if (stack_grows_downwards) - sigstk.ss_sp += SIGSTKSZ - 1; - sigstk.ss_onstack = 0; - (void)sigstack(&sigstk, NULL); -# endif - } -} - -#endif - -/* - * We need correct prototypes for a signal function, otherwise mean compilers - * will barf when the second argument to signal() is ``wrong''. - * Let me try it with a few tricky defines from my own osdef.h (jw). - */ -#if defined(SIGWINCH) -static RETSIGTYPE -sig_winch SIGDEFARG(sigarg) { - /* this is not required on all systems, but it doesn't hurt anybody */ - signal(SIGWINCH, (RETSIGTYPE (*)())sig_winch); - do_resize = TRUE; - SIGRETURN; -} - -#endif - -#if defined(SIGINT) -static RETSIGTYPE -catch_sigint SIGDEFARG(sigarg) { - /* this is not required on all systems, but it doesn't hurt anybody */ - signal(SIGINT, (RETSIGTYPE (*)())catch_sigint); - got_int = TRUE; - SIGRETURN; -} - -#endif - -#if defined(SIGPWR) -static RETSIGTYPE -catch_sigpwr SIGDEFARG(sigarg) { - /* this is not required on all systems, but it doesn't hurt anybody */ - signal(SIGPWR, (RETSIGTYPE (*)())catch_sigpwr); - /* - * I'm not sure we get the SIGPWR signal when the system is really going - * down or when the batteries are almost empty. Just preserve the swap - * files and don't exit, that can't do any harm. - */ - ml_sync_all(FALSE, FALSE); - SIGRETURN; -} - -#endif - -#if defined(FEAT_LIBCALL) || defined(PROTO) /* * A simplistic version of setjmp() that only allows one level of using. * Don't call twice before calling mch_endjmp()!. @@ -363,9 +129,6 @@ catch_sigpwr SIGDEFARG(sigarg) { */ void mch_startjmp() { -#ifdef SIGHASARG - lc_signal = 0; -#endif lc_active = TRUE; } @@ -374,165 +137,6 @@ void mch_endjmp() lc_active = FALSE; } -void mch_didjmp() -{ -# if defined(HAVE_SIGALTSTACK) || defined(HAVE_SIGSTACK) - /* On FreeBSD the signal stack has to be reset after using siglongjmp(), - * otherwise catching the signal only works once. */ - init_signal_stack(); -# endif -} - -#endif - -/* - * This function handles deadly signals. - * It tries to preserve any swap files and exit properly. - * (partly from Elvis). - * NOTE: Avoid unsafe functions, such as allocating memory, they can result in - * a deadlock. - */ -static RETSIGTYPE -deathtrap SIGDEFARG(sigarg) { - static int entered = 0; /* count the number of times we got here. - Note: when memory has been corrupted - this may get an arbitrary value! */ -#ifdef SIGHASARG - int i; -#endif - - /* - * Catch a crash in protected code. - * Restores the environment saved in lc_jump_env, which looks like - * SETJMP() returns 1. - */ - if (lc_active) { -#if defined(SIGHASARG) - lc_signal = sigarg; -#endif - lc_active = FALSE; /* don't jump again */ - LONGJMP(lc_jump_env, 1); - /* NOTREACHED */ - } - -#ifdef SIGHASARG -# ifdef SIGQUIT - /* While in os_delay() we go to cooked mode to allow a CTRL-C to - * interrupt us. But in cooked mode we may also get SIGQUIT, e.g., when - * pressing CTRL-\, but we don't want Vim to exit then. */ - if (in_os_delay && sigarg == SIGQUIT) - SIGRETURN; -# endif - - /* When SIGHUP, SIGQUIT, etc. are blocked: postpone the effect and return - * here. This avoids that a non-reentrant function is interrupted, e.g., - * free(). Calling free() again may then cause a crash. */ - if (entered == 0 - && (0 -# ifdef SIGHUP - || sigarg == SIGHUP -# endif -# ifdef SIGQUIT - || sigarg == SIGQUIT -# endif -# ifdef SIGTERM - || sigarg == SIGTERM -# endif -# ifdef SIGPWR - || sigarg == SIGPWR -# endif -# ifdef SIGUSR1 - || sigarg == SIGUSR1 -# endif -# ifdef SIGUSR2 - || sigarg == SIGUSR2 -# endif - ) - && !vim_handle_signal(sigarg)) - SIGRETURN; -#endif - - /* Remember how often we have been called. */ - ++entered; - - /* Set the v:dying variable. */ - set_vim_var_nr(VV_DYING, (long)entered); - -#ifdef SIGHASARG - /* try to find the name of this signal */ - for (i = 0; signal_info[i].sig != -1; i++) - if (sigarg == signal_info[i].sig) - break; - deadly_signal = sigarg; -#endif - - full_screen = FALSE; /* don't write message to the GUI, it might be - * part of the problem... */ - /* - * If something goes wrong after entering here, we may get here again. - * When this happens, give a message and try to exit nicely (resetting the - * terminal mode, etc.) - * When this happens twice, just exit, don't even try to give a message, - * stack may be corrupt or something weird. - * When this still happens again (or memory was corrupted in such a way - * that "entered" was clobbered) use _exit(), don't try freeing resources. - */ - if (entered >= 3) { - reset_signals(); /* don't catch any signals anymore */ - may_core_dump(); - if (entered >= 4) - _exit(8); - exit(7); - } - if (entered == 2) { - /* No translation, it may call malloc(). */ - OUT_STR("Vim: Double signal, exiting\n"); - out_flush(); - getout(1); - } - - /* No translation, it may call malloc(). */ -#ifdef SIGHASARG - sprintf((char *)IObuff, "Vim: Caught deadly signal %s\n", - signal_info[i].name); -#else - sprintf((char *)IObuff, "Vim: Caught deadly signal\n"); -#endif - - /* Preserve files and exit. This sets the really_exiting flag to prevent - * calling free(). */ - preserve_exit(); - - - SIGRETURN; -} - -#if defined(_REENTRANT) && defined(SIGCONT) -/* - * On Solaris with multi-threading, suspending might not work immediately. - * Catch the SIGCONT signal, which will be used as an indication whether the - * suspending has been done or not. - * - * On Linux, signal is not always handled immediately either. - * See https://bugs.launchpad.net/bugs/291373 - * - * volatile because it is used in signal handler sigcont_handler(). - */ -static volatile int sigcont_received; -static RETSIGTYPE sigcont_handler SIGPROTOARG; - -/* - * signal handler for SIGCONT - */ -static RETSIGTYPE -sigcont_handler SIGDEFARG(sigarg) { - sigcont_received = TRUE; - SIGRETURN; -} - -#endif - - /* * If the machine has job control, use it to suspend the program, * otherwise fake it by starting a new shell. @@ -589,7 +193,6 @@ void mch_init() Rows = 24; out_flush(); - set_signals(); #ifdef MACOS_CONVERT mac_conv_init(); @@ -598,159 +201,6 @@ void mch_init() event_init(); } -static void set_signals() -{ -#if defined(SIGWINCH) - /* - * WINDOW CHANGE signal is handled with sig_winch(). - */ - signal(SIGWINCH, (RETSIGTYPE (*)())sig_winch); -#endif - - /* - * We want the STOP signal to work, to make mch_suspend() work. - * For "rvim" the STOP signal is ignored. - */ -#ifdef SIGTSTP - signal(SIGTSTP, restricted ? SIG_IGN : SIG_DFL); -#endif -#if defined(_REENTRANT) && defined(SIGCONT) - signal(SIGCONT, sigcont_handler); -#endif - - /* - * We want to ignore breaking of PIPEs. - */ -#ifdef SIGPIPE - signal(SIGPIPE, SIG_IGN); -#endif - -#ifdef SIGINT - catch_int_signal(); -#endif - - /* - * Ignore alarm signals (Perl's alarm() generates it). - */ -#ifdef SIGALRM - signal(SIGALRM, SIG_IGN); -#endif - - /* - * Catch SIGPWR (power failure?) to preserve the swap files, so that no - * work will be lost. - */ -#ifdef SIGPWR - signal(SIGPWR, (RETSIGTYPE (*)())catch_sigpwr); -#endif - - /* - * Arrange for other signals to gracefully shutdown Vim. - */ - catch_signals(deathtrap, SIG_ERR); - -} - -#if defined(SIGINT) || defined(PROTO) -/* - * Catch CTRL-C (only works while in Cooked mode). - */ -static void catch_int_signal() -{ - signal(SIGINT, (RETSIGTYPE (*)())catch_sigint); -} - -#endif - -void reset_signals() -{ - catch_signals(SIG_DFL, SIG_DFL); -#if defined(_REENTRANT) && defined(SIGCONT) - /* SIGCONT isn't in the list, because its default action is ignore */ - signal(SIGCONT, SIG_DFL); -#endif -} - -static void catch_signals( - RETSIGTYPE (*func_deadly)(), - RETSIGTYPE (*func_other)() - ) -{ - int i; - - for (i = 0; signal_info[i].sig != -1; i++) - if (signal_info[i].deadly) { -#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) - struct sigaction sa; - - /* Setup to use the alternate stack for the signal function. */ - sa.sa_handler = func_deadly; - sigemptyset(&sa.sa_mask); -# if defined(__linux__) && defined(_REENTRANT) - /* On Linux, with glibc compiled for kernel 2.2, there is a bug in - * thread handling in combination with using the alternate stack: - * pthread library functions try to use the stack pointer to - * identify the current thread, causing a SEGV signal, which - * recursively calls deathtrap() and hangs. */ - sa.sa_flags = 0; -# else - sa.sa_flags = SA_ONSTACK; -# endif - sigaction(signal_info[i].sig, &sa, NULL); -#else -# if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGVEC) - struct sigvec sv; - - /* Setup to use the alternate stack for the signal function. */ - sv.sv_handler = func_deadly; - sv.sv_mask = 0; - sv.sv_flags = SV_ONSTACK; - sigvec(signal_info[i].sig, &sv, NULL); -# else - signal(signal_info[i].sig, func_deadly); -# endif -#endif - } else if (func_other != SIG_ERR) - signal(signal_info[i].sig, func_other); -} - -/* - * Handling of SIGHUP, SIGQUIT and SIGTERM: - * "when" == a signal: when busy, postpone and return FALSE, otherwise - * return TRUE - * "when" == SIGNAL_BLOCK: Going to be busy, block signals - * "when" == SIGNAL_UNBLOCK: Going to wait, unblock signals, use postponed - * signal - * Returns TRUE when Vim should exit. - */ -int vim_handle_signal(int sig) -{ - static int got_signal = 0; - static int blocked = TRUE; - - switch (sig) { - case SIGNAL_BLOCK: blocked = TRUE; - break; - - case SIGNAL_UNBLOCK: blocked = FALSE; - if (got_signal != 0) { - kill(getpid(), got_signal); - got_signal = 0; - } - break; - - default: if (!blocked) - return TRUE; /* exit! */ - got_signal = sig; -#ifdef SIGPWR - if (sig != SIGPWR) -#endif - got_int = TRUE; /* break any loops */ - break; - } - return FALSE; -} - /* * Check_win checks whether we have an interactive stdout. */ @@ -1153,26 +603,11 @@ int mch_nodetype(char_u *name) void mch_early_init() { - /* - * Setup an alternative stack for signals. Helps to catch signals when - * running out of stack space. - * Use of sigaltstack() is preferred, it's more portable. - * Ignore any errors. - */ -#if defined(HAVE_SIGALTSTACK) || defined(HAVE_SIGSTACK) - signal_stack = (char *)alloc(SIGSTKSZ); - init_signal_stack(); -#endif - time_init(); } #if defined(EXITFREE) || defined(PROTO) void mch_free_mem() { -# if defined(HAVE_SIGALTSTACK) || defined(HAVE_SIGSTACK) - vim_free(signal_stack); - signal_stack = NULL; -# endif vim_free(oldtitle); vim_free(oldicon); } @@ -1239,7 +674,6 @@ void mch_exit(int r) } out_flush(); ml_close_all(TRUE); /* remove all memfiles */ - may_core_dump(); #ifdef MACOS_CONVERT mac_conv_cleanup(); @@ -1254,14 +688,6 @@ void mch_exit(int r) exit(r); } -static void may_core_dump() -{ - if (deadly_signal != 0) { - signal(deadly_signal, SIG_DFL); - kill(getpid(), deadly_signal); /* Die using the signal we caught */ - } -} - void mch_settmode(int tmode) { static int first = TRUE; @@ -1749,7 +1175,7 @@ int mch_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg) } } } else if (pid == 0) { /* child */ - reset_signals(); /* handle signals normally */ + signal_stop(); /* handle signals normally */ if (opts & (kShellOptHideMess | kShellOptExpand)) { int fd; @@ -1846,8 +1272,7 @@ int mch_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg) * While child is running, ignore terminating signals. * Do catch CTRL-C, so that "got_int" is set. */ - catch_signals(SIG_IGN, SIG_ERR); - catch_int_signal(); + signal_reject_deadly(); /* * For the GUI we redirect stdin, stdout and stderr to our window. @@ -2266,7 +1691,7 @@ finished: if (tmode == TMODE_RAW) settmode(TMODE_RAW); did_settmode = TRUE; - set_signals(); + signal_accept_deadly(); if (WIFEXITED(status)) { /* LINTED avoid "bitwise operation on signed value" */ @@ -2365,9 +1790,7 @@ select_eintr: # ifdef EINTR if (ret == -1 && errno == EINTR) { /* Check whether window has been resized, EINTR may be caused by - * SIGWINCH. */ - if (do_resize) - handle_resize(); + * SIGWINCH. FIXME this is broken for now */ /* Interrupted by a signal, need to try again. We ignore msec * here, because we do want to check even after a timeout if @@ -3035,7 +2458,6 @@ int *number_result; # if defined(USE_DLOPEN) dlerr = NULL; # endif - mch_didjmp(); } else { retval_str = NULL; @@ -3127,5 +2549,3 @@ int *number_result; - - |