Allowing change callbacks to recurse still

The last fix had a panic where a guard should have been allowed to be
created. Change callbacks detect when they are about to be fired from
themselves, and prevent the deadlock.
This commit is contained in:
Jonathan Johnson 2024-09-25 11:17:23 -07:00
parent e54bbd743d
commit 2045d5fb4a
No known key found for this signature in database
GPG key ID: A66D6A34D6620579

View file

@ -1227,7 +1227,7 @@ impl<T> Dynamic<T> {
}
fn lock_inner<const READONLY: bool>(&self) -> DynamicGuard<'_, T, READONLY> {
let guard = self.0.state().expect("deadlocked");
let mut guard = self.0.state().expect("deadlocked");
// Before allowing a lock, we need to ensure that the current change
// callbacks aren't executing. Otherwise, during drop of this guard, if
// we notify of changes from a second thread than one set is already
@ -1235,16 +1235,19 @@ impl<T> Dynamic<T> {
// other and deadlocking. By ensuring a single guard and change
// callbacks cycle can exist at any one time, we prevent this deadlock.
if !READONLY && guard.callbacks.currently_executing.lock().thread.is_some() {
let current_thread_id = std::thread::current().id();
let callbacks = guard.callbacks.clone();
let mut executing = callbacks.currently_executing.lock();
loop {
match &executing.thread {
Some(th) if th == &current_thread_id => panic!("deadlocked"),
Some(_) => callbacks.sync.wait(&mut executing),
None => break,
};
}
guard.unlocked(|| {
let current_thread_id = std::thread::current().id();
let mut executing = callbacks.currently_executing.lock();
loop {
match &executing.thread {
Some(th) if th == &current_thread_id => break,
None => break,
Some(_) => callbacks.sync.wait(&mut executing),
};
}
});
}
DynamicGuard {
guard: DynamicOrOwnedGuard::Dynamic(guard),