mirror of
https://github.com/Z3Prover/z3
synced 2025-05-09 16:55:47 +00:00
148 lines
3.2 KiB
C++
148 lines
3.2 KiB
C++
/*++
|
|
Copyright (c) 2011 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
scoped_ctrl_c.cpp
|
|
|
|
Abstract:
|
|
|
|
Scoped control-c handler.
|
|
|
|
Author:
|
|
|
|
Leonardo de Moura (leonardo) 2011-04-27.
|
|
Mikulas Patocka 2025-04-05. (rewritten to be thread safe)
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#include <signal.h>
|
|
#include <cstring>
|
|
#include <mutex>
|
|
#include "util/scoped_ctrl_c.h"
|
|
#include "util/gparams.h"
|
|
|
|
#ifdef _WINDOWS
|
|
#define USE_SIGNAL
|
|
#endif
|
|
|
|
static std::recursive_mutex context_lock;
|
|
static std::vector<scoped_ctrl_c *> active_contexts;
|
|
#ifdef USE_SIGNAL
|
|
static void (*old_handler)(int);
|
|
#else
|
|
static sigset_t context_old_set;
|
|
static struct sigaction old_sigaction;
|
|
static unsigned signal_lock_depth = 0;
|
|
#endif
|
|
static bool signal_handled = false;
|
|
|
|
void signal_lock(void) {
|
|
#ifdef USE_SIGNAL
|
|
context_lock.lock();
|
|
#else
|
|
sigset_t set, old_set;
|
|
sigemptyset(&set);
|
|
sigaddset(&set, SIGINT);
|
|
if (sigprocmask(SIG_BLOCK, &set, &old_set))
|
|
abort();
|
|
context_lock.lock();
|
|
signal_lock_depth++;
|
|
if (signal_lock_depth == 1)
|
|
context_old_set = old_set;
|
|
#endif
|
|
}
|
|
|
|
void signal_unlock(void) {
|
|
#ifdef USE_SIGNAL
|
|
context_lock.unlock();
|
|
#else
|
|
bool restore;
|
|
sigset_t old_set = context_old_set;
|
|
signal_lock_depth--;
|
|
restore = !signal_lock_depth;
|
|
context_lock.unlock();
|
|
if (restore) {
|
|
if (sigprocmask(SIG_SETMASK, &old_set, NULL))
|
|
abort();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void test_and_unhandle(void) {
|
|
if (!signal_handled)
|
|
return;
|
|
for (auto a : active_contexts) {
|
|
if (a->m_first)
|
|
return;
|
|
}
|
|
#ifdef USE_SIGNAL
|
|
signal(SIGINT, old_handler);
|
|
#else
|
|
if (sigaction(SIGINT, &old_sigaction, NULL))
|
|
abort();
|
|
#endif
|
|
signal_handled = false;
|
|
}
|
|
|
|
static void on_sigint(int) {
|
|
signal_lock();
|
|
#ifdef USE_SIGNAL
|
|
if (signal_handled)
|
|
signal(SIGINT, on_sigint);
|
|
#endif
|
|
for (auto a : active_contexts) {
|
|
if (a->m_first)
|
|
a->m_cancel_eh(CTRL_C_EH_CALLER);
|
|
if (a->m_once)
|
|
a->m_first = false;
|
|
}
|
|
test_and_unhandle();
|
|
signal_unlock();
|
|
}
|
|
|
|
scoped_ctrl_c::scoped_ctrl_c(event_handler & eh, bool once, bool enabled):
|
|
m_cancel_eh(eh),
|
|
m_first(true),
|
|
m_once(once),
|
|
m_enabled(enabled),
|
|
m_old_scoped_ctrl_c(g_obj) {
|
|
if (gparams::get_value("ctrl_c") == "false")
|
|
m_enabled = false;
|
|
if (m_enabled) {
|
|
signal_lock();
|
|
active_contexts.push_back(this);
|
|
if (!signal_handled) {
|
|
#ifdef USE_SIGNAL
|
|
old_handler = signal(SIGINT, on_sigint);
|
|
#else
|
|
struct sigaction sa;
|
|
memset(&sa, 0, sizeof(struct sigaction));
|
|
sa.sa_handler = on_sigint;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = SA_RESTART;
|
|
if (sigaction(SIGINT, &sa, &old_sigaction))
|
|
abort();
|
|
#endif
|
|
signal_handled = true;
|
|
}
|
|
signal_unlock();
|
|
}
|
|
}
|
|
|
|
scoped_ctrl_c::~scoped_ctrl_c() {
|
|
if (m_enabled) {
|
|
signal_lock();
|
|
for (auto it = active_contexts.begin(); it != active_contexts.end(); it++) {
|
|
if (*it == this) {
|
|
active_contexts.erase(it);
|
|
goto found;
|
|
}
|
|
}
|
|
abort();
|
|
found:
|
|
test_and_unhandle();
|
|
signal_unlock();
|
|
}
|
|
}
|