/* 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
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>
#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]))
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)
{
/* ========================================================================= */
-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(),
/* Uninstall the handlers. */
-static void
+static _GL_ASYNC_SAFE void
uninstall_handlers (void)
{
size_t i;
/* The signal handler. It gets called asynchronously. */
-static void
+static _GL_ASYNC_SAFE void
fatal_signal_handler (int sig)
{
for (;;)
actions_count = n;
action = actions[n].action;
/* Execute the action. */
- action ();
+ action (sig);
}
/* Now execute the signal's default action.
}
+/* 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)
{
cleanup_initialized = true;
}
+ int ret = 0;
+
if (actions_count == actions_allocated)
{
/* Extend the actions array. Note that we cannot use xrealloc(),
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. */
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
actions[actions_count]. */
actions[actions_count].action = action;
actions_count++;
+
+ done:
+ if (mt) gl_lock_unlock (at_fatal_signal_lock);
+
+ return ret;
}
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;
}