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..792eeb010 100644 --- a/src/util/cancel_eh.h +++ b/src/util/cancel_eh.h @@ -18,28 +18,44 @@ Revision History: --*/ #pragma once +#include #include "util/event_handler.h" +#include "util/scoped_ctrl_c.h" /** \brief Generic event handler for invoking cancel method. */ 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 { + signal_lock(); if (!m_canceled) { m_caller_id = caller_id; m_canceled = true; m_obj.inc_cancel(); } + signal_unlock(); + } + bool canceled() { + bool ret; + if (!m_canceled) + return false; + signal_lock(); + ret = m_canceled; + signal_unlock(); + return ret; + } + void reset() { + signal_lock(); + m_canceled = false; + signal_unlock(); } - bool canceled() const { return m_canceled; } - void reset() { m_canceled = false; } T& t() { return m_obj; } void set_auto_cancel() { m_auto_cancel = true; } }; diff --git a/src/util/rlimit.cpp b/src/util/rlimit.cpp index 28656bc63..f73acd200 100644 --- a/src/util/rlimit.cpp +++ b/src/util/rlimit.cpp @@ -19,16 +19,12 @@ Revision History: #include "util/rlimit.h" #include "util/common_msgs.h" #include "util/mutex.h" - - -static DECLARE_MUTEX(g_rlimit_mux); +#include "util/scoped_ctrl_c.h" void initialize_rlimit() { - ALLOC_MUTEX(g_rlimit_mux); } void finalize_rlimit() { - DEALLOC_MUTEX(g_rlimit_mux); } reslimit::reslimit() { @@ -77,57 +73,66 @@ char const* reslimit::get_cancel_msg() const { } void reslimit::push_child(reslimit* r) { - lock_guard lock(*g_rlimit_mux); + signal_lock(); m_children.push_back(r); + signal_unlock(); } void reslimit::pop_child() { - lock_guard lock(*g_rlimit_mux); + signal_lock(); m_count += m_children.back()->m_count; m_children.back()->m_count = 0; m_children.pop_back(); + signal_unlock(); } void reslimit::pop_child(reslimit* r) { - lock_guard lock(*g_rlimit_mux); + signal_lock(); for (unsigned i = 0; i < m_children.size(); ++i) { if (m_children[i] == r) { m_count += r->m_count; r->m_count = 0; m_children.erase(m_children.begin() + i); - return; + break; } } + signal_unlock(); } void reslimit::cancel() { - lock_guard lock(*g_rlimit_mux); + signal_lock(); set_cancel(m_cancel+1); + signal_unlock(); } void reslimit::reset_cancel() { - lock_guard lock(*g_rlimit_mux); + signal_lock(); set_cancel(0); + signal_unlock(); } void reslimit::inc_cancel() { - lock_guard lock(*g_rlimit_mux); + signal_lock(); set_cancel(m_cancel + 1); + signal_unlock(); } void reslimit::dec_cancel() { - lock_guard lock(*g_rlimit_mux); + signal_lock(); if (m_cancel > 0) { set_cancel(m_cancel-1); } + signal_unlock(); } void reslimit::set_cancel(unsigned f) { + signal_lock(); m_cancel = f; for (unsigned i = 0; i < m_children.size(); ++i) { m_children[i]->set_cancel(f); } + signal_unlock(); } @@ -151,8 +156,9 @@ void reslimit::push_timeout(unsigned ms) { } void reslimit::inc_cancel(unsigned k) { - lock_guard lock(*g_rlimit_mux); + signal_lock(); set_cancel(m_cancel + k); + signal_unlock(); } void reslimit::auto_cancel() { @@ -164,4 +170,4 @@ void reslimit::auto_cancel() { void reslimit::auto_cancel() { UNREACHABLE(); } -#endif \ No newline at end of file +#endif diff --git a/src/util/scoped_ctrl_c.cpp b/src/util/scoped_ctrl_c.cpp index a3f5ee772..dab994db8 100644 --- a/src/util/scoped_ctrl_c.cpp +++ b/src/util/scoped_ctrl_c.cpp @@ -12,46 +12,136 @@ Abstract: Author: Leonardo de Moura (leonardo) 2011-04-27. + Mikulas Patocka 2025-04-05. (rewritten to be thread safe) Revision History: --*/ -#include +#include +#include +#include +#include "util/gparams.h" #include "util/scoped_ctrl_c.h" -static scoped_ctrl_c * g_obj = nullptr; +#ifdef _WINDOWS +#define USE_SIGNAL +#endif -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 std::recursive_mutex context_lock; +static std::vector 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(); } - else { - signal(SIGINT, g_obj->m_old_handler); - raise(SIGINT); +#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_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) { +#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() { +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..f686962f4 100644 --- a/src/util/scoped_ctrl_c.h +++ b/src/util/scoped_ctrl_c.h @@ -21,13 +21,14 @@ Revision History: #include "util/event_handler.h" #include "util/util.h" +void signal_lock(void); +void signal_unlock(void); + struct scoped_ctrl_c { event_handler & m_cancel_eh; 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. diff --git a/src/util/scoped_timer.cpp b/src/util/scoped_timer.cpp index 286dcda8c..2347132ce 100644 --- a/src/util/scoped_timer.cpp +++ b/src/util/scoped_timer.cpp @@ -120,10 +120,9 @@ scoped_timer::~scoped_timer() { void scoped_timer::initialize() { #ifndef _WINDOWS - static bool pthread_atfork_set = false; - if (!pthread_atfork_set) { + static std::atomic pthread_atfork_set = false; + if (!pthread_atfork_set.exchange(true)) { pthread_atfork(finalize, nullptr, nullptr); - pthread_atfork_set = true; } #endif }