From a8ad1a931416bac9f60df88c108547fc5483ab5f Mon Sep 17 00:00:00 2001 From: akifcorduk Date: Thu, 26 Feb 2026 23:51:37 -0800 Subject: [PATCH 1/2] fix race condition in add_external_solutions_to_population --- cpp/src/mip_heuristics/diversity/population.cu | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index c2138c91cd..601fe232f0 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -179,10 +179,17 @@ void population_t::add_external_solution(const std::vector& solut template void population_t::add_external_solutions_to_population() { - // early exit to avoid taking the population lock - if (!solutions_in_external_queue_.load()) { return; } - - auto new_sol_vector = get_external_solutions(); + std::vector> new_sol_vector; + { + // do the mutex here, otherwise it is a race condition. + // preemption will be set right after the solutions_in_external_queue_ is set in BB + // but if we don't acquire the mutex here, the writes to solutions_in_external_queue_ + // is not visible. Early return then finishes the heuristics and returns no solution. + std::lock_guard lock(solution_mutex); + // early exit to avoid the function(even though it will do nothing) + if (!solutions_in_external_queue_.load()) { return; } + new_sol_vector = get_external_solutions(); + } add_solutions_from_vec(std::move(new_sol_vector)); } @@ -198,7 +205,6 @@ void population_t::preempt_heuristic_solver() template std::vector> population_t::get_external_solutions() { - std::lock_guard lock(solution_mutex); std::vector> return_vector; i_t counter = 0; f_t new_best_feasible_objective = best_feasible_objective; From df78b7146c81326c66fef00478c4b99c1e8414f2 Mon Sep 17 00:00:00 2001 From: akifcorduk Date: Fri, 27 Feb 2026 02:48:27 -0800 Subject: [PATCH 2/2] revert mutex and only remove the early return check --- cpp/src/mip_heuristics/diversity/population.cu | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index 601fe232f0..bca87223d9 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -179,17 +179,8 @@ void population_t::add_external_solution(const std::vector& solut template void population_t::add_external_solutions_to_population() { - std::vector> new_sol_vector; - { - // do the mutex here, otherwise it is a race condition. - // preemption will be set right after the solutions_in_external_queue_ is set in BB - // but if we don't acquire the mutex here, the writes to solutions_in_external_queue_ - // is not visible. Early return then finishes the heuristics and returns no solution. - std::lock_guard lock(solution_mutex); - // early exit to avoid the function(even though it will do nothing) - if (!solutions_in_external_queue_.load()) { return; } - new_sol_vector = get_external_solutions(); - } + // don't do early exit checks here. mutex needs to be acquired to prevent race conditions + auto new_sol_vector = get_external_solutions(); add_solutions_from_vec(std::move(new_sol_vector)); } @@ -205,6 +196,7 @@ void population_t::preempt_heuristic_solver() template std::vector> population_t::get_external_solutions() { + std::lock_guard lock(solution_mutex); std::vector> return_vector; i_t counter = 0; f_t new_best_feasible_objective = best_feasible_objective;