diff libinterp/corefcn/sighandlers.cc @ 17958:1adf3710bb68

Working CTRL-C handling implementation for Win32. * libgui/src/thread-manager.cc (sighandlers.h): New include. (windows_thread_manager::interrupt): Call w32_raise_sigint instead of ::raise. * libinterp/corefcn/sighandlers.h (w32_raise_sigint): New declatation. * libinterp/cirefcn/sighandlers.cc (user_abort): Forward declare. (class w32_interrupt_manager): New singleton helper class. (w32_raise_sigint): New function. (user_abort): Call w32_interrupt_manager::octave_jump_to_enclosing_context instead of octave_jump_to_enclosing_context on Win32 platform. (sigint_handler): Call w32_interrupt_manager::user_abort instead of user_abort on Win32 platform. (octave_catch_interrupts, octave_ignore_interrupts, octave_set_interrupt_handler): Call w32_interrupt_manager::init on Win32 platform. * liboctave/util/oct-rl-edit.c (octave_rl_initialize): Set rl_catch_signals to 0 on Win32 platform.
author Michael Goffioul <michael.goffioul@gmail.com>
date Tue, 19 Nov 2013 14:21:38 -0500
parents 870f3e12e163
children 9cfba3616503
line wrap: on
line diff
--- a/libinterp/corefcn/sighandlers.cc
+++ b/libinterp/corefcn/sighandlers.cc
@@ -74,6 +74,153 @@
 // List of signals we have caught since last call to octave_signal_handler.
 static bool octave_signals_caught[NSIG];
 
+// Forward declaration.
+static void user_abort (const char *sig_name, int sig_number);
+
+#if defined (__WIN32__) && ! defined (__CYGWIN__)
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+class
+w32_interrupt_manager
+{
+public:
+  ~w32_interrupt_manager (void)
+  {
+    if (thread)
+      CloseHandle (thread);
+  }
+
+  static bool init (void) { return instance_ok (); }
+
+  static void octave_jump_to_enclosing_context (void)
+  {
+    if (instance_ok ())
+      instance->do_octave_jump_to_enclosing_context ();
+  }
+
+  static void user_abort (const char *sig_name, int sig_number)
+  {
+    if (instance_ok ())
+      instance->do_user_abort (sig_name, sig_number);
+  }
+
+  static void raise_sigint (void)
+  {
+    if (instance_ok ())
+      instance->do_raise_sigint ();
+  }
+
+private:
+  w32_interrupt_manager (void)
+    : thread (0), thread_id (0)
+  {
+    thread_id = GetCurrentThreadId ();
+
+    DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
+                     GetCurrentProcess (), &thread, 0, FALSE,
+                     DUPLICATE_SAME_ACCESS);
+  }
+
+  static void octave_jump_to_enclosing_context_sync (void)
+  {
+#ifdef _MSC_VER
+    _fpreset ();
+#endif
+    ::octave_jump_to_enclosing_context ();
+  }
+
+  void do_octave_jump_to_enclosing_context (void)
+  {
+    bool is_interrupt_thread = (GetCurrentThreadId () == thread_id);
+
+    if (is_interrupt_thread)
+      octave_jump_to_enclosing_context_sync ();
+    else
+      {
+        CONTEXT threadContext;
+
+        SuspendThread (thread);
+        threadContext.ContextFlags = CONTEXT_CONTROL;
+        GetThreadContext (thread, &threadContext);
+        threadContext.Eip = (DWORD) octave_jump_to_enclosing_context_sync;
+        SetThreadContext (thread, &threadContext);
+        ResumeThread (thread);
+      }
+  }
+
+  void do_user_abort (const char *sig_name, int sig_number)
+  {
+    bool is_interrupt_thread = (GetCurrentThreadId () == thread_id);
+
+    if (is_interrupt_thread)
+      ::user_abort (sig_name, sig_number);
+    else
+      {
+        SuspendThread (thread);
+        ::user_abort (sig_name, sig_number);
+        ResumeThread (thread);
+      }
+  }
+
+  void do_raise_sigint (void)
+  {
+    bool is_interrupt_thread = (GetCurrentThreadId () == thread_id);
+
+    if (is_interrupt_thread)
+      ::raise (SIGINT);
+    else
+      {
+        SuspendThread (thread);
+        ::raise (SIGINT);
+        ResumeThread (thread);
+      }
+  }
+
+  static bool instance_ok (void)
+  {
+    bool retval = true;
+
+    if (! instance)
+      {
+        instance = new w32_interrupt_manager ();
+
+        if (instance)
+          singleton_cleanup_list::add (cleanup_instance);
+      }
+
+    if (! instance)
+      {
+        ::error ("unable to create w32_interrupt_manager");
+
+        retval = false;
+      }
+
+    return retval;
+  }
+
+  static void cleanup_instance (void) { delete instance; instance = 0; }
+
+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;
+
+  static w32_interrupt_manager* instance;
+};
+
+w32_interrupt_manager* w32_interrupt_manager::instance = 0;
+
+void w32_raise_sigint (void)
+{
+  w32_interrupt_manager::raise_sigint ();
+}
+
+#endif
+
 // Signal handler return type.
 #ifndef BADSIG
 #define BADSIG (void (*)(int))-1
@@ -351,7 +498,11 @@
           if (octave_interrupt_state == 0)
             octave_interrupt_state = 1;
 
+#if defined (__WIN32__) && ! defined (__CYGWIN__)
+          w32_interrupt_manager::octave_jump_to_enclosing_context ();
+#else
           octave_jump_to_enclosing_context ();
+#endif
         }
       else
         {
@@ -378,7 +529,11 @@
 static void
 sigint_handler (int sig)
 {
+#if defined (__WIN32__) && ! defined (__CYGWIN__)
+  w32_interrupt_manager::user_abort (strsignal (sig), sig);
+#else
   user_abort (strsignal (sig), sig);
+#endif
 }
 
 #ifdef SIGPIPE
@@ -401,6 +556,10 @@
 {
   octave_interrupt_handler retval;
 
+#if defined (__WIN32__) && ! defined (__CYGWIN__)
+  w32_interrupt_manager::init ();
+#endif
+
 #ifdef SIGINT
   retval.int_handler = octave_set_signal_handler (SIGINT, sigint_handler);
 #endif
@@ -417,6 +576,10 @@
 {
   octave_interrupt_handler retval;
 
+#if defined (__WIN32__) && ! defined (__CYGWIN__)
+  w32_interrupt_manager::init ();
+#endif
+
 #ifdef SIGINT
   retval.int_handler = octave_set_signal_handler (SIGINT, SIG_IGN);
 #endif
@@ -434,6 +597,10 @@
 {
   octave_interrupt_handler retval;
 
+#if defined (__WIN32__) && ! defined (__CYGWIN__)
+  w32_interrupt_manager::init ();
+#endif
+
 #ifdef SIGINT
   retval.int_handler = octave_set_signal_handler (SIGINT, h.int_handler,
                                                   restart_syscalls);