#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include "ovs-thread.h"
#include "poll-loop.h"
#include "shash.h"
#include "sset.h"
static int signal_fds[2];
static volatile sig_atomic_t stored_sig_nr = SIG_ATOMIC_MAX;
-static void fatal_signal_init(void);
+static struct ovs_mutex mutex;
+
static void atexit_handler(void);
static void call_hooks(int sig_nr);
-static void
+/* Initializes the fatal signal handling module. Calling this function is
+ * optional, because calling any other function in the module will also
+ * initialize it. However, in a multithreaded program, the module must be
+ * initialized while the process is still single-threaded. */
+void
fatal_signal_init(void)
{
static bool inited = false;
if (!inited) {
size_t i;
+ assert_single_threaded();
inited = true;
+ ovs_mutex_init_recursive(&mutex);
xpipe_nonblocking(signal_fds);
for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) {
}
}
-/* Registers 'hook_cb' to be called when a process termination signal is
- * raised. If 'run_at_exit' is true, 'hook_cb' is also called during normal
- * process termination, e.g. when exit() is called or when main() returns.
+/* Registers 'hook_cb' to be called from inside poll_block() following a fatal
+ * signal. 'hook_cb' does not need to be async-signal-safe. In a
+ * multithreaded program 'hook_cb' might be called from any thread, with
+ * threads other than the one running 'hook_cb' in unknown states.
*
- * 'hook_cb' is not called immediately from the signal handler but rather the
- * next time the poll loop iterates, so it is freed from the usual restrictions
- * on signal handler functions.
+ * If 'run_at_exit' is true, 'hook_cb' is also called during normal process
+ * termination, e.g. when exit() is called or when main() returns.
*
* If the current process forks, fatal_signal_fork() may be called to clear the
* parent process's fatal signal hooks, so that 'hook_cb' is only called when
{
fatal_signal_init();
+ ovs_mutex_lock(&mutex);
ovs_assert(n_hooks < MAX_HOOKS);
hooks[n_hooks].hook_cb = hook_cb;
hooks[n_hooks].cancel_cb = cancel_cb;
hooks[n_hooks].aux = aux;
hooks[n_hooks].run_at_exit = run_at_exit;
n_hooks++;
+ ovs_mutex_unlock(&mutex);
}
/* Handles fatal signal number 'sig_nr'.
if (sig_nr != SIG_ATOMIC_MAX) {
char namebuf[SIGNAL_NAME_BUFSIZE];
+ ovs_mutex_lock(&mutex);
+
VLOG_WARN("terminating with signal %d (%s)",
(int)sig_nr, signal_name(sig_nr, namebuf, sizeof namebuf));
call_hooks(sig_nr);
* termination status reflects that we were killed by this signal */
signal(sig_nr, SIG_DFL);
raise(sig_nr);
+
+ ovs_mutex_unlock(&mutex);
+ OVS_NOT_REACHED();
}
}
void
fatal_signal_add_file_to_unlink(const char *file)
{
+ fatal_signal_init();
+
+ ovs_mutex_lock(&mutex);
if (!added_hook) {
added_hook = true;
fatal_signal_add_hook(unlink_files, cancel_files, NULL, true);
}
sset_add(&files, file);
+ ovs_mutex_unlock(&mutex);
}
/* Unregisters 'file' from being unlinked when the program terminates via
void
fatal_signal_remove_file_to_unlink(const char *file)
{
+ fatal_signal_init();
+
+ ovs_mutex_lock(&mutex);
sset_find_and_delete(&files, file);
+ ovs_mutex_unlock(&mutex);
}
/* Like fatal_signal_remove_file_to_unlink(), but also unlinks 'file'.
int
fatal_signal_unlink_file_now(const char *file)
{
- int error = unlink(file) ? errno : 0;
+ int error;
+
+ fatal_signal_init();
+
+ ovs_mutex_lock(&mutex);
+
+ error = unlink(file) ? errno : 0;
if (error) {
VLOG_WARN("could not unlink \"%s\" (%s)", file, ovs_strerror(error));
}
fatal_signal_remove_file_to_unlink(file);
+ ovs_mutex_unlock(&mutex);
+
return error;
}
{
size_t i;
+ assert_single_threaded();
+
for (i = 0; i < n_hooks; i++) {
struct hook *h = &hooks[i];
if (h->cancel_cb) {