-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
RFC: add futures and task system to libcore #2418
Conversation
Summary of differences from the original RFC:
|
Firstly, is Secondly, why are As far as raw implementations of the Basically, instead of the executor making a trait object out of https://play.rust-lang.org/?gist=079aa77b8679fcf80fd9d56cf7fd00cb&version=stable Thinking on this, there could be some situations - Sorry for the wall of text, but what are your thoughts? At the moment I can't see any real disadvantage to doing this*, but there could be some big issue that I missed entirely, so I look forward to hearing other people's opinions on this. *my playground doesn't use |
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.
As mentioned in the previous RFC, this is roughly in line with what I had hoped the end result would be 👍. I included a couple of nits / questions inline.
/// Any task executor must provide a way of signaling that a task it owns | ||
/// is ready to be `poll`ed again. Executors do so by providing a wakeup handle | ||
/// type that implements this trait. | ||
pub trait Wake: Send + Sync { |
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.
Why does this trait need to be moved into core
? If UnsafeWake
exists in core, then would it not be possible to leave this trait in a crate?
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, this could live out of tree to start with. To be clear, though, I do expect more of the futures crate to move into core over time.
/// Provides the reason that an executor was unable to spawn. | ||
pub struct SpawnErrorKind { .. } | ||
|
||
impl SpawnErrorKind { |
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.
Tokio currently requires an "at capacity" error variant.
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.
👍
/// spawn failures. | ||
/// | ||
/// NB: this will remain unstable until the final `Executor` trait is ready. | ||
pub fn executor(&mut self) -> &mut BoxExecutor; |
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.
BoxExecutor
is not defined anywhere. Is it possible to implement FuturesUnordered
with this current definition?
Also, given the known issues* with this strategy, would it be wiser to punt on executor being tied to context?
* spawning in drop and spawning from a function.
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 should be Executor
.
We can always delay on stabilizing this portion of the API if we are feeling uneasy. Remember, everything that goes into std goes on the nightly channel indefinitely; there's a separate process for actually committing it to stable.
A little downside of fn main() {
let async = print_async(); // No no
println!("Hello from main");
futures::block_on(async);
} Edit: In my PR to the companion RFC, I've just called it |
Since the variable cannot be called "async", it could be consistently called "op" in the documentation. A consistent name would be good. "op" would be wonderfully short let op = print_async(); // Can't call it "async"!
futures::block_on(op); Edit: ° Example: In JavaScript, when I create a Alternative names for
I think the similarity between the |
I am not bothered by the naming issue. Normally when I create a future in JS I either await it on the spot (no need for a binding) or gather multiple different things to await them at once later in which case I give them "business" names like customerPromise, basketPromise, etc.. I have never been tempted to name anything "async" :-) |
@MajorBreakfast In that case, why not just name the variable |
@rushmorem Unfortunately that'd be misleading. The new RFC standardizes |
|
@tmccombs Exactly. "overall task". The meaning of "overall task" would be clearer if it consisted of child tasks. Currently we have to explain what an |
A task is more than simply an |
(It doesn't feel right to me to name a variable |
text/0000-async.md
Outdated
/// of data on a socket, then the task is recorded so that when data arrives, | ||
/// it is woken up (via `cx.waker()`). Once a task has been woken up, | ||
/// it should attempt to `poll` the computation again, which may or may not | ||
/// produce a final value at that time. |
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.
As I said in the last RFC, I think it's important to make it explicit that it's not an error to poll a future even before a registered 'interest' has caused a task wakeup (otherwise you can't be sure you can implement join/select).
@rpjohnst I've edited my post above to reflect this. My point stands, though: Having two terms for this makes it just more complicated than it needs to be. I mean, it's easier to understand if we don't have that. One less thing to learn and such @glaebhoerl Good point. " |
@MajorBreakfast I think that @rpjohnst's point is that there are two distinct concepts here: async values, and tasks. A task is something that is being actively run by an executor, whereas an async value is by itself inert. Tasks generally work with async values internally, and are responsible for driving them to completion. Understanding this distinction is critical to understanding Rust's unique approach to async programming, so I personally find having distinct terms for the concepts to be very helpful in teaching. Regarding the larger question: I think that |
Does this difference really merit its own terminology? I mean a "moving car" is still called a "car". No special term required to make the difference clear.
I'd say it depends. Sometimes it'd just be irrelevant or confusing. For instance in the example code for |
It's very similar to the distinction between "process" and "program." A task has extra data associated with it beyond the |
@rpjohnst Yeah sure. But the user doesn't need to care about such implementation details because they're invisible. As far as the user is concerned it's a "running task". (Or a "running program" in your example) |
@MajorBreakfast But that's just the thing: an |
@aturon Yes. it'd be just a " |
Currently for waking on the same thread I use For the multi-threaded waker I have to following design idea, but not yet implemented. Have a single struct, lets call this I think this wouldn't allocate (much), only the initial setup and maybe adding onto the channel, this would depend on the implementation. So I wouldn't be double-allocating in this design. (I know of the design flaw that It's unknown when to poll the |
A similar API to the one proposed by this RFC is currently implemented and available on nightly ( I'd like to propose that we postpone this RFC as-is until we've had a chance to test out and iterate the current implementation. Proposals for changes can be discussed via issues on the futures-rs Github and on the wg-net-async Discord channel. Once the system has stopped undergoing significant churn or new discussion around the APIs that would be stabilized in |
@cramertj I've been wondering what the difference is between an "accepted RFC that has an unstable impl in std" and a "postponed/not-accepted RFC that has an unstable impl in std". They seem functionally the same... |
There are definitely similarities, but the key difference is that we won't stabilize a feature without first accepting an RFC followed by FCP, then having a separate stabilization proposal for the feature followed by FCP. Normally we don't allow implementation of features that haven't been through the RFC/eRFC process, but in this case the work in std was necessary in order to implement and experiment with RFC 2394 (async/await). The key takeaway is that nothing here can be proposed for stabilization until an RFC has been merged. |
This would've been the perfect application of an eRFC in my opinion. Ideally the general outline of what was desired would've been proposed here and implementation experiments begun once it was merged. That way there's a clear process of:
|
@jimmycuadra I agree! I think that would've been a good way to do it. As the situation stands, I think the async/await RFC is roughly acting as our eRFC here in that it explains in detail what syntax will be supported, but not what the actual libraries or implementation will look like, so that aspect is still undergoing experimentation, and a new RFC will be opened once the API has consensus and is changing infrequently. |
I agree with you both:
|
@rfcbot fcp postpone Formally registering @cramertj's postponement above! |
Team member @alexcrichton has proposed to postpone this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
If the RFC has been postponed, why is there a final comment period? |
FCP is part of the normal process. The RFC is not actually postponed until FCP finishes. |
This gives anyone who objects to the proposal of postponement to make their case if something was missed, just like any other decision.
… On Sep 3, 2018, at 1:21 PM, Carl Lerche ***@***.***> wrote:
If the RFC has been postponed, why is there a final comment period?
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.
|
Is there going to be quick-n-dirty eRFC for justifying the experimentation in compiler/stdlib, or is that too much bureaucracy? (Only the proper RFC once we are ready for that?) |
@golddranks The RFC justifying the current experimentation is the async/await RFC. |
The final comment period, with a disposition to postpone, as per the review above, is now complete. By the power vested in me by Rust, I hereby postpone this RFC. |
Note: this is a heavily-revised, more conservative version of #2395
This RFC provides the library component for the first-class
async
/await
syntax proposed in a companion RFC. It is intentionally minimal, including the smallest set of mechanisms needed to support async/await with borrowing and interoperation with the futures crate. Those mechanisms are:libcore
Future
trait, which integrates thePinMut
APIs with the task system to provide futures (i.e. asynchronous values).Rendered