I am trying to concurrently process arriving UDP packets in Tokio. However the following MWE does not do what I expected:
extern crate futures;
extern crate tokio_core;
extern crate tokio_io;
use futures::{Future, Stream};
use std::net::SocketAddr;
use tokio_core::net::{UdpCodec, UdpSocket};
use tokio_core::reactor::Core;
// just a codec to send and receive bytes
pub struct LineCodec;
impl UdpCodec for LineCodec {
type In = (SocketAddr, Vec<u8>);
type Out = (SocketAddr, Vec<u8>);
fn decode(&mut self, addr: &SocketAddr, buf: &[u8]) -> std::io::Result<Self::In> {
Ok((*addr, buf.to_vec()))
}
fn encode(&mut self, (addr, buf): Self::Out, into: &mut Vec<u8>) -> SocketAddr {
into.extend(buf);
addr
}
}
fn compute(addr: SocketAddr, msg: Vec<u8>) -> Box<Future<Item = (), Error = ()>> {
println!("Starting to compute for: {}", addr);
// sleep is a placeholder for a long computation
std::thread::sleep(std::time::Duration::from_secs(8));
println!("Done computing for for: {}", addr);
Box::new(futures::future::ok(()))
}
fn main() {
let mut core = Core::new().unwrap();
let handle = core.handle();
let listening_addr = "127.0.0.1:8080".parse::<SocketAddr>().unwrap();
let socket = UdpSocket::bind(&listening_addr, &handle).unwrap();
println!("Listening on: {}", socket.local_addr().unwrap());
let (writer, reader) = socket.framed(LineCodec).split();
let socket_read = reader.for_each(|(addr, msg)| {
println!("Got {:?}", msg);
handle.spawn(compute(addr, msg));
Ok(())
});
core.run(socket_read).unwrap();
}
Connecting two terminals with $ nc -u localhost 8080 and sending some text, I can see that the message from the second terminal is processed after the first finished.
What do I have to change?
As #Stefan said in another answer, you should not block in the asynchronous code. Given your example, it looks like the sleep is a placeholder for some long computation. So instead of using a timeout, you should delegate that computation to another thread like this example:
extern crate futures;
extern crate futures_cpupool;
use futures::Future;
use futures_cpupool::CpuPool;
...
let pool = CpuPool::new_num_cpus();
...
fn compute(handle: &Handle, addr: SocketAddr, _msg: Vec<u8>) -> Box<Future<Item = (), Error = ()>> {
// I don't know enough about Tokio to know how to make `pool` available here
pool.spawn_fn (|| {
println!("Starting to compute for: {}", addr);
std::thread::sleep(std::time::Duration::from_secs(8));
println!("Done computing for for: {}", addr);
Ok(())
})
}
Never sleep in async code (and avoid any other blocking calls too).
You might want to use Timeout instead like this:
Playground
fn compute(handle: &Handle, addr: SocketAddr, _msg: Vec<u8>) -> Box<Future<Item = (), Error = ()>> {
println!("Starting to compute for: {}", addr);
Box::new(
Timeout::new(std::time::Duration::from_secs(8), handle)
.unwrap()
.map_err(|e| panic!("timeout failed: {:?}", e))
.and_then(move |()| {
println!("Done computing for for: {}", addr);
Ok(())
}),
)
}
Related
I am trying to use hyper to grab the content of an HTML page and would like to synchronously return the output of a future. I realized I could have picked a better example since synchronous HTTP requests already exist, but I am more interested in understanding whether we could return a value from an async calculation.
extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate tokio;
use futures::{future, Future, Stream};
use hyper::Client;
use hyper::Uri;
use hyper_tls::HttpsConnector;
use std::str;
fn scrap() -> Result<String, String> {
let scraped_content = future::lazy(|| {
let https = HttpsConnector::new(4).unwrap();
let client = Client::builder().build::<_, hyper::Body>(https);
client
.get("https://hyper.rs".parse::<Uri>().unwrap())
.and_then(|res| {
res.into_body().concat2().and_then(|body| {
let s_body: String = str::from_utf8(&body).unwrap().to_string();
futures::future::ok(s_body)
})
}).map_err(|err| format!("Error scraping web page: {:?}", &err))
});
scraped_content.wait()
}
fn read() {
let scraped_content = future::lazy(|| {
let https = HttpsConnector::new(4).unwrap();
let client = Client::builder().build::<_, hyper::Body>(https);
client
.get("https://hyper.rs".parse::<Uri>().unwrap())
.and_then(|res| {
res.into_body().concat2().and_then(|body| {
let s_body: String = str::from_utf8(&body).unwrap().to_string();
println!("Reading body: {}", s_body);
Ok(())
})
}).map_err(|err| {
println!("Error reading webpage: {:?}", &err);
})
});
tokio::run(scraped_content);
}
fn main() {
read();
let content = scrap();
println!("Content = {:?}", &content);
}
The example compiles and the call to read() succeeds, but the call to scrap() panics with the following error message:
Content = Err("Error scraping web page: Error { kind: Execute, cause: None }")
I understand that I failed to launch the task properly before calling .wait() on the future but I couldn't find how to properly do it, assuming it's even possible.
Standard library futures
Let's use this as our minimal, reproducible example:
async fn example() -> i32 {
42
}
Call executor::block_on:
use futures::executor; // 0.3.1
fn main() {
let v = executor::block_on(example());
println!("{}", v);
}
Tokio
Use the tokio::main attribute on any function (not just main!) to convert it from an asynchronous function to a synchronous one:
use tokio; // 0.3.5
#[tokio::main]
async fn main() {
let v = example().await;
println!("{}", v);
}
tokio::main is a macro that transforms this
#[tokio::main]
async fn main() {}
Into this:
fn main() {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async { {} })
}
This uses Runtime::block_on under the hood, so you can also write this as:
use tokio::runtime::Runtime; // 0.3.5
fn main() {
let v = Runtime::new().unwrap().block_on(example());
println!("{}", v);
}
For tests, you can use tokio::test.
async-std
Use the async_std::main attribute on the main function to convert it from an asynchronous function to a synchronous one:
use async_std; // 1.6.5, features = ["attributes"]
#[async_std::main]
async fn main() {
let v = example().await;
println!("{}", v);
}
For tests, you can use async_std::test.
Futures 0.1
Let's use this as our minimal, reproducible example:
use futures::{future, Future}; // 0.1.27
fn example() -> impl Future<Item = i32, Error = ()> {
future::ok(42)
}
For simple cases, you only need to call wait:
fn main() {
let s = example().wait();
println!("{:?}", s);
}
However, this comes with a pretty severe warning:
This method is not appropriate to call on event loops or similar I/O situations because it will prevent the event loop from making progress (this blocks the thread). This method should only be called when it's guaranteed that the blocking work associated with this future will be completed by another thread.
Tokio
If you are using Tokio 0.1, you should use Tokio's Runtime::block_on:
use tokio; // 0.1.21
fn main() {
let mut runtime = tokio::runtime::Runtime::new().expect("Unable to create a runtime");
let s = runtime.block_on(example());
println!("{:?}", s);
}
If you peek in the implementation of block_on, it actually sends the future's result down a channel and then calls wait on that channel! This is fine because Tokio guarantees to run the future to completion.
See also:
How can I efficiently extract the first element of a futures::Stream in a blocking manner?
As this is the top result that come up in search engines by the query "How to call async from sync in Rust", I decided to share my solution here. I think it might be useful.
As #Shepmaster mentioned, back in version 0.1 futures crate had beautiful method .wait() that could be used to call an async function from a sync one. This must-have method, however, was removed from later versions of the crate.
Luckily, it's not that hard to re-implement it:
trait Block {
fn wait(self) -> <Self as futures::Future>::Output
where Self: Sized, Self: futures::Future
{
futures::executor::block_on(self)
}
}
impl<F,T> Block for F
where F: futures::Future<Output = T>
{}
After that, you can just do following:
async fn example() -> i32 {
42
}
fn main() {
let s = example().wait();
println!("{:?}", s);
}
Beware that this comes with all the caveats of original .wait() explained in the #Shepmaster's answer.
This works for me using tokio:
tokio::runtime::Runtime::new()?.block_on(fooAsyncFunction())?;
I have this piece of code which is supposed to serialize a futures::stream::Stream to a Write. I want this code to return Err if write_all fails, but I don't see a way to get that out of the callback to for_each. I found How to send data through a futures Stream by writing through the io::Write trait?, but I don't understand how to make it work in my situation.
use std::io::Write;
use std::error::Error;
use futures::stream::StreamExt;
pub async fn download(url: &str, mut dest: impl Write) -> Result<(), Box<dyn Error>> {
let byte_stream = reqwest::get(url).await?.bytes_stream();
byte_stream.for_each(|bytes| {
if let Ok(bytes) = bytes {
dest.write_all(&bytes).expect("failed to write");
}
futures::future::ready(())
}).await;
Ok(())
}
Cargo.toml
[dependencies]
reqwest = { version = "0.11", features = ["json", "stream"] }
tokio = { version = "1", features = ["full"] }
futures = "0.3"
bytes = "1"
I'd probably use StreamExt::next and write the loop out a bit more manually:
use futures::stream::StreamExt;
use std::{error::Error, io::Write};
pub async fn download(url: &str, mut dest: impl Write) -> Result<(), Box<dyn Error>> {
let mut byte_stream = reqwest::get(url).await?.bytes_stream();
while let Some(bytes) = byte_stream.next().await {
let bytes = bytes?;
dest.write_all(&bytes)?;
}
Ok(())
}
Notes:
These is TryStreamExt::try_for_each which can be used here, but it wants to take ownership of dest so I went in a different direction.
Mixing synchronous IO (std::io::Write) and asynchronous IO (reqwest) is a bad idea without taking special care, which is not demonstrated in this answer.
See also:
What is the best approach to encapsulate blocking I/O in future-rs?
I'd like a function which asynchronously processes a variable amount of (Sink, Stream) tuples.
use futures::channel::mpsc;
use futures::{Sink, Stream, SinkExt, StreamExt};
async fn foo(v: Vec<(Box<dyn Sink<Error = std::io::Error>>, Box<dyn Stream<Item = u8>>)>) {
for (mut tx, mut rx) in v {
let _ = tx.send(0);
let _ = rx.next().await;
}
}
#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (tx, mut rx) = mpsc::channel(32);
foo(vec![(Box::new(tx), Box::new(rx))]).await;
Ok(())
}
But I get this compilation error:
error[E0107]: wrong number of type arguments: expected 1, found 0
--> src/main.rs:4:30
|
4 | async fn foo(v: Vec<(Box<dyn Sink<Error = std::io::Error>>, Box<dyn Stream<Item = u8>>)>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 type argument
I was prompted to declare the associated type for the trait object that way by the compiler itself. I'm unsure why it does not accept it.
The compiler wants you to specify the "type argument" of the Sink. This is not the error type, but the type of the item being sent down the sink, as in Sink<Foo>. You specify u8 as the type of the stream, and are sending the value unchanged between one and the other, so you probably want a Sink<u8>.
Once you do that, the compiler will next complain that you need to specify the Error associated type (this time for real). However if you specify std::io::Error, the call to foo() from main() won't compile because the implementation of Sink for mpsc::Sender specifies its own mpsc::SendError as the error type.
Finally, both the sink and the stream need to be pinned so they can live across await points. This is done by using Pin<Box<...>> instead of Box<...> and Box::pin(...) instead of Box::new(...).
With the above changes, a version that compiles looks like this:
use futures::channel::mpsc;
use futures::{Sink, SinkExt, Stream, StreamExt};
use std::pin::Pin;
async fn foo(
v: Vec<(
Pin<Box<dyn Sink<u8, Error = mpsc::SendError>>>,
Pin<Box<dyn Stream<Item = u8>>>,
)>,
) {
for (mut tx, mut rx) in v {
let _ = tx.send(0);
let _ = rx.next().await;
}
}
#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (tx, rx) = mpsc::channel(32);
foo(vec![(Box::pin(tx), Box::pin(rx))]).await;
Ok(())
}
In hyper 0.12.33, how do I implement hyper::service::Service for a struct ?
I have tried the following but it is not sufficient as it seems that in 0.12 the Future trait is not provided automatically anymore for a struct that implements Service:
use futures::future::Future;
use hyper::{Body, Request, Response};
struct MyStruct;
impl MyStruct {
pub fn new() -> Self {
MyStruct
}
}
impl hyper::service::Service for MyStruct {
type ReqBody = Body;
type ResBody = Body;
type Error = hyper::Error;
type Future = Box<Future<Item = Response<Body>, Error = hyper::Error>>;
fn call(&mut self, req: Request<Body>) -> Self::Future {
unimplemented!()
}
}
fn main() {
let addr = "0.0.0.0:8080".parse().unwrap();
let server = hyper::Server::bind(&addr)
.serve(|| MyStruct::new())
.map_err(|e| eprintln!("server error: {}", e));
hyper::rt::run(server);
}
gives me the build error message:
Standard Error
Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `MyStruct: futures::future::Future` is not satisfied
--> src/main.rs:26:10
|
26 | .serve(|| MyStruct::new())
| ^^^^^ the trait `futures::future::Future` is not implemented for `MyStruct`
|
= note: required because of the requirements on the impl of `hyper::service::make_service::MakeServiceRef<hyper::server::tcp::addr_stream::AddrStream>` for `[closure#src/main.rs:26:16: 26:34]`
error[E0599]: no method named `map_err` found for type `hyper::server::Server<hyper::server::tcp::AddrIncoming, [closure#src/main.rs:26:16: 26:34]>` in the current scope
--> src/main.rs:27:10
|
27 | .map_err(|e| eprintln!("server error: {}", e));
| ^^^^^^^
|
= note: the method `map_err` exists but the following trait bounds were not satisfied:
`&mut hyper::server::Server<hyper::server::tcp::AddrIncoming, [closure#src/main.rs:26:16: 26:34]> : futures::future::Future`
`hyper::server::Server<hyper::server::tcp::AddrIncoming, [closure#src/main.rs:26:16: 26:34]> : futures::future::Future`
This example gives one way. It compiles and runs with v0.14.12
#![deny(warnings)]
use std::task::{Context, Poll};
use futures_util::future;
use hyper::service::Service;
use hyper::{Body, Request, Response, Server};
const ROOT: &str = "/";
#[derive(Debug)]
pub struct Svc;
impl Service<Request<Body>> for Svc {
type Response = Response<Body>;
type Error = hyper::Error;
type Future = future::Ready<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Ok(()).into()
}
fn call(&mut self, req: Request<Body>) -> Self::Future {
let rsp = Response::builder();
let uri = req.uri();
if uri.path() != ROOT {
let body = Body::from(Vec::new());
let rsp = rsp.status(404).body(body).unwrap();
return future::ok(rsp);
}
let body = Body::from(Vec::from(&b"heyo!"[..]));
let rsp = rsp.status(200).body(body).unwrap();
future::ok(rsp)
}
}
pub struct MakeSvc;
impl<T> Service<T> for MakeSvc {
type Response = Svc;
type Error = std::io::Error;
type Future = future::Ready<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Ok(()).into()
}
fn call(&mut self, _: T) -> Self::Future {
future::ok(Svc)
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// pretty_env_logger::init();
let addr = "127.0.0.1:1337".parse().unwrap();
let server = Server::bind(&addr).serve(MakeSvc);
println!("Listening on http://{}", addr);
server.await?;
Ok(())
}
The indirection (MakeSvc -> Src) appears to follow from the architecture of Hyper, as described in this issue:
There's two steps involved here, and both make use of Service:
The MakeSvc is a Service that creates Svcs for each connection.
The Svc is a Service to handle requests on a single connection.
I have a concept project where the client sends a server a number (PrimeClientRequest), the server computes if the value is prime or not, and returns a response (PrimeClientResponse). I want the client to be a simple CLI which prompts the user for a number, sends the request to the server, and displays the response. Ideally I want to do this using TcpClient from Tokio and Streams from Futures-Rs.
I've written a Tokio server using services and I want to reuse the same codec and proto for the client.
Part of the client is a function called read_prompt which returns a Stream. Essentially it is an infinite loop at which each iteration reads in some input from stdin.
Here's the relevant code:
main.rs
use futures::{Future, Stream};
use std::env;
use std::net::SocketAddr;
use tokio_core::reactor::Core;
use tokio_prime::protocol::PrimeClientProto;
use tokio_prime::request::PrimeRequest;
use tokio_proto::TcpClient;
use tokio_service::Service;
mod cli;
fn main() {
let mut core = Core::new().unwrap();
let handle = core.handle();
let addr_string = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
let remote_addr = addr_string.parse::<SocketAddr>().unwrap();
println!("Connecting on {}", remote_addr);
let tcp_client = TcpClient::new(PrimeClientProto).connect(&remote_addr, &handle);
core.run(tcp_client.and_then(|client| {
client
.call(PrimeRequest { number: Ok(0) })
.and_then(|response| {
println!("RESP = {:?}", response);
Ok(())
})
})).unwrap();
}
cli.rs
use futures::{Future, Sink, Stream};
use futures::sync::mpsc;
use std::{io, thread};
use std::io::{Stdin, Stdout};
use std::io::prelude::*;
pub fn read_prompt() -> impl Stream<Item = u64, Error = ()> {
let (tx, rx) = mpsc::channel(1);
thread::spawn(move || loop {
let thread_tx = tx.clone();
let input = prompt(io::stdout(), io::stdin()).unwrap();
let parsed_input = input
.parse::<u64>()
.map_err(|_| io::Error::new(io::ErrorKind::Other, "invalid u64"));
thread_tx.send(parsed_input.unwrap()).wait().unwrap();
});
rx
}
fn prompt(stdout: Stdout, stdin: Stdin) -> io::Result<String> {
let mut stdout_handle = stdout.lock();
stdout_handle.write(b"> ")?;
stdout_handle.flush()?;
let mut buf = String::new();
let mut stdin_handle = stdin.lock();
stdin_handle.read_line(&mut buf)?;
Ok(buf.trim().to_string())
}
With the code above, the client sends a single request to the server before the client terminates. I want to be able to use the stream generated from read_prompt to provide input to the TcpClient and make a request per item in the stream. How would I go about doing this?
The full code can be found at joshleeb/tokio-prime.
The solution I have come up with (so far) has been to use the LoopFn in the Future-Rs crate. It's not ideal as a new connection still has to be made but it is at least a step in the right direction.
main.rs
use futures::{future, Future};
use std::{env, io};
use std::net::SocketAddr;
use tokio_core::reactor::{Core, Handle};
use tokio_prime::protocol::PrimeClientProto;
use tokio_prime::request::PrimeRequest;
use tokio_proto::TcpClient;
use tokio_service::Service;
mod cli;
fn handler<'a>(
handle: &'a Handle, addr: &'a SocketAddr
) -> impl Future<Item = (), Error = ()> + 'a {
cli::prompt(io::stdin(), io::stdout())
.and_then(move |number| {
TcpClient::new(PrimeClientProto)
.connect(addr, handle)
.and_then(move |client| Ok((client, number)))
})
.and_then(|(client, number)| {
client
.call(PrimeRequest { number: Ok(number) })
.and_then(|response| {
println!("{:?}", response);
Ok(())
})
})
.or_else(|err| {
println!("! {}", err);
Ok(())
})
}
fn main() {
let mut core = Core::new().unwrap();
let handle = core.handle();
let addr_string = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
let remote_addr = addr_string.parse::<SocketAddr>().unwrap();
println!("Connecting on {}", remote_addr);
let client = future::loop_fn((), |_| {
handler(&handle, &remote_addr)
.map(|_| -> future::Loop<(), ()> { future::Loop::Continue(()) })
});
core.run(client).ok();
}
cli.rs
use futures::prelude::*;
use std::io;
use std::io::{Stdin, Stdout};
use std::io::prelude::*;
#[async]
pub fn prompt(stdin: Stdin, stdout: Stdout) -> io::Result<u64> {
let mut stdout_handle = stdout.lock();
stdout_handle.write(b"> ")?;
stdout_handle.flush()?;
let mut buf = String::new();
let mut stdin_handle = stdin.lock();
stdin_handle.read_line(&mut buf)?;
parse_input(buf.trim().to_string())
}
fn parse_input(s: String) -> io::Result<u64> {
s.parse::<u64>()
.map_err(|_| io::Error::new(io::ErrorKind::Other, "invalid u64"))
}