-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Improve docs for Waker::noop and LocalWaker::noop #128064
Conversation
In 6f8a944, titled Change return type of unstable `Waker::noop()` from `Waker` to `&Waker`. the summary line for Waker was changed: - /// Creates a new `Waker` that does nothing when `wake` is called. + /// Returns a reference to a `Waker` that does nothing when used. and the sentence about clone was added. LocalWaker's docs were not changed, even though the types were, but there is no explanation for why not. It seems like it was simply a slip induced by the clone-and-hack.
I am not too familiar with these areas of async, so probably best to let somebody else take a look. r? libs |
// Note! Much of the documentation for this method is duplicated | ||
// in the docs for `Waker::noop`. | ||
// If you edit it, consider editing the other copy too. | ||
// |
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.
This is pervasively true, I don't feel it's worth a comment? If you want to fix it, then I would prefer a macro that expands into a few doc attrs, so it's actually fixed.
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 that it's pervasively true. I have seen similar situations in many other places in the rust-lang/rust codebase, often with unintended divergence as we see here. I'm afraid I don't have the tuits to engage in a campaign of docs deduplication. I find it difficult to predict the style and taste preferences in the Rust Project. I feel someone closer to the core of the project should demonstrate how this ought to be done, and probably the libs team should (at least implicitly, by lazy consensus) approve the idiom. (The docs are not precisely identical, so the macro wouldn't be entirely straightforward.)
IMO the comment is very helpful, even with the remaining duplication, because it tells you precisely where you need to edit the clone-and-hack. If that comment had been in place, perhaps 6f8a944 wouldn't have contained the slip. It's fairly lightweight answer to the problem.
If you don't agree, or want to insist that I do substantially more work instead of this stopgap, I will remove these hunks from this MR branch.
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.
Hmm... I see. I tried to do the refactoring I originally suggested and found it more of a pain in the ass than I expected. As-is, this seems adequate. It makes me vaguely annoyed, which made me initially reluctant about it, but on reflection it is an annoyance with the preexisting state of things.
library/core/src/task/wake.rs
Outdated
/// in production code, | ||
/// from within a non-`async`, non-`poll`, function. | ||
/// | ||
/// Using a no-op waker for that purpose would cause wakeups to be lost: |
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.
This text is, in my opinion, either incorrect or not specific enough about the situation it is warning against.
Waker::noop()
is a perfectly good way to implement executing a task in the case where it is an acceptable performance tradeoff, or suitable for the application, to poll the task every time through some sort of loop (or to poll the task exactly once, as in now_or_never()
). No wakeups are “lost” because the task is unconditionally awake. This is, to my mind, the main use case of Waker::noop()
: to implement async execution trivially when it isn't worth the complexity of implementing it better.
It can only cause lost wakeups if you mix polling the task this way with polling the task another way (which is not trivial to do, since it involves shared ownership of the task, or using noop()
within a future combinator). I get the impression from #98286 (comment) that your use case is perhaps doing something like that, but the text in this change is talking about non-async functions only, not mixing polling from a non-async function and polling from an executor.
Could you rephrase this to be more specific about the situation you are warning against?
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. I'll see what I can do.
I came here to do this because in conversations with colleagues, some of whom are fairly new to Rust, especially async Rust, hadn't understood the implications.
I can see that there can be situations where wakers aren't important, particularly in an executor. I think using a noop waker in a future implementation, without juggling caller(s)' waker(s), is probably a bug. Am I wrong about that?
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 think using a noop waker in a future implementation, without juggling caller(s)' waker(s), is probably a bug. Am I wrong about that?
Yes, if you are writing a future combinator (that is, impl Future for...
that delegates to other Future
s), then you should usually either pass on the provided context's waker, or create your own delegating non-noop wakers (if you want to track which of several child futures woke).
But the text doesn't say “when you are implementing Future::poll
”. It says “from within a non-async
, non-poll
, function”, which rules out that case. So, I am confused.
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.
since it involves shared ownership of the task, or using
noop()
within a future combinator
Right. That is the scenario I'm trying to warn about, particularly using noop within a futures combinator.
I have tried to improve the text to be more specific. What do you think now? (I've avoided the term "future combinator" since it seems rather abstruse, and the reader who is about to make the mistake I'm warning about may not realise that that's what they're writing.)
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.
What do you think now?
That’s specific enough that it no longer has the “vague scary warning” problem, yes. Further comments:
-
I don't think the words “runtime-agnostic” are adding anything here; if the future has knowledge about the runtime then that potentially overrides everything about the conventional
Future
/Waker
protocol, so it doesn't need to be called out here. -
It’s still possible to use a noop waker correctly in this scenario. In particular, the usual time a wrapper future is likely to be without
Context
is at construction, and futures are always polled once to start. So, ifFoo
wrapsBar
and pollsBar
with a noop waker whenFoo
is constructed, then that's fine as long asFoo
also pollsBar
with the propagated waker whenFoo
is polled for the first time. I don't have an idea for how to describe this sort of scenario concisely, though.
I've avoided the term "future combinator" since it seems rather abstruse
Yes, and pedantically, you can have a future that wraps other futures without it being purely a combinator, so this is more correct.
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 understand what, @ijackson, you're trying to do here, and I appreciate the intent. And so it kind of saddens me to be so negative on this change, but...
When implementing a runtime-agnostic future which wraps other future(s), do not use this function as a way to be able to call
poll
methods when you don't have your caller'sContext
available. Using a no-op waker for that purpose would cause wakeups to be lost: the inner future would store only the no-op waker, replacing the proper waker for the waiting task.
In addition to the good points that @kpreid raised, when I read this, I think to myself, "anyone who is able to parse and interpret those two sentences already knows the limitations without needing to be told."
That's what I think the nature of the problem is here. It seems harder to write an accessible and correct warning than it is to just write an accessible statement of what this does do.
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.
Actually, how about something like this? Expanding the current paragraph that says “This is mostly useful for writing tests…”, we could write:
This is mostly useful for writing tests that need a
Context
to poll some futures, but are not expecting those futures to wake the waker or do not need to do anything specific if it happens. More generally, usingWaker::noop()
to poll a future discards the notification of when the future should be polled again, and therefore, it should only be used when such a notification will not be needed to make progress.
I think this would fairly well get across some useful points:
- What you are doing here is discarding a message. (People understand that this can mean that progress stops because the message is not delivered where it is needed.)
- It is reasonable to discard a message specifically when nothing needs to be done about it.
without getting further into the details of precisely classifying what situations this applies to.
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 like your suggested text. Thank you! I will adopt it into the MR.
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.
While I understand the motivation here, I don't think these changes are an improvement.
We should carefully describe what the no-op waker does. We don't need such a strong warning here about what it doesn't do.
Maybe there is a way to say what's trying to be said here, but as it's written, it just feels "off" to me, and I'm not sure it's an important enough point to try to rework this.
I've adopted @kpreid's suggestion, with some light edits. I hope folks like this 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.
Yes, I think this is much better as it informs more "positively" than originally framed, answering "What is this for?" It's when you don't care about the notification.
I'm good with this assuming @traviscross doesn't have any further comments, though I would appreciate it if you rebased the history to have less of the back-and-forth before we r+ it.
As @workingjubilee said, this is a better framing. Thanks for the changes. |
// Note! Much of the documentation for this method is duplicated | ||
// in the docs for `LocalWaker::noop`. | ||
// If you edit it, consider editing the other copy too. | ||
// | ||
/// This is mostly useful for writing tests that need a [`Context`] to poll | ||
/// some futures, but are not expecting those futures to wake the waker or | ||
/// do not need to do anything specific if it happens. | ||
/// |
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 think this would read slightly better as a single paragraph, since the "More generally…" is directly referring to what was just introduced, whereas the paragraphs above and below this pair are much more unrelated.
/// |
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 think I prefer it in two paragraphs. The first paragraph is about tests; then the second paragraph is indeed more general.
I think if we join these together the order should be reversed, so that the general case, with the clear statement about discarded notifications, isn't buried in a paragraph which on first glance seems to be just about testing.
// Note! Much of the documentation for this method is duplicated | ||
// in the docs for `Waker::noop`. | ||
// If you edit it, consider editing the other copy too. | ||
// | ||
/// This is mostly useful for writing tests that need a [`Context`] to poll | ||
/// some futures, but are not expecting those futures to wake the waker or | ||
/// do not need to do anything specific if it happens. | ||
/// |
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.
(Matching above suggestion.)
/// |
Based on a suggestion from @kpreid, with some further editing.
bd8d151
to
9a95573
Compare
Right. Done. |
Thanks! @bors r+ rollup |
…jubilee Improve docs for Waker::noop and LocalWaker::noop * Add a warning about a likely misuse. (See my commit message for longer rationale.) * Apply some probably-accidentally-omitted changes to `LocalWaker`'s docs * Add a comment about the clone-and-hack of the docs I have used [semantic linefeeds](https://rhodesmill.org/brandon/2012/one-sentence-per-line/) for the docs formatting.
…jubilee Improve docs for Waker::noop and LocalWaker::noop * Add a warning about a likely misuse. (See my commit message for longer rationale.) * Apply some probably-accidentally-omitted changes to `LocalWaker`'s docs * Add a comment about the clone-and-hack of the docs I have used [semantic linefeeds](https://rhodesmill.org/brandon/2012/one-sentence-per-line/) for the docs formatting.
…jubilee Improve docs for Waker::noop and LocalWaker::noop * Add a warning about a likely misuse. (See my commit message for longer rationale.) * Apply some probably-accidentally-omitted changes to `LocalWaker`'s docs * Add a comment about the clone-and-hack of the docs I have used [semantic linefeeds](https://rhodesmill.org/brandon/2012/one-sentence-per-line/) for the docs formatting.
…kingjubilee Rollup of 10 pull requests Successful merges: - rust-lang#128064 (Improve docs for Waker::noop and LocalWaker::noop) - rust-lang#128922 (rust-analyzer: use in-tree `pattern_analysis` crate) - rust-lang#128965 (Remove `print::Pat` from the printing of `WitnessPat`) - rust-lang#128977 (Only try to modify file times of a writable file on Windows) - rust-lang#129018 (Migrate `rlib-format-packed-bundled-libs` and `native-link-modifier-bundle` `run-make` tests to rmake) - rust-lang#129037 (Port `run-make/libtest-json` and `run-make/libtest-junit` to rmake) - rust-lang#129078 (`ParamEnvAnd::fully_perform`: we have an `ocx`, use it) - rust-lang#129110 (Add a comment explaining the return type of `Ty::kind`.) - rust-lang#129111 (Port the `sysroot-crates-are-unstable` Python script to rmake) - rust-lang#129135 (crashes: more tests) r? `@ghost` `@rustbot` modify labels: rollup
…kingjubilee Rollup of 9 pull requests Successful merges: - rust-lang#128064 (Improve docs for Waker::noop and LocalWaker::noop) - rust-lang#128922 (rust-analyzer: use in-tree `pattern_analysis` crate) - rust-lang#128965 (Remove `print::Pat` from the printing of `WitnessPat`) - rust-lang#129018 (Migrate `rlib-format-packed-bundled-libs` and `native-link-modifier-bundle` `run-make` tests to rmake) - rust-lang#129037 (Port `run-make/libtest-json` and `run-make/libtest-junit` to rmake) - rust-lang#129078 (`ParamEnvAnd::fully_perform`: we have an `ocx`, use it) - rust-lang#129110 (Add a comment explaining the return type of `Ty::kind`.) - rust-lang#129111 (Port the `sysroot-crates-are-unstable` Python script to rmake) - rust-lang#129135 (crashes: more tests) r? `@ghost` `@rustbot` modify labels: rollup
Rollup merge of rust-lang#128064 - ijackson:noop-waker-doc, r=workingjubilee Improve docs for Waker::noop and LocalWaker::noop * Add a warning about a likely misuse. (See my commit message for longer rationale.) * Apply some probably-accidentally-omitted changes to `LocalWaker`'s docs * Add a comment about the clone-and-hack of the docs I have used [semantic linefeeds](https://rhodesmill.org/brandon/2012/one-sentence-per-line/) for the docs formatting.
LocalWaker
's docsI have used semantic linefeeds for the docs formatting.