3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-07-04 22:36:10 +00:00

try to apply the lease cancel/reslimit race condition bugfix to smt_parallel from the tactic

This commit is contained in:
Ilana Shapiro 2026-06-21 15:18:46 -07:00
parent e846b2a9e7
commit 293cb384c2
2 changed files with 84 additions and 31 deletions

View file

@ -763,22 +763,25 @@ namespace smt {
if (m_config.m_global_backbones) { if (m_config.m_global_backbones) {
bb_candidates local_candidates = find_backbone_candidates(); bb_candidates local_candidates = find_backbone_candidates();
b.collect_backbone_candidates(m_l2g, local_candidates); b.collect_backbone_candidates(m_l2g, local_candidates);
if (!m.inc()) bool lease_canceled = false;
if (!b.checkpoint_worker(id, lease, lease_canceled))
return; return;
if (lease_canceled) {
LOG_WORKER(1, " abandoning canceled lease\n");
continue;
}
} }
lbool r = check_cube(cube); lbool r = check_cube(cube);
if (b.lease_canceled(lease)) { bool lease_canceled = false;
if (!b.checkpoint_worker(id, lease, lease_canceled))
return;
if (lease_canceled) {
LOG_WORKER(1, " abandoning canceled lease\n"); LOG_WORKER(1, " abandoning canceled lease\n");
lease = {};
m.limit().dec_cancel();
continue; continue;
} }
if (!m.inc())
return;
switch (r) { switch (r) {
case l_undef: { case l_undef: {
update_max_thread_conflicts(); update_max_thread_conflicts();
@ -790,7 +793,6 @@ namespace smt {
if (!atom) if (!atom)
goto check_cube_start; goto check_cube_start;
b.try_split(m_l2g, id, lease, atom, m_config.m_threads_max_conflicts); b.try_split(m_l2g, id, lease, atom, m_config.m_threads_max_conflicts);
lease = {};
simplify(); simplify();
break; break;
} }
@ -825,7 +827,6 @@ namespace smt {
b.backtrack(m_l2g, id, core_to_use, lease); b.backtrack(m_l2g, id, core_to_use, lease);
if (m_config.m_core_minimize) if (m_config.m_core_minimize)
b.enqueue_core_minimization(m_l2g, source, unsat_core); b.enqueue_core_minimization(m_l2g, source, unsat_core);
lease = {};
if (m_config.m_share_conflicts) if (m_config.m_share_conflicts)
b.collect_clause(m_l2g, id, mk_not(mk_and(unsat_core))); b.collect_clause(m_l2g, id, mk_not(mk_and(unsat_core)));
@ -1105,14 +1106,43 @@ namespace smt {
return r; return r;
} }
void parallel::batch_manager::release_lease_unlocked(unsigned worker_id, node* n) { void parallel::batch_manager::set_canceled_unlocked() {
if (worker_id >= m_worker_leases.size()) if (m_state != state::is_running)
return; return;
auto &lease = m_worker_leases[worker_id]; cancel_background_threads();
if (!lease.leased_node || lease.leased_node != n) }
void parallel::batch_manager::release_worker_lease_unlocked(unsigned worker_id, node_lease& lease) {
if (worker_id >= m_worker_leases.size()) {
lease = {};
return; return;
m_search_tree.dec_active_workers(lease.leased_node); }
auto& stored_lease = m_worker_leases[worker_id];
if (!stored_lease.leased_node || stored_lease.leased_node != lease.leased_node) {
lease = {};
return;
}
bool cancel_signaled = stored_lease.cancel_signaled;
m_search_tree.dec_active_workers(stored_lease.leased_node);
stored_lease = {};
lease = {}; lease = {};
if (cancel_signaled)
p.m_workers[worker_id]->limit().dec_cancel();
}
bool parallel::batch_manager::attempt_release_canceled_lease_unlocked(unsigned worker_id, node_lease& lease) {
if (m_state != state::is_running || !lease.leased_node || worker_id >= m_worker_leases.size())
return false;
auto& stored_lease = m_worker_leases[worker_id];
if (stored_lease.leased_node != lease.leased_node)
return false;
if (!m_search_tree.is_lease_canceled(stored_lease.leased_node))
return false;
release_worker_lease_unlocked(worker_id, lease);
return true;
} }
void parallel::batch_manager::cancel_closed_leases_unlocked(unsigned source_worker_id) { void parallel::batch_manager::cancel_closed_leases_unlocked(unsigned source_worker_id) {
@ -1132,7 +1162,7 @@ namespace smt {
} }
void parallel::batch_manager::backtrack(ast_translation &l2g, unsigned worker_id, expr_ref_vector const &core, void parallel::batch_manager::backtrack(ast_translation &l2g, unsigned worker_id, expr_ref_vector const &core,
node_lease const &lease) { node_lease& lease) {
std::scoped_lock lock(mux); std::scoped_lock lock(mux);
vector<cube_config::literal> g_core; vector<cube_config::literal> g_core;
for (auto c : core) for (auto c : core)
@ -1363,7 +1393,7 @@ namespace smt {
} }
void parallel::batch_manager::backtrack_unlocked(ast_translation& l2g, unsigned worker_id, expr_ref_vector const& core, void parallel::batch_manager::backtrack_unlocked(ast_translation& l2g, unsigned worker_id, expr_ref_vector const& core,
node_lease const* lease, vector<node_lease> const* targets) { node_lease* lease, vector<node_lease> const* targets) {
if (m_state != state::is_running) if (m_state != state::is_running)
return; return;
@ -1379,8 +1409,9 @@ namespace smt {
// i.e. worker 1 splits this node, but then worker 2 determines UNSAT --> worker 2 is stale but we still close this node and backtrack // i.e. worker 1 splits this node, but then worker 2 determines UNSAT --> worker 2 is stale but we still close this node and backtrack
did_backtrack = true; did_backtrack = true;
IF_VERBOSE(1, verbose_stream() << "Batch manager backtracking.\n"); IF_VERBOSE(1, verbose_stream() << "Batch manager backtracking.\n");
release_lease_unlocked(worker_id, lease->leased_node); node* leased_node = lease->leased_node;
m_search_tree.backtrack(lease->leased_node, g_core); release_worker_lease_unlocked(worker_id, *lease);
m_search_tree.backtrack(leased_node, g_core);
} }
if (targets) { if (targets) {
for (auto const& target : *targets) { for (auto const& target : *targets) {
@ -1410,32 +1441,52 @@ namespace smt {
} }
void parallel::batch_manager::try_split(ast_translation &l2g, unsigned worker_id, void parallel::batch_manager::try_split(ast_translation &l2g, unsigned worker_id,
node_lease const &lease, expr *atom, unsigned effort) { node_lease& lease, expr *atom, unsigned effort) {
std::scoped_lock lock(mux); std::scoped_lock lock(mux);
if (m_state != state::is_running) if (m_state != state::is_running)
return; return;
if (m_search_tree.is_lease_canceled(lease.leased_node)) if (m_search_tree.is_lease_canceled(lease.leased_node)) {
attempt_release_canceled_lease_unlocked(worker_id, lease);
return; return;
}
expr_ref lit(m), nlit(m); expr_ref lit(m), nlit(m);
lit = l2g(atom); lit = l2g(atom);
nlit = mk_not(m, lit); nlit = mk_not(m, lit);
bool did_split = m_search_tree.try_split(lease.leased_node, lit, nlit, effort); node* leased_node = lease.leased_node;
bool did_split = m_search_tree.try_split(leased_node, lit, nlit, effort);
release_lease_unlocked(worker_id, lease.leased_node); release_worker_lease_unlocked(worker_id, lease);
if (did_split) { if (did_split) {
++m_stats.m_num_cubes; ++m_stats.m_num_cubes;
m_stats.m_max_cube_depth = std::max(m_stats.m_max_cube_depth, lease.leased_node->depth() + 1); m_stats.m_max_cube_depth = std::max(m_stats.m_max_cube_depth, leased_node->depth() + 1);
IF_VERBOSE(1, verbose_stream() << "Batch manager splitting on literal: " << mk_bounded_pp(lit, m, 3) << "\n"); IF_VERBOSE(1, verbose_stream() << "Batch manager splitting on literal: " << mk_bounded_pp(lit, m, 3) << "\n");
} }
} }
void parallel::batch_manager::release_lease(unsigned worker_id, node_lease const &lease) { bool parallel::batch_manager::checkpoint_worker(unsigned worker_id, node_lease& lease, bool& lease_canceled) {
std::scoped_lock lock(mux); std::scoped_lock lock(mux);
release_lease_unlocked(worker_id, lease.leased_node); lease_canceled = false;
SASSERT(worker_id < p.m_workers.size());
if (attempt_release_canceled_lease_unlocked(worker_id, lease)) {
lease_canceled = true;
return true;
}
if (p.m_workers[worker_id]->limit().inc())
return true;
if (attempt_release_canceled_lease_unlocked(worker_id, lease)) {
lease_canceled = true;
return true;
}
set_canceled_unlocked();
return false;
} }
bool parallel::batch_manager::lease_canceled(node_lease const &lease) { bool parallel::batch_manager::lease_canceled(node_lease const &lease) {

View file

@ -178,9 +178,11 @@ namespace smt {
} }
void backtrack_unlocked(ast_translation& l2g, unsigned worker_id, expr_ref_vector const& core, void backtrack_unlocked(ast_translation& l2g, unsigned worker_id, expr_ref_vector const& core,
node_lease const* lease = nullptr, vector<node_lease> const* targets = nullptr); node_lease* lease = nullptr, vector<node_lease> const* targets = nullptr);
void collect_clause_unlocked(ast_translation &l2g, unsigned source_worker_id, expr *clause); void collect_clause_unlocked(ast_translation &l2g, unsigned source_worker_id, expr *clause);
void release_lease_unlocked(unsigned worker_id, node* n); void set_canceled_unlocked();
void release_worker_lease_unlocked(unsigned worker_id, node_lease& lease);
bool attempt_release_canceled_lease_unlocked(unsigned worker_id, node_lease& lease);
void cancel_closed_leases_unlocked(unsigned source_worker_id); void cancel_closed_leases_unlocked(unsigned source_worker_id);
void collect_matching_targets_unlocked(node* source, expr* lit, vector<cube_config::literal> const& core, void collect_matching_targets_unlocked(node* source, expr* lit, vector<cube_config::literal> const& core,
vector<node_lease>& targets); vector<node_lease>& targets);
@ -217,14 +219,14 @@ namespace smt {
} }
bool get_cube(ast_translation& g2l, unsigned id, expr_ref_vector& cube, bool is_first_run, node_lease& lease); bool get_cube(ast_translation& g2l, unsigned id, expr_ref_vector& cube, bool is_first_run, node_lease& lease);
void backtrack(ast_translation& l2g, unsigned worker_id, expr_ref_vector const& core, node_lease const& lease); void backtrack(ast_translation& l2g, unsigned worker_id, expr_ref_vector const& core, node_lease& lease);
void enqueue_core_minimization(ast_translation& l2g, node* source, expr_ref_vector const& core); void enqueue_core_minimization(ast_translation& l2g, node* source, expr_ref_vector const& core);
bool wait_for_core_min_job(ast_translation& g2l, node*& source, bool wait_for_core_min_job(ast_translation& g2l, node*& source,
expr_ref_vector& core, reslimit& lim); expr_ref_vector& core, reslimit& lim);
void publish_minimized_core(ast_translation& l2g, expr_ref_vector const& asms, node* source, void publish_minimized_core(ast_translation& l2g, expr_ref_vector const& asms, node* source,
unsigned original_core_size, expr_ref_vector const& minimized_core); unsigned original_core_size, expr_ref_vector const& minimized_core);
void try_split(ast_translation& l2g, unsigned worker_id, node_lease const& lease, expr* atom, unsigned effort); void try_split(ast_translation& l2g, unsigned worker_id, node_lease& lease, expr* atom, unsigned effort);
void release_lease(unsigned worker_id, node_lease const& lease); bool checkpoint_worker(unsigned worker_id, node_lease& lease, bool& lease_canceled);
bool lease_canceled(node_lease const& lease); bool lease_canceled(node_lease const& lease);
void collect_clause(ast_translation& l2g, unsigned source_worker_id, expr* clause); void collect_clause(ast_translation& l2g, unsigned source_worker_id, expr* clause);
@ -426,4 +428,4 @@ namespace smt {
lbool operator()(expr_ref_vector const& asms); lbool operator()(expr_ref_vector const& asms);
}; };
} }