How do I propagate errors from tokio::spawn's future? - rust

When the http_service.call() function is called, a Result is returned and I want to propagate it to the function calling run().
pub async fn run(config: BootingInfo) -> Result<(), Box<dyn Error>> {
...
loop {
...
tokio::spawn(async move {
http_service.call(connection).await;
});
}
Ok(())
}
I tried the following but failed.
tokio::spawn(async move {
http_service.call(connection).await?;
});
How do I propagate errors from tokio::spawn's future?

Related

How can I use stored child process var in struct after it's been assigned to self?

I want to store a child process in a struct and be able to run functions "start_process" and "kill_process". I want to have it in a asynchronous function to potentially be able to spawn multiple processes at the same time and get the status as they exit.
use std::{error::Error, process::Stdio};
use tokio::{
io::{AsyncBufReadExt, BufReader},
process::{self, Child},
};
pub struct ProcessHandler {
child: Option<Child>,
}
impl ProcessHandler {
pub fn new() -> Self {
Self { child: None }
}
pub async fn kill_process(mut self) {
if let Some(mut child) = self.child.take() {
child.kill().await.expect("Failed to kill child process");
}
}
pub async fn start_process(&mut self) -> Result<(), Box<dyn Error>> {
let mut child = process::Command::new("ping")
.arg("8.8.8.8")
.stdout(Stdio::piped())
.spawn()
.expect("Couldn't run 'ping'");
self.child = Some(child); // <--- child is moved here
let mut reader = BufReader::new(child.stdout.take().unwrap()).lines();
tokio::spawn(async move {
let status = child
.wait()
.await
.expect("child process encountered an error");
println!("child status was: {}", status);
});
while let Some(line) = reader.next_line().await? {
println!("line:{:?}", line);
}
Ok(())
}
}
How should I write my code to not get the move of the child variable when setting self.child = Some(child)?
Now I get the error message: borrow of moved value: child value borrowed here after move

tokio::spawn makes axum won't take request in parallel

// src/server.rs
use axum::{
extract::Path,
response::{IntoResponse, Response},
routing::get,
};
pub struct Server {}
impl Server {
pub async fn run() -> Result<(), Box<dyn std::error::Error>> {
let axum_http_make_service = axum::Router::new()
.route("/:sec", get(wait_sec_event))
.into_make_service();
let http_server =
axum::Server::bind(&"0.0.0.0:4000".parse().unwrap()).serve(axum_http_make_service);
let http_handle = tokio::spawn(http_server);
let _ = tokio::try_join!(http_handle)?;
Ok(())
}
}
async fn wait_sec_event(Path(sec): Path<String>) -> Response {
let a = std::time::Duration::from_secs(sec.parse::<u64>().unwrap());
std::thread::sleep(a);
"yo".into_response()
}
// src/app.rs
use std::net::SocketAddr;
use crate::server;
pub struct App {
port: SocketAddr,
}
impl App {
pub fn new(p: SocketAddr) -> Self {
Self { port: p }
}
pub async fn run(self) -> Result<(), Box<dyn std::error::Error>> {
server::Server::run().await
}
}
// src/main.rs
use std::net::SocketAddr;
use app::App;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// build our application with a single route
let app = App::new(SocketAddr::from(([0, 0, 0, 0], 4000)));
app.run().await
}
pub mod app;
pub mod server;
When I tried to implement a axum server I found that if I put axum::Server::bind(&"0.0.0.0:4000".parse().unwrap()).serve(axum_http_make_service); into tokio::spawn instead of just await.unwrap() it
the server just can't accept requests in parallel.
means if I do curl 127.0.0.1:4000/10 then curl 127.0.0.1:4000/3 ,
the later request won't execute until the first one is finished This won't happen if I just await.unwrap() it.
Any idea where I might make a mistake?
You use std::thread::sleep, blocking the thread, which you shouldn't do in an async environment because it can prevent other tasks on the same thread to run like you experienced.
Use tokio::time::sleep instead:
async fn wait_sec_event(Path(sec): Path<String>) -> Response {
let a = std::time::Duration::from_secs(sec.parse::<u64>().unwrap());
tokio::time::sleep(a).await;
"yo".into_response()
}
I believe the difference in behaviour is because more or less by chance the tasks get spawned on different threads in your directly awaiting scenario while they get spawned on the same thread when using tokio::spawn.

