I start up a Tokio runtime with code like this:
tokio::run(my_future);
My future goes on to start a bunch of tasks in response to various conditions.
One of those tasks is responsible for determining when the program should shut down. However, I don't know how to have that task gracefully terminate the program. Ideally, I'd like to find a way for this task to cause the run function call to terminate.
Below is an example of the kind of program I would like to write:
extern crate tokio;
use tokio::prelude::*;
use std::time::Duration;
use std::time::Instant;
use tokio::timer::{Delay, Interval};
fn main() {
let kill_future = Delay::new(Instant::now() + Duration::from_secs(3));
let time_print_future = Interval::new_interval(Duration::from_secs(1));
let mut runtime = tokio::runtime::Runtime::new().expect("failed to start new Runtime");
runtime.spawn(time_print_future.for_each(|t| Ok(println!("{:?}", t))).map_err(|_| ()));
runtime.spawn(
kill_future
.map_err(|_| {
eprintln!("Timer error");
})
.map(move |()| {
// TODO
unimplemented!("Shutdown the runtime!");
}),
);
// TODO
unimplemented!("Block until the runtime is shutdown");
println!("Done");
}
shutdown_now seems promising, but upon further investigation, it's probably not going to work. In particular, it takes ownership of the runtime, and Tokio is probably not going to allow both the main thread (where the runtime was created) and some random task to own the runtime.
You can use a oneshot channel to communicate from inside the runtime to outside. When the delay expires, we send a single message through the channel.
Outside of the runtime, once we receive that message we initiate a shutdown of the runtime and wait for it to finish.
use std::time::{Duration, Instant};
use tokio::{
prelude::*,
runtime::Runtime,
sync::oneshot,
timer::{Delay, Interval},
}; // 0.1.15
fn main() {
let mut runtime = Runtime::new().expect("failed to start new Runtime");
let (tx, rx) = oneshot::channel();
runtime.spawn({
let every_second = Interval::new_interval(Duration::from_secs(1));
every_second
.for_each(|t| Ok(println!("{:?}", t)))
.map_err(drop)
});
runtime.spawn({
let in_three_seconds = Delay::new(Instant::now() + Duration::from_secs(3));
in_three_seconds
.map_err(|_| eprintln!("Timer error"))
.and_then(move |_| tx.send(()))
});
rx.wait().expect("unable to wait for receiver");
runtime
.shutdown_now()
.wait()
.expect("unable to wait for shutdown");
println!("Done");
}
See also:
How do I gracefully shutdown the Tokio runtime in response to a SIGTERM?
Is there any way to shutdown `tokio::runtime::current_thread::Runtime`?
How can I stop the hyper HTTP web server and return an error?
Related
I have a UDP socket that is receiving data
pub async fn start() -> Result<(), std::io::Error> {
loop {
let mut data = vec![0; 1024];
socket.recv_from(&mut data).await?;
}
}
This code is currently blocked on the .await when there is no data coming in. I want to gracefully shut down my server from my main thread, so how do I send a signal to this .await that it should stop sleeping and shut down instead?
Note: The Tokio website has a page on graceful shutdown.
If you have more than one task to kill, you should use a broadcast channel to send shutdown messages. You can use it together with tokio::select!.
use tokio::sync::broadcast::Receiver;
// You may want to log errors rather than return them in this function.
pub async fn start(kill: Receiver<()>) -> Result<(), std::io::Error> {
tokio::select! {
output = real_start() => output,
_ = kill.recv() => Err(...),
}
}
pub async fn real_start() -> Result<(), std::io::Error> {
loop {
let mut data = vec![0; 1024];
socket.recv_from(&mut data).await?;
}
}
Then to kill all the tasks, send a message on the channel.
To kill only a single task, you can use the JoinHandle::abort method, which will kill the task as soon as possible. Note that this method is available only in Tokio 1.x and 0.3.x, and to abort a task using Tokio 0.2.x, see the next section below.
let task = tokio::spawn(start());
...
task.abort();
As an alternative to JoinHandle::abort, you can use abortable from the futures crate. When you spawn the task, you do the following:
let (task, handle) = abortable(start());
tokio::spawn(task);
Then later you can kill the task by calling the abort method.
handle.abort();
Of course, a channel with select! can also be used to kill a single task, perhaps combined with an oneshot channel rather than a broadcast channel.
All of these methods guarantee that the real_start method is killed at an .await. It is not possible to kill the task while it is running code between two .awaits. You can read more about why this is here.
The mini-redis project contains an accessible real-world example of graceful shutdown of a server. Additionally, the Tokio tutorial has chapters on both select and channels.
I am trying to introduce a timeout in my RPC requests using tokio:timer:Timeout:
use std::time::{Duration, Instant};
use tokio::prelude::*;
use tokio::timer::Delay;
fn main() {
let when = Instant::now() + Duration::from_millis(4000);
let task = Delay::new(when)
.and_then(|_| {
println!("Hello world!");
Ok(())
})
.map_err(|e| panic!("delay errored; err={:?}", e));
let task_with_timeout = task
.timeout(Duration::from_millis(3000))
.map_err(|e| println!("Timeout hit {:?}", e));
let _ = task_with_timeout.wait().expect("Failure");
// tokio::run(task_with_timeout);
}
If I run my future_with_timeout with tokio::run(), it works as expected.
However, calling wait on the task_with_timeout results in the task future getting an error:
thread 'main' panicked at 'delay errored; err=Error(Shutdown)'
instead of getting
Timeout hit Error(Elapsed)
I don't understand the difference here between using tokio::run() and wait().
Playground link
How do I make the code work using wait?
I wouldn't, and it's possible that you just can't.
Read the documentation for the timer module:
These types must be used from within the context of the Runtime or a timer context must be setup explicitly. See the tokio-timer crate for more details on how to setup a timer context.
Following the thread, we get to tokio_timer::with_default which requires a Tokio executor and a Timer. The executor uses the Enter type, which itself wants a future to block on.
All of this is to say that Tokio's futures may rely on features outside of the pure executor. If I understand the terms correctly (and it's likely I do not), those features are provided by the reactor. Calling wait has no knowledge of that.
See also:
How do I synchronously return a value calculated in an asynchronous Future in stable Rust?
Consider the following code:
extern crate tokio; // Executor runtime
use tokio::prelude::*;
use tokio::net::TcpListener;
use std::net::SocketAddr;
fn main() {
let addr = "127.0.0.1:8118".parse::<SocketAddr>().unwrap();
let listener = TcpListener::bind(&addr)
.expect("unable to bind TCP listener");
tokio::run(listener.incoming()
.map_err(|e| eprintln!("failed to accept socket; error = {:?}", e))
.for_each(|mut socket| {
let mut buf = vec![];
socket.read_to_end(&mut buf).unwrap();
println!("Received: {:#?}", buf);
Ok(())
})
);
}
When I run this and send something to port 8118 I get the following error:
thread 'tokio-runtime-worker-0' panicked at 'called `Result::unwrap()` on an `Err` value: Kind(WouldBlock)', src/libcore/result.rs:997:5
I imagine there is some way to put to put my socket in blocking mode, or perhaps catch the error and do something with it. I wonder what the standard, canonical way to approach this problem is.
I'd rather not block, since I want the server to do other things while waiting for clients, so an async / threaded solution would be fantastic.
You are using Tokio, a library where the entire purpose is to enable asynchronous IO. You never want to perform blocking operations in the asynchronous event loop.
Instead, either go all-in on async or avoid it completely and use simpler, coarser threads.
Tokio's io::read_to_end creates a future that is capable of reading all the data from a socket:
use std::net::SocketAddr;
use tokio::{net::TcpListener, prelude::*}; // 0.1.22
fn main() {
let addr = "127.0.0.1:8118".parse::<SocketAddr>().unwrap();
let listener = TcpListener::bind(&addr).expect("unable to bind TCP listener");
tokio::run(
listener
.incoming()
.and_then(|s| tokio::io::read_to_end(s, vec![]))
.map_err(|e| panic!("failed: {:?}", e))
.for_each(|(_socket, buf)| {
println!("Received: {:#?}", buf);
Ok(())
}),
);
}
See also:
How do I prevent TcpStream from blocking on a read?
Why would the first write to Mio's TcpStream after accepting give an "operation would block" error?
What is the standard way to get a Rust thread out of blocking operations?
What is the best approach to encapsulate blocking I/O in future-rs?
Why does Future::select choose the future with a longer sleep period first?
I'm trying to write a simple Rust program that reads Docker stats using shiplift and exposes them as Prometheus metrics using rust-prometheus.
The shiplift stats example runs correctly on its own, and I'm trying to integrate it in the server as
fn handle(_req: Request<Body>) -> Response<Body> {
let docker = Docker::new();
let containers = docker.containers();
let id = "my-id";
let stats = containers
.get(&id)
.stats().take(1).wait();
for s in stats {
println!("{:?}", s);
}
// ...
}
// in main
let make_service = || {
service_fn_ok(handle)
};
let server = Server::bind(&addr)
.serve(make_service);
but it appears that the stream hangs forever (I cannot produce any error message).
I've also tried the same refactor (using take and wait instead of tokio::run) in the shiplift example, but in that case I get the error executor failed to spawn task: tokio::spawn failed (is a tokio runtime running this future?). Is tokio somehow required by shiplift?
EDIT:
If I've understood correctly, my attempt does not work because wait will block tokio executor and stats will never produce results.
shiplift's API is asynchronous, meaning wait() and other functions return a Future, instead of blocking the main thread until a result is ready. A Future won't actually do any I/O until it is passed to an executor. You need to pass the Future to tokio::run as in the example you linked to. You should read the tokio docs to get a better understanding of how to write asynchronous code in rust.
There were quite a few mistakes in my understanding of how hyper works. Basically:
if a service should handle futures, do not use service_fn_ok to create it (it is meant for synchronous services): use service_fn;
do not use wait: all futures use the same executor, the execution will just hang forever (there is a warning in the docs but oh well...);
as ecstaticm0rse notices, hyper::rt::spawn could be used to read stats asynchronously, instead of doing it in the service
Is tokio somehow required by shiplift?
Yes. It uses hyper, which throws executor failed to spawn task if the default tokio executor is not available (working with futures nearly always requires an executor anyway).
Here is a minimal version of what I ended up with (tokio 0.1.20 and hyper 0.12):
use std::net::SocketAddr;
use std::time::{Duration, Instant};
use tokio::prelude::*;
use tokio::timer::Interval;
use hyper::{
Body, Response, service::service_fn_ok,
Server, rt::{spawn, run}
};
fn init_background_task(swarm_name: String) -> impl Future<Item = (), Error = ()> {
Interval::new(Instant::now(), Duration::from_secs(1))
.map_err(|e| panic!(e))
.for_each(move |_instant| {
futures::future::ok(()) // unimplemented: call shiplift here
})
}
fn init_server(address: SocketAddr) -> impl Future<Item = (), Error = ()> {
let service = move || {
service_fn_ok(|_request| Response::new(Body::from("unimplemented")))
};
Server::bind(&address)
.serve(service)
.map_err(|e| panic!("Server error: {}", e))
}
fn main() {
let background_task = init_background_task("swarm_name".to_string());
let server = init_server(([127, 0, 0, 1], 9898).into());
run(hyper::rt::lazy(move || {
spawn(background_task);
spawn(server);
Ok(())
}));
}
I want to use Delay to do some work later. If I use tokio::run, it just works fine, but it panics when using tokio::spawn:
use std::sync::mpsc;
use std::time::*;
use tokio::prelude::*; // 0.1.14
fn main() {
let (tx, rx) = mpsc::channel();
let task = tokio::timer::Delay::new(Instant::now() + Duration::from_secs(1))
.map(move |_| {
tx.send(String::from("hello")).unwrap();
()
})
.map_err(|e| {
panic!("{:?}", e);
});
tokio::spawn(task);
let msg = rx.recv().unwrap();
println!("{}", msg);
}
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: SpawnError { is_shutdown: true }', src/libcore/result.rs:1009:5
I need to use spawn not run if I want various tasks to work concurrently. How to change the code to make it work?
The documentation for tokio::spawn states:
This function will panic if the default executor is not set or if spawning onto the default executor returns an error.
Effectively, this means that tokio::spawn should only be called from inside a call to tokio::run.
Since you have only a single future to execute, you might as well just pass it directly to tokio::run. If you had multiple futures, then you can make make use of future::lazy to construct a lazily-evaluated future that will call spawn when it eventually runs:
use std::time::*;
use tokio::prelude::*; // 0.1.14
fn main() {
tokio::run(futures::lazy(|| {
tokio::spawn(wait_one_sec().map(|_| println!("One")));
tokio::spawn(wait_one_sec().map(|_| println!("Two")));
Ok(())
}));
}
fn wait_one_sec() -> impl Future<Item = (), Error = ()> {
tokio::timer::Delay::new(Instant::now() + Duration::from_secs(1))
.map(drop)
.map_err(|e| panic!("{:?}", e))
}
Note that if you forget the futures::lazy then you will get the same error. This is because the arguments to functions are evaluated eagerly, which means that the call to tokio::spawn happens first, causing the same sequence of events.
use std::sync::mpsc;
I think it's highly doubtful that you want to use the standard libraries channels, as they are not async-aware and thus will block — a very bad thing in async code.
Instead, you probably want futures::sync::mpsc.