Open SeseMueller opened 1 month ago
After doing some digging, this seems to be caused by https://github.com/rust-lang/futures-rs/issues/2775. See their comment for a good explanation of why it's happening.
The issue is also known at https://github.com/tokio-rs/tokio/issues/2542, which came up with a similar example as I did.
For me, this means that I can fix my problem by moving from std::process::Command
to async_process::Command
, because that is await
-ed, but the general issue still persists.
(A similar problem was discussed:https://github.com/bytecodealliance/wasmtime/issues/2876, but it's unlikely that the cause is the same as I did not find any evidence of mixed executors.)
Expected Behavior
The tokio
timeout
andsleep
should work and trigger after the supplied time.Current Behavior
They are "blocked" by another tokio thread executing and do not trigger before that thread is done.
Steps to Reproduce (for bugs)
I created two examples; one where the problem occurs when using actix_web, and one where it doesn't occur when using tokio on very similar code. See Context for explanation.
Tokio example: working code
Actix_web example: Has the problem
Edit: Using
async_process::Command
and then awaiting instead of usingstd::process::Command
does solve this particular case.Context
I am writing a REST API in actix_web that is heavily relying on streams to the client.
While I was adding a heartbeat so that the connection to the client wouldn't time out and the client could be sure that the server was still alive, I came across the issue that some tokio functionality like
sleep
,select!
(with sleep) andtimeout
don't properly work.I managed to reduce it to the two examples above.
In both cases, a channel is spawned inside an async closure, which sender is given to a new tokio task that takes long to finish. If the Receiver is then awaited using a tokio timeout (or a tokio sleep is called), the timeout (or sleep) doesn't trigger after the given time, but only after the Receiver is available.
In the context I used it, the async closure was then unfolded into a stream, which is then served to a client. This causes the stream to become "stuck" and not send a heartbeat, until the Receiver can recieve.
In the tokio example, the stream is instead given to the main function, pinned, and iterated through until completion. Here, the stream does not become "stuck" and instead sends a heartbeat once every second, as expected.
(I put this issue on actix-web because it seems to happen because of the way streaming is handeled and because the tokio example works)
Your Environment
The given examples work on fresh install.
rustc -V
): rustc 1.81.0 (eeb90cda1 2024-09-04)