changeset 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 e5566719e0a1
children 1329866151be
files libgui/src/thread-manager.cc libinterp/corefcn/sighandlers.cc libinterp/corefcn/sighandlers.h liboctave/util/oct-rl-edit.c
diffstat 4 files changed, 177 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/libgui/src/thread-manager.cc	Sun Nov 10 11:06:58 2013 -0500
+++ b/libgui/src/thread-manager.cc	Tue Nov 19 14:21:38 2013 -0500
@@ -33,6 +33,7 @@
 #include <sys/types.h>
 #include <signal.h>
 
+#include "sighandlers.h"
 #include "thread-manager.h"
 
 #if defined (__WIN32__) && ! defined (__CYGWIN__)
@@ -47,15 +48,7 @@
 
   void interrupt (void)
   {
-    // FIXME: This doesn't work when octave_interrupt_immediately is
-    // true.  Octave crashes, presumably in the call to
-    // octave_jump_to_enclosing_context.  Does this happen because the
-    // jump occurs while Octave is running in the wrong thread?  That
-    // was happening on Unixy systems until we started using
-    // pthread_kill and blocking interrupts from all threads except the
-    // one running the Octave interpreter.
-
-    ::raise (SIGINT);
+    w32_raise_sigint ();
   }
 };
 
--- a/libinterp/corefcn/sighandlers.cc	Sun Nov 10 11:06:58 2013 -0500
+++ b/libinterp/corefcn/sighandlers.cc	Tue Nov 19 14:21:38 2013 -0500
@@ -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);
--- a/libinterp/corefcn/sighandlers.h	Sun Nov 10 11:06:58 2013 -0500
+++ b/libinterp/corefcn/sighandlers.h	Tue Nov 19 14:21:38 2013 -0500
@@ -82,6 +82,10 @@
 octave_set_interrupt_handler (const volatile octave_interrupt_handler&,
                               bool restart_syscalls = true);
 
+#if defined (__WIN32__) && ! defined (__CYGWIN__)
+extern OCTINTERP_API void w32_raise_sigint (void);
+#endif
+
 // extern void ignore_sigchld (void);
 
 // Maybe this should be in a separate file?
--- a/liboctave/util/oct-rl-edit.c	Sun Nov 10 11:06:58 2013 -0500
+++ b/liboctave/util/oct-rl-edit.c	Tue Nov 19 14:21:38 2013 -0500
@@ -395,6 +395,10 @@
 void
 octave_rl_initialize (void)
 {
+#if defined (__WIN32__) && ! defined (__CYGWIN__)
+  rl_catch_signals = 0;
+#endif
+
   rl_initialize ();
 }