changeset 24520:c5c11b07598a

refactor signal handling (bug #52757) Given that any library code may now be multi-threaded it is not likely to be save to jump out of a signal handler when executing a call to a library function. We are almost assured to fail if the call to siglongjmp occurs in a different thread from the corresponding call to sigsetjmp. Similarly, we should avoid calling the interrupt signal handler function in an arbitrary thread. This is already done by default on Windows systems. On Posix systems, we now set up a separate thread to handle asynchronous signals (not just SIGINT). We no longer attempt to jump out of the signal handler. Instead, we only set global status variables and respond to them in octave_quit. This means that some external library functions are no longer interruptible. If that becomes important, then we will need to look for other alternatives such as setting up worker threads that can be canceled when a SIGINT is detected. * sighandlers.cc (interrupt_manager, base_interrupt_manager, w32_interrupt_manager, posix_interrupt_manager): Delete classes and all uses. (respond_to_pending_signals): Rename from signal_handler. Change all uses. Take action on more signals. (generic_sig_handler): Simply record that a signal was caught and add it to the list of caught signals. (deadly_sig_handler): New function. (fpe_sig_handler): Rename from sigfpe_handler. Don't make specific to Alpha systems. Simply warn that an exception has occurred. (sigint_handler): Simply record that an interrupt signal was caught. (sigterm_handler, sigpipe_handler): Delete. (install_signal_handlers): Rework actions for various signals. (Vsigquit_dumps_octave_core): New static variable. (Fsigquit_dumps_octave_core): New function. * io.txi: Document sigquit_dumps_octave_core. * f77-fcn.h, f77-fcn.c (xSTRINGIZE, STRINGIZE, F77_XFCN_ERROR): Delete macros. (F77_XFCN): Don't do setjmp/longjmp exception handling. (f77_exception_encountered): Delete global variable. (XSTOPX): Don't set f77_exception_encountered. If message is empty, report "unknown error in fortran subroutine". * eigs-base.cc, gsvd.cc: Delete all direct uses of f77_exception_encountered. * liboctave/util/f77-extern.cc: Delete file. * liboctave/util/module.mk: Update. * cmd-edit.h, cmd-edit.cc (command_editor::interrupt_exception): New class. (command_editor::handle_interrupt_signal): New static function. (command_editor::do_handle_interrupt_signal): New virtual function. (command_editor::event_hanlder): Call handle_interrupt_signal if octave_interrupt_state is not zero. (command_editor::instance_ok): Set event hook here. (command_editor::add_event_hook): Not here. (command_editor::remove_event_hook): Never remove event hook. (gnu_readline::do_handle_interrupt_signal): New function. (gnu_readline::do_readline): Retry if an interrupt exception occurs inside readline. * oct-rl-edit.h, oct-rl-edit.c (octave_rl_recover_from_interrupt): New function. * quit.h, cquit.c (current_context, octave_save_current_context, octave_restore_current_context, octave_jump_to_enclosing_context, octave_interrupt_immediately, octave_jmp_buf): Delete. * quit.h: Don't include <setjmp.h> (octave_set_current_context): Delete macro. (octave_exit_exception_status, octave_exit_exception_safe_to_return): Mark as deprecated. (BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE_1): Delete. (BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE_2): Delete. (BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE, END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE): Define to only create a dummy do-while scope. (BEGIN_INTERRUPT_WITH_EXCEPTIONS, END_INTERRUPT_WITH_EXCEPTIONS): Define to only create a dummy scope. * interpreter.cc (interpreter::recover_from_exception): Don't reset octave_interrupt_immediately. * main-cli.cc (main): Call octave_block_async_signals. * main.in.cc (main): Call octave_block_async_signals initially. Only in install signal handlers and unblock signals if starting managed subprocess. * main-window.cc (octave_interpreter::executed): Don't unblock interrupt signals. * signal-wrappers.h, signal-wrappers.c (signal_watcher, octave_async_signals, block_or_unblock_signal_by_name): New static functions. (octave_set_default_signal_handler, octave_set_default_signal_handler_by_name, octave_block_signal_by_name, octave_unblock_signal_by_name, octave_create_interrupt_watcher_thread, octave_block_async_signals, octave_unblock_async_signals): New functions. (octave_block_interrupt_signal, octave_unblock_interrupt_signal): Treat SIGBREAK as an interrupt signal. (octave_block_child): Also handle SIGCLD the same as SIGCHLD. (print_sigset, print_sigmask): New static functions. (octave_show_sigmask): New function.
author John W. Eaton <jwe@octave.org>
date Wed, 03 Jan 2018 07:52:11 -0500
parents 6c31b0376908
children 30bcf1723ca8
files doc/interpreter/io.txi libgui/src/main-window.cc libinterp/corefcn/gl2ps-print.cc libinterp/corefcn/interpreter.cc libinterp/corefcn/sighandlers.cc libinterp/corefcn/sighandlers.h liboctave/numeric/eigs-base.cc liboctave/numeric/gsvd.cc liboctave/util/cmd-edit.cc liboctave/util/cmd-edit.h liboctave/util/cquit.c liboctave/util/f77-extern.cc liboctave/util/f77-fcn.c liboctave/util/f77-fcn.h liboctave/util/module.mk liboctave/util/oct-rl-edit.c liboctave/util/oct-rl-edit.h liboctave/util/quit.h liboctave/wrappers/signal-wrappers.c liboctave/wrappers/signal-wrappers.h src/main-cli.cc src/main.in.cc
diffstat 22 files changed, 628 insertions(+), 761 deletions(-) [+]
line wrap: on
line diff
--- a/doc/interpreter/io.txi	Thu Jan 04 10:09:23 2018 -0500
+++ b/doc/interpreter/io.txi	Wed Jan 03 07:52:11 2018 -0500
@@ -252,6 +252,8 @@
 
 @DOCSTRING(sighup_dumps_octave_core)
 
+@DOCSTRING(sigquit_dumps_octave_core)
+
 @DOCSTRING(sigterm_dumps_octave_core)
 
 @DOCSTRING(octave_core_file_options)
--- a/libgui/src/main-window.cc	Thu Jan 04 10:09:23 2018 -0500
+++ b/libgui/src/main-window.cc	Wed Jan 03 07:52:11 2018 -0500
@@ -93,8 +93,6 @@
 {
   m_thread_manager.register_current_thread ();
 
-  octave_thread_manager::unblock_interrupt_signal ();
-
   // The application context owns the interpreter.
 
   octave::interpreter& interp = m_app_context->create_interpreter ();
--- a/libinterp/corefcn/gl2ps-print.cc	Thu Jan 04 10:09:23 2018 -0500
+++ b/libinterp/corefcn/gl2ps-print.cc	Wed Jan 03 07:52:11 2018 -0500
@@ -440,7 +440,8 @@
                     nwrite = std::fwrite (fcn, 1, strlen (fcn), fp);
                     if (nwrite != strlen (fcn))
                       {
-                        octave::signal_handler ();
+                        // FIXME: is this the best thing to do here?
+                        respond_to_pending_signals ();
                         error ("gl2ps_renderer::draw: internal pipe error");
                       }
                   }
@@ -448,7 +449,8 @@
                 nwrite = std::fwrite (str, 1, nread, fp);
                 if (nwrite != nread)
                   {
-                    signal_handler ();   // Clear SIGPIPE signal
+                    // FIXME: is this the best thing to do here?
+                    respond_to_pending_signals ();   // Clear SIGPIPE signal
                     error ("gl2ps_renderer::draw: internal pipe error");
                   }
               }
--- a/libinterp/corefcn/interpreter.cc	Thu Jan 04 10:09:23 2018 -0500
+++ b/libinterp/corefcn/interpreter.cc	Wed Jan 03 07:52:11 2018 -0500
@@ -839,7 +839,7 @@
 
     can_interrupt = true;
 
-    octave_signal_hook = signal_handler;
+    octave_signal_hook = respond_to_pending_signals;
     octave_interrupt_hook = nullptr;
     octave_bad_alloc_hook = nullptr;
 
@@ -883,7 +883,7 @@
 
     can_interrupt = true;
 
-    octave_signal_hook = signal_handler;
+    octave_signal_hook = respond_to_pending_signals;
     octave_interrupt_hook = nullptr;
     octave_bad_alloc_hook = nullptr;
 
@@ -937,7 +937,7 @@
 
     can_interrupt = true;
 
-    octave_signal_hook = signal_handler;
+    octave_signal_hook = respond_to_pending_signals;
     octave_interrupt_hook = nullptr;
     octave_bad_alloc_hook = nullptr;
 
