Skip to content

Commit d500e32

Browse files
Fix MPSC force-stop hangs after the consumer thread exits.
Clear consumer_thread_id when a pool worker leaves run() so ~Pool() does not wait on a dead consumer, and stop pools outside pools_mutex so a blocking MPSC drain cannot deadlock with get_pool() during live shutdown. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 1708045 commit d500e32

2 files changed

Lines changed: 15 additions & 3 deletions

File tree

src/threading/scheduler/Pool.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ namespace threading {
267267
}
268268
catch (const ShutdownThreadException&) {
269269
Pool::current_pool = nullptr;
270+
consumer_thread_id = std::thread::id{};
270271
return;
271272
}
272273
}

src/threading/scheduler/Scheduler.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,20 @@ namespace threading {
103103

104104
void Scheduler::stop(bool force) {
105105
running.store(false, std::memory_order_release);
106-
const std::lock_guard<std::mutex> lock(pools_mutex);
107-
for (const auto& pool : pools) {
108-
pool.second->stop(force ? Pool::StopType::FORCE : Pool::StopType::NORMAL);
106+
107+
// Copy pool pointers under the mutex, then stop outside it. Pool::stop(FORCE) on
108+
// single-consumer (MPSC) pools may block until that pool's worker drains the queue;
109+
// workers can call get_pool() during that drain, which needs pools_mutex.
110+
std::vector<std::shared_ptr<Pool>> pools_to_stop;
111+
{
112+
const std::lock_guard<std::mutex> lock(pools_mutex);
113+
pools_to_stop.reserve(pools.size());
114+
for (const auto& pool : pools) {
115+
pools_to_stop.push_back(pool.second);
116+
}
117+
}
118+
for (const auto& pool : pools_to_stop) {
119+
pool->stop(force ? Pool::StopType::FORCE : Pool::StopType::NORMAL);
109120
}
110121
}
111122

0 commit comments

Comments
 (0)