Comments (10)
I agree that these points are not explained very clearly. I'm going to try to rework the docs sooner than later...
Part of the problem is that the tokio reactor doesn't behave the same as something like node's event loop, so thinking about it as you are (i.e. "have a callback after some time / next tick / etc...) is a bit incorrect, so of course it's going to be confusing (not your fault, the docs aren't structured well).
from tokio.
FWIW if it helps, these are some translations for what may familiar concepts to you:
have a callback ASAP, but at the end of the queue, like node.js's process.nextTick
This is basically:
handle.spawn(future::lazy(|| /* your callback */));
The basic idea is that the core fundamental of the tokio-core event loop is that everything is a future, and that's really the only way to interact with the event loop as well. So in this case you'll execute some code as a future which gets spawned into the reactor later.
have a callback after some time, like node.js's process.setTimeout and process.setInterval.
Similar to above, this'd look like:
let timeout = Timeout::new(dur, &handle).unwrap();
handle.spawn(timeout.then(|_| {
// code after timeout fires
}));
Like above everything's a future, including Timeout
. You're then responsible for spawning that into the reactor and otherwise managing what happens after it fires. there's an Interval
type in tokio-core as well for a Stream
of events rather than just one event.
have a callback when a file descriptor is ready for reading or writing (or error/hangup/etc)
This is the primary use case of the PollEvented
type. You can construct a PollEvented
with any type that implements mio::Evented
, which you can implement for arbitrary file descriptors through mio::unix::EventedFd
. You'll then use various methods on PollEvented
to trigger the local future to wake up when it's ready. There's not really a direct analog for "run this callback when a file descriptor is ready" but rather it's moreso "when this file descriptor is ready your future gets notified", and then you define what to do with that notification.
basic stuff like making the main loop run and quit, like glib's g_main_loop_quit.
Right now the idea is that you could do something like core.run(futures::empty())
to run forever, or if you want to run with an exit signal you could do something like:
let (tx, rx) = oneshot::channel();
// ...
core.run(rx).unwrap();
In that example when you send a message on tx
you'll cause the reactor to terminate by returning from run
.
The general theme here is the "traditional event loop operations" are actually relatively heavyweight and expensive. For example scheduling callbacks involves boxing up closures or implementing support for an exit signal may be unnecessary for some applications. The Core
type itself is attempting to be a relatively general-purpose event loop while being as low-cost as possible.
Also in general the theme is that we need much better docs! As @carllerche mentioned we're trying to focus on improving the documentation soon
from tokio.
Thanks @alexcrichton for these explanations, indeed it does help. And most of this is clear, just two follow-ups:
There's not really a direct analog for "run this callback when a file descriptor is ready" but rather it's moreso "when this file descriptor is ready your future gets notified", and then you define what to do with that notification.
So, I'm supposed to call a function in the dbus library that takes whatever flags come from poll
, so that means I'm supposed to use PollEvented::poll_read
and PollEvented::poll_write
to reassemble the poll flags (right?), then it seems we're missing at least PollEvented::poll_err
and PollEvented::poll_hangup
?
In that example when you send a message on tx you'll cause the reactor to terminate by returning from run.
And so if I want to do other things while waiting on that channel, e g, running a server, would I then combine the channel future with the server future using some future combinator, or would I just use handle.spawn
for the server future and then core.run
for the channel future?
from tokio.
@diwic yeah so unfortunately integrating a library like dbus is likely going to be pretty difficult to integrate with tokio-core. In general it's just a tough problem! I've had some experience with this integrating the curl library into tokio-core with tokio-curl where the unix implementation is pretty hairy. When I was talking with @brson as he sent you your initial PR it sounded like the core primitives of curl vs dbus were similar, so I'd imagine that it's somewhat nontrivial to implement with dbus as well unfortunately :(. I'm more than willing to chat with you online about this! If you have questions feel free to reach out to me on IRC.
Right now stuff like "err" and "hangup" are handled by UnixReady
in mio itself, although right now tokio-core doesn't export this through the PollEvented
type, leading to PRs such as tokio-rs/tokio-core#199. I'll try to put something together today though which exposes the entire mio::Ready
structure so you can poke at all the events. Currently there is no method to listen for the is_hup
or is_error
event with tokio-core alone.
For running other stuff on the reactor yeah, you've got two options:
- Futures
spawn
'd onto the reactor will always run while thecore.run
call is waiting (e.g. this is the concurrency of tokio-core) - The future passed to
core.run
could use, for example,select
. That way when either future resolves (the never ending future of your server or the channel for shutdown) thecore.run
call will return.
from tokio.
Ok I've submitted tokio-rs/tokio-core#208 to learn about hup/error events
from tokio.
@alexcrichton cool, thanks for wanting to help out. Somebody to ask is exactly what I need to be able to make some progress here. Given your previous answer I was able to get a smoke test up and running and the err/hangup things, I think, are only relevant in case dbus shuts down or other weird things happen. Still it would be good to have proper support for it.
If you don't mind me asking in this thread: The next thing I've run into, that I don't understand, is how to cancel futures that I've spawned to the event loop. In essence, how do you do if you have a "master future" that controls a list of "child futures" that should all be on the event loop in parallel, and you want to cancel/remove one of the child futures? I currently do handle.spawn
for all child futures, but then I can't cancel them.
from tokio.
Nice! And yeah no worries discussing here.
In general cancellation with futures is "drop the future". So the question of "how do I cancel a future?" is then "how do I drop this future?". This basically means that you'll always need some handle to ownership of the future you'd like to drop if you'd like to cancel it at some point. Typically this happens naturally as pending futures fall out of scope and are canceled (e.g. struct fields or other local variables).
So if you pass a future literally to handle.spawn
it's then impossible to cancel it. The event loop took ownership of that future and you've got no way to cancel it (a Core
doesn't allow cancelling a running future). What you'd want instead, though, is to hand the Core
a wrapper around the future you'd like to cancel. You can sort of see an example of this in rust-lang/futures-rs#455 and also in CpuPool::spawn
today. The common theme there is that when you spawn a future you're given back a handle to the completed value. When you drop this handle it sends a signal back to the spawned future that it should exit immediately (and therefore get dropped). With that construction cancellation is "drop the future's handle".
Does that make sense?
from tokio.
@alexcrichton Having understood some more of Tokio (i e, that an "inner future" can wakeup an "outer future" because the task is connected to the "outer future", this is how Select
works), I think I'll rewrite my implementation to use that strategy instead, which avoids the problem altogether.
I would then use UnparkEvent to know why my task was woken up, but that requires EventSet
, and EventSet does not seem implemented for anything? What would typically implement EventSet, perhaps Mutex<HashSet<usize>>
...?
Btw, a major source of confusion was that task::park()
is very different from thread::park
; the former just returns a handle, it does not actually park anything. Maybe task::current()
would be a better name?
from tokio.
Ah yeah right now EventSet
doesn't have any public implementors as we wanted to retain flexibility moving forward. You may wish to look at the Stack
type in the futures crate which implements this (it's basically Mutex<HashSet<usize>>
sorta)
About confusion with task::park
, I've got good news for you! (we're renaming to task::current
)
from tokio.
Hopefully this is resolved now. Closing due to inactivity. Feel free to open again on the repo that fits best. This repo is being reclaimed to implement tokio-rs/tokio-rfcs#3.
from tokio.
Related Issues (20)
- Proposed list of Metrics to Stabilize HOT 5
- Ability to split a JoinSet into spawn and join halves HOT 1
- runtime metrics blocking threads miscount
- Add File::create_new convenience c'tor HOT 1
- Tonic server uses encapsulation HOT 2
- Missing methods `join_set::Builder::spawn_blocking()` and `join_set::Builder::spawn_blocking_on()` HOT 2
- Add a `CancellationToken` method for running a future until completion or cancellation HOT 1
- CI failure on windows HOT 3
- Vectored IO for `write_all_buf` HOT 1
- Doc bug in `Command::stdin`? HOT 1
- Include relevant values in ReadBuf.put_slice() panic message HOT 1
- File IO hangs after timeout / cancellation HOT 2
- Use `[lints.rust.unexpected_cfgs.check-cfg]` instead of hacky check-cfg workaround HOT 2
- Helper struct/wrapper for a stream of named pipe connections HOT 7
- tokio::sync::mpsc::bounded::Receiver<T>::is_empty() returns false when recv().await blocks HOT 3
- Feature tokio::sync::mpsc::Receiver::wait_close(&self) HOT 11
- Panic at linked_list.rs - reborn HOT 5
- Every 32 messages `is_empty()` on `Receiver` and `UnboundedReceiver` returns `false` even though len == 0 HOT 1
- LengthDelimitedCodec misses that last N bytes of the frame with num_skip(0) HOT 1
- Segment fault at `poll_future` HOT 8
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from tokio.