@@ -1249,7 +1249,6 @@
   void interpreter::recover_from_exception (void)
   {
     can_interrupt = true;
-    octave_interrupt_immediately = 0;
     octave_interrupt_state = 0;
     octave_signal_caught = 0;
     octave_exception_state = octave_no_exception;
--- a/libinterp/corefcn/sighandlers.cc	Thu Jan 04 10:09:23 2018 -0500
+++ b/libinterp/corefcn/sighandlers.cc	Wed Jan 03 07:52:11 2018 -0500
@@ -74,269 +74,15 @@
   // no effect if Vcrash_dumps_octave_core is FALSE.
   static bool Vsighup_dumps_octave_core = true;
 
+  // Similar to Vsighup_dumps_octave_core, but for SIGQUIT signal.
+  static bool Vsigquit_dumps_octave_core = true;
+
   // Similar to Vsighup_dumps_octave_core, but for SIGTERM signal.
   static bool Vsigterm_dumps_octave_core = true;
 
   // List of signals we have caught since last call to signal_handler.
   static bool *signals_caught = nullptr;
 
-  // Forward declarations.
-  static void user_terminate (int sig_number);
-  static void user_abort (int sig_number);
-
-  class
-  base_interrupt_manager
-  {
-  public:
-
-    base_interrupt_manager (void) { }
-
-    // No copying!
-
-    base_interrupt_manager (const base_interrupt_manager&) = delete;
-
-    base_interrupt_manager& operator = (const base_interrupt_manager&) = delete;
-
-    virtual ~base_interrupt_manager (void) = default;
-
-    virtual void do_jump_to_enclosing_context (void) = 0;
-
-    virtual void do_user_terminate (int sig_number) = 0;
-
-    virtual void do_user_abort (int sig_number) = 0;
-
-    virtual void do_raise_sigint (void) = 0;
-  };
-
-#if defined (OCTAVE_USE_WINDOWS_API)
-
-  class
-  w32_interrupt_manager : public base_interrupt_manager
-  {
-  public:
-
-    w32_interrupt_manager (void)
-      : thread (0), thread_id (0)
-    {
-      thread_id = GetCurrentThreadId ();
-
-      DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
-                       GetCurrentProcess (), &thread, 0, FALSE,
-                       DUPLICATE_SAME_ACCESS);
-    }
-
-    // No copying!
-
-    w32_interrupt_manager (const w32_interrupt_manager&) = delete;
-
-    w32_interrupt_manager& operator = (const w32_interrupt_manager&) = delete;
-
-    ~w32_interrupt_manager (void) = default;
-
-    static void jump_to_enclosing_context_sync (void)
-    {
-#if defined (_MSC_VER)
-      _fpreset ();
-#endif
-      ::octave_jump_to_enclosing_context ();
-    }
-
-    void do_jump_to_enclosing_context (void)
-    {
-      bool is_interrupt_thread = (GetCurrentThreadId () == thread_id);
-
-      if (is_interrupt_thread)
-        jump_to_enclosing_context_sync ();
-      else
-        {
-
-          CONTEXT threadContext;
-
-          SuspendThread (thread);
-          threadContext.ContextFlags = CONTEXT_CONTROL;
-          GetThreadContext (thread, &threadContext);
-
-#if (defined (__MINGW64__) || defined (_WIN64))
-          threadContext.Rip = reinterpret_cast<DWORD64> (jump_to_enclosing_context_sync);
-#else
-          threadContext.Eip = reinterpret_cast<DWORD> (jump_to_enclosing_context_sync);
-#endif
-
-          SetThreadContext (thread, &threadContext);
-
-          ResumeThread (thread);
-        }
-    }
-
-    void do_user_terminate (int sig_number)
-    {
-      bool is_interrupt_thread = (GetCurrentThreadId () == thread_id);
-
-      if (is_interrupt_thread)
-        user_terminate (sig_number);
-      else
-        {
-          SuspendThread (thread);
-          user_terminate (sig_number);
-          ResumeThread (thread);
-        }
-    }
-
-    void do_user_abort (int sig_number)
-    {
-      bool is_interrupt_thread = (GetCurrentThreadId () == thread_id);
-
-      if (is_interrupt_thread)
-        user_abort (sig_number);
-      else
-        {
-          SuspendThread (thread);
-          user_abort (sig_number);
-          ResumeThread (thread);
-        }
-    }
-
-    void do_raise_sigint (void)
-    {
-      bool is_interrupt_thread = (GetCurrentThreadId () == thread_id);
-
-      if (is_interrupt_thread)
-        octave_raise_wrapper (SIGINT);
-      else
-        {
-          SuspendThread (thread);
-          octave_raise_wrapper (SIGINT);
-          ResumeThread (thread);
-        }
-    }
-
-  private:
-
-    // A handle to the thread that is running the octave interpreter.
-    HANDLE thread;
-
-    // The ID of the thread that is running the octave interpreter.
-    DWORD thread_id;
-  };
-
-#endif
-
-  class
-  posix_interrupt_manager : public base_interrupt_manager
-  {
-  public:
-
-    posix_interrupt_manager (void)
-      : base_interrupt_manager ()
-    { }
-
-    // No copying!
-
-    posix_interrupt_manager (const posix_interrupt_manager&) = delete;
-
-    posix_interrupt_manager&
-    operator = (const posix_interrupt_manager&) = delete;
-
-    ~posix_interrupt_manager (void) = default;
-
-    void do_jump_to_enclosing_context (void)
-    {
-      ::octave_jump_to_enclosing_context ();
-    }
-
-    void do_user_terminate (int sig_number)
-    {
-      user_terminate (sig_number);
-    }
-
-    void do_user_abort (int sig_number)
-    {
-      user_abort (sig_number);
-    }
-
-    void do_raise_sigint (void)
-    {
-      octave_raise_wrapper (SIGINT);
-    }
-  };
-
-  class
-  interrupt_manager
-  {
-  public:
-
-    ~interrupt_manager (void) = default;
-
-    static bool init (void) { return instance_ok (); }
-
-    static void jump_to_enclosing_context (void)
-    {
-      if (instance_ok ())
-        instance->do_jump_to_enclosing_context ();
-    }
-
-    static void user_terminate (int sig_number)
-    {
-      if (instance_ok ())
-        instance->do_user_terminate (sig_number);
-    }
-
-    static void user_abort (int sig_number)
-    {
-      if (instance_ok ())
-        instance->do_user_abort (sig_number);
-    }
-
-    static void raise_sigint (void)
-    {
-      if (instance_ok ())
-        instance->do_raise_sigint ();
-    }
-
-  private:
-
-    interrupt_manager (void) { }
-
-    // No copying!
-
-    interrupt_manager (const interrupt_manager&) = delete;
-
-    interrupt_manager& operator = (const interrupt_manager&) = delete;
-
-    static bool instance_ok (void)
-    {
-      bool retval = true;
-
-      if (! instance)
-        {
-          instance = create_instance ();
-
-          if (instance)
-            singleton_cleanup_list::add (cleanup_instance);
-        }
-
-      if (! instance)
-        error ("unable to create interrupt_manager");
-
-      return retval;
-    }
-
-    static base_interrupt_manager * create_instance (void)
-    {
-#if defined (OCTAVE_USE_WINDOWS_API)
-      return new w32_interrupt_manager ();
-#else
-      return new posix_interrupt_manager ();
-#endif
-    }
-
-    static void cleanup_instance (void) { delete instance; instance = nullptr; }
-
-    static base_interrupt_manager *instance;
-  };
-
-  base_interrupt_manager *interrupt_manager::instance = nullptr;
-
   static void
   my_friendly_exit (int sig, bool save_vars = true)
   {
@@ -356,31 +102,95 @@
   // we have caught.
 
   void
-  signal_handler (void)
+  respond_to_pending_signals (void)
   {
     // The list of signals is relatively short, so we will just go
     // linearly through the list.
 
-    static int sigchld;
-    static int sigfpe;
+    // Interrupt signals are currently handled separately.
+
+    static int sigint;
+    static const bool have_sigint
+      = octave_get_sig_number ("SIGINT", &sigint);
+
+    static int sigbreak;
+    static const bool have_sigbreak
+    = octave_get_sig_number ("SIGBREAK", &sigbreak);
+
+    // Termination signals.
+
     static int sighup;
+    static const bool have_sighup
+      = octave_get_sig_number ("SIGHUP", &sighup);
+
+    static int sigquit;
+    static const bool have_sigquit
+      = octave_get_sig_number ("SIGQUIT", &sigquit);
+
     static int sigterm;
-    static int sigpipe;
+    static const bool have_sigterm
+      = octave_get_sig_number ("SIGTERM", &sigterm);
+
+    // Alarm signals.
+
+    static int sigalrm;
+    static const bool have_sigalrm
+      = octave_get_sig_number ("SIGALRM", &sigalrm);
+
+    // I/O signals.
+
+    static int sigio;
+    static const bool have_sigio
+      = octave_get_sig_number ("SIGIO", &sigio);
+
+    static int siglost;
+    static const bool have_siglost
+      = octave_get_sig_number ("SIGLOST", &siglost);
 
-    static const bool have_sigchld = octave_get_sig_number ("SIGCHLD", &sigchld);
-    static const bool have_sigfpe = octave_get_sig_number ("SIGFPE", &sigfpe);
-    static const bool have_sighup = octave_get_sig_number ("SIGHUP", &sighup);
-    static const bool have_sigterm = octave_get_sig_number ("SIGTERM", &sigterm);
-    static const bool have_sigpipe = octave_get_sig_number ("SIGPIPE", &sigpipe);
+    static int sigpipe;
+    static const bool have_sigpipe
+      = octave_get_sig_number ("SIGPIPE", &sigpipe);
+
+    // Job control signals.
+
+    static int sigchld;
+    static const bool have_sigchld
+      = octave_get_sig_number ("SIGCHLD", &sigchld);
+
+    static int sigcld;
+    static const bool have_sigcld
+      = octave_get_sig_number ("SIGCLD", &sigcld);
+
+    // Resource limit signals.
+
+    static int sigxcpu;
+    static const bool have_sigxcpu
+      = octave_get_sig_number ("SIGXCPU", &sigxcpu);
 
-    for (int i = 0; i < octave_num_signals (); i++)
+    static int sigxfsz;
+    static const bool have_sigxfsz
+      = octave_get_sig_number ("SIGXFSZ", &sigxfsz);
+
+    // User signals.
+
+    static int sigusr1;
+    static const bool have_sigusr1
+      = octave_get_sig_number ("SIGUSR1", &sigusr1);
+
+    static int sigusr2;
+    static const bool have_sigusr2
+      = octave_get_sig_number ("SIGUSR2", &sigusr2);
+
+    for (int sig = 0; sig < octave_num_signals (); sig++)
       {
-        if (signals_caught[i])
+        if (signals_caught[sig])
           {
-            signals_caught[i] = false;
+            signals_caught[sig] = false;
 
-            if (have_sigchld && i == sigchld)
+            if ((have_sigchld && sig == sigchld)
+                || (have_sigcld && sig == sigcld))
               {
+                // FIXME: should we block or ignore?
                 volatile interrupt_handler saved_interrupt_handler
                   = ignore_interrupts ();
 
@@ -394,14 +204,41 @@
 
                 child_list::reap ();
               }
-            else if (have_sigfpe && i == sigfpe)
-              std::cerr << "warning: floating point exception" << std::endl;
-            else if (have_sighup && i == sighup)
+            else if (have_sigpipe && sig == sigpipe)
+              {
+                std::cerr << "warning: broken pipe" << std::endl;
+
+                // Don't loop forever on account of this.
+                // FIXME: is this really needed?  Does it do anything
+                // useful now?
+
+                if (pipe_handler_error_count++ > 100
+                    && octave_interrupt_state >= 0)
+                  octave_interrupt_state++;
+              }
+            else if (have_sighup && sig == sighup)
               my_friendly_exit (sighup, Vsighup_dumps_octave_core);
-            else if (have_sigterm && i == sigterm)
+            else if (have_sigquit && sig == sigquit)
+              my_friendly_exit (sigquit, Vsigquit_dumps_octave_core);
+            else if (have_sigterm && sig == sigterm)
               my_friendly_exit (sigterm, Vsigterm_dumps_octave_core);
-            else if (have_sigpipe && i == sigpipe)
-              std::cerr << "warning: broken pipe" << std::endl;
+            else if ((have_sigalrm && sig == sigalrm)
+                     || (have_sigio && sig == sigio)
+                     || (have_siglost && sig == siglost)
+                     || (have_sigxcpu && sig == sigxcpu)
+                     || (have_sigxfsz && sig == sigxfsz)
+                     || (have_sigusr1 && sig == sigusr1)
+                     || (have_sigusr2 && sig == sigusr2))
+              std::cerr << "warning: ignoring signal: "
+                        << octave_strsignal_wrapper (sig)
+                        << std::endl;
+            else if ((have_sigint && sig == sigint)
+                     || (have_sigbreak && sig == sigbreak))
+              /* handled separately; do nothing */;
+            else
+              std::cerr << "warning: ignoring unexpected signal: "
+                        << octave_strsignal_wrapper (sig)
+                        << std::endl;
           }
       }
   }
@@ -423,45 +260,45 @@
   static void
   generic_sig_handler (int sig)
   {
+    // FIXME: this function may execute in a separate signal handler or
+    // signal watcher thread so it should probably be more careful about
+    // how it accesses global objects.
+
     octave_signal_caught = 1;
 
     signals_caught[sig] = true;
+
+    static int sigint;
+    static const bool have_sigint
+      = octave_get_sig_number ("SIGINT", &sigint);
+
+    static int sigbreak;
+    static const bool have_sigbreak
+      = octave_get_sig_number ("SIGBREAK", &sigbreak);
+
+    if ((have_sigint && sig == sigint)
+        || (have_sigbreak && sig == sigbreak))
+      octave_interrupt_state++;
   }
 
-#if defined (__alpha__)
   static void
-  sigfpe_handler (int sig)
+  deadly_sig_handler (int sig)
   {
-    if (can_interrupt && octave_interrupt_state >= 0)
-      {
-        octave_signal_caught = 1;
+    std::cerr << "fatal: caught signal "
+              << octave_strsignal_wrapper (sig)
+              << " -- stopping myself..." << std::endl;
 
-        signals_caught[sig] = true;
+    octave_set_default_signal_handler (sig);
 
-        octave_interrupt_state++;
-      }
+    octave_raise_wrapper (sig);
   }
-#endif
-
-  // Handle SIGHUP and SIGTERM.
 
   static void
-  user_terminate (int sig_number)
+  fpe_sig_handler (int)
   {
-    if (! octave_initialized)
-      exit (1);
-
-    octave_signal_caught = 1;
+    // FIXME: is there something better we can do?
 
-    signals_caught[sig_number] = true;
-
-    if (octave_interrupt_immediately)
-      {
-        // Try to get to a place where it is safe to throw an
-        // exception.
-
-        interrupt_manager::jump_to_enclosing_context ();
-      }
+    std::cerr << "warning: floating point exception" << std::endl;
   }
 
   // Handle SIGINT.
@@ -471,97 +308,23 @@
   // for SIGINT only.
 
   static void
-  user_abort (int sig_number)
+  sigint_handler (int)
   {
     if (! octave_initialized)
       exit (1);
 
     if (can_interrupt)
       {
-        if (Vdebug_on_interrupt)
-          {
-            if (! octave_debug_on_interrupt_state)
-              {
-                tree_evaluator::debug_mode = true;
-                octave_debug_on_interrupt_state = true;
-
-                return;
-              }
-            else
-              {
-                // Clear the flag and do normal interrupt stuff.
-
-                tree_evaluator::debug_mode
-                  = bp_table::have_breakpoints () || Vdebugging;
-                octave_debug_on_interrupt_state = false;
-              }
-          }
-
-        if (octave_interrupt_immediately)
-          {
-            if (octave_interrupt_state == 0)
-              octave_interrupt_state = 1;
-
-            // Try to get to a place where it is safe to throw an
-            // exception.
-
-            interrupt_manager::jump_to_enclosing_context ();
-          }
-        else
-          {
-            // If we are already cleaning up from a previous interrupt,
-            // take note of the fact that another interrupt signal has
-            // arrived.
-
-            if (octave_interrupt_state < 0)
-              octave_interrupt_state = 0;
-
-            octave_signal_caught = 1;
-            octave_interrupt_state++;
-
-            if (application::interactive ()
-                && ! application::forced_interactive ()
-                && octave_interrupt_state == 2)
-              std::cerr << "Press Control-C again to abort." << std::endl;
-
-            if (octave_interrupt_state >= 3)
-              my_friendly_exit (sig_number, true);
-          }
+        octave_signal_caught = 1;
+        octave_interrupt_state++;
       }
   }
 
-  static void
-  sigterm_handler (int sig)
-  {
-    interrupt_manager::user_terminate (sig);
-  }
-
-  static void
-  sigint_handler (int sig)
-  {
-    interrupt_manager::user_abort (sig);
-  }
-
-  static void
-  sigpipe_handler (int sig)
-  {
-    octave_signal_caught = 1;
-
-    signals_caught[sig] = true;
-
-    // Don't loop forever on account of this.
-
-    if (pipe_handler_error_count++ > 100 && octave_interrupt_state >= 0)
-      octave_interrupt_state++;
-  }
-
   interrupt_handler
   catch_interrupts (void)
   {
     interrupt_handler retval;
 
-    interrupt_manager::init ();
-
     retval.int_handler = set_signal_handler ("SIGINT", sigint_handler);
     retval.brk_handler = set_signal_handler ("SIGBREAK", sigint_handler);
 
@@ -573,8 +336,6 @@
   {
     interrupt_handler retval;
 
-    interrupt_manager::init ();
-
     retval.int_handler = set_signal_handler ("SIGINT", SIG_IGN);
     retval.brk_handler = set_signal_handler ("SIGBREAK", SIG_IGN);
 
@@ -587,8 +348,6 @@
   {
     interrupt_handler retval;
 
-    interrupt_manager::init ();
-
     retval.int_handler = set_signal_handler ("SIGINT", h.int_handler,
                                              restart_syscalls);
 
@@ -609,63 +368,68 @@
     for (int i = 0; i < octave_num_signals (); i++)
       signals_caught[i] = false;
 
+    // Interrupt signals.
+
     catch_interrupts ();
 
-    set_signal_handler ("SIGABRT", generic_sig_handler);
-    set_signal_handler ("SIGALRM", generic_sig_handler);
-    set_signal_handler ("SIGBUS", generic_sig_handler);
-    set_signal_handler ("SIGCHLD", generic_sig_handler);
+    // Program Error signals.  These are most likely unrecoverable for
+    // us.
 
-    // SIGCLD
-    // SIGCONT
-
-    set_signal_handler ("SIGEMT", generic_sig_handler);
+    set_signal_handler ("SIGABRT", deadly_sig_handler);
+    set_signal_handler ("SIGBUS", deadly_sig_handler);
+    set_signal_handler ("SIGEMT", deadly_sig_handler);
+    set_signal_handler ("SIGILL", deadly_sig_handler);
+    // SIGIOT is normally another name for SIGABRT.
+    set_signal_handler ("SIGIOT", deadly_sig_handler);
+    set_signal_handler ("SIGSEGV", deadly_sig_handler);
+    set_signal_handler ("SIGSYS", deadly_sig_handler);
+    set_signal_handler ("SIGTRAP", deadly_sig_handler);
 
-#if defined (__alpha__)
-    set_signal_handler ("SIGFPE", sigfpe_handler);
-#else
-    set_signal_handler ("SIGFPE", generic_sig_handler);
-#endif
+    // Handle SIGFPE separately.
+
+    set_signal_handler ("SIGFPE", fpe_sig_handler);
 
-    set_signal_handler ("SIGHUP", sigterm_handler);
-    set_signal_handler ("SIGILL", generic_sig_handler);
+    // Handle other signals for which the default action is to terminate
+    // the program.
 
-    // SIGINFO
-    // SIGINT
+    // Termination signals.
+
+    set_signal_handler ("SIGHUP", generic_sig_handler);
+    set_signal_handler ("SIGQUIT", generic_sig_handler);
+    set_signal_handler ("SIGTERM", generic_sig_handler);
 
-    set_signal_handler ("SIGIOT", generic_sig_handler);
-    set_signal_handler ("SIGLOST", generic_sig_handler);
-    set_signal_handler ("SIGPIPE", sigpipe_handler);
-    set_signal_handler ("SIGPOLL", SIG_IGN);
+    // Alarm signals.
 
-    // SIGPROF
-    // SIGPWR
+    set_signal_handler ("SIGALRM", generic_sig_handler);
+    set_signal_handler ("SIGVTALRM", generic_sig_handler);
+    set_signal_handler ("SIGPROF", generic_sig_handler);
 
-    set_signal_handler ("SIGQUIT", generic_sig_handler);
-    set_signal_handler ("SIGSEGV", generic_sig_handler);
+    // I/O signals.
+
+    set_signal_handler ("SIGLOST", generic_sig_handler);
+    set_signal_handler ("SIGPIPE", generic_sig_handler);
 
-    // SIGSTOP
+    // Job control signals.  We only recognize signals about child
+    // processes.
+
+    set_signal_handler ("SIGCHLD", generic_sig_handler);
+    set_signal_handler ("SIGCLD", generic_sig_handler);
+
+    // Resource limit signals.
 
-    set_signal_handler ("SIGSYS", generic_sig_handler);
-    set_signal_handler ("SIGTERM", sigterm_handler);
-    set_signal_handler ("SIGTRAP", generic_sig_handler);
+    // FIXME: does it really make sense to try to handle the CPU limit
+    // signal?
 
-    // SIGTSTP
-    // SIGTTIN
-    // SIGTTOU
-    // SIGURG
+    set_signal_handler ("SIGXCPU", generic_sig_handler);
+    set_signal_handler ("SIGXFSZ", generic_sig_handler);
+
+    // User signals.
 
     set_signal_handler ("SIGUSR1", generic_sig_handler);
     set_signal_handler ("SIGUSR2", generic_sig_handler);
-    set_signal_handler ("SIGVTALRM", generic_sig_handler);
-    set_signal_handler ("SIGIO", SIG_IGN);
 
-#if 0
-    set_signal_handler ("SIGWINCH", sigwinch_handler);
-#endif
-
-    set_signal_handler ("SIGXCPU", generic_sig_handler);
-    set_signal_handler ("SIGXFSZ", generic_sig_handler);
+    // This does nothing on Windows systems.
+    octave_create_interrupt_watcher_thread (generic_sig_handler);
   }
 
   static void
@@ -813,6 +577,37 @@
 %!error (sighup_dumps_octave_core (1, 2))
 */
 
+DEFUN (sigquit_dumps_octave_core, args, nargout,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {@var{val} =} sigquit_dumps_octave_core ()
+@deftypefnx {} {@var{old_val} =} sigquit_dumps_octave_core (@var{new_val})
+@deftypefnx {} {} sigquit_dumps_octave_core (@var{new_val}, "local")
+Query or set the internal variable that controls whether Octave tries
+to save all current variables to the file @file{octave-workspace} if it
+receives a quit signal.
+
+When called from inside a function with the @qcode{"local"} option, the
+variable is changed locally for the function and any subroutines it calls.
+The original variable value is restored when exiting the function.
+@end deftypefn */)
+{
+  return set_internal_variable (octave::Vsigquit_dumps_octave_core,
+                                args, nargout,
+                                "sigquit_dumps_octave_core");
+}
+
+/*
+%!test
+%! orig_val = sigquit_dumps_octave_core ();
+%! old_val = sigquit_dumps_octave_core (! orig_val);
+%! assert (orig_val, old_val);
+%! assert (sigquit_dumps_octave_core (), ! orig_val);
+%! sigquit_dumps_octave_core (orig_val);
+%! assert (sigquit_dumps_octave_core (), orig_val);
+
+%!error (sigquit_dumps_octave_core (1, 2))
+*/
+
 DEFUN (sigterm_dumps_octave_core, args, nargout,
        doc: /* -*- texinfo -*-
 @deftypefn  {} {@var{val} =} sigterm_dumps_octave_core ()
--- a/libinterp/corefcn/sighandlers.h	Thu Jan 04 10:09:23 2018 -0500
+++ b/libinterp/corefcn/sighandlers.h	Wed Jan 03 07:52:11 2018 -0500
@@ -66,7 +66,7 @@
 
   extern OCTINTERP_API void install_signal_handlers (void);
 
-  extern OCTINTERP_API void signal_handler (void);
+  extern OCTINTERP_API void respond_to_pending_signals (void);
 
   extern OCTINTERP_API interrupt_handler catch_interrupts (void);
 
--- a/liboctave/numeric/eigs-base.cc	Thu Jan 04 10:09:23 2018 -0500
+++ b/liboctave/numeric/eigs-base.cc	Wed Jan 03 07:52:11 2018 -0500
@@ -149,9 +149,6 @@
                            x, 1, 0.0, y, 1
                            F77_CHAR_ARG_LEN (1)));
 
-  if (f77_exception_encountered)
-    (*current_liboctave_error_handler) ("eigs: unrecoverable error in dgemv");
-
   return true;
 }
 
@@ -184,9 +181,6 @@
                            F77_DBLE_CMPLX_ARG (y), 1
                            F77_CHAR_ARG_LEN (1)));
 
-  if (f77_exception_encountered)
-    (*current_liboctave_error_handler) ("eigs: unrecoverable error in zgemv");
-
   return true;
 }
 
@@ -769,10 +763,6 @@
 
       info = tmp_info;
 
-      if (f77_exception_encountered)
-        (*current_liboctave_error_handler)
-          ("eigs: unrecoverable exception encountered in dsaupd");
-
       if (disp > 0 && ! octave::math::isnan (workl[iptr (5)-1]))
         {
           if (iter++)
@@ -855,10 +845,6 @@
      ipntr, workd, workl, lwork, info2 F77_CHAR_ARG_LEN(1) F77_CHAR_ARG_LEN(1)
      F77_CHAR_ARG_LEN(2));
 
-  if (f77_exception_encountered)
-    (*current_liboctave_error_handler)
-      ("eigs: unrecoverable exception encountered in dseupd");
-
   if (info2 == 0)
     {
       for (F77_INT i = ip(4); i < k; i++)
@@ -1049,10 +1035,6 @@
          F77_CHAR_ARG_LEN(1) F77_CHAR_ARG_LEN(2));
       info = tmp_info;
 
-      if (f77_exception_encountered)
-        (*current_liboctave_error_handler)
-          ("eigs: unrecoverable exception encountered in dsaupd");
-
       if (disp > 0 && ! octave::math::isnan (workl[iptr (5)-1]))
         {
           if (iter++)
@@ -1172,10 +1154,6 @@
      k, tol, presid, p, v, n, iparam, ipntr, workd, workl, lwork, info2
      F77_CHAR_ARG_LEN(1) F77_CHAR_ARG_LEN(1) F77_CHAR_ARG_LEN(2));
 
-  if (f77_exception_encountered)
-    (*current_liboctave_error_handler)
-      ("eigs: unrecoverable exception encountered in dseupd");
-
   if (info2 == 0)
     {
       for (F77_INT i = ip(4); i < k; i++)
@@ -1342,10 +1320,6 @@
 
       info = tmp_info;
 
-      if (f77_exception_encountered)
-        (*current_liboctave_error_handler)
-          ("eigs: unrecoverable exception encountered in dsaupd");
-
       if (disp > 0 && ! octave::math::isnan (workl[iptr (5)-1]))
         {
           if (iter++)
@@ -1428,10 +1402,6 @@
      k, tol, presid, p, v, n, iparam, ipntr, workd, workl, lwork, info2
      F77_CHAR_ARG_LEN(1) F77_CHAR_ARG_LEN(1) F77_CHAR_ARG_LEN(2));
 
-  if (f77_exception_encountered)
-    (*current_liboctave_error_handler)
-      ("eigs: unrecoverable exception encountered in dseupd");
-
   if (info2 == 0)
     {
       for (F77_INT i = ip(4); i < k; i++)
@@ -1640,10 +1610,6 @@
 
       info = tmp_info;
 
-      if (f77_exception_encountered)
-        (*current_liboctave_error_handler)
-          ("eigs: unrecoverable exception encountered in dnaupd");
-
       if (disp > 0 && ! octave::math::isnan(workl[iptr(5)-1]))
         {
           if (iter++)
@@ -1740,10 +1706,6 @@
   // on exit, if (and only if) rvec == true, k may have been increased by one
   // and be equal to ip(4), see dngets.
 
-  if (f77_exception_encountered)
-    (*current_liboctave_error_handler)
-      ("eigs: unrecoverable exception encountered in dneupd");
-
   if (! rvec && ip(4) > k)
     k = ip(4);
 
@@ -1984,10 +1946,6 @@
 
       info = tmp_info;
 
-      if (f77_exception_encountered)
-        (*current_liboctave_error_handler)
-          ("eigs: unrecoverable exception encountered in dnaupd");
-
       if (disp > 0 && ! octave::math::isnan (workl[iptr (5)-1]))
         {
           if (iter++)
@@ -2121,10 +2079,6 @@
   // On exit, if (and only if) rvec == true, k may have been increased by one
   // and be equal to ip(4), see dngets.
 
-  if (f77_exception_encountered)
-    (*current_liboctave_error_handler)
-      ("eigs: unrecoverable exception encountered in dneupd");
-
   if (! rvec && ip(4) > k)
     k = ip(4);
 
@@ -2347,10 +2301,6 @@
 
       info = tmp_info;
 
-      if (f77_exception_encountered)
-        (*current_liboctave_error_handler)
-          ("eigs: unrecoverable exception encountered in dnaupd");
-
       if (disp > 0 && ! octave::math::isnan(workl[iptr(5)-1]))
         {
           if (iter++)
@@ -2447,10 +2397,6 @@
   // On exit, if (and only if) rvec == true, k may have been increased by one
   // and be equal to ip(4), see dngets.
 
-  if (f77_exception_encountered)
-    (*current_liboctave_error_handler)
-      ("eigs: unrecoverable exception encountered in dneupd");
-
   if (! rvec && ip(4) > k)
     k = ip(4);
 
@@ -2709,10 +2655,6 @@
 
       info = tmp_info;
 
-      if (f77_exception_encountered)
-        (*current_liboctave_error_handler)
-          ("eigs: unrecoverable exception encountered in znaupd");
-
       if (disp > 0 && ! octave::math::isnan (workl[iptr (5)-1]))
         {
           if (iter++)
@@ -2800,10 +2742,6 @@
      F77_DBLE_CMPLX_ARG (workd), F77_DBLE_CMPLX_ARG (workl), lwork, rwork,
      info2 F77_CHAR_ARG_LEN(1) F77_CHAR_ARG_LEN(1) F77_CHAR_ARG_LEN(2));
 
-  if (f77_exception_encountered)
-    (*current_liboctave_error_handler)
-      ("eigs: unrecoverable exception encountered in zneupd");
-
   if (info2 == 0)
     {
       for (F77_INT i = ip(4); i < k; i++)
@@ -3002,10 +2940,6 @@
 
       info = tmp_info;
 
-      if (f77_exception_encountered)
-        (*current_liboctave_error_handler)
-          ("eigs: unrecoverable exception encountered in znaupd");
-
       if (disp > 0 && ! octave::math::isnan(workl[iptr(5)-1]))
         {
           if (iter++)
@@ -3131,10 +3065,6 @@
      F77_DBLE_CMPLX_ARG (workd), F77_DBLE_CMPLX_ARG (workl), lwork, rwork,
      info2 F77_CHAR_ARG_LEN(1) F77_CHAR_ARG_LEN(1) F77_CHAR_ARG_LEN(2));
 
-  if (f77_exception_encountered)
-    (*current_liboctave_error_handler)
-      ("eigs: unrecoverable exception encountered in zneupd");
-
   if (info2 == 0)
     {
       for (F77_INT i = ip(4); i < k; i++)
@@ -3314,10 +3244,6 @@
 
       info = tmp_info;
 
-      if (f77_exception_encountered)
-        (*current_liboctave_error_handler)
-          ("eigs: unrecoverable exception encountered in znaupd");
-
       if (disp > 0 && ! octave::math::isnan(workl[iptr(5)-1]))
         {
           if (iter++)
@@ -3406,10 +3332,6 @@
      F77_DBLE_CMPLX_ARG (workd), F77_DBLE_CMPLX_ARG (workl), lwork, rwork,
      info2 F77_CHAR_ARG_LEN(1) F77_CHAR_ARG_LEN(1) F77_CHAR_ARG_LEN(2));
 
-  if (f77_exception_encountered)
-    (*current_liboctave_error_handler)
-      ("eigs: unrecoverable exception encountered in zneupd");
-
   if (info2 == 0)
     {
       for (F77_INT i = ip(4); i < k; i++)
--- a/liboctave/numeric/gsvd.cc	Thu Jan 04 10:09:23 2018 -0500
+++ b/liboctave/numeric/gsvd.cc	Wed Jan 03 07:52:11 2018 -0500
@@ -282,9 +282,6 @@
                       tmp_dataA, m, tmp_dataB, p, alpha, beta, u,
                       nrow_u, v, nrow_v, q, nrow_q, work, iwork.data (), info);
 
-      if (f77_exception_encountered)
-        (*current_liboctave_error_handler) ("unrecoverable error in *ggsvd");
-
       if (info < 0)
         (*current_liboctave_error_handler) ("*ggsvd.f: argument %d illegal",
                                             -info);
--- a/liboctave/util/cmd-edit.cc	Thu Jan 04 10:09:23 2018 -0500
+++ b/liboctave/util/cmd-edit.cc	Wed Jan 03 07:52:11 2018 -0500
@@ -195,6 +195,8 @@
 
     void do_interrupt (bool);
 
+    void do_handle_interrupt_signal (void);
+
     static int operate_and_get_next (int, int);
 
     static int history_search_backward (int, int);
@@ -287,20 +289,31 @@
 
     const char *p = prompt.c_str ();
 
-    BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
-
-    char *line = ::octave_rl_readline (p);
-
-    if (line)
+    while (true)
       {
-        retval = line;
+        try
+          {
+            char *line = ::octave_rl_readline (p);
+
+            if (line)
+              {
+                retval = line;
 
-        free (line);
+                free (line);
+              }
+            else
+              eof = true;
+
+            break;
+          }
+        catch (command_editor::interrupt_exception&)
+          {
+            // Is this right?
+            std::cout << "\n";
+
+            // Try again...
+          }
       }
-    else
-      eof = true;
-
-    END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
 
     return retval;
   }
@@ -782,6 +795,17 @@
     ::octave_rl_done (arg);
   }
 
+  void
+  gnu_readline::do_handle_interrupt_signal (void)
+  {
+    octave_signal_caught = 0;
+    octave_interrupt_state = 0;
+
+    ::octave_rl_recover_from_interrupt ();
+
+    throw command_editor::interrupt_exception ();
+  }
+
   int
   gnu_readline::operate_and_get_next (int /* count */, int /* c */)
   {
@@ -1064,7 +1088,11 @@
         make_command_editor ();
 
         if (instance)
-          singleton_cleanup_list::add (cleanup_instance);
+          {
+            instance->set_event_hook (event_handler);
+
+            singleton_cleanup_list::add (cleanup_instance);
+          }
       }
 
     if (! instance)
@@ -1131,6 +1159,9 @@
   int
   command_editor::event_handler (void)
   {
+    if (octave_interrupt_state)
+      handle_interrupt_signal ();
+
     event_hook_lock.lock ();
 
     std::set<event_hook_fcn> hook_set (event_hook_set);
@@ -1539,12 +1570,7 @@
   {
     autolock guard (event_hook_lock);
 
-    if (instance_ok ())
-      {
-        event_hook_set.insert (f);
-
-        instance->set_event_hook (event_handler);
-      }
+    event_hook_set.insert (f);
   }
 
   void
@@ -1552,16 +1578,11 @@
   {
     autolock guard (event_hook_lock);
 
-    if (instance_ok ())
-      {
-        auto p = event_hook_set.find (f);
+    auto p = event_hook_set.find (f);
 
-        if (p != event_hook_set.end ())
-          event_hook_set.erase (p);
+    if (p != event_hook_set.end ())
+      event_hook_set.erase (p);
 
-        if (event_hook_set.empty ())
-          instance->restore_event_hook ();
-      }
   }
 
   void
@@ -1629,6 +1650,13 @@
     return retval;
   }
 
+  void
+  command_editor::handle_interrupt_signal (void)
+  {
+    if (instance_ok ())
+      instance->do_handle_interrupt_signal ();
+  }
+
   // Return a string which will be printed as a prompt.  The string may
   // contain special characters which are decoded as follows:
   //
--- a/liboctave/util/cmd-edit.h	Thu Jan 04 10:09:23 2018 -0500
+++ b/liboctave/util/cmd-edit.h	Wed Jan 03 07:52:11 2018 -0500
@@ -40,6 +40,8 @@
   {
   protected:
 
+    class interrupt_exception { };
+
     command_editor (void)
       : command_number (0), interrupted (false), initial_input () { }
 
@@ -220,6 +222,8 @@
 
     static void cleanup_instance (void) { delete instance; instance = nullptr; }
 
+    static void handle_interrupt_signal (void);
+
   protected:
 
     // To use something other than the GNU readline library, derive a new
@@ -350,6 +354,8 @@
 
     virtual void do_interrupt (bool) { }
 
+    virtual void do_handle_interrupt_signal (void) { }
+
     int do_insert_initial_input (void);
 
     int read_octal (const std::string& s);
--- a/liboctave/util/cquit.c	Thu Jan 04 10:09:23 2018 -0500
+++ b/liboctave/util/cquit.c	Wed Jan 03 07:52:11 2018 -0500
@@ -29,38 +29,21 @@
 
 #include "quit.h"
 
-octave_jmp_buf current_context;
-
-void
-octave_save_current_context (void *save_buf)
-{
-  memcpy (save_buf, current_context, sizeof (octave_jmp_buf));
-}
-
-void
-octave_restore_current_context (void *save_buf)
-{
-  memcpy (current_context, save_buf, sizeof (octave_jmp_buf));
-}
-
-void
-octave_jump_to_enclosing_context (void)
-{
-#if defined (OCTAVE_HAVE_SIG_JUMP)
-  siglongjmp (current_context, 1);
-#else
-  longjmp (current_context, 1);
-#endif
-}
-
-sig_atomic_t octave_interrupt_immediately = 0;
-
 sig_atomic_t octave_interrupt_state = 0;
 
 sig_atomic_t octave_exception_state = 0;
 
+#if defined (HAVE_PRAGMA_GCC_DIAGNOSTIC)
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
 sig_atomic_t octave_exit_exception_status = 0;
 
 sig_atomic_t octave_exit_exception_safe_to_return = 0;
 
+#if defined (HAVE_PRAGMA_GCC_DIAGNOSTIC)
+#  pragma GCC diagnostic pop
+#endif
+
 volatile sig_atomic_t octave_signal_caught = 0;
--- a/liboctave/util/f77-extern.cc	Thu Jan 04 10:09:23 2018 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-/*
-
-Copyright (C) 1996-2017 John W. Eaton
-
-This file is part of Octave.
-
-Octave 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 3 of the License, or
-(at your option) any later version.
-
-Octave 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 Octave; see the file COPYING.  If not, see
-<http://www.gnu.org/licenses/>.
-
-*/
-
-#if defined (HAVE_CONFIG_H)
-#  include "config.h"
-#endif
-
-#include "f77-fcn.h"
-#include "lo-error.h"
-
-// This whole file is a kluge just to avoid unresolved symbol errors
-// when creating shared versions of liboctave.
-
-// So we can check to see if an exception has occurred.
-int f77_exception_encountered = 0;
--- a/liboctave/util/f77-fcn.c	Thu Jan 04 10:09:23 2018 -0500
+++ b/liboctave/util/f77-fcn.c	Wed Jan 03 07:52:11 2018 -0500
@@ -58,13 +58,14 @@
   unsigned long slen = desc.mask.len;
 #endif
 
-  f77_exception_encountered = 1;
+  /* Skip printing message if it is just a single blank character.  */
+  if (! (s && slen > 0 && ! (slen == 1 && *s == ' ')))
+    {
+      s = "unknown error in fortran subroutine";
+      slen = strlen (s);
+    }
 
-  /* Skip printing message if it is just a single blank character.  */
-  if (s && slen > 0 && ! (slen == 1 && *s == ' '))
-    (*current_liboctave_error_handler) ("%.*s", slen, s);
-
-  octave_jump_to_enclosing_context ();
+  (*current_liboctave_error_handler) ("%.*s", slen, s);
 
   F77_NORETURN (0)
 }
--- a/liboctave/util/f77-fcn.h	Thu Jan 04 10:09:23 2018 -0500
+++ b/liboctave/util/f77-fcn.h	Wed Jan 03 07:52:11 2018 -0500
@@ -36,55 +36,13 @@
 extern "C" {
 #endif
 
-/* Hack to stringize macro results. */
-#define xSTRINGIZE(x) #x
-#define STRINGIZE(x) xSTRINGIZE(x)
-
-/* How to print an error for the F77_XFCN macro. */
-
-#define F77_XFCN_ERROR(f, F)                            \
-  (*current_liboctave_error_handler)                    \
-    ("exception encountered in Fortran subroutine %s",  \
-     STRINGIZE (F77_FUNC (f, F)))
-
-/* This can be used to call a Fortran subroutine that might call
-   XSTOPX.  XSTOPX will call lonjmp with current_context.  Once back
-   here, we'll restore the previous context and return.  We may also
-   end up here if an interrupt is processed when the Fortran
-   subroutine is called.  In that case, we resotre the context and go
-   to the top level. */
+/* This macro is obsolete.  */
 
-#define F77_XFCN(f, F, args)                                            \
-  do                                                                    \
-    {                                                                   \
-      octave_jmp_buf saved_context;                                     \
-      sig_atomic_t saved_octave_interrupt_immediately = octave_interrupt_immediately; \
-      f77_exception_encountered = 0;                                    \
-      octave_save_current_context (saved_context);                      \
-      if (octave_set_current_context)                                   \
-        {                                                               \
-          octave_interrupt_immediately = saved_octave_interrupt_immediately; \
-          octave_restore_current_context (saved_context);               \
-          if (f77_exception_encountered)                                \
-            F77_XFCN_ERROR (f, F);                                      \
-          else                                                          \
-            octave_rethrow_exception ();                                \
-        }                                                               \
-      else                                                              \
-        {                                                               \
-          octave_interrupt_immediately++;                               \
-          F77_FUNC (f, F) args;                                         \
-          octave_interrupt_immediately--;                               \
-          octave_restore_current_context (saved_context);               \
-        }                                                               \
-    }                                                                   \
-  while (0)
-
-/* So we can check to see if an exception has occurred. */
-OCTAVE_API extern int f77_exception_encountered;
+#define F77_XFCN(f, F, args)                    \
+  F77_FUNC (f, F) args
 
 #if ! defined (F77_FCN)
-#define F77_FCN(f, F) F77_FUNC (f, F)
+#  define F77_FCN(f, F) F77_FUNC (f, F)
 #endif
 
 /*
--- a/liboctave/util/module.mk	Thu Jan 04 10:09:23 2018 -0500
+++ b/liboctave/util/module.mk	Wed Jan 03 07:52:11 2018 -0500
@@ -76,7 +76,6 @@
   %reldir%/lo-ieee.cc \
   %reldir%/lo-regexp.cc \
   %reldir%/lo-utils.cc \
-  %reldir%/f77-extern.cc \
   %reldir%/quit.cc \
   %reldir%/oct-base64.cc \
   %reldir%/oct-glob.cc \
--- a/liboctave/util/oct-rl-edit.c	Thu Jan 04 10:09:23 2018 -0500
+++ b/liboctave/util/oct-rl-edit.c	Wed Jan 03 07:52:11 2018 -0500
@@ -471,4 +471,12 @@
   return META (c);
 }
 
+void
+octave_rl_recover_from_interrupt (void)
+{
+  rl_free_line_state ();
+  rl_cleanup_after_signal ();
+  rl_reset_after_signal ();
+}
+
 #endif
--- a/liboctave/util/oct-rl-edit.h	Thu Jan 04 10:09:23 2018 -0500
+++ b/liboctave/util/oct-rl-edit.h	Wed Jan 03 07:52:11 2018 -0500
@@ -182,6 +182,8 @@
 
 extern int octave_rl_meta (char);
 
+extern void octave_rl_recover_from_interrupt (void);
+
 #if defined (__cplusplus)
 }
 #endif
--- a/liboctave/util/quit.h	Thu Jan 04 10:09:23 2018 -0500
+++ b/liboctave/util/quit.h	Wed Jan 03 07:52:11 2018 -0500
@@ -25,8 +25,6 @@
 
 #include "octave-config.h"
 
-#include <setjmp.h>
-
 /* The signal header is just needed for the sig_atomic_t type.  */
 #if defined (__cplusplus)
 #  include <csignal>
@@ -36,28 +34,6 @@
 #  include <signal.h>
 #endif
 
-#if defined (OCTAVE_HAVE_SIG_JUMP)
-
-typedef sigjmp_buf octave_jmp_buf;
-
-#define octave_set_current_context sigsetjmp (current_context, 1)
-
-#else
-
-typedef jmp_buf octave_jmp_buf;
-
-#define octave_set_current_context setjmp (current_context)
-
-#endif
-
-OCTAVE_API extern octave_jmp_buf current_context;
-
-OCTAVE_API extern void octave_save_current_context (void *);
-
-OCTAVE_API extern void octave_restore_current_context (void *);
-
-OCTAVE_NORETURN OCTAVE_API extern void octave_jump_to_enclosing_context (void);
-
 #if defined (__cplusplus)
 
 namespace octave
@@ -164,8 +140,6 @@
   octave_quit_exception = 4
 };
 
-OCTAVE_API extern sig_atomic_t octave_interrupt_immediately;
-
 /*
   > 0: interrupt pending
     0: no interrupt pending
@@ -175,8 +149,10 @@
 
 OCTAVE_API extern sig_atomic_t octave_exception_state;
 
+OCTAVE_DEPRECATED (4.4, "see the Octave documentation for other options")
 OCTAVE_API extern sig_atomic_t octave_exit_exception_status;
 
+OCTAVE_DEPRECATED (4.4, "see the Octave documentation for other options")
 OCTAVE_API extern sig_atomic_t octave_exit_exception_safe_to_return;
 
 OCTAVE_API extern volatile sig_atomic_t octave_signal_caught;
@@ -226,90 +202,34 @@
   while (0)
 #endif
 
-/* Normally, you just want to use
-
-     BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
-     ... some code that calls a "foreign" function ...
-     END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
-
-   but sometimes it is useful to do something like
-
-     BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE_1;
-     ... custom code here, normally ending in a call to
-         octave_rethrow_exception ...
-     BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE_2;
-
-   so that you can perform extra clean up operations before throwing
-   the interrupt exception.  */
+/* The following macros are obsolete.  Interrupting immediately by
+   calling siglongjmp or similar from a signal handler is asking for
+   trouble.  We need another way to handle that situation.  Rather
+   than remove them, however, please leave them in place until we can
+   either find a replacement or determine that a given block of code
+   does not need special treatment.  They are defined to create a
+   dummy do-while block to match the previous definitions.  */
 
 #define BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE     \
-  BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE_1;        \
-  octave_rethrow_exception ();                          \
-  BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE_2
+  do                                                    \
+    {
 
-#define BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE_1           \
-  do                                                            \
-    {                                                           \
-      octave_jmp_buf saved_context;                             \
-                                                                \
-      octave_save_current_context (saved_context);              \
-                                                                \
-      if (octave_set_current_context)                           \
-        {                                                       \
-          octave_restore_current_context (saved_context)
-
-#define BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE_2   \
-        }                                               \
-      else                                              \
-        {                                               \
-          octave_interrupt_immediately++
-
-#define END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE               \
-          octave_interrupt_immediately--;                       \
-          octave_restore_current_context (saved_context);       \
-          octave_quit ();                                       \
-        }                                                       \
-    }                                                           \
+#define END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE       \
+    }                                                   \
   while (0)
 
 #if defined (__cplusplus)
 
-#define BEGIN_INTERRUPT_WITH_EXCEPTIONS                                 \
-  sig_atomic_t saved_octave_interrupt_immediately = octave_interrupt_immediately; \
-                                                                        \
-  try                                                                   \
-    {                                                                   \
-      octave_interrupt_immediately = 0;
+/* Likewise, these are obsolete.  They are defined to create a
+   dummy scope to match the previous versions that created a try-catch
+   block.  */
 
-#define END_INTERRUPT_WITH_EXCEPTIONS                                   \
-    }                                                                   \
-  catch (const octave::interrupt_exception&)                            \
-    {                                                                   \
-      octave_interrupt_immediately = saved_octave_interrupt_immediately; \
-      octave_jump_to_enclosing_context ();                              \
-    }                                                                   \
-  catch (const octave::execution_exception&)                            \
-    {                                                                   \
-      octave_interrupt_immediately = saved_octave_interrupt_immediately; \
-      octave_exception_state = octave_exec_exception;                   \
-      octave_jump_to_enclosing_context ();                              \
-    }                                                                   \
-  catch (const std::bad_alloc&)                                         \
-    {                                                                   \
-      octave_interrupt_immediately = saved_octave_interrupt_immediately; \
-      octave_exception_state = octave_alloc_exception;                  \
-      octave_jump_to_enclosing_context ();                              \
-    }                                                                   \
-  catch (const octave::exit_exception& ex)                              \
-    {                                                                   \
-      octave_interrupt_immediately = saved_octave_interrupt_immediately; \
-      octave_exception_state = octave_quit_exception;                   \
-      octave_exit_exception_status = ex.exit_status ();                 \
-      octave_exit_exception_safe_to_return = ex.safe_to_return ();      \
-      octave_jump_to_enclosing_context ();                              \
-    }                                                                   \
-                                                                        \
-  octave_interrupt_immediately = saved_octave_interrupt_immediately
+#define BEGIN_INTERRUPT_WITH_EXCEPTIONS         \
+  {
+
+#define END_INTERRUPT_WITH_EXCEPTIONS           \
+  }
+
 #endif
 
 #if defined (__cplusplus)
--- a/liboctave/wrappers/signal-wrappers.c	Thu Jan 04 10:09:23 2018 -0500
+++ b/liboctave/wrappers/signal-wrappers.c	Wed Jan 03 07:52:11 2018 -0500
@@ -31,6 +31,7 @@
 
 #include <sys/types.h>
 #include <signal.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -388,6 +389,18 @@
           : 0);
 }
 
+octave_sig_handler *
+octave_set_default_signal_handler (int sig)
+{
+  return octave_set_signal_handler_internal (sig, SIG_DFL, true);
+}
+
+octave_sig_handler *
+octave_set_default_signal_handler_by_name (const char *signame)
+{
+  return octave_set_signal_handler_by_name (signame, SIG_DFL, true);
+}
+
 int
 octave_num_signals (void)
 {
@@ -403,15 +416,20 @@
 void *
 octave_block_child (void)
 {
-#if defined (SIGCHLD)
+#if defined (SIGCHLD) || defined (SIGCLD)
 
   sigset_info *context = (sigset_info *) malloc (sizeof (sigset_info));
 
   if (context)
     {
+      sigemptyset (&(context->ovar));
       sigemptyset (&(context->nvar));
+#if defined (SIGCHLD)
       sigaddset (&(context->nvar), SIGCHLD);
-      sigemptyset (&(context->ovar));
+#endif
+#if defined (SIGCLD)
+      sigaddset (&(context->nvar), SIGCLD);
+#endif
       sigprocmask (SIG_BLOCK, &(context->nvar), &(context->ovar));
     }
 
@@ -441,6 +459,7 @@
 block_or_unblock_signal (int how, int sig)
 {
 #if ! defined (__WIN32__) || defined (__CYGWIN__)
+
   // Blocking/unblocking signals at thread level is only supported
   // on platform with fully compliant POSIX threads. This is not
   // supported on Win32. Moreover, we have to make sure that SIGINT
@@ -455,6 +474,7 @@
   sigaddset (&signal_mask, sig);
 
   pthread_sigmask (how, &signal_mask, 0);
+
 #else
 
   octave_unused_parameter (how);
@@ -467,14 +487,42 @@
 octave_block_interrupt_signal (void)
 {
   block_or_unblock_signal (SIG_BLOCK, SIGINT);
+
+#if defined (SIGBREAK)
+  block_or_unblock_signal (SIG_BLOCK, SIGBREAK);
+#endif
 }
 
 void
 octave_unblock_interrupt_signal (void)
 {
   block_or_unblock_signal (SIG_UNBLOCK, SIGINT);
+
+#if defined (SIGBREAK)
+  block_or_unblock_signal (SIG_UNBLOCK, SIGBREAK);
+#endif
 }
 
+static void
+block_or_unblock_signal_by_name (int how, const char *signame)
+{
+  int sig;
+
+  if (octave_get_sig_number (signame, &sig))
+    block_or_unblock_signal (how, sig);
+}
+
+void
+octave_block_signal_by_name (const char *signame)
+{
+  block_or_unblock_signal_by_name (SIG_BLOCK, signame);
+}
+
+void
+octave_unblock_signal_by_name (const char *signame)
+{
+  block_or_unblock_signal_by_name (SIG_UNBLOCK, signame);
+}
 
 /* Allow us to save the signal mask and then restore it to the most
    recently saved value.  This is necessary when using the POSIX signal
@@ -498,8 +546,202 @@
   sigprocmask (SIG_SETMASK, &octave_signal_mask, 0);
 }
 
+static const sigset_t *
+octave_async_signals (void)
+{
+  static bool initialized = false;
+  static sigset_t sigmask;
+
+  if (! initialized)
+    {
+      sigemptyset (&sigmask);
+
+      // The signals listed here should match the list of signals that
+      // we handle in the signal handler thread.
+
+      // Interrupt signals.
+
+#if defined (SIGINT)
+      sigaddset (&sigmask, SIGINT);
+#endif
+
+#if defined (SIGBREAK)
+      sigaddset (&sigmask, SIGBREAK);
+#endif
+
+      // Termination signals.
+
+#if defined (SIGHUP)
+      sigaddset (&sigmask, SIGHUP);
+#endif
+
+#if defined (SIGQUIT)
+      sigaddset (&sigmask, SIGQUIT);
+#endif
+
+#if defined (SIGTERM)
+      sigaddset (&sigmask, SIGTERM);
+#endif
+
+      // Alarm signals.
+
+#if defined (SIGALRM)
+      sigaddset (&sigmask, SIGALRM);
+#endif
+
+#if defined (SIGVTALRM)
+      sigaddset (&sigmask, SIGVTALRM);
+#endif
+
+      // I/O signals.
+
+#if defined (SIGLOST)
+      sigaddset (&sigmask, SIGLOST);
+#endif
+
+#if defined (SIGPIPE)
+      sigaddset (&sigmask, SIGPIPE);
+#endif
+
+      // Job control signals.
+
+#if defined (SIGCHLD)
+      sigaddset (&sigmask, SIGCHLD);
+#endif
+
+#if defined (SIGCLD)
+      sigaddset (&sigmask, SIGCLD);
+#endif
+
+      // Resource limit signals.
+
+#if defined (SIGXCPU)
+      sigaddset (&sigmask, SIGXCPU);
+#endif
+
+#if defined (SIGXFSZ)
+      sigaddset (&sigmask, SIGXFSZ);
+#endif
+
+      initialized = true;
+    }
+
+  return &sigmask;
+}
+
+void
+octave_block_async_signals (void)
+{
+#if ! defined (__WIN32__) || defined (__CYGWIN__)
+  pthread_sigmask (SIG_BLOCK, octave_async_signals (), 0);
+#endif
+}
+
+void
+octave_unblock_async_signals (void)
+{
+#if ! defined (__WIN32__) || defined (__CYGWIN__)
+  pthread_sigmask (SIG_UNBLOCK, octave_async_signals (), 0);
+#endif
+}
+
 int
 octave_raise_wrapper (int signum)
 {
   return raise (signum);
 }
+
+static void *
+signal_watcher (void *arg)
+{
+  octave_sig_handler *handler = (octave_sig_handler *) arg;
+
+  octave_unblock_async_signals ();
+
+  const sigset_t *async_signals = octave_async_signals ();
+
+  while (1)
+    {
+      int sig_caught;
+
+      if (sigwait (async_signals, &sig_caught))
+        {
+          // FIXME: what else should we do?
+          abort ();
+        }
+
+      // Let handler have complete control over what to do.
+      (*handler) (sig_caught);
+    }
+}
+
+void
+octave_create_interrupt_watcher_thread (octave_sig_handler *handler)
+{
+#if ! defined (__WIN32__)
+  pthread_t sighandler_thread_id;
+
+  if (pthread_create (&sighandler_thread_id, 0, signal_watcher, handler))
+    {
+      // FIXME: what else should we do?
+      abort ();
+    }
+#else
+  octave_unblock_async_signals ();
+
+  octave_unused_parameter (handler);
+#endif
+}
+
+#if ! defined (__WIN32__)
+static void
+print_sigset (FILE *of, const char *prefix, const sigset_t *sigset)
+{
+  int sig;
+  int cnt = 0;
+
+  for (sig = 1; sig < NSIG; sig++)
+    {
+      if (sigismember (sigset, sig))
+        {
+          cnt++;
+          fprintf (of, "%ld: %s%d (%s)\n", pthread_self (), prefix, sig,
+                   strsignal (sig));
+        }
+    }
+
+  if (cnt == 0)
+    fprintf (of, "%ld: %s<empty signal set>\n", pthread_self (), prefix);
+}
+
+static int
+print_sigmask (FILE *of, const char *msg)
+{
+  sigset_t sigmask;
+
+  if (msg)
+    fprintf (of, "%s", msg);
+
+  if (pthread_sigmask (SIG_BLOCK, NULL, &sigmask) == -1)
+    return -1;
+
+  print_sigset (of, "\t\t", &sigmask);
+
+  return 0;
+}
+#endif
+
+void
+octave_show_sigmask (const char *msg)
+{
+#if ! defined (__WIN32__)
+  if (! msg)
+    msg = "signal mask\n";
+
+  print_sigmask (stderr, msg);
+#else
+  octave_unused_parameter (msg);
+
+  fputs ("no signal mask on Windows systems\n", stderr);
+#endif
+}
--- a/liboctave/wrappers/signal-wrappers.h	Thu Jan 04 10:09:23 2018 -0500
+++ b/liboctave/wrappers/signal-wrappers.h	Wed Jan 03 07:52:11 2018 -0500
@@ -43,14 +43,20 @@
 
 extern bool octave_get_sig_number (const char *signame, int *signum);
 
+octave_sig_handler *
+octave_set_signal_handler_internal (int sig, octave_sig_handler *handler,
+                                    bool restart_syscalls);
+
 extern octave_sig_handler *
 octave_set_signal_handler_by_name (const char *signame,
                                    octave_sig_handler *handler,
                                    bool restart_syscalls);
 
-octave_sig_handler *
-octave_set_signal_handler_internal (int sig, octave_sig_handler *handler,
-                                    bool restart_syscalls);
+extern octave_sig_handler *
+octave_set_default_signal_handler (int sig);
+
+extern octave_sig_handler *
+octave_set_default_signal_handler_by_name (const char *signame);
 
 extern int octave_num_signals (void);
 
@@ -62,12 +68,26 @@
 
 extern void octave_unblock_interrupt_signal (void);
 
+extern void octave_block_signal_by_name (const char *signame);
+
+extern void octave_unblock_signal_by_name (const char *signame);
+
 extern void octave_save_signal_mask (void);
 
 extern void octave_restore_signal_mask (void);
 
+extern void octave_block_async_signals (void);
+
+extern void octave_unblock_async_signals (void);
+
 extern int octave_raise_wrapper (int signum);
 
+extern void
+octave_create_interrupt_watcher_thread (octave_sig_handler *handler);
+
+// This can be useful for debugging.
+extern void octave_show_sigmask (const char *);
+
 #if defined __cplusplus
 }
 #endif
--- a/src/main-cli.cc	Thu Jan 04 10:09:23 2018 -0500
+++ b/src/main-cli.cc	Wed Jan 03 07:52:11 2018 -0500
@@ -34,6 +34,7 @@
 #include "liboctinterp-build-info.h"
 
 #include "oct-env.h"
+#include "signal-wrappers.h"
 
 #include "defaults.h"
 #include "octave.h"
@@ -83,6 +84,8 @@
 {
   check_hg_versions ();
 
+  octave_block_async_signals ();
+
   octave::sys::env::set_program_name (argv[0]);
 
   octave::cli_application app (argc, argv);
--- a/src/main.in.cc	Thu Jan 04 10:09:23 2018 -0500
+++ b/src/main.in.cc	Wed Jan 03 07:52:11 2018 -0500
@@ -92,6 +92,10 @@
 static void
 install_signal_handlers (void)
 {
+  // FIXME: do we need to handle and forward all the signals that Octave
+  // handles, or is it sufficient to only forward things like SIGINT,
+  // SIGBREAK, SIGABRT, SIGQUIT, and possibly a few others?
+
   gui_driver_set_signal_handler ("SIGINT", gui_driver_sig_handler);
   gui_driver_set_signal_handler ("SIGBREAK", gui_driver_sig_handler);
   gui_driver_set_signal_handler ("SIGABRT", gui_driver_sig_handler);
@@ -329,6 +333,16 @@
 
   new_argv[0] = strsave (file.c_str ());
 
+  // The Octave interpreter may be multithreaded.  If so, we attempt to
+  // ensure that signals are delivered to the main interpreter thread
+  // and no others by blocking signals before we exec the Octave
+  // interpreter executable.  When that process starts, it will unblock
+  // signals in the main interpreter thread.  When running the GUI as a
+  // subprocess, we also unblock signals that the parent process handles
+  // so we can forward them to the child.
+
+  octave_block_async_signals ();
+
 #if defined (HAVE_OCTAVE_QT_GUI) && ! defined (OCTAVE_USE_WINDOWS_API)
 
   if (gui_libs && start_gui)
@@ -343,8 +357,6 @@
       // function pthread_thread_manager::interrupt in the file
       // libgui/src/thread-manager.cc.
 
-      install_signal_handlers ();
-
       gui_pid = octave_fork_wrapper ();
 
       if (gui_pid < 0)
@@ -370,6 +382,10 @@
         {
           // Parent.  Forward signals to child while waiting for it to exit.
 
+          install_signal_handlers ();
+
+          octave_unblock_async_signals ();
+
           int status;
 
           while (true)