-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
tokio: distinguish LocalSet::enter() with being polled #6016
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, this change looks good to me --- thanks for adding a test reproducing the bug!
I had some minor suggestions for potentially simplifying some aspects of the implementation a bit. Let me know what you think!
tokio/src/task/local.rs
Outdated
let res = CURRENT.try_with(|LocalData { ctx, entered, .. }| { | ||
struct Reset<'a> { | ||
ctx_ref: &'a RcCell<Context>, | ||
val: Option<Rc<Context>>, | ||
entered_ref: &'a Cell<bool>, | ||
ctx_val: Option<Rc<Context>>, | ||
entered_val: bool, | ||
} | ||
impl<'a> Drop for Reset<'a> { | ||
fn drop(&mut self) { | ||
self.ctx_ref.replace(self.val.take()); | ||
self.ctx_ref.set(self.ctx_val.take()); | ||
self.entered_ref.set(self.entered_val) | ||
} | ||
} | ||
let old = ctx.replace(Some(self.context.clone())); | ||
let ctx_old = ctx.replace(Some(self.context.clone())); | ||
let entered_old = entered.replace(false); | ||
|
||
let _reset = Reset { | ||
ctx_ref: ctx, | ||
val: old, | ||
entered_ref: entered, | ||
ctx_val: ctx_old, | ||
entered_val: entered_old, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
take it or leave it: the Reset
code got substantially more complex in this change, and it's used in two different places. now that it's no longer fairly trivial, what do you think about moving this out of the with
and with_if_possible
functions, so that we can use one implementation in both places. Something like:
impl LocalData {
fn enter(&self, context: Rc<Context>) -> Reset<'_> {
let ctx_val = self.ctx.replace(Some(context));
let entered_val = self.entered.replace(false);
Reset {
ctx_ref: self.ctx,
entered_ref: self.entered,
ctx_val: ctx_old,
entered_val: entered_old,
}
}
}
struct Reset<'a> {
ctx_ref: &'a RcCell<Context>,
entered_ref: &'a Cell<bool>,
ctx_val: Option<Rc<Context>>,
entered_val: bool,
}
impl<'a> Drop for Reset<'a> {
fn drop(&mut self) {
self.ctx_ref.set(self.ctx_val.take());
self.entered_ref.set(self.entered_val)
}
}
and then changing with
and with_if_possible
to just call
let _reset = local_data.enter(self.context.clone());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! I've moved out, and renamed it to more verbose name.
tokio/src/task/local.rs
Outdated
ctx_ref: &'a RcCell<Context>, | ||
val: Option<Rc<Context>>, | ||
entered_ref: &'a Cell<bool>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit, not a big deal: both ctx_ref
and entered_ref
reference fields of the LocalData
. we could make this struct a word smaller by changing these to a single &'a LocalData
reference, and just using that to access both ctx
and entered
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a good idea. I've changed it!
tokio/src/task/local.rs
Outdated
pub struct LocalEnterGuard(Option<Rc<Context>>); | ||
pub struct LocalEnterGuard { | ||
ctx: Option<Rc<Context>>, | ||
entered: bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we add a comment here describing how entered
is used and what it represents? thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added! Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry if this is overly nitpicky, but I think this comment could maybe be improved a bit --- it would be nice if it explained why we need to differentiate between enter
and polling the LocalSet
, and what behavior is controlled based on that. I don't think that's currelty all that clear from reading the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, sorry! I've corrected it!
@hawkw Thank you for providing an awesome review! |
@hawkw Hello! I am sorry but can we proceed this? |
Hi, sorry for not getting back to you sooner --- I'm taking another look now! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, this looks good to me. I think it would be nice to document the behavior with the entered flag and when the LocalSet
future is woken, but I feel good about the implementation and tests, and would be happy to merge this PR. Thank you!
tokio/src/task/local.rs
Outdated
pub struct LocalEnterGuard(Option<Rc<Context>>); | ||
pub struct LocalEnterGuard { | ||
ctx: Option<Rc<Context>>, | ||
entered: bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry if this is overly nitpicky, but I think this comment could maybe be improved a bit --- it would be nice if it explained why we need to differentiate between enter
and polling the LocalSet
, and what behavior is controlled based on that. I don't think that's currelty all that clear from reading the code.
Co-authored-by: Eliza Weisman <[email protected]>
@hawkw I am really sorry for delayed works. Your kind correction helped me a lot! Thank you very much! |
tokio/src/task/local.rs
Outdated
/// Should be called except when we call `LocalSet::enter`. | ||
/// Especially when we poll a LocalSet. | ||
#[must_use = "dropping this guard will reset the entered state"] | ||
fn enter(&self, ctx: Rc<Context>) -> LocalDataEnterGuard<'_> { | ||
let ctx = self.ctx.replace(Some(ctx)); | ||
let entered = self.entered.replace(false); | ||
LocalDataEnterGuard { | ||
local_data_ref: self, | ||
ctx, | ||
entered, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was reading over this code, and I'm confused about this. Why does calling enter
set entered
to false
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's because of the name of the user-facing level functions. So this LocalData::enter
should be called when we poll, and the value entered
should be set only if user called the LocalSer::enter
, which is public.
I agree that it's confused, but could not find suitable naming..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we rename the field to something like wake_on_spawn_local
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @Darksonn, the entered
field could be renamed to something clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you very much! I've renamed it!
But... wake_on_spawn_local
vs wake_on_schedule
.. which would be better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wake_on_schedule
also makes sense to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! I've renamed it to wake_on_schdule
!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks.
Motivation
This fixes: #5020
Solution
If we enter a
LocalSet
, then theLocalEnterGuard
setsCURRENT
with it. But theCURRENT
is being used with both of these two situations:But as we know,
LocalEnterGuard
should be related only with (1). When we schedule it, we should check if it is being polled, or just being entered. So I added a boolean variable to distinguish these situations.