-
-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Don't use an asynchronous mutex #74
Comments
I guess I'm using the async mutex for the other reason, namely that I don't want bb8 to block the application task if the mutex is held by another task. Are you saying that's not a good reason to use an async mutex? Edit: also, thanks for reviewing the code at this level of detail! |
As long as the mutex is only ever held locked for very short amounts of time, the amount of blocking the mutex introduces is not an issue. The issue with blocking the thread is that the task doesn't yield back to the executor, thus not allowing other tasks to run. However, very short durations of blocking do not cause these issues. This is for the same reason that it is ok to perform small amounts of CPU-bound code inside an asynchronous task. Note that asynchronous mutexes have a synchronous mutex inside. I have written a bit about this here, and we also talk about it in the mini-redis example. Additionally, our new tutorial has a section on this. It is also worth mentioning that your destructor can deadlock in some edge cases, as Tokio's mutex hooks into Tokio's coop system. |
Thanks, that's helpful. I would argue in this case "Additionally, when you do want shared access to an IO resource, it is often better to spawn a task to manage the IO resource, and to use message passing to communicate with that task." is applicable (and I think this would likely make the implementation more clear). What do you think? |
That is certainly also a reasonable approach. The main disadvantage is that it requires spawning, tying you closely to the executor, but you are already doing that. In my opinion, both are reasonable approaches here. If you want to pursue this direction, you could quite reasonably make this part of the
I imagine your task could look something like this: let mut open_connections = /* some sort of container */;
loop {
tokio::select! {
msg = connection_request.recv() => {
if let Some(oneshot) = msg {
oneshot.send(open_connections.get_connection());
} else {
// the pool has been dropped
return;
}
},
msg = return_connection.recv() => {
// you will be keeping a sender alive in `open_connections`,
// so this can't fail
open_connections.put_back(msg.unwrap());
},
_ = reaping_interval.tick(), if !open_connections.is_empty() => {
open_connections.reap();
},
}
} Since |
Thanks! Do you have any intuition for which approach will result in lower latencies? |
Not really. |
The mutex around
internals
is never held locked across an.await
as far as I can see, so it should not be using an asynchronous mutex. Changing to a synchronous mutex would allow you to fix the following issues:Spin lock
https://github.com/khuey/bb8/blob/e2978dfb1ac86842901e344efcf198dc75fab488/bb8/src/lib.rs#L628-L632
Use of
block_on
in destructorhttps://github.com/khuey/bb8/blob/e2978dfb1ac86842901e344efcf198dc75fab488/bb8/src/lib.rs#L798-L809
The text was updated successfully, but these errors were encountered: