mirror of
https://github.com/Z3Prover/z3
synced 2025-04-23 17:15:31 +00:00
#7418 - circumvent use of timer threads to make WASM integration of z3 easier
The scoped_timer uses a std::therad. Spawning this thread fails in cases of WASM. Instead of adapting builds and using async features at the level of WASM and the client, we expose a specialized version of z3 that doesn't use threads at all, neither for solvers nor for timers. The tradeoff is that the periodic poll that checks for timeout directly queries the global clock each time. We characterize it as based on polling.
This commit is contained in:
parent
94f0aff47f
commit
71bad7159b
8 changed files with 124 additions and 31 deletions
|
@ -25,11 +25,12 @@ Revision History:
|
|||
*/
|
||||
template<typename T>
|
||||
class cancel_eh : public event_handler {
|
||||
bool m_canceled;
|
||||
bool m_canceled = false;
|
||||
bool m_auto_cancel = false;
|
||||
T & m_obj;
|
||||
public:
|
||||
cancel_eh(T & o): m_canceled(false), m_obj(o) {}
|
||||
~cancel_eh() override { if (m_canceled) m_obj.dec_cancel(); }
|
||||
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) {
|
||||
m_caller_id = caller_id;
|
||||
|
@ -39,5 +40,7 @@ public:
|
|||
}
|
||||
bool canceled() const { return m_canceled; }
|
||||
void reset() { m_canceled = false; }
|
||||
T& t() { return m_obj; }
|
||||
void set_auto_cancel() { m_auto_cancel = true; }
|
||||
};
|
||||
|
||||
|
|
|
@ -31,11 +31,7 @@ void finalize_rlimit() {
|
|||
DEALLOC_MUTEX(g_rlimit_mux);
|
||||
}
|
||||
|
||||
reslimit::reslimit():
|
||||
m_cancel(0),
|
||||
m_suspend(false),
|
||||
m_count(0),
|
||||
m_limit(std::numeric_limits<uint64_t>::max()) {
|
||||
reslimit::reslimit() {
|
||||
}
|
||||
|
||||
uint64_t reslimit::count() const {
|
||||
|
@ -117,7 +113,7 @@ void reslimit::reset_cancel() {
|
|||
|
||||
void reslimit::inc_cancel() {
|
||||
lock_guard lock(*g_rlimit_mux);
|
||||
set_cancel(m_cancel+1);
|
||||
set_cancel(m_cancel + 1);
|
||||
}
|
||||
|
||||
void reslimit::dec_cancel() {
|
||||
|
@ -133,3 +129,39 @@ void reslimit::set_cancel(unsigned f) {
|
|||
m_children[i]->set_cancel(f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef POLLING_TIMER
|
||||
void reslimit::push_timeout(unsigned ms) {
|
||||
m_num_timers++;
|
||||
if (m_cancel > 0) {
|
||||
++m_cancel;
|
||||
return;
|
||||
}
|
||||
if (m_timeout_ms != 0) {
|
||||
double ms_taken = 1000 * m_timer.get_seconds();
|
||||
if (ms_taken > m_timeout_ms)
|
||||
return;
|
||||
if (m_timeout_ms - ms_taken < ms)
|
||||
return;
|
||||
}
|
||||
m_timer = timer();
|
||||
m_timeout_ms = ms;
|
||||
}
|
||||
|
||||
void reslimit::inc_cancel(unsigned k) {
|
||||
lock_guard lock(*g_rlimit_mux);
|
||||
set_cancel(m_cancel + k);
|
||||
}
|
||||
|
||||
void reslimit::auto_cancel() {
|
||||
--m_num_timers;
|
||||
dec_cancel();
|
||||
}
|
||||
|
||||
#else
|
||||
void reslimit::auto_cancel() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
#endif
|
|
@ -19,6 +19,7 @@ Revision History:
|
|||
#pragma once
|
||||
|
||||
#include "util/vector.h"
|
||||
#include "util/timer.h"
|
||||
#include <atomic>
|
||||
|
||||
void initialize_rlimit();
|
||||
|
@ -29,16 +30,38 @@ void finalize_rlimit();
|
|||
*/
|
||||
|
||||
class reslimit {
|
||||
std::atomic<unsigned> m_cancel;
|
||||
bool m_suspend;
|
||||
uint64_t m_count;
|
||||
uint64_t m_limit;
|
||||
svector<uint64_t> m_limits;
|
||||
ptr_vector<reslimit> m_children;
|
||||
std::atomic<unsigned> m_cancel = 0;
|
||||
bool m_suspend = false;
|
||||
uint64_t m_count = 0;
|
||||
uint64_t m_limit = std::numeric_limits<uint64_t>::max();
|
||||
#ifdef POLLING_TIMER
|
||||
timer m_timer;
|
||||
unsigned m_timeout_ms = 0;
|
||||
unsigned m_num_timers = 0;
|
||||
#endif
|
||||
svector<uint64_t> m_limits;
|
||||
ptr_vector<reslimit> m_children;
|
||||
|
||||
|
||||
void set_cancel(unsigned f);
|
||||
friend class scoped_suspend_rlimit;
|
||||
|
||||
#ifdef POLLING_TIMER
|
||||
bool is_timeout() { return m_timer.ms_timeout(m_timeout_ms) && (inc_cancel(m_num_timers), pop_timeout(), true); }
|
||||
void inc_cancel(unsigned k);
|
||||
#else
|
||||
inline bool is_timeout() { return false; }
|
||||
#endif
|
||||
|
||||
#ifdef POLLING_TIMER
|
||||
|
||||
void pop_timeout() {
|
||||
m_timeout_ms = 0;
|
||||
}
|
||||
|
||||
void push_timeout(unsigned ms);
|
||||
#endif
|
||||
|
||||
public:
|
||||
reslimit();
|
||||
void push(unsigned delta_limit);
|
||||
|
@ -52,15 +75,21 @@ public:
|
|||
uint64_t count() const;
|
||||
void reset_count() { m_count = 0; }
|
||||
|
||||
bool suspended() const { return m_suspend; }
|
||||
inline bool not_canceled() const { return (m_cancel == 0 && m_count <= m_limit) || m_suspend; }
|
||||
inline bool is_canceled() const { return !not_canceled(); }
|
||||
#ifdef POLLING_TIMER
|
||||
void set_timeout(unsigned ms) { push_timeout(ms); }
|
||||
#endif
|
||||
bool suspended() const { return m_suspend; }
|
||||
inline bool not_canceled() {
|
||||
return m_suspend || (m_cancel == 0 && m_count <= m_limit && !is_timeout());
|
||||
}
|
||||
inline bool is_canceled() { return !not_canceled(); }
|
||||
char const* get_cancel_msg() const;
|
||||
void cancel();
|
||||
void reset_cancel();
|
||||
|
||||
void inc_cancel();
|
||||
void dec_cancel();
|
||||
void auto_cancel();
|
||||
};
|
||||
|
||||
class scoped_rlimit {
|
||||
|
|
|
@ -22,6 +22,8 @@ Revision History:
|
|||
#include "util/scoped_timer.h"
|
||||
#include "util/mutex.h"
|
||||
#include "util/util.h"
|
||||
#include "util/cancel_eh.h"
|
||||
#include "util/rlimit.h"
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
|
@ -79,6 +81,14 @@ scoped_timer::scoped_timer(unsigned ms, event_handler * eh) {
|
|||
if (ms == 0 || ms == UINT_MAX)
|
||||
return;
|
||||
|
||||
#ifdef POLLING_TIMER
|
||||
auto* r = dynamic_cast<cancel_eh<reslimit>*>(eh);
|
||||
if (r) {
|
||||
r->t().set_timeout(ms);
|
||||
r->set_auto_cancel();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
workers.lock();
|
||||
if (available_workers.empty()) {
|
||||
// start new thead
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue