Fixed a possible deadlock in block_until_updated

The animation doctest randomly failed on me this morning, and I isolated
it to the order of the two mutexes. There's no reason to hold the
deadlock mutex for more than the check for deadlock, so I switched which
mutex the condvar is synchronizing using to ensure that another thread
couldn't cause a deadlock by dropping a DynamicMutexGuard while the
block_until_updated thread is performing its state check.
This commit is contained in:
Jonathan Johnson 2023-12-31 10:21:45 -08:00
parent be483a92bd
commit e4dfd9320d
No known key found for this signature in database
GPG key ID: A66D6A34D6620579

View file

@ -1594,16 +1594,18 @@ impl<T> DynamicReader<T> {
/// This function panics if this value is already locked by the current
/// thread.
pub fn block_until_updated(&mut self) -> bool {
let mut deadlock_state = self.source.during_callback_state.lock().ignore_poison();
assert!(
deadlock_state
self.source
.during_callback_state
.lock()
.ignore_poison()
.as_ref()
.map_or(true, |state| state.locked_thread
!= std::thread::current().id()),
"deadlocked"
);
let mut state = self.source.state.lock().ignore_poison();
loop {
let state = self.source.state.lock().ignore_poison();
if state.wrapped.generation != self.read_generation {
return true;
} else if state.readers == Arc::strong_count(&self.source)
@ -1611,10 +1613,9 @@ impl<T> DynamicReader<T> {
{
return false;
}
drop(state);
// Wait for a notification of a change, which is synch
deadlock_state = self.source.sync.wait(deadlock_state).ignore_poison();
state = self.source.sync.wait(state).ignore_poison();
}
}