mirror of
https://github.com/danbulant/cushy
synced 2026-06-21 15:42:07 +00:00
Fixed change callback deadlock
Very hard to intentionally reproduce, thankfully the second time I saw it I was in the debugger and was able to reason about the code path that could have gotten in that particular state. Comment explains the actual situation.
This commit is contained in:
parent
8561b6cf5e
commit
e54bbd743d
3 changed files with 23 additions and 2 deletions
|
|
@ -87,6 +87,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- When a keyboard-activated widget activates another widget during its callback,
|
||||
the key-up event now sends the deactivate event to the finally-activated
|
||||
widget.
|
||||
- A rare deadlock occurring when multiple threads were racing to execute
|
||||
`Dynamic<T>` change callbacks has been fixed.
|
||||
|
||||
### Added
|
||||
|
||||
|
|
|
|||
21
src/value.rs
21
src/value.rs
|
|
@ -1227,8 +1227,27 @@ impl<T> Dynamic<T> {
|
|||
}
|
||||
|
||||
fn lock_inner<const READONLY: bool>(&self) -> DynamicGuard<'_, T, READONLY> {
|
||||
let 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
|
||||
// occuring on, both sets of invocations can end up waiting on each
|
||||
// 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 == ¤t_thread_id => panic!("deadlocked"),
|
||||
Some(_) => callbacks.sync.wait(&mut executing),
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
}
|
||||
DynamicGuard {
|
||||
guard: DynamicOrOwnedGuard::Dynamic(self.0.state().expect("deadlocked")),
|
||||
guard: DynamicOrOwnedGuard::Dynamic(guard),
|
||||
accessed_mut: false,
|
||||
prevent_notifications: false,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1819,7 +1819,7 @@ where
|
|||
this.synchronize_platform_window(&mut window);
|
||||
|
||||
// Perform an initial layout.
|
||||
let _result = this.prepare(window, graphics);
|
||||
this.prepare(window, graphics);
|
||||
|
||||
this
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue