Bump to m4 1.4.19
[platform/upstream/m4.git] / lib / fatal-signal.c
index 8f7cb8e..ba5fb53 100644 (file)
@@ -1,5 +1,5 @@
 /* Emergency actions in case of a fatal signal.
-   Copyright (C) 2003-2004, 2006-2013 Free Software Foundation, Inc.
+   Copyright (C) 2003-2004, 2006-2021 Free Software Foundation, Inc.
    Written by Bruno Haible <bruno@clisp.org>, 2003.
 
    This program is free software: you can redistribute it and/or modify
@@ -13,7 +13,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
 
 #include <config.h>
@@ -26,8 +26,9 @@
 #include <signal.h>
 #include <unistd.h>
 
+#include "glthread/lock.h"
+#include "thread-optim.h"
 #include "sig-handler.h"
-#include "xalloc.h"
 
 #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
 
@@ -85,6 +86,10 @@ static int fatal_signals[] =
 static void
 init_fatal_signals (void)
 {
+  /* This function is multithread-safe even without synchronization, because
+     if two threads execute it simultaneously, the fatal_signals[] array will
+     not change any more after the first of the threads has completed this
+     function.  */
   static bool fatal_signals_initialized = false;
   if (!fatal_signals_initialized)
     {
@@ -107,7 +112,7 @@ init_fatal_signals (void)
 /* ========================================================================= */
 
 
-typedef void (*action_t) (void);
+typedef _GL_ASYNC_SAFE void (*action_t) (int sig);
 
 /* Type of an entry in the actions array.
    The 'action' field is accessed from within the fatal_signal_handler(),
@@ -131,7 +136,7 @@ static struct sigaction saved_sigactions[64];
 
 
 /* Uninstall the handlers.  */
-static void
+static _GL_ASYNC_SAFE void
 uninstall_handlers (void)
 {
   size_t i;
@@ -148,7 +153,7 @@ uninstall_handlers (void)
 
 
 /* The signal handler.  It gets called asynchronously.  */
-static void
+static _GL_ASYNC_SAFE void
 fatal_signal_handler (int sig)
 {
   for (;;)
@@ -162,7 +167,7 @@ fatal_signal_handler (int sig)
       actions_count = n;
       action = actions[n].action;
       /* Execute the action.  */
-      action ();
+      action (sig);
     }
 
   /* Now execute the signal's default action.
@@ -200,11 +205,18 @@ install_handlers (void)
 }
 
 
+/* Lock that makes at_fatal_signal multi-thread safe.  */
+gl_lock_define_initialized (static, at_fatal_signal_lock)
+
 /* Register a cleanup function to be executed when a catchable fatal signal
    occurs.  */
-void
+int
 at_fatal_signal (action_t action)
 {
+  bool mt = gl_multithreaded ();
+
+  if (mt) gl_lock_lock (at_fatal_signal_lock);
+
   static bool cleanup_initialized = false;
   if (!cleanup_initialized)
     {
@@ -213,6 +225,8 @@ at_fatal_signal (action_t action)
       cleanup_initialized = true;
     }
 
+  int ret = 0;
+
   if (actions_count == actions_allocated)
     {
       /* Extend the actions array.  Note that we cannot use xrealloc(),
@@ -222,9 +236,15 @@ at_fatal_signal (action_t action)
       size_t old_actions_allocated = actions_allocated;
       size_t new_actions_allocated = 2 * actions_allocated;
       actions_entry_t *new_actions =
-        XNMALLOC (new_actions_allocated, actions_entry_t);
-      size_t k;
+        (actions_entry_t *)
+        malloc (new_actions_allocated * sizeof (actions_entry_t));
+      if (new_actions == NULL)
+        {
+          ret = -1;
+          goto done;
+        }
 
+      size_t k;
       /* Don't use memcpy() here, because memcpy takes non-volatile arguments
          and is therefore not guaranteed to complete all memory stores before
          the next statement.  */
@@ -233,8 +253,15 @@ at_fatal_signal (action_t action)
       actions = new_actions;
       actions_allocated = new_actions_allocated;
       /* Now we can free the old actions array.  */
+      /* No, we can't do that.  If fatal_signal_handler is running in a
+         different thread and has already fetched the actions pointer (getting
+         old_actions) but not yet accessed its n-th element, that thread may
+         crash when accessing an element of the already freed old_actions
+         array.  */
+      #if 0
       if (old_actions != static_actions)
         free (old_actions);
+      #endif
     }
   /* The two uses of 'volatile' in the types above (and ISO C 99 section
      5.1.2.3.(5)) ensure that we increment the actions_count only after
@@ -242,6 +269,11 @@ at_fatal_signal (action_t action)
      actions[actions_count].  */
   actions[actions_count].action = action;
   actions_count++;
+
+ done:
+  if (mt) gl_lock_unlock (at_fatal_signal_lock);
+
+  return ret;
 }
 
 
@@ -251,36 +283,90 @@ at_fatal_signal (action_t action)
 static sigset_t fatal_signal_set;
 
 static void
-init_fatal_signal_set (void)
+do_init_fatal_signal_set (void)
 {
-  static bool fatal_signal_set_initialized = false;
-  if (!fatal_signal_set_initialized)
-    {
-      size_t i;
+  size_t i;
 
-      init_fatal_signals ();
+  init_fatal_signals ();
 
-      sigemptyset (&fatal_signal_set);
-      for (i = 0; i < num_fatal_signals; i++)
-        if (fatal_signals[i] >= 0)
-          sigaddset (&fatal_signal_set, fatal_signals[i]);
+  sigemptyset (&fatal_signal_set);
+  for (i = 0; i < num_fatal_signals; i++)
+    if (fatal_signals[i] >= 0)
+      sigaddset (&fatal_signal_set, fatal_signals[i]);
+}
 
-      fatal_signal_set_initialized = true;
-    }
+/* Ensure that do_init_fatal_signal_set is called once only.  */
+gl_once_define(static, fatal_signal_set_once)
+
+static void
+init_fatal_signal_set (void)
+{
+  gl_once (fatal_signal_set_once, do_init_fatal_signal_set);
 }
 
+/* Lock and counter that allow block_fatal_signals/unblock_fatal_signals pairs
+   to occur in different threads and even overlap in time.  */
+gl_lock_define_initialized (static, fatal_signals_block_lock)
+static unsigned int fatal_signals_block_counter = 0;
+
 /* Temporarily delay the catchable fatal signals.  */
 void
 block_fatal_signals (void)
 {
-  init_fatal_signal_set ();
-  sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
+  bool mt = gl_multithreaded ();
+
+  if (mt) gl_lock_lock (fatal_signals_block_lock);
+
+  if (fatal_signals_block_counter++ == 0)
+    {
+      init_fatal_signal_set ();
+      sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
+    }
+
+  if (mt) gl_lock_unlock (fatal_signals_block_lock);
 }
 
 /* Stop delaying the catchable fatal signals.  */
 void
 unblock_fatal_signals (void)
 {
+  bool mt = gl_multithreaded ();
+
+  if (mt) gl_lock_lock (fatal_signals_block_lock);
+
+  if (fatal_signals_block_counter == 0)
+    /* There are more calls to unblock_fatal_signals() than to
+       block_fatal_signals().  */
+    abort ();
+  if (--fatal_signals_block_counter == 0)
+    {
+      init_fatal_signal_set ();
+      sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
+    }
+
+  if (mt) gl_lock_unlock (fatal_signals_block_lock);
+}
+
+
+unsigned int
+get_fatal_signals (int signals[64])
+{
+  init_fatal_signal_set ();
+
+  {
+    int *p = signals;
+    size_t i;
+
+    for (i = 0; i < num_fatal_signals; i++)
+      if (fatal_signals[i] >= 0)
+        *p++ = fatal_signals[i];
+    return p - signals;
+  }
+}
+
+const sigset_t *
+get_fatal_signal_set (void)
+{
   init_fatal_signal_set ();
-  sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
+  return &fatal_signal_set;
 }