我有一个Tokio应用程序,应在发生错误时返回。这是 使用两个任务之间共享的一次性通道实现。当任何 任务检测到一个错误,它向通道发出信号,该通道被另一个接收 任务。
但是即使在错误检测任务向通道发出信号之后,其他任务
不返回-select!
块根本没有意识到频道
发出信号。完整代码:
#![recursion_limit = "256"]
extern crate futures;
extern crate tokio;
extern crate tokio_net;
extern crate tokio_sync;
use std::io::Write;
use std::net::SocketAddr;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::tcp::split::{TcpStreamReadHalf, TcpStreamWriteHalf};
use tokio::net::TcpStream;
use tokio_sync::oneshot;
use futures::select;
use futures::future::FutureExt;
#[derive(Debug)]
enum AppErr {
CantConnect(std::io::Error),
}
fn main() {
let executor = tokio::runtime::Runtime::new().unwrap();
executor.spawn(async {
match client_task().await {
Ok(()) => {}
Err(err) => {
println!("Error: {:?}", err);
}
}
});
executor.shutdown_on_idle();
}
async fn client_task() -> Result<(), AppErr> {
let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
print!("Connecting... ");
let _ = std::io::stdout().flush();
let sock = TcpStream::connect(&addr)
.await
.map_err(AppErr::CantConnect)?;
println!("Connected.");
let (read, write) = sock.split();
let (abort_in_task_snd, abort_in_task_rcv) = oneshot::channel();
let (abort_out_task_snd, abort_out_task_rcv) = oneshot::channel();
tokio::spawn(handle_incoming(read, abort_in_task_rcv, abort_out_task_snd));
tokio::spawn(handle_outgoing(
write,
abort_out_task_rcv,
abort_in_task_snd,
));
Ok(())
}
async fn handle_incoming(
mut conn: TcpStreamReadHalf,
abort_in: oneshot::Receiver<()>,
abort_out: oneshot::Sender<()>,
) {
let mut read_buf: [u8; 1024] = [0; 1024];
let mut abort_in_fused = abort_in.fuse();
loop {
select! {
abort_ret = abort_in_fused => {
// TODO match abort_ret {..}
println!("abort signalled, handle_incoming returning");
return;
},
bytes = conn.read(&mut read_buf).fuse() => {
match bytes {
Err(io_err) => {
println!("IO error when reading input stream: {:?}", io_err);
println!("Aborting");
abort_out.send(()).unwrap();
return;
}
Ok(bytes) => {
if bytes == 0 {
println!("Connection closed from the other end. Aborting.");
abort_out.send(()).unwrap();
return;
}
println!("Read {} bytes: {:?}", bytes, &read_buf[0..bytes]);
}
}
}
}
}
}
async fn handle_outgoing(
mut conn: TcpStreamWriteHalf,
abort_in: oneshot::Receiver<()>,
abort_out: oneshot::Sender<()>,
) {
let mut stdin = tokio::io::stdin();
let mut read_buf: [u8; 1024] = [0; 1024];
let mut abort_in_fused = abort_in.fuse();
loop {
select! {
abort_ret = abort_in_fused => {
println!("Abort signalled, handle_outgoing returning");
return;
}
input = stdin.read(&mut read_buf).fuse() => {
match input {
Err(io_err) => {
println!("IO error when reading stdin: {:?}", io_err);
println!("Aborting");
abort_out.send(()).unwrap();
return;
}
Ok(bytes) => {
if bytes == 0 {
println!("stdin closed, aborting");
abort_out.send(()).unwrap();
return;
}
println!("handle_outgoing read {} bytes", bytes);
match conn.write_all(&read_buf[0..bytes]).await {
Ok(()) => {
},
Err(io_err) => {
println!("Error when sending: {:?}", io_err);
println!("Aborting");
abort_out.send(()).unwrap();
return;
}
}
}
}
},
}
}
}
依赖项:
futures-preview = { version = "0.3.0-alpha.18", features = ["async-await", "nightly"] }
tokio = "0.2.0-alpha.2"
tokio-net = "0.2.0-alpha.2"
tokio-sync = "0.2.0-alpha.2"
因此,当另一端的连接关闭或出现错误时,我 向频道发送信号:
println!("Connection closed from the other end. Aborting.");
abort_out.send(()).unwrap();
return;
但是由于某种原因,其他任务却没有注意到:
select! {
// Never runs:
abort_ret = abort_in_fused => {
// TODO match abort_ret {..}
println!("abort signalled, handle_incoming returning");
return;
},
...
}
这可以通过两种方式看到:
abort_ret
情况下的打印永远不会运行。
在另一端关闭连接后,进程将打印Connection
closed from the other end. Aborting.
,但不会返回。当我附上
gdb我看到了这个回溯:
...
#14 0x000055f10391ab7e in tokio_executor::enter::Enter::block_on (self=0x7ffc167ba5a0, f=...) at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-executor-0.2.0-alpha.2/src/enter.rs:121
#15 0x000055f103863a28 in tokio::runtime::threadpool::Runtime::shutdown_on_idle (self=...) at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.0-alpha.2/src/runtime/threadpool/mod.rs:219
#16 0x000055f10384c01b in chat0_client::main () at src/chat0_client.rs:36
因此它已在Tokio事件循环中被阻止。
除了直接回答外,我对如何调试它的指针也很感兴趣。
谢谢。