Why does my Future implementation get stuck at the start? [duplicate]

This question already has answers here:
How to implement a Future or Stream that polls an async fn?
(2 answers)
Closed 5 months ago.
Although the code compiles, I don't understand why when using await directly it gets stuck in the first request. Why do I need to use the method execute_requests instead of calling it on the Future implementation?
// ...
async fn send(url: &str) {
println!("Sending to URL {}", url);
// Simulate the sending process
sleep(Duration::from_millis(500)).await;
}
type Request = Pin<Box<dyn Future<Output = ()>>>;
struct Proxy;
impl Proxy {
async fn execute_requests(&self) {
let request_1 = async {
send("url 1").await;
};
let request_2 = async {
send("url 2").await;
};
let mut requests: Vec<Request> = vec![];
requests.push(Box::pin(request_2));
requests.push(Box::pin(request_1));
while let Some(request) = requests.pop() {
request.await;
}
}
}
impl Future for Proxy {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut queue_process = Box::pin(self.execute_requests());
queue_process.as_mut().poll(cx)
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let proxy = Proxy;
// Executes both requests
// Ok(proxy.execute_requests().await)
// FIXME: Timeouts on the first request
Ok(proxy.await)
}
Rust Playground
execute_requests is a simplification: it needs access to self to get the requests and other things.
Each time you poll, you create a new execute_requests() future and poll it once. It will never advance to the next poll - next time your poll() is called, a new future will be created and will be polled once, and so on.
Instead, you should store the execute_requests() future within Proxy, and poll the same future:
struct Proxy(Pin<Box<dyn Future<Output = ()>>>);
impl Proxy {
fn new() -> Self {
Self(Box::pin(Self::execute_requests()))
}
async fn execute_requests() {
let request_1 = async {
send("url 1").await;
};
let request_2 = async {
send("url 2").await;
};
let mut requests: Vec<Request> = vec![];
requests.push(Box::pin(request_2));
requests.push(Box::pin(request_1));
while let Some(request) = requests.pop() {
request.await;
}
}
}
impl Future for Proxy {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.0.as_mut().poll(cx)
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let proxy = Proxy::new();
// Executes both requests
// Ok(proxy.execute_requests().await)
// FIXME: Timeouts on the first request
Ok(proxy.await)
}
Playground.

Callback to mutable self

