diff options
Diffstat (limited to 'src/sysdep.c')
-rw-r--r-- | src/sysdep.c | 280 |
1 files changed, 120 insertions, 160 deletions
diff --git a/src/sysdep.c b/src/sysdep.c index b84e6a4ea3a..dbfd9efc7d4 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -21,9 +21,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #define SYSTIME_INLINE EXTERN_INLINE -#include <signal.h> +#include <execinfo.h> #include <stdio.h> -#include <setjmp.h> #ifdef HAVE_PWD_H #include <pwd.h> #include <grp.h> @@ -107,9 +106,6 @@ extern char *getwd (char *); static int emacs_get_tty (int, struct emacs_tty *); static int emacs_set_tty (int, struct emacs_tty *, int); -#if defined TIOCNOTTY || defined USG5 || defined CYGWIN -static _Noreturn void croak (char *); -#endif /* ULLONG_MAX is missing on Red Hat Linux 7.3; see Bug#11781. */ #ifndef ULLONG_MAX @@ -302,27 +298,34 @@ wait_for_termination_1 (pid_t pid, int interruptible) termination of subprocesses, perhaps involving a kernel bug too, but no idea what it is. Just as a hunch we signal SIGCHLD to see if that causes the problem to go away or get worse. */ - sigsetmask (sigmask (SIGCHLD)); + sigset_t sigchild_mask; + sigemptyset (&sigchild_mask); + sigaddset (&sigchild_mask, SIGCHLD); + pthread_sigmask (SIG_SETMASK, &sigchild_mask, 0); + if (0 > kill (pid, 0)) { - sigsetmask (SIGEMPTYMASK); + pthread_sigmask (SIG_SETMASK, &empty_mask, 0); kill (getpid (), SIGCHLD); break; } if (wait_debugging) sleep (1); else - sigpause (SIGEMPTYMASK); + sigsuspend (&empty_mask); #else /* not BSD_SYSTEM, and not HPUX version >= 6 */ #ifdef WINDOWSNT wait (0); break; #else /* not WINDOWSNT */ - sigblock (sigmask (SIGCHLD)); + sigset_t blocked; + sigemptyset (&blocked); + sigaddset (&blocked, SIGCHLD); + pthread_sigmask (SIG_BLOCK, &blocked, 0); errno = 0; if (kill (pid, 0) == -1 && errno == ESRCH) { - sigunblock (sigmask (SIGCHLD)); + pthread_sigmask (SIG_UNBLOCK, &blocked, 0); break; } @@ -456,11 +459,11 @@ child_setup_tty (int out) #endif /* not MSDOS */ -/* Record a signal code and the handler for it. */ +/* Record a signal code and the action for it. */ struct save_signal { int code; - void (*handler) (int); + struct sigaction action; }; static void save_signal_handlers (struct save_signal *); @@ -506,7 +509,7 @@ sys_subshell (void) saved_handlers[0].code = SIGINT; saved_handlers[1].code = SIGQUIT; saved_handlers[2].code = SIGTERM; -#ifdef SIGIO +#ifdef USABLE_SIGIO saved_handlers[3].code = SIGIO; saved_handlers[4].code = 0; #else @@ -618,8 +621,9 @@ save_signal_handlers (struct save_signal *saved_handlers) { while (saved_handlers->code) { - saved_handlers->handler - = (void (*) (int)) signal (saved_handlers->code, SIG_IGN); + struct sigaction action; + emacs_sigaction_init (&action, SIG_IGN); + sigaction (saved_handlers->code, &action, &saved_handlers->action); saved_handlers++; } } @@ -629,118 +633,80 @@ restore_signal_handlers (struct save_signal *saved_handlers) { while (saved_handlers->code) { - signal (saved_handlers->code, saved_handlers->handler); + sigaction (saved_handlers->code, &saved_handlers->action, 0); saved_handlers++; } } -#ifndef SIGIO -/* If SIGIO is broken, don't do anything. */ -void -init_sigio (int fd) -{ -} - -static void -reset_sigio (int fd) -{ -} - -void -request_sigio (void) -{ -} - -void -unrequest_sigio (void) -{ -} - -#else -#ifdef F_SETFL - +#ifdef USABLE_SIGIO static int old_fcntl_flags[MAXDESC]; +#endif void init_sigio (int fd) { -#ifdef FASYNC +#ifdef USABLE_SIGIO old_fcntl_flags[fd] = fcntl (fd, F_GETFL, 0) & ~FASYNC; fcntl (fd, F_SETFL, old_fcntl_flags[fd] | FASYNC); -#endif interrupts_deferred = 0; +#endif } static void reset_sigio (int fd) { -#ifdef FASYNC +#ifdef USABLE_SIGIO fcntl (fd, F_SETFL, old_fcntl_flags[fd]); #endif } -#ifdef FASYNC /* F_SETFL does not imply existence of FASYNC */ -/* XXX Uhm, FASYNC is not used anymore here. */ -/* XXX Yeah, but you need it for SIGIO, don't you? */ - void request_sigio (void) { +#ifdef USABLE_SIGIO + sigset_t unblocked; + if (noninteractive) return; -#ifdef SIGWINCH - sigunblock (sigmask (SIGWINCH)); -#endif - sigunblock (sigmask (SIGIO)); + sigemptyset (&unblocked); +# ifdef SIGWINCH + sigaddset (&unblocked, SIGWINCH); +# endif + sigaddset (&unblocked, SIGIO); + pthread_sigmask (SIG_UNBLOCK, &unblocked, 0); interrupts_deferred = 0; +#endif } void unrequest_sigio (void) { - if (noninteractive) - return; +#ifdef USABLE_SIGIO + sigset_t blocked; -#if 0 /* XXX What's wrong with blocking SIGIO under X? */ - if (x_display_list) + if (noninteractive) return; -#endif -#ifdef SIGWINCH - sigblock (sigmask (SIGWINCH)); -#endif - sigblock (sigmask (SIGIO)); + sigemptyset (&blocked); +# ifdef SIGWINCH + sigaddset (&blocked, SIGWINCH); +# endif + sigaddset (&blocked, SIGIO); + pthread_sigmask (SIG_BLOCK, &blocked, 0); interrupts_deferred = 1; -} - -#else /* no FASYNC */ -#ifndef MSDOS - -void -request_sigio (void) -{ - if (noninteractive || read_socket_hook) - return; - - croak ("request_sigio"); +#endif } void -unrequest_sigio (void) +ignore_sigio (void) { - if (noninteractive || read_socket_hook) - return; - - croak ("unrequest_sigio"); +#ifdef USABLE_SIGIO + signal (SIGIO, SIG_IGN); +#endif } -#endif /* MSDOS */ -#endif /* FASYNC */ -#endif /* F_SETFL */ -#endif /* SIGIO */ - /* Getting and setting emacs_tty structures. */ @@ -1470,86 +1436,64 @@ init_system_name (void) } } -/* POSIX signals support - DJB */ -/* Anyone with POSIX signals should have ANSI C declarations */ - sigset_t empty_mask; -#ifndef WINDOWSNT - -signal_handler_t -sys_signal (int signal_number, signal_handler_t action) +/* Store into *ACTION a signal action suitable for Emacs, with handler + HANDLER. */ +void +emacs_sigaction_init (struct sigaction *action, signal_handler_t handler) { - struct sigaction new_action, old_action; - sigemptyset (&new_action.sa_mask); - new_action.sa_handler = action; - new_action.sa_flags = 0; + sigemptyset (&action->sa_mask); + action->sa_handler = handler; + action->sa_flags = 0; #if defined (SA_RESTART) - /* Emacs mostly works better with restartable system services. If this - flag exists, we probably want to turn it on here. - However, on some systems (only hpux11 at present) this resets the - timeout of `select' which means that `select' never finishes if - it keeps getting signals. - We define BROKEN_SA_RESTART on those systems. */ - /* It's not clear why the comment above says "mostly works better". --Stef - When SYNC_INPUT is set, we don't want SA_RESTART because we need to poll + /* SA_RESTART causes interruptible functions with timeouts (e.g., + 'select') to reset their timeout on some platforms (e.g., + HP-UX 11), which is not what we want. Also, when Emacs is + interactive, we don't want SA_RESTART because we need to poll for pending input so we need long-running syscalls to be interrupted after a signal that sets the interrupt_input_pending flag. */ /* Non-interactive keyboard input goes through stdio, where we always want restartable system calls. */ -# if defined (BROKEN_SA_RESTART) || defined (SYNC_INPUT) if (noninteractive) -# endif - new_action.sa_flags = SA_RESTART; + action->sa_flags = SA_RESTART; #endif - sigaction (signal_number, &new_action, &old_action); - return (old_action.sa_handler); } -#endif /* WINDOWSNT */ - -#ifndef __GNUC__ -/* If we're compiling with GCC, we don't need this function, since it - can be written as a macro. */ -sigset_t -sys_sigmask (int sig) -{ - sigset_t mask; - sigemptyset (&mask); - sigaddset (&mask, sig); - return mask; -} +#ifdef FORWARD_SIGNAL_TO_MAIN_THREAD +static pthread_t main_thread; #endif -/* I'd like to have these guys return pointers to the mask storage in here, - but there'd be trouble if the code was saving multiple masks. I'll be - safe and pass the structure. It normally won't be more than 2 bytes - anyhow. - DJB */ - -sigset_t -sys_sigblock (sigset_t new_mask) +/* If we are on the main thread, handle the signal SIG with HANDLER. + Otherwise, redirect the signal to the main thread, blocking it from + this thread. POSIX says any thread can receive a signal that is + associated with a process, process group, or asynchronous event. + On GNU/Linux that is not true, but for other systems (FreeBSD at + least) it is. */ +void +handle_on_main_thread (int sig, signal_handler_t handler) { - sigset_t old_mask; - pthread_sigmask (SIG_BLOCK, &new_mask, &old_mask); - return (old_mask); -} + /* Preserve errno, to avoid race conditions with signal handlers that + might change errno. Races can occur even in single-threaded hosts. */ + int old_errno = errno; -sigset_t -sys_sigunblock (sigset_t new_mask) -{ - sigset_t old_mask; - pthread_sigmask (SIG_UNBLOCK, &new_mask, &old_mask); - return (old_mask); -} + bool on_main_thread = true; +#ifdef FORWARD_SIGNAL_TO_MAIN_THREAD + if (! pthread_equal (pthread_self (), main_thread)) + { + sigset_t blocked; + sigemptyset (&blocked); + sigaddset (&blocked, sig); + pthread_sigmask (SIG_BLOCK, &blocked, 0); + pthread_kill (main_thread, sig); + on_main_thread = false; + } +#endif + if (on_main_thread) + handler (sig); -sigset_t -sys_sigsetmask (sigset_t new_mask) -{ - sigset_t old_mask; - pthread_sigmask (SIG_SETMASK, &new_mask, &old_mask); - return (old_mask); + errno = old_errno; } - #if !defined HAVE_STRSIGNAL && !HAVE_DECL_SYS_SIGLIST static char *my_sys_siglist[NSIG]; @@ -1564,6 +1508,10 @@ init_signals (void) { sigemptyset (&empty_mask); +#ifdef FORWARD_SIGNAL_TO_MAIN_THREAD + main_thread = pthread_self (); +#endif + #if !defined HAVE_STRSIGNAL && !HAVE_DECL_SYS_SIGLIST if (! initialized) { @@ -1856,6 +1804,33 @@ snprintf (char *buf, size_t bufsize, char const *format, ...) } #endif +/* If a backtrace is available, output the top lines of it to stderr. + Do not output more than BACKTRACE_LIMIT or BACKTRACE_LIMIT_MAX lines. + This function may be called from a signal handler, so it should + not invoke async-unsafe functions like malloc. */ +void +emacs_backtrace (int backtrace_limit) +{ + enum { BACKTRACE_LIMIT_MAX = 500 }; + void *buffer[BACKTRACE_LIMIT_MAX + 1]; + int bounded_limit = min (backtrace_limit, BACKTRACE_LIMIT_MAX); + int npointers = backtrace (buffer, bounded_limit + 1); + if (npointers) + ignore_value (write (STDERR_FILENO, "\nBacktrace:\n", 12)); + backtrace_symbols_fd (buffer, bounded_limit, STDERR_FILENO); + if (bounded_limit < npointers) + ignore_value (write (STDERR_FILENO, "...\n", 4)); +} + +#ifndef HAVE_NTGUI +/* Using emacs_abort lets GDB return from a breakpoint here. */ +void +emacs_abort (void) +{ + fatal_error_backtrace (SIGABRT, 10); +} +#endif + int emacs_open (const char *path, int oflag, int mode) { @@ -1933,11 +1908,9 @@ emacs_write (int fildes, const char *buf, ptrdiff_t nbyte) { if (errno == EINTR) { -#ifdef SYNC_INPUT /* I originally used `QUIT' but that might causes files to be truncated if you hit C-g in the middle of it. --Stef */ process_pending_signals (); -#endif continue; } else @@ -2020,19 +1993,6 @@ getwd (char *pathname) } #endif /* !defined (HAVE_GETWD) || defined (BROKEN_GETWD) */ - -/* - * This function will go away as soon as all the stubs fixed. (fnf) - */ - -void -croak (char *badfunc) -{ - printf ("%s not yet implemented\r\n", badfunc); - reset_all_sys_modes (); - exit (1); -} - #endif /* USG */ /* Directory routines for systems that don't have them. */ |