tokio-rs / tokio

A runtime for writing reliable asynchronous applications with Rust. Provides I/O, networking, scheduling, timers, ...
https://tokio.rs
MIT License
27.13k stars 2.49k forks source link

WouldBlock error with udp socket #2500

Open bbigras opened 4 years ago

bbigras commented 4 years ago

Version

``` │ │ │ ├── tokio v0.2.20 │ │ │ │ └── tokio-macros v0.2.5 │ │ │ └── tokio-util v0.3.1 │ │ │ └── tokio v0.2.20 (*) │ │ ├── tokio v0.2.20 (*) │ │ ├── tokio v0.2.20 (*) │ │ ├── tokio-rustls v0.13.0 │ │ │ ├── tokio v0.2.20 (*) │ ├── tokio v0.2.20 (*) │ ├── tokio-rustls v0.13.0 (*) ├── tokio v0.2.20 (*) ``` ## Platform

Linux hostname 5.4.39 #1-NixOS SMP Wed May 6 06:15:17 UTC 2020 x86_64 GNU/Linux

Description

I use the serenity crate to build a discord bot. That crate isn't async but I would like to use async code with it.

The code "works" on my computer at home but not on my tiny vps at digital ocean. I wonder if it's because it only has 1 CPU.

The wouldblock error seems to happen while I'm reading the socket (with a send_to before).

    let mut buf = vec![0; 1024];
    let (n, _peer) = socket.recv_from(&mut buf).await?;

One weird thing is that last night I had the 'wouldblock' error but this morning it just hangs.

I can't reproduce with simpler code right now but it's something like:

fn main() -> Result<(), Error> {
    use tokio::runtime::Runtime;
    let mut rt = Runtime::new().unwrap();

    let handle = rt.handle().clone();

    // also tried tokio::task::spawn_blocking
    thread::spawn(move || {
        start_serenity(handle).unwrap();
    });

    use futures::future;

    let f = future::pending::<()>();
    rt.block_on(f);
}

// some serenity command
#[command]
fn something(ctx: &mut Context, msg: &Message) -> CommandResult {
    // get the handle from ctx

    let (tx, rx) = unbounded();

    handle.spawn(async move {
        // here I use a tokio::net::UdpSocket
        // send result using tx channel
    });

    let result = rx.await;
}
Darksonn commented 4 years ago

That sounds weird. It shouldn't return WouldBlock. I need more details, though.

bbigras commented 4 years ago

I'm going to try to reproduce.

Meanwhile. my vps rebooted (and did an OS update) last night. I wonder if that changed anything.

Also the wouldblock error messages had "Resource temporarily unavailable (os error 11)". I have no idea if it's always the case or not.

Darksonn commented 4 years ago

Are you still having issues with this?

bbigras commented 4 years ago

Yes.

If I swap tokio for async-std and it hangs I still have the "WouldBlock" error. Does that mean that the problem has nothing to do with tokio?

My program is still running stuff using tokio. I use serenity which uses reqwest which uses tokio.

Darksonn commented 4 years ago

I mean Tokio and async-std share the same IO backend mio, so it might be an issue on that level. If you are able to create a small test case that shows the error, that would be amazing.

Alternatively, a way to help out would be to add some prints in poll_recv_from to check which question mark returned the error.

#[doc(hidden)]
pub fn poll_recv_from(
    &self,
    cx: &mut Context<'_>,
    buf: &mut [u8],
) -> Poll<Result<(usize, SocketAddr), io::Error>> {
    match self.io.poll_read_ready(cx, mio::Ready::readable()) {
        Poll::Ready(Ok(())) => {},
        Poll::Ready(Err(e)) => {
            if e.kind() == io::ErrorKind::WouldBlock {
                println!("WouldBlock from poll_read_ready");
            }
            return Poll::Ready(Err(e))
        },
        Poll::Pending => return Poll::Pending,
    }

    match self.io.get_ref().recv_from(buf) {
        Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
            if let Err(e) = self.io.clear_read_ready(cx, mio::Ready::readable()) {
                if e.kind() == io::ErrorKind::WouldBlock {
                    println!("WouldBlock from clear_read_ready");
                }
                Poll::Ready(Err(e))
            } else {
                Poll::Pending
            }
        }
        x => Poll::Ready(x),
    }
}

You can use an alternate version of Tokio with a patch section.

[dependencies]
tokio = { version = "0.2.21", features = ["full"] }

[patch.crates-io]
tokio = { path = '/path/to/modified/tokio' }
bbigras commented 4 years ago

I'll try that today.

Btw I also have the problem with async-std 1.6.0-beta.1 which uses the new smol runtime. Which isn't based on mio I think.

bbigras commented 4 years ago

I'm still trying to find which commit I had that was using tokio::net::UdpSocket and had the WouldBlock error.

The code I'm testing right now just blocks forever (on my tiny vps but is fine on my desktop). Can I do something to debug that?

bbigras commented 4 years ago

If I had a firewall issue, would I get an WouldBlock message and I would have to use tokio::time::timeout?

EDIT: It seems to be a firewall or wireguard issue. I forgot that my vps has a vpn with the other host.