←back to thread

517 points bkolobara | 3 comments | | HN request time: 0.001s | source
Show context
Spivak ◴[] No.45041771[source]
How do you encode the locking issue in the type system, it seems magical? Can you just never hold any locks when calling await, is it smart enough to know that this scheduler might move work between threads?
replies(5): >>45041806 #>>45041833 #>>45041852 #>>45041891 #>>45041898 #
vlovich123 ◴[] No.45041833[source]
Presumably the author is using tokio which requires the future constructed (e.g the async function) to be Send (either because of the rules of Rust or annotated as Send) since tokio is a work-stealing runtime and any thread might end up executing a given future (or even start executing and then during a pause move it for completion on another thread). std::sync::MutexGuard intentionally isn't annotated with Send because there are platforms that require the acquiring thread be the one to unlock the mutex.

One caveat though - using a normal std Mutex within an async environment is an antipattern and should not be done - you can cause all sorts of issues & I believe even deadlock your entire code. You should be using tokio sync primitives (e.g. tokio Mutex) which can yield to the reactor when it needs to block. Otherwise the thread that's running the future blocks forever waiting for that mutex and that reactor never does anything else which isn't how tokio is designed).

So the compiler is warning about 1 problem, but you also have to know to be careful to know not to call blocking functions in an async function.

replies(6): >>45041892 #>>45041938 #>>45041964 #>>45042014 #>>45042145 #>>45042479 #
maplant ◴[] No.45041892[source]
> using a normal std Mutex within an async environment is an antipattern and should not be done

This is simply not true, and the tokio documentation says as much:

"Contrary to popular belief, it is ok and often preferred to use the ordinary Mutex from the standard library in asynchronous code."

https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html#wh...

replies(2): >>45042193 #>>45044049 #
vlovich123 ◴[] No.45044049[source]
I think that’s a dangerous recommendation and ignores the consequence of doing this within a single threaded reactor (your app will hang permanently if the lock is contended) or assumes you won’t have N threads trying to acquire this lock where N is the size of your thread pool which is entirely possible on a heavily accessed server if the core state that needs to be accessed by every request is locked with said mutex.
replies(1): >>45044232 #
1. maplant ◴[] No.45044232[source]
If you're using a single threaded reactor you don't need a mutex at all; a Rc<RefCell<_>> will do just fine. And if you want other tasks to yield until you're done holding the borrow, the solution is simple: don't await until you're done. https://docs.rs/tokio/latest/tokio/task/struct.LocalSet.html...

there are absolutely situations where tokio's mutex and rwlock are useful, but the vast majority of the time you shouldn't need them

replies(1): >>45045733 #
2. vlovich123 ◴[] No.45045733[source]
As you know Rc<RefCell> won't work for a data structure that's being accessed by other threads (e.g. spawn_blocking or explicitly accessed threads). Just because you are using the single-threaded reactor does not mean you don't have other threads to access the data.
replies(1): >>45045859 #
3. maplant ◴[] No.45045859[source]
It seems like you've found a very specific situation where tokio's Mutex is helpful but it is simply not common to be structuring an async rust application with a single threaded reactor and copious uses of spawn blocking. The advice to go for the std Mutex first is generally good