Replies: 2 comments
-
There are a few different ways to handle the problem of holding dynamically borrowed state (RwLock and refcell) over await points:
let state = use_ref(cx, || String::new());
// clone whatever state you need for async out of the lock
let mut current_state = state.read().clone();
cx.spawn(async move {
// Do some async work
sleep(100).await;
current_state += "hello world";
// And then modify the original state synchronously
*state.write() = current_state;
});
|
Beta Was this translation helpful? Give feedback.
-
The git version of dioxus uses signals for local, context, and global state management. Signals can be optionally Send + Sync: RefCell and RwLock work very similarly. RwLock is just a send version of RefCell. Both can have issues with race conditions in async tasks because they both perform borrow checking at runtime |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
...that's when RwLock causes BorrowMutError in
wasm-bindgen-futures
.Writeup here to save time for anyone else who sees this. I did not find anything similar in github or discord.
Big Picture
I was looking for a way to:
web
targetIn summary, I tried:
use_shared_state
-> saw some BorrowMutErrors, even when usingwith
andwith_mut
(although, in retrospect, those also could be explained by holding the RefCell across theawait
)std::sync::RwLock
in place ofRefCell
(described below)parking_lot::RwLock
instead (may be incompatible with dioxus? because of necessary wasm target features)tokio
's asyncRwLock
but since most uses of it are in synchronous renders, I'd rather avoid it if possible)This feels like it will be a common pattern: an app wrapping a library which both makes network calls and holds local state. The docs examples show how to make async calls, but those don't contend for a resource.
The error
This may be a problem in
wasm-bindgen-futures
, but my assumption is that the problem was how I used it. Hence a discussion in this repo rather than an issue in this repo or that one.web
target, running in Chromestd::sync::RwLock
use_shared_state_provider
touse_shared_rw_state_provider
specifically to avoid the race conditions causingBorrowMutError
and get away fromRefCell
and its runtime panicsUnfortunately, even with
RwLock
around state instead ofRefCell
, I saw this:Stack Trace
This occurred, reliably, when I clicked a button spawning a future which used a write lock on that shared state. It acquired the lock and made some progress within the future until a different component attempts to read the lock and then panics. I would expect that other component to just block on rendering until the write is complete. Turns out, though, that there were
await
s hidden within theasync
call I was makingThe RefCell is not within
dioxus
but appears to be this one, panicking hereI know that there are caveats to using
std::sync::Mutex
in an async context to include panics. I thought I had been careful not to hold the lock across anawait
point. In any case this was not the behavior that I had expected (re-borrowing of aRefCell
within a core library).Relevant issue: wasm-bindgen/wasm-bindgen#2562, where a maintainer suggests a problem with the runtime. I suppose that could be the cause here, although it seems less likely within Chrome.
`use_shared_rw_state` implementation
Modified from
use_shared_state
to mix inuse_rw
. I planned to contribute a PR with this, if it's not outmoded by 0.5 and if I can solve this particular issue. There's some dead code; intent was to keep the diff minimal.Q: Is this useful to contribute? Is there a benefit to the added reliability of a RwLock over a RefCell?
The Fix
I refactored the async logic into two parts:
Relevant Links
Beta Was this translation helpful? Give feedback.
All reactions