Skip to content
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

Merged
merged 3 commits into from
Aug 16, 2024

Conversation

ijackson
Copy link
Contributor

  • 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 for the docs formatting.

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.
@rustbot
Copy link
Collaborator

rustbot commented Jul 22, 2024

r? @tgross35

rustbot has assigned @tgross35.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jul 22, 2024
@tgross35
Copy link
Contributor

I am not too familiar with these areas of async, so probably best to let somebody else take a look.

r? libs

@rustbot rustbot assigned workingjubilee and unassigned tgross35 Jul 22, 2024
library/core/src/task/wake.rs Outdated Show resolved Hide resolved
Comment on lines +801 to +798
// 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.
//
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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 Show resolved Hide resolved
/// 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:
Copy link
Contributor

@kpreid kpreid Jul 22, 2024

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?

Copy link
Contributor Author

@ijackson ijackson Jul 23, 2024

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?

Copy link
Contributor

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 Futures), 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.

Copy link
Contributor Author

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.)

Copy link
Contributor

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, if Foo wraps Bar and polls Bar with a noop waker when Foo is constructed, then that's fine as long as Foo also polls Bar with the propagated waker when Foo 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.

Copy link
Contributor

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's Context 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.

Copy link
Contributor

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, using Waker::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.

Copy link
Contributor Author

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.

Copy link
Contributor

@traviscross traviscross left a 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.

@ijackson
Copy link
Contributor Author

ijackson commented Aug 5, 2024

I've adopted @kpreid's suggestion, with some light edits. I hope folks like this better.

Copy link
Member

@workingjubilee workingjubilee left a 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.

@traviscross
Copy link
Contributor

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.
///
Copy link
Contributor

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.

Suggested change
///

Copy link
Contributor Author

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.
///
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Matching above suggestion.)

Suggested change
///

Based on a suggestion from @kpreid, with some further editing.
@ijackson
Copy link
Contributor Author

I would appreciate it if you rebased the history to have less of the back-and-forth before we r+ it.

Right. Done.

@workingjubilee
Copy link
Member

Thanks!

@bors r+ rollup

@bors
Copy link
Contributor

bors commented Aug 15, 2024

📌 Commit 9a95573 has been approved by workingjubilee

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 15, 2024
workingjubilee added a commit to workingjubilee/rustc that referenced this pull request Aug 16, 2024
…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.
workingjubilee added a commit to workingjubilee/rustc that referenced this pull request Aug 16, 2024
…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.
workingjubilee added a commit to workingjubilee/rustc that referenced this pull request Aug 16, 2024
…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.
bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 16, 2024
…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
bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 16, 2024
…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
@bors bors merged commit f624f2d into rust-lang:master Aug 16, 2024
6 checks passed
@rustbot rustbot added this to the 1.82.0 milestone Aug 16, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Aug 16, 2024
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants