home *** CD-ROM | disk | FTP | other *** search
- /* Handling asynchronous signals.
- Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
- Copyright (C) 1995 Ben Wing.
-
- This file is part of XEmacs.
-
- XEmacs is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- XEmacs is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
-
- You should have received a copy of the GNU General Public License
- along with XEmacs; see the file COPYING. If not, write to the Free
- Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /* Just to make sure we don't use any global vars below */
- #define DONT_DECLARE_MAC_VARS
-
- #include <config.h>
- #include "lisp.h"
-
- #include "device.h"
- #include "events.h" /* for signal_fake_event() */
- #include "frame.h"
- #include "sysdep.h"
- #include "syssignal.h"
- #include "systime.h"
-
- #include <errno.h>
-
- /* Set to 1 when a quit-check signal (either a SIGIO interrupt or
- the asynch. timeout for poll-for-quit) occurs. The QUITP
- macro may look at this. */
- volatile int quit_check_signal_happened;
-
- /* Count of the number of times a quit-check signal has occurred.
- Some stuff in event-Xt.c looks at this. */
- volatile int quit_check_signal_tick_count;
-
- /* Set to 1 when a SIGINT (or SIGQUIT) interrupt is processed.
- maybe_read_quit_event() looks at this. */
- volatile int sigint_happened;
-
- /* Set to 1 when an asynch. timeout signal occurs. */
- static volatile int alarm_happened;
-
- /* This is used to synchronize setting the waiting_for_user_input_p
- flag. */
- static volatile int alarm_happened_while_emacs_was_blocking;
-
- #if !defined (SIGIO) && !defined (DONT_POLL_FOR_QUIT)
- int poll_for_quit_id;
- #endif
-
- #ifndef SIGCHLD
- int poll_for_sigchld_id;
- #endif
-
- /* This variable is used to communicate to a lisp
- process-filter/sentinel/asynchronous callback (via the function
- Fwaiting_for_user_input_p below) whether XEmacs was waiting for
- user-input when that process-filter was called. */
- static int waiting_for_user_input_p;
-
- static int interrupts_slowed_down;
-
- #define SLOWED_DOWN_INTERRUPTS_SECS 5
- #define NORMAL_QUIT_CHECK_TIMEOUT_MSECS 250
- #define NORMAL_SIGCHLD_CHECK_TIMEOUT_MSECS 250
-
- /* Used so that signals can break out of system calls that aren't
- naturally interruptible. */
-
- jmp_buf break_system_call_jump;
- int can_break_system_calls;
-
-
- /**********************************************************************/
- /* Asynchronous timeout functions */
- /**********************************************************************/
-
- /* The pending timers are stored in an ordered list, where the first timer
- on the list is the first one to fire. Times recorded here are
- absolute. */
- static struct low_level_timeout *async_timer_queue;
-
- /* Nonzero means async timers are temporarily suppressed. */
- static int async_timer_suppress_count;
-
- static void
- set_one_shot_timer (EMACS_TIME interval)
- {
- #ifdef HAVE_SETITIMER
- struct itimerval it;
- it.it_value = interval;
- EMACS_SET_SECS_USECS (it.it_interval, 0, 0);
- setitimer (ITIMER_REAL, &it, 0);
- #else
- int secs;
- EMACS_TIME_TO_INT (interval, secs);
- alarm (secs);
- #endif
- }
-
- static void
- reset_interval_timer (void)
- {
- EMACS_TIME interval;
-
- /* Get the interval to set. If an interval is available,
- make sure it's not zero (this is a valid return, but it will
- cause the timer to get disabled, so convert it to a very short
- time). */
- if (get_low_level_timeout_interval (async_timer_queue, &interval))
- {
- if (EMACS_SECS (interval) == 0 && EMACS_USECS (interval) == 0)
- EMACS_SET_USECS (interval, 1);
- }
- else
- /* A time of 0 means "disable". */
- EMACS_SET_SECS_USECS (interval, 0, 0);
-
- set_one_shot_timer (interval);
- }
-
- int
- event_stream_add_async_timeout (EMACS_TIME time)
- {
- int id = add_low_level_timeout (&async_timer_queue, time);
-
- /* If this timeout is at the head of the queue, then we need to
- set the timer right now for this timeout. Otherwise, things
- are fine as-is; after the timers ahead of us are signalled,
- the timer will be set for us. */
-
- if (async_timer_queue->id == id)
- reset_interval_timer ();
-
- return id;
- }
-
- void
- event_stream_remove_async_timeout (int id)
- {
- int first = (async_timer_queue && async_timer_queue->id == id);
- remove_low_level_timeout (&async_timer_queue, id);
-
- /* If we removed the timeout from the head of the queue, then
- we need to reset the interval timer right now. */
- if (first)
- reset_interval_timer ();
- }
-
- /* Handle an alarm once each second and read pending input
- so as to handle a C-g if it comes in. */
-
- static SIGTYPE
- alarm_signal (int signo)
- {
- if (interrupts_slowed_down)
- {
- something_happened = 1; /* tell QUIT to wake up */
- /* we are in "slowed-down interrupts" mode; the only alarm
- happening here is the slowed-down quit-check alarm, so
- we set this flag.
-
- Do NOT set alarm_happened, because we don't want anyone
- looking at the timeout queue. We didn't set it and
- it needs to stay the way it is. */
- quit_check_signal_happened = 1;
-
- /* can_break_system_calls is set when we want to break out of
- non-interruptible system calls. */
- if (can_break_system_calls)
- {
- /* reset the flag for safety and such. Do this *before*
- unblocking or reestablishing the signal to avoid potential
- race conditions. */
- can_break_system_calls = 0;
- EMACS_UNBLOCK_SIGNAL (signo);
- EMACS_REESTABLISH_SIGNAL (signo, alarm_signal);
- longjmp (break_system_call_jump, 0);
- }
-
- EMACS_REESTABLISH_SIGNAL (signo, alarm_signal);
- SIGRETURN;
- }
-
- something_happened = 1; /* tell QUIT to wake up */
- alarm_happened = 1;
- if (emacs_is_blocking)
- alarm_happened_while_emacs_was_blocking = 1;
- /* #### This is for QUITP. When it is run, it may not be the
- place to do arbitrary stuff like run asynch. handlers, but
- it needs to know whether the poll-for-quit asynch. timeout
- went off. Rather than put the code in to compute this
- specially, we just set this flag. Should fix this. */
- quit_check_signal_happened = 1;
- signal_fake_event ();
-
- EMACS_REESTABLISH_SIGNAL (signo, alarm_signal);
- SIGRETURN;
- }
-
- static void
- init_async_timeouts (void)
- {
- signal (SIGALRM, alarm_signal);
- async_timer_suppress_count = 0;
- }
-
- /* Turn off async timeouts. */
-
- static void
- stop_async_timeouts (void)
- {
- if (async_timer_suppress_count == 0)
- {
- /* If timer was on, turn it off. */
- EMACS_TIME time;
- EMACS_SET_SECS_USECS (time, 0, 0);
- set_one_shot_timer (time);
- }
- async_timer_suppress_count++;
- }
-
- /* Turn on async timeouts again. */
-
- static void
- start_async_timeouts (void)
- {
- assert (async_timer_suppress_count > 0);
- async_timer_suppress_count--;
- if (async_timer_suppress_count == 0)
- {
- /* Some callers turn off async timeouts and then use the alarm
- for their own purposes; so reinitialize everything. */
- signal (SIGALRM, alarm_signal);
- reset_interval_timer ();
- }
- }
-
- /* Some functions don't like being interrupted with SIGALRM or SIGIO.
- Previously we were calling stop_interrupts() / start_interrupts(),
- but then if the program hangs in one of those functions, e.g.
- waiting for a connect(), we're really screwed. So instead we
- just "slow them down". We do this by disabling all interrupts
- and then installing a timer of length fairly large, like 5 or
- 10 secs. That way, any "legitimate" connections (which should
- take a fairly short amount of time) go through OK, but we can
- interrupt bogus ones. */
-
- void
- slow_down_interrupts (void)
- {
- EMACS_TIME time;
-
- /* We have to set the flag *before* setting the slowed-down timer,
- to avoid a race condition -- if the signal occurs between the
- call to set_one_shot_timer() and the setting of this flag,
- alarm_happened will get set, which will be a Bad Thing if
- there were no timeouts on the queue. */
- interrupts_slowed_down++;
- if (interrupts_slowed_down == 1)
- {
- stop_interrupts ();
- EMACS_SET_SECS_USECS (time, SLOWED_DOWN_INTERRUPTS_SECS, 0);
- set_one_shot_timer (time);
- }
- }
-
- void
- speed_up_interrupts (void)
- {
- if (interrupts_slowed_down > 0)
- {
- start_interrupts ();
- /* Change this flag AFTER fiddling with interrupts, for the same
- race-condition reasons as above. */
- interrupts_slowed_down--;
- }
- }
-
- static void
- handle_alarm_going_off (void)
- {
- int interval_id;
-
- /* If asynch. timeouts are blocked, then don't do anything now,
- but make this function get called again next QUIT.
-
- #### This is a bit inefficient because there will be function call
- overhead each time QUIT occurs. */
-
- if (!NILP (Vinhibit_quit))
- {
- something_happened = 1;
- alarm_happened = 1;
- return;
- }
-
- interval_id = pop_low_level_timeout (&async_timer_queue, 0);
-
- reset_interval_timer ();
- if (alarm_happened_while_emacs_was_blocking)
- {
- alarm_happened_while_emacs_was_blocking = 0;
- waiting_for_user_input_p = 1;
- }
- event_stream_deal_with_async_timeout (interval_id);
- waiting_for_user_input_p = 0;
- }
-
- #ifdef HAVE_SETITIMER
- unsigned int
- alarm (unsigned int howlong)
- {
- struct itimerval old_it, new_it;
-
- /* If alarm() gets called when polling isn't disabled, it can mess
- up the periodic timer. */
- assert (async_timer_suppress_count > 0);
-
- new_it.it_value.tv_sec = howlong;
- new_it.it_value.tv_usec = 0;
- new_it.it_interval.tv_sec = 0;
- new_it.it_interval.tv_usec = 0;
- setitimer (ITIMER_REAL, &new_it, &old_it);
-
- /* Never return zero if there was a timer outstanding. */
- return old_it.it_value.tv_sec + (old_it.it_value.tv_usec > 0 ? 1 : 0);
- }
- #endif
-
- DEFUN ("waiting-for-user-input-p", Fwaiting_for_user_input_p,
- Swaiting_for_user_input_p,
- 0, 0, 0,
- "Return non-nil if XEmacs is waiting for input from the user.\n\
- This is intended for use by asynchronous timeout callbacks and by\n\
- asynchronous process output filters and sentinels (not yet implemented\n\
- in XEmacs). It will always be nil if XEmacs is not inside of\n\
- an asynchronout timeout or process callback.")
- ()
- {
- return ((waiting_for_user_input_p) ? Qt : Qnil);
- }
-
-
- /**********************************************************************/
- /* Control-G checking */
- /**********************************************************************/
-
- /* Set this for debugging, to have a way to get out */
- int stop_character; /* #### not currently implemented */
-
- /* This routine is called in response to a SIGINT or SIGQUIT.
- On TTY's, one of these two signals will get generated in response
- to C-g. (When running under X, C-g is handled using the SIGIO
- handler, which sets a flag telling the QUIT macro to scan the
- unread events for a ^G.)
-
- Otherwise it sets the Lisp variable quit-flag not-nil.
- This causes eval to throw, when it gets a chance.
- If quit-flag is already non-nil, it stops the job right away. */
-
- static SIGTYPE
- interrupt_signal (int sig)
- {
- /* This function can GC (?!) */
- /* Must preserve main program's value of errno. */
- int old_errno = errno;
-
- EMACS_REESTABLISH_SIGNAL (sig, interrupt_signal);
-
- /* with the macroized error-checking stuff, the garbage below
- may mess things up because XDEVICE() and such can use and
- change global vars. */
- #if ! (defined (ERROR_CHECK_TYPECHECK) && defined (MACROIZE_ERROR_CHECKING))
- if (sigint_happened && DEVICEP (Vcontrolling_terminal) &&
- DEVICE_LIVE_P (XDEVICE (Vcontrolling_terminal)) &&
- !emacs_is_blocking)
- {
- char c;
- fflush (stdout);
- reset_initial_device ();
- EMACS_UNBLOCK_SIGNAL (sig);
- #ifdef SIGTSTP /* Support possible in later USG versions */
- /*
- * On systems which can suspend the current process and return to the original
- * shell, this command causes the user to end up back at the shell.
- * The "Auto-save" and "Abort" questions are not asked until
- * the user elects to return to emacs, at which point he can save the current
- * job and either dump core or continue.
- */
- sys_suspend ();
- #else
- #ifdef VMS
- if (sys_suspend () == -1)
- {
- stdout_out ("Not running as a subprocess;\n");
- stdout_out ("you can continue or abort.\n");
- }
- #else /* not VMS */
- /* Perhaps should really fork an inferior shell?
- But that would not provide any way to get back
- to the original shell, ever. */
- stdout_out ("No support for stopping a process on this operating system;\n");
- stdout_out ("you can continue or abort.\n");
- #endif /* not VMS */
- #endif /* not SIGTSTP */
- stdout_out ("Auto-save? (y or n) ");
- fflush (stdout);
- if (((c = getc (stdin)) & ~040) == 'Y')
- Fdo_auto_save (Qnil, Qnil);
- while (c != '\n')
- c = getc (stdin);
- #ifdef VMS
- stdout_out ("Abort (and enter debugger)? (y or n) ");
- #else /* not VMS */
- stdout_out ("Abort (and dump core)? (y or n) ");
- #endif /* not VMS */
- fflush (stdout);
- if (((c = getc (stdin)) & ~040) == 'Y')
- abort ();
- while (c != '\n')
- c = getc (stdin);
- stdout_out ("Continuing...\n");
- fflush (stdout);
- reinit_initial_device ();
- MARK_FRAME_CHANGED (XFRAME (DEVICE_SELECTED_FRAME
- (XDEVICE (Vcontrolling_terminal))));
- }
- else
- #endif /* ! (defined (ERROR_CHECKING) && defined (MACROIZE_ERROR_CHECKING)) */
- {
- /* Else request quit when it's safe */
- Vquit_flag = Qt;
- sigint_happened = 1;
- signal_fake_event ();
- }
- errno = old_errno;
- SIGRETURN;
- }
-
- /* The effect of this function is to set Vquit_flag if the user pressed
- ^G and discard the ^G, so as to not notice the same ^G again. */
- int
- check_quit (void)
- {
- if (quit_check_signal_happened)
- {
- quit_check_signal_happened = 0;
- event_stream_quit_p ();
- return 1;
- }
- else
- return 0;
- }
-
- int
- check_what_happened (void) /* called from QUIT when
- something_happened gets set */
- {
- something_happened = 0;
- if (alarm_happened)
- {
- alarm_happened = 0;
- handle_alarm_going_off ();
- }
- return check_quit ();
- }
-
-
- #if !defined (SIGIO) && !defined (DONT_POLL_FOR_QUIT)
-
- static void
- init_poll_for_quit (void)
- {
- /* Check for C-g every 1/4 of a second.
-
- #### This is just a guess. Some investigation will have to be
- done to see what the best value is. The best value is the
- smallest possible value that doesn't cause a significant amount
- of running time to be spent in C-g checking. */
- poll_for_quit_id =
- event_stream_generate_wakeup (NORMAL_QUIT_CHECK_TIMEOUT_MSECS,
- NORMAL_QUIT_CHECK_TIMEOUT_MSECS,
- Qnil, Qnil, 1);
- }
-
- #endif /* not SIGIO and not DONT_POLL_FOR_QUIT */
-
- #ifndef SIGCHLD
-
- static void
- init_poll_for_sigchld (void)
- {
- /* Check for terminated processes every 1/4 of a second.
-
- #### This is just a guess. Some investigation will have to be
- done to see what the best value is. The best value is the
- smallest possible value that doesn't cause a significant amount
- of running time to be spent in process-termination checking. */
- poll_for_sigchld_id =
- event_stream_generate_wakeup (NORMAL_SIGCHLD_CHECK_TIMEOUT_MSECS,
- NORMAL_SIGCHLD_CHECK_TIMEOUT_MSECS,
- Qnil, Qnil, 1);
- }
-
- #endif /* not SIGCHLD */
-
- #ifdef SIGIO
-
- static void
- input_available_signal (int signo)
- {
- something_happened = 1; /* tell QUIT to wake up */
- quit_check_signal_happened = 1;
- quit_check_signal_tick_count++;
- EMACS_REESTABLISH_SIGNAL (signo, input_available_signal);
- SIGRETURN;
- }
-
- #endif /* SIGIO */
-
-
- /**********************************************************************/
- /* Enabling/disabling signals */
- /**********************************************************************/
-
- static int interrupts_initted;
-
- void
- stop_interrupts (void)
- {
- if (!interrupts_initted)
- return;
- #ifdef SIGIO
- unrequest_sigio ();
- #endif
- stop_async_timeouts ();
- }
-
- void
- start_interrupts (void)
- {
- if (!interrupts_initted)
- return;
- #ifdef SIGIO
- request_sigio ();
- #endif
- start_async_timeouts ();
- }
-
-
- /************************************************************************/
- /* initialization */
- /************************************************************************/
-
- void
- init_signals_very_early (void)
- {
- /* Catch all signals that would kill us. */
- if (! noninteractive || initialized)
- {
- /* Don't catch these signals in batch mode if not initialized.
- On some machines, this sets static data that would make
- signal fail to work right when the dumped Emacs is run. */
- signal (SIGHUP, fatal_error_signal);
- signal (SIGQUIT, fatal_error_signal);
- signal (SIGILL, fatal_error_signal);
- signal (SIGTRAP, fatal_error_signal);
- #ifdef SIGABRT
- signal (SIGABRT, fatal_error_signal);
- #endif
- #ifdef SIGHWE
- signal (SIGHWE, fatal_error_signal);
- #endif
- #ifdef SIGPRE
- signal (SIGPRE, fatal_error_signal);
- #endif
- #ifdef SIGORE
- signal (SIGORE, fatal_error_signal);
- #endif
- #ifdef SIGUME
- signal (SIGUME, fatal_error_signal);
- #endif
- #ifdef SIGDLK
- signal (SIGDLK, fatal_error_signal);
- #endif
- #ifdef SIGCPULIM
- signal (SIGCPULIM, fatal_error_signal);
- #endif
- #ifdef SIGIOT
- signal (SIGIOT, fatal_error_signal);
- #endif
- #ifdef SIGEMT
- signal (SIGEMT, fatal_error_signal);
- #endif
- signal (SIGFPE, fatal_error_signal);
- #ifdef SIGBUS
- signal (SIGBUS, fatal_error_signal);
- #endif
- signal (SIGSEGV, fatal_error_signal);
- #ifdef SIGSYS
- signal (SIGSYS, fatal_error_signal);
- #endif
- signal (SIGPIPE, fatal_error_signal);
- signal (SIGTERM, fatal_error_signal);
- #ifdef SIGXCPU
- signal (SIGXCPU, fatal_error_signal);
- #endif
- #ifdef SIGXFSZ
- signal (SIGXFSZ, fatal_error_signal);
- #endif /* SIGXFSZ */
-
- #ifdef SIGDANGER
- /* This just means available memory is getting low. */
- signal (SIGDANGER, memory_warning_signal);
- #endif
-
- #ifdef SIGLOST
- signal (SIGLOST, fatal_error_signal);
- #endif
- #ifdef SIGSTKFLT /* coprocessor stack fault under Linux */
- signal (SIGSTKFLT, fatal_error_signal);
- #endif
- #ifdef SIGUSR1
- signal (SIGUSR1, fatal_error_signal);
- #endif
- #ifdef SIGUSR2
- signal (SIGUSR2, fatal_error_signal);
- #endif
- #ifdef SIGALRM
- /* This will get reset later, once we're capable of handling
- this properly. */
- signal (SIGALRM, fatal_error_signal);
- #endif
- #ifdef SIGVTALRM
- signal (SIGVTALRM, fatal_error_signal);
- #endif
- #ifdef SIGPROF
- signal (SIGPROF, fatal_error_signal);
- #endif
- #ifdef SIGUNUSED /* exists under Linux, and will kill process! */
- signal (SIGUNUSED, fatal_error_signal);
- #endif
-
- #ifdef AIX
- /* 20 is SIGCHLD, 21 is SIGTTIN, 22 is SIGTTOU. */
- #ifndef _I386
- signal (SIGIOINT, fatal_error_signal);
- #endif
- signal (SIGGRANT, fatal_error_signal);
- signal (SIGRETRACT, fatal_error_signal);
- signal (SIGSOUND, fatal_error_signal);
- signal (SIGMSG, fatal_error_signal);
- #endif /* AIX */
- }
- }
-
- void
- syms_of_signal (void)
- {
- defsubr (&Swaiting_for_user_input_p);
- }
-
- void
- init_interrupts_late (void)
- {
- if (!noninteractive)
- {
- signal (SIGINT, interrupt_signal);
- #ifdef HAVE_TERMIO
- /* On systems with TERMIO, C-g is set up for both SIGINT and SIGQUIT
- and we can't tell which one it will give us. */
- signal (SIGQUIT, interrupt_signal);
- #endif /* HAVE_TERMIO */
- init_async_timeouts ();
- #ifdef SIGIO
- signal (SIGIO, input_available_signal);
- # ifdef SIGPOLL
- /* Some systems (e.g. Motorola SVR4) losingly have different
- values for SIGIO and SIGPOLL, and send SIGPOLL instead of
- SIGIO. On those same systems, an uncaught SIGPOLL kills the
- process. */
- signal (SIGPOLL, input_available_signal);
- # endif
- #elif !defined (DONT_POLL_FOR_QUIT)
- init_poll_for_quit ();
- #endif
- }
-
- #ifndef SIGCHLD
- init_poll_for_sigchld ();
- #endif
-
- EMACS_UNBLOCK_ALL_SIGNALS ();
-
- interrupts_initted = 1;
- }
-
-