Is there a way (in rust) to send a mutable borrowed self to a callback without the mem::replace hack I am using in the following MWE? I am using rust stable (1.11.0).
use std::mem;
trait Actable {
fn act(&mut self);
}
// Not Cloneable
struct SelfCaller {
message: String,
callback: Box<FnMut(&mut SelfCaller)>,
// other stuff
}
impl Actable for SelfCaller {
fn act(&mut self) {
fn noop(_: &mut SelfCaller) {}
let mut callback = mem::replace(&mut self.callback, Box::new(noop));
callback(self);
mem::replace(&mut self.callback, callback);
}
}
impl Drop for SelfCaller {
fn drop(&mut self) {/* unimiportant to the story */}
}
fn main() {
fn change(messenger: &mut SelfCaller) {
messenger.message = "replaced message".to_owned();
}
let mut messenger = SelfCaller {
message: "initial message".to_owned(),
callback: Box::new(change),
};
messenger.act();
println!("{}", &messenger.message);
}
Play
No, there is no way, because it is unsafe to do so. Here's an example that demonstrates why (requires a nightly compiler).
#![feature(fn_traits)]
#![feature(unboxed_closures)]
use std::mem;
trait Actable {
fn act(&mut self);
}
struct SelfCaller {
message: String,
callback: Box<FnMut(&mut SelfCaller)>,
}
impl Actable for SelfCaller {
fn act(&mut self) {
let mut callback: &mut Box<FnMut(&mut SelfCaller)> = unsafe { mem::transmute(&mut self.callback) };
println!("calling callback");
callback(self);
println!("called callback");
}
}
struct Callback;
impl Drop for Callback {
fn drop(&mut self) {
println!("Callback dropped!");
}
}
impl<'a> FnOnce<(&'a mut SelfCaller,)> for Callback {
type Output = ();
extern "rust-call" fn call_once(mut self, args: (&mut SelfCaller,)) {
self.call_mut(args)
}
}
impl<'a> FnMut<(&'a mut SelfCaller,)> for Callback {
extern "rust-call" fn call_mut(&mut self, (messenger,): (&mut SelfCaller,)) {
println!("changing callback");
messenger.callback = Box::new(|messenger| {});
println!("changed callback");
messenger.message = "replaced message".to_owned();
}
}
fn main() {
let change = Callback;
let mut messenger = SelfCaller {
message: "initial message".to_owned(),
callback: Box::new(change),
};
messenger.act();
println!("{}", &messenger.message);
}
The output of this program is:
calling callback
changing callback
Callback dropped!
changed callback
called callback
replaced message
OK, so what's going on? First, I've written the implementation of act for SelfCaller in such a way that I can call the callback without mem::replace, using mem::transmute to get the compiler to generate a new lifetime disconnected from self.
Then, I've written a callback (using the struct Callback, since I needed a type that implements both FnMut and Drop to demonstrate the problem) that mutates the SelfCaller by changing its callback member. This has the effect of dropping the previous callback, which is the callback that is currently executing! If Callback contained data members, attempting to read them would cause undefined behavior, since they are now in deallocated memory (we dropped the whole Box).
By the way, in your code using mem::replace, callbacks cannot change the callback, since you restore the callback after the callback call ends.
No, this is not possible with your code. If it were possible, you could easily construct an example that destroys memory safety, by for example accessing freed memory (this is left as an exercise for the reader 😉).
You could think about whether or not the FnMut really needs all of the fields of SelfCaller. If not, you can pass the (hopefully few) single fields as arguments. If not, you can create another type (let's call it Inner) that contains all fields important to the callback and pass it to the function.
If you don't need callbacks that borrow an environment, you can use a function instead of the closure:
trait Actable {
fn act(&mut self);
}
struct SelfCaller {
message: String,
callback: fn(&mut SelfCaller),
}
impl Actable for SelfCaller {
fn act(&mut self) {
(self.callback)(self);
}
}
fn main() {
fn change(messenger: &mut SelfCaller) {
messenger.message = "replaced message".to_owned();
}
let mut messenger = SelfCaller {
message: "initial message".to_owned(),
callback: change,
};
messenger.act();
println!("{}", &messenger.message);
}

Using trait methods in threads

Basically, I'm making a program that's listening to a bunch of ports and that handles incoming packets in different ways. I decide to bundle this code into a Trait:
use std::old_io::{TcpStream, TcpListener, Listener, Acceptor, EndOfFile, IoResult};
use std::thread::Thread;
trait Server {
fn new(port: u16) -> Self;
fn hostname(&self) -> &String;
fn initialize(&self) {
let acceptor = TcpListener::bind(self.hostname().as_slice()).listen().unwrap();
Thread::spawn(move|| {
let mut acceptor = acceptor;
for incoming_stream in acceptor.incoming() {
match incoming_stream {
Ok(stream) => {
self.handle_client(stream);
},
Err(ref e) if e.kind == EndOfFile => break,
Err(e) => panic!("Unexpected error: {}", e),
}
}
});
}
fn handle_client(&self, stream: TcpStream) -> ();
}
pub struct InternodeServer {
hostname: String,
}
impl Server for InternodeServer {
fn new(port: u16) -> InternodeServer {
let hostname = format!("127.0.0.1:{}", port);
InternodeServer {
hostname: hostname,
}
}
fn hostname(&self) -> &String {
&self.hostname
}
fn handle_client(&self, stream: TcpStream) {
println!("Received connection");
let mut stream = stream;
let response = b"Hello\r\n";
let _ = stream.write_all(response);
let _ = stream.close_write();
}
}
fn main() {
let test_server = <InternodeServer as Server>::new(9337);
test_server.initialize();
}
However, this code won't work because you can't send Self. This is the error I receive:
test.rs:11:9: 11:22 error: the trait `core::marker::Send` is not implemented for the type `Self` [E0277]
test.rs:11 Thread::spawn(move|| {
^~~~~~~~~~~~~
test.rs:11:9: 11:22 note: `Self` cannot be sent between threads safely
test.rs:11 Thread::spawn(move|| {
^~~~~~~~~~~~~
So I also tried making handle_client a static method to avoid self. To do this, I simply changed handle_client to:
fn handle_client(stream: TcpStream)
And referenced it by doing:
Server::handle_client(stream);
However, I can't reference InternodeServer's static methods from Server's initialize method. When compiling, I get an error like:
test.rs:16:25: 16:46 error: type annotations required: cannot resolve `_ : Server` [E0283]
test.rs:16 Server::handle_client(stream);
^~~~~~~~~~~~~~~~~~~~~
test.rs:16:25: 16:46 note: required by `Server::handle_client`
test.rs:16 Server::handle_client(stream);
Is there any way around this?
Here's a smaller reproduction of the error:
use std::thread::Thread;
trait Server {
fn initialize(&self) {
Thread::spawn(move || self.handle_client());
}
fn handle_client(&self);
}
fn main() {}
The problem is that the argument passed to Thread::spawn must be Send. You are trying to move self into the closure, but your trait doesn't guarantee Send, so the closure can't be Send.
We can attempt to go down that path with trait Server: Send, but then we get "cannot infer an appropriate lifetime" errors because Send also requires 'static (for now). Also, it seems very strange to move yourself into a closure.
Really, I think you want to split up your code. Move handle_client into a separate trait and then ensure that implementations of that trait are Send:
use std::thread::Thread;
trait Server {
fn initialize<D>(&self, driver: D)
where D: Driver + Send
{
Thread::spawn(move || driver.handle_client());
}
}
trait Driver {
fn handle_client(&self);
}
fn main() {}
I don't think that rust will allow you to invoke object methods directly from other thread because "move" closures cannot borrow anything, only move.
So you have to use some kind of inter-thread communication tool, for example, channels:
use std::thread::Thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Sender, Receiver, RecvError};
use std::net::{TcpStream, TcpListener};
use std::io::{ErrorKind, Write};
trait Server {
fn new(port: u16) -> Self;
fn hostname(&self) -> &String;
fn initialize(&mut self, _detached: bool) {
let acceptor = TcpListener::bind(self.hostname().as_slice()).unwrap();
let server_tx = self.make_pipe();
Thread::spawn(move|| {
for incoming_stream in acceptor.incoming() {
match incoming_stream {
Ok(stream) => server_tx.send(Arc::new(Mutex::new(stream))).unwrap(),
Err(ref e) if e.kind() == ErrorKind::NotConnected => break,
Err(e) => panic!("Unexpected error: {}", e),
}
}
});
}
fn handle_client(&self, stream: Arc<Mutex<TcpStream>>);
fn make_pipe(&mut self) -> Sender<Arc<Mutex<TcpStream>>>;
fn run(&self);
}
pub struct InternodeServer {
hostname: String,
client_rx: Option<Receiver<Arc<Mutex<TcpStream>>>>,
}
impl Server for InternodeServer {
fn new(port: u16) -> InternodeServer {
let hostname = format!("127.0.0.1:{}", port);
InternodeServer {
hostname: hostname,
client_rx: None,
}
}
fn make_pipe(&mut self) -> Sender<Arc<Mutex<TcpStream>>> {
let (server_tx, client_rx) = channel();
self.client_rx = Some(client_rx);
server_tx
}
fn hostname(&self) -> &String {
&self.hostname
}
fn handle_client(&self, stream_arc: Arc<Mutex<TcpStream>>) {
println!("Received connection");
let mut stream = stream_arc.lock().unwrap();
let response = b"Hello\r\n";
let _ = stream.write_all(response);
let _ = drop(stream);
}
fn run(&self) {
loop {
match self.client_rx.as_ref().unwrap().recv() {
Ok(stream) => self.handle_client(stream),
Err(RecvError) => break,
}
}
}
}
fn main() {
let mut s = <InternodeServer as Server>::new(10101);
s.initialize(false);
s.run();
}

Resources