-
Notifications
You must be signed in to change notification settings - Fork 650
park/unpark are tricky to use #136
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
Comments
Related: #131 (comment)
(though I'm a little unclear on "I'll arrange myself to get notified later") |
I haven't had time to digest everything, but in your example, it doesn't look like |
That's a fair point, but it sort of gives evidence to the thought that not all futures are equal, you need to know how they work instead of relying on the trait to sort it out. I'll be honest, I've no idea how you could use that method in the context of tokio given the need for a Core. Also, minor correction to my post: "(though from what I can gather, this can just be a no-op implementation)" - this isn't true, it actually needs to unpark the correct task. |
Thanks for the report! There's a few points I want to help clear up first, and I think that may help? The implementation of #77 has rationale for why it was implemented, but to reiterate:
In general it's not expected that you call With that in mind...
Ah this is actually the fun part! The implementation there does indeed register interest on underlying events as part of the calls to The contract of
I think this depends on your perspective and when you're mentioning For
I don't think though that anything is necessarily implied about when/where events have to happen. You don't, for example, have to spawn a thread if you return If a future has
I'd be very interested to drill more into this! It's very much the intention of this crate that it is easy to implement
As @carllerche mentioned the flaw here is that the event loop isn't running. The future will be completed on the event loop, owned by the current thread, so Here you'll want to do Does all that make sense! I'd love to get to the bottom of this and see what the key areas are that we need to improve, clearly something's tricky! |
First off, thanks for the comprehensive response. The proxy example is very sneaky, thanks for pointing that out :) You've also cleared up a significant misunderstanding of mine.
I think the documentation needs to be more forward about mentioning event loops/executors. These are crucial building blocks and I hadn't realised! In particular, you say "You simply, as a future, just need to ensure that a notification (unpark) is sent when the value is ready." - taking this sentence in isolation is very confusing ("how can I ensure a notification is sent? what if I don't own the calling code?") until you realise that there's an implicit expectation of having your own event loop/executor that you can stash wakeup conditions in to make sure they get checked every now and again. tokio is a great example - it provides futures than in theory magically register themselves for wakeup on calling poll...but actually there's a dependency on having the reactor core running. The documentation says the following about futures: "The poll method is not intended to be called in general, but rather is typically called in the context of a "task" which drives a future to completion." Actually, this seems like it needs to say: "It is not possible to call the "In general wait is very dangerous to call on arbitrary futures and needs to be a careful consideration to ensure that the future will always complete on another thread." - this probably needs calling out better as well. My misunderstandings aside, I had a think about "My key problem is that right now it's not easy to implement futures or streams on your own types (part of the problem being that callbacks are troublesome in rust)." and came to a realisation about why I'm finding it tricky - I'm trying to use futures at a lower level than they're intended to be used at. Specifically, I just want to be able to call poll on a future, and if it's not ready then I'll come back later. I'd also be fine with a requirement to wrap a bunch of futures in a task and call poll on that - the crucial aspect is that these futures are self-contained and can easily be plugged into an existing application that isn't built around tokio/futures-rs. Because the of current contract (a notification will be registered) the thing that's actually hard (contrary to me saying it's creating futures and stream is general) is creating a self-contained future that doesn't require additional machinery (like the tokio reactor core) to support notification. This is why the implementation of tokio-rs/tokio-core#24 will never return In truth, additional machinery isn't fundamentally a bad thing - having all tokio futures registering their interest via the core means you only need one call to epoll. But it does mean that the
This is true. The crucial question for me is how you implement this at the edges, i.e. where you initially generate a NotReady. That's why I talk about the machinery above - you need it to be present and running to make sure you can reliably schedule a notification.
I think this is covered above, but to state explicitly: my issue was that I didn't want to use the reactor core for the IO I was doing, but if I don't use it then I have to implement my own event loop or it becomes painful to handle notifications.
Sorry, I'm not clear what this sentence means. I understand the surrounding sentences, but this one has me confused! Again, thanks for the response - I've got a much better understanding of how things are meant to work together now. I'm not sure I 100% agree that notifications should be compulsory (it'd be nice to be able to opt-out), but I can see there would be complexity involved. Aside from documentation being more forward about both event loops and futures possibly implicitly depending on other things, I think I'd like to see better treatment for applications that only want part of the application to use tokio/futures. tokio-rs/tokio-core#24 is a step towards this, but it makes me a touch uncomfortable that there's no way to express "this will never return NotReady, it's just a way to turn the event loop". Potentially it's trying to use the wrong abstraction. I'll leave a couple of thoughts on there. |
Oh man, lots to digest here! About the documentation bits, I'd definitely love to improve wherever possible. One caveat though is that I don't think we should document It's also definitely our intention that futures can work with any form of backend, whether it be tokio-core or not. Additionally, it should slot in if possible to existing applications! Which is to say, friction here is definitely not intended :) Note that the "machinery" here is indeed all employed only at the edge nodes, it rarely happens in the Put another way, this "extra machinery" if you really don't want to use it should be optional. Yes if you don't use a tokio-core event loop you'll have to write your own, but it sounds like that's the use case you're tackling here?
Oh I only meant to say that most uses of futures will end up using a stock implementation of executors like |
At present the tokio reactor takes over an entire thread when you want to start it - there's no way to 'cooperatively' run the reactor alongside other things. Hence the I've got an application that runs code every 'tick', using epoll and the like from before futures existed, along with other async libraries that don't have any async notification method at all. Here's a simplified example for illustration purposes:
I'd like to convert part of this application ( However, I can't do this because tcp connection futures require the presence of the tokio reactor core machinery to wake up and continue executing when they're ready...but the tokio reactor core currently doesn't allow you to use it at all without taking over the whole thread. ssh2-rs is a possible useful example here. Sessions may be async, which means I can create async channels and use them in an async application. Unfortunately, even if I did all the work to ensure plumb the underlying tcp stream into the tokio reactor core, it wouldn't be ideal - libssh2 supports multiple channels interleaving, but the event would fire for any channel receiving data. Instead of using this somewhat broken abstraction, I'd prefer to just call the ssh2-rs methods myself and run a reactor core alongside for any 'normal' tokio-supported futures. |
Hm so this issue feels pretty far afield of why it was first opened at this point. It seems like your last comment boils down to "
I think it'd be worth though coming back to the original points. Is this issue still value? Should this stay open? |
You're right, I've meandered off topic. Coming back to park/unpark, probably the only thing that needs doing is "I think the documentation needs to be more forward about mentioning event loops/executors." Since I'm the one confused, I'll do this at some point, mentioning that "event loops are the expected way to register notifications" in a few places. I might also look at writing a tutorial on writing an event loop, because I haven't seen one and think it'd be very useful for people wanting to understand the internals. I don't mind if this issue is open or closed in the meantime. I'll respond to your two points in the PR since it's more directly related. |
Ok, awesome! I don't mind leaving this open in the meantime to track documentation. I'd love to have a tutorial on writing an event loop, it sounds like a great idea! |
After reading this I am really confused. So if I am not supposed to call poll directly on future how I am supposed to ignore futures that are not ready yet? To give background information: This all worked fine with the other library that I was using... So how should I be doing this? |
@WaDelma working at the boundaries of futures is unfortunately not the most ergonomic right now. If you're not running inside of another future or if you're responsible for running futures yourself it's somewhat tricky to ensure everything works out. The task that's iterating through all the chunks, is that not itself a future? One that's resolved once all the chunks are loaded? The essence of the solution will be to push the boundary of the future higher to the edges of the system, or if you're feeling intrepid you can dive into using |
Not really... When I iterate through all chunks I want to just render those that are loaded and ignore those that are still on the way. Rendering should never block. |
In that case I'd probably recommend a construction like:
You can then drop the future after that, it'll do as much work as it can internally and then if you're not interested in the rest it'll all get canceled. |
Well I don't want to cancel the loading either... I'll just go back to using the other library. |
I, too, am struggling to understand how I can use futures because of this issue. When I read that the future will "register interest of the current task in the value being produced" when My situation: I'm accepting new TCP connections in one future and create a wrapper struct to do some buffering on them. I push those wrappers though a channel to another future. This second future's If I return I have no idea how I'm supposed to call |
We now have extensive documentation at both https://tokio.rs/docs/going-deeper/futures-model/ and https://tokio.rs/docs/going-deeper/tasks/, so I'm going to close this. |
The link to |
Quote from #129 (comment), brought here since it's a bit of a tangent. Related to #77
wait_{future,stream}
onSpawn
(which will hang forever unless you've done it by the book)..poll()
so it can be a small cog in a larger machine - but futures may not work unless they're inTask
's so Ispawn
it, but now I need to callpoll_future
with anunpark
argument (though from what I can gather, this can just be a no-op implementation).NotReady
you're forced to separate out (or duplicate) this logic and either block on it in a thread or dispatch another async task to check for readiness (except the latter one won't work unless you're in an event loop and can grab a reference to it - if someone has calledSpawn::wait_*
you must spawn a thread I think? Which seems like a very difficult api to correctly behave under). Admittedly, this can be very efficient (e.g. a single async task to accumulate and check for readiness on all tcp sockets you have open), but it's also additional complexity you can't opt out of.I somewhat like the old way with
poll
andschedule
, except withschedule
returning ok, err or notimplemented.My key problem is that right now it's not easy to implement futures or streams on your own types (part of the problem being that callbacks are troublesome in rust). It's not an easy problem to solve, but I'm not convinced that making everyone have a go is a good idea. In fact, unless I'm gravely misunderstanding (never a remote possibility), even tokio-core doesn't get it right! In theory the following example should exit immediately after the first connection, in practice it just hangs forever. Strace shows it hung in futex (because the thread has parked without a wakeup being registered), whereas https://tokio-rs.github.io/tokio-core/tokio_core/index.html sits in epoll_wait.
The text was updated successfully, but these errors were encountered: