async-std
async-std copied to clipboard
minor code change causes stream to block
hi,
i've been using async-std for some time now and I think I might have hit a bug.
my code is here https://gitlab.ensimag.fr/wagnerf/listen/-/tree/blocking
(it is on the "blocking" branch)
if you launch it right now (cargo run) it's fine : you can use left/up arrows in the graphical menu to select an entry.
now, in src/video/tune_selector.rs if you comment line 84 and uncomment line 85 suddenly the keyboard event stream becomes unresponsive. you cannot select anything anymore in the menu.
it is surprising for me because the changes between the working and non-working version are really really minor.
what's more it means touching the video frames stream makes the keyboard events stream stall.
i'm not sure where to go from there, so any help would be appreciated.
note that i can postpone this modification, so there is no emergency for me.
Hi, I am not super familiar with GitLab, can you produce a diff view somehow?
hi, yes, thanks for asking that's very nice.
by writing things down for this post I found the difference between the two versions of the code but I still cannot understand why this causes keyboard events to disappear.
so, here is the idea: i have two streams:
- an sdl events stream:
let mut sdl_events = SDLEvents::new(sdl_context)?; // a stream
let tune_selection = sdl_events.try_for_each(|e| {
// EVENT HANDLER HERE...
Ok(())
});
this stream is coming from a custom struct where I implement the Stream trait myself:
impl Stream for SDLEvents {
type Item = sdl2::event::Event;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if Pin::new(&mut self.delay).poll(cx).is_pending() {
return Poll::Pending;
}
if let Some(event) = self.pump.poll_event() {
Poll::Ready(Some(event))
} else {
// if we fail polling we should wait a bit before retrying
self.delay.reset(std::time::Duration::from_millis(50));
Poll::Pending
}
}
}
In both versions of my code this stream is unchanged. Now the other stream is a video stream, displaying the different frames. In the working version it is like this:
let frame_duration = std::time::Duration::new(1, 0) / display.framerate as u32;
let mut frames = async_std::stream::interval(frame_duration);
let video = frames.try_for_each(|_| {
selector.as_ref().render(display)?;
Ok(())
});
The render
method is clearing the screen and then displaying all gfx primitives then presenting the screen.
In the stalling version it is like this:
let video = display.play(|display| {
selector.as_ref().render(display)?;
Ok(())
});
with the play
method being:
pub(crate) async fn play<V: FnMut(&mut Self) -> Result<()>>(
&mut self,
mut video: V,
) -> Result<()> {
let mut ticks = async_std::stream::interval(Duration::new(1, 0) / self.framerate as u32);
ticks
.try_for_each(|_| -> Result<()> {
self.clear();
video(self)?;
self.canvas.present(); // CULPRIT LINE
Ok(())
})
.await
}
So, the CULPRIT LINE
here is the one causing problems.
I forgot that the render
method already presents the canvas and so it is presented twice in this version.
In all cases both streams are then consumed using a race:
let tune = async_std::task::block_on(
async { Result::Err(video.await.err().unwrap()) }
.race(async { Result::Ok(tune_selection.await.err().unwrap()) }),
);
So, I can easily solve my problem by removing the call to the second present
. However this still does not explain why a change in the video stream make all keyboard events disappear.
I know they do because if I add some println in the keyboard events handler it stops printing almost immediately.
do you have any idea of what could be going on ?
thanks a lot for your time.
If you want to get the code you can use git clone https://gitlab.ensimag.fr/wagnerf/listen.git
and then git checkout blocking
to switch to the blocking branch.