changeset 31792:30b6a8be7128 stable

attempt to avoid race condition when initializing list of async signals * cxx-signal-helpers.cc: New file. Move (async_signals): New file-scope static const variable. (octave_block_async_signals, octave_unblock_async_signals, signal_watcher, octave_create_interrupt_watcher_thread): Move functions here from signal-wrappers.c. Use file-scope async_signals variable instead of calling octave_async_signals to get list of asynchronous signals. (init_async_signals): Rename from octave_async_signals and move here from signal-wrappers.cc. Return sigset_t object instead of pointer to static storage that is local to the function. * module.mk: Update.
author John W. Eaton <jwe@octave.org>
date Thu, 08 Dec 2022 13:57:18 -0500
parents abe72b9464d6
children 0aa88d03fa50 17d568574e1c
files liboctave/wrappers/cxx-signal-helpers.cc liboctave/wrappers/module.mk liboctave/wrappers/signal-wrappers.c liboctave/wrappers/signal-wrappers.h
diffstat 4 files changed, 218 insertions(+), 150 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/liboctave/wrappers/cxx-signal-helpers.cc	Thu Dec 08 13:57:18 2022 -0500
@@ -0,0 +1,209 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2016-2022 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// 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
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined (__WIN32__) && ! defined (__CYGWIN__)
+#  include <windows.h>
+#else
+#  include <pthread.h>
+#endif
+
+#include "signal-wrappers.h"
+
+#if ! defined (__WIN32__) || defined (__CYGWIN__)
+
+// The following pattern often used in C code to initialize a static
+// variable could possibly cause trouble in multi-threaded code:
+//
+//   TYPE * get_var (void) {
+//     static bool initialized = false;
+//     static TYPE *var;
+//     if (! initialized) {
+//       var = ...;
+//       initialized = true;
+//     }
+//     return var;
+//   }
+//
+// Changing this code to
+//
+//   static TYPE *var = init_var (void);
+//
+// doesn't work in C because the static variable can't be initialized by
+// a function call.  So we have to do this job in C++.  To avoid calling
+// new, initialize sigset_t rather than a pointer to allocated storage.
+
+static const sigset_t
+init_async_signals (void)
+{
+  sigset_t sigmask;
+
+  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
+
+  return sigmask;
+}
+
+// Initialized once, is const so we never write to it again and it can
+// be accessed by multiple threads without locking.
+
+static const sigset_t async_signals = init_async_signals ();
+
+#endif
+
+void
+octave_block_async_signals (void)
+{
+#if ! defined (__WIN32__) || defined (__CYGWIN__)
+  pthread_sigmask (SIG_BLOCK, &async_signals, 0);
+#endif
+}
+
+void
+octave_unblock_async_signals (void)
+{
+#if ! defined (__WIN32__) || defined (__CYGWIN__)
+  pthread_sigmask (SIG_UNBLOCK, &async_signals, 0);
+#endif
+}
+
+#if ! defined (__WIN32__) || defined (__CYGWIN__)
+
+static void *
+signal_watcher (void *arg)
+{
+  octave_sig_handler *handler = reinterpret_cast<octave_sig_handler *> (arg);
+
+  octave_unblock_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);
+    }
+}
+
+#endif
+
+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,
+                      reinterpret_cast<void *> (handler)))
+    {
+      // FIXME: what else should we do?
+      abort ();
+    }
+#else
+  octave_unblock_async_signals ();
+
+  octave_unused_parameter (handler);
+#endif
+}
--- a/liboctave/wrappers/module.mk	Sun Jan 29 13:03:23 2023 -0500
+++ b/liboctave/wrappers/module.mk	Thu Dec 08 13:57:18 2022 -0500
@@ -46,6 +46,7 @@
   %reldir%/async-system-wrapper.c \
   %reldir%/base64-wrappers.c \
   %reldir%/canonicalize-file-name-wrapper.c \
+  %reldir%/cxx-signal-helpers.cc \
   %reldir%/dirent-wrappers.c \
   %reldir%/fcntl-wrappers.c \
   %reldir%/filepos-wrappers.c \
--- a/liboctave/wrappers/signal-wrappers.c	Sun Jan 29 13:03:23 2023 -0500
+++ b/liboctave/wrappers/signal-wrappers.c	Thu Dec 08 13:57:18 2022 -0500
@@ -576,107 +576,6 @@
   sigprocmask (SIG_SETMASK, (sigset_t *) mask, 0);
 }
 
-#if ! defined (__WIN32__)
-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;
-}
-#endif
-
-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)
 {
@@ -684,50 +583,6 @@
 }
 
 #if ! defined (__WIN32__)
-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);
-    }
-}
-#endif
-
-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)
 {
--- a/liboctave/wrappers/signal-wrappers.h	Sun Jan 29 13:03:23 2023 -0500
+++ b/liboctave/wrappers/signal-wrappers.h	Thu Dec 08 13:57:18 2022 -0500
@@ -87,17 +87,20 @@
 
 extern OCTAVE_API void octave_set_signal_mask (void *mask);
 
-extern OCTAVE_API void octave_block_async_signals (void);
+extern OCTAVE_API int octave_raise_wrapper (int signum);
+
+// This function can be useful for debugging.
 
-extern OCTAVE_API void octave_unblock_async_signals (void);
+extern OCTAVE_API void octave_show_sigmask (const char *);
 
-extern OCTAVE_API int octave_raise_wrapper (int signum);
+// The next three functions are defined in cxx-signal-helpers.cc.
 
 extern OCTAVE_API void
 octave_create_interrupt_watcher_thread (octave_sig_handler *handler);
 
-// This can be useful for debugging.
-extern OCTAVE_API void octave_show_sigmask (const char *);
+extern OCTAVE_API void octave_block_async_signals (void);
+
+extern OCTAVE_API void octave_unblock_async_signals (void);
 
 #if defined __cplusplus
 }