diff --git a/src/params/context_params.cpp b/src/params/context_params.cpp index a5c907208..702e0898d 100644 --- a/src/params/context_params.cpp +++ b/src/params/context_params.cpp @@ -157,6 +157,7 @@ void context_params::updt_params(params_ref const & p) { void context_params::collect_param_descrs(param_descrs & d) { insert_rlimit(d); insert_timeout(d); + insert_ctrl_c(d); d.insert("well_sorted_check", CPK_BOOL, "type checker", "false"); d.insert("type_check", CPK_BOOL, "type checker (alias for well_sorted_check)", "true"); d.insert("auto_config", CPK_BOOL, "use heuristics to automatically select solver and configure it", "true"); diff --git a/src/util/cancel_eh.h b/src/util/cancel_eh.h index d609b957a..5f998194a 100644 --- a/src/util/cancel_eh.h +++ b/src/util/cancel_eh.h @@ -18,6 +18,7 @@ Revision History: --*/ #pragma once +#include #include "util/event_handler.h" /** @@ -25,16 +26,15 @@ Revision History: */ template class cancel_eh : public event_handler { - bool m_canceled = false; + std::atomic m_canceled = false; bool m_auto_cancel = false; T & m_obj; public: cancel_eh(T & o): m_obj(o) {} ~cancel_eh() override { if (m_canceled) m_obj.dec_cancel(); if (m_auto_cancel) m_obj.auto_cancel(); } void operator()(event_handler_caller_t caller_id) override { - if (!m_canceled) { + if (!m_canceled.exchange(true)) { m_caller_id = caller_id; - m_canceled = true; m_obj.inc_cancel(); } } diff --git a/src/util/scoped_ctrl_c.cpp b/src/util/scoped_ctrl_c.cpp index a3f5ee772..55ff642df 100644 --- a/src/util/scoped_ctrl_c.cpp +++ b/src/util/scoped_ctrl_c.cpp @@ -12,46 +12,99 @@ Abstract: Author: Leonardo de Moura (leonardo) 2011-04-27. + Mikulas Patocka 2025-04-03. (rewritten to be thread safe) Revision History: --*/ -#include +#include +#include +#include "util/gparams.h" #include "util/scoped_ctrl_c.h" -static scoped_ctrl_c * g_obj = nullptr; +static std::mutex context_lock; +static std::vector active_contexts; +static sigset_t context_old_set; +static struct sigaction old_sigaction; +static bool signal_handled = false; -static void on_ctrl_c(int) { - if (g_obj->m_first) { - g_obj->m_cancel_eh(CTRL_C_EH_CALLER); - if (g_obj->m_once) { - g_obj->m_first = false; - signal(SIGINT, on_ctrl_c); // re-install the handler - } +static void signal_lock(void) { + sigset_t set, old_set; + sigemptyset(&set); + sigaddset(&set, SIGINT); + if (sigprocmask(SIG_BLOCK, &set, &old_set)) + abort(); + context_lock.lock(); + context_old_set = old_set; +} + +static void signal_unlock(void) { + sigset_t old_set = context_old_set; + context_lock.unlock(); + if (sigprocmask(SIG_SETMASK, &old_set, NULL)) + abort(); +} + +static void test_and_unhandle(void) { + if (!signal_handled) + return; + for (auto a : active_contexts) { + if (a->m_first) + return; } - else { - signal(SIGINT, g_obj->m_old_handler); - raise(SIGINT); + if (sigaction(SIGINT, &old_sigaction, NULL)) + abort(); + signal_handled = false; +} + +static void on_sigint(int) { + signal_lock(); + 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_cancel_eh(eh), m_first(true), m_once(once), - m_enabled(enabled), - m_old_scoped_ctrl_c(g_obj) { + m_enabled(enabled) { + if (gparams::get_value("ctrl_c") == "false") + m_enabled = false; if (m_enabled) { - g_obj = this; - m_old_handler = signal(SIGINT, on_ctrl_c); + signal_lock(); + active_contexts.push_back(this); + if (!signal_handled) { + 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(); + signal_handled = true; + } + signal_unlock(); } } -scoped_ctrl_c::~scoped_ctrl_c() { +scoped_ctrl_c::~scoped_ctrl_c() { if (m_enabled) { - g_obj = m_old_scoped_ctrl_c; - if (m_old_handler != SIG_ERR) { - signal(SIGINT, m_old_handler); + 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(); } } diff --git a/src/util/scoped_ctrl_c.h b/src/util/scoped_ctrl_c.h index 09bc55c05..2b67f59a5 100644 --- a/src/util/scoped_ctrl_c.h +++ b/src/util/scoped_ctrl_c.h @@ -26,8 +26,6 @@ struct scoped_ctrl_c { bool m_first; bool m_once; bool m_enabled; - void (STD_CALL *m_old_handler)(int); - scoped_ctrl_c * m_old_scoped_ctrl_c; public: // If once == true, then the cancel_eh is invoked only at the first Ctrl-C. // The next time, the old signal handler will take over.