Notify the closer while still holding the gate
A call scope used to drop the lock after decrementing the in-flight count and notify afterwards. That left a window: the closer's predicate is already true once the count reaches zero, so a scope preempted between the unlock and the notify could come back to find the runtime torn down and signal a condition variable that no longer exists. ThreadSanitizer reports it as a broadcast racing the destroy. The decrement and the notify now happen under one lock guard. The closer cannot return from its wait until the scope releases the gate, so the condition variable is alive for as long as anything can signal it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
27ae3cb292
commit
a4753f4e9f
1 changed files with 8 additions and 4 deletions
|
|
@ -190,12 +190,16 @@ public:
|
|||
++runtime_->active_calls;
|
||||
entered_ = true;
|
||||
}
|
||||
// The notify stays under `gate`, and that is the whole point of it. Released
|
||||
// first, the closer's predicate is already true, so it can return from its
|
||||
// wait, run the teardown and destroy the runtime — condition variable
|
||||
// included — before this thread reaches the notify. Holding the lock across
|
||||
// both means the closer cannot leave `signal.wait` until this scope has let
|
||||
// go, so the condition variable is still alive when it is signalled.
|
||||
~CallScope() {
|
||||
if (!entered_) return;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(runtime_->gate);
|
||||
--runtime_->active_calls;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(runtime_->gate);
|
||||
--runtime_->active_calls;
|
||||
runtime_->signal.notify_all();
|
||||
}
|
||||
CallScope(const CallScope&) = delete;
|
||||
|
|
|
|||
Loading…
Reference in a new issue