-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
Problem
When I create a sync store and write to it from another thread while writing/reading it from dioxus renders, I get a deadlock between the two threads.
Steps to reproduce the behavior:
fn app() -> Element {
let mut store: Store<HashMap<usize, String>, WriteSignal<HashMap<usize, String>, SyncStorage>>
= use_hook(move || Store::new_maybe_sync(HashMap::<usize, String>::default())).into();
use_hook({
let mut s = store.clone();
move || {
thread::spawn(move || {
let (mut n, mut rng) = (0usize, rand::thread_rng());
loop {
n = n.wrapping_add(1);
for i in 1..120 {
s.write().insert(i, n.to_string());
}
thread::sleep(Duration::from_millis(rng.gen_range(1..120)));
}
});
}
});
let items: Vec<_> = store.read().clone().into_iter().collect();
rsx! {
for (k, v) in items {
input {
key: "{k}",
value: "{v}",
oninput: move |e| { store.write().insert(k, e.value().into()); }
}
}
}
}Results in this log when I use the deadlock_detection feature from parking_lot:
deadlock from detect_deadlocks feature
Sometimes I have to resize the window or restart the app to make it occur.
Expected behavior
No deadlocking when writing/reading to SyncStorage stores from another thread and the Dioxus thread.
Environment:
- Dioxus version: 0.17.1
- Rust version: 1.92.0-nightly
- OS info: Arch Linux x86_64
- App platform: Desktop/Webview
Questionnaire
I am trying to figure out how to make SelectorScope on write return WriteMetadata that on drop will do the mark_dirty, but I'm a bit over my head with all the abstractions here diving in from scratch.
I believe that the issue is an AB BA locking problem where SelectorScope immediately does self.mark_dirty() on writes: https://github.com/DioxusLabs/dioxus/blob/main/packages/stores/src/scope.rs#L212 which I believe tries to lock subscribers to notify them, then tries to grab the write lock on the value, but when rendering the scope with a read on the signal - I believe that the dioxus thread is trying to grab a lock on the value, then trying to grab a lock on the subscribers.
This is in contrast to signals which return a guard which notifies subscribers when dropped: https://github.com/DioxusLabs/dioxus/blob/main/packages/signals/src/signal.rs#L528
Also, a side note, I'm not sure if the way I made the hook is correct - but it would be nice to have this in prelude:
pub fn use_sync_store<T, F>(make_default: F) -> Store<T, WriteSignal<T, SyncStorage>>
where
T: 'static + Send + Sync,
F: FnOnce() -> T + 'static,
{
use_hook(move || Store::new_maybe_sync(make_default())).into()
}