decoder/stateless: add utility method to wait for the next event

The proper way to wait for the next event is the poll the decoder's
event file descriptor instead of calling decode() over and over again
until an event is delivered, like our simple_decode_loop() helper
currently does.

Provide a decoder helper that does that and make it available as the
public interface. While probably not useful for real-life scenarios, it
is handy to have.

This also allows us to timeout instead of looping infinitely if the
decoder is starved for output buffers, as currently happens with
Fluster's RPS_E_qualcomm_5 H.265 test vector.
diff --git a/src/decoder/stateless.rs b/src/decoder/stateless.rs
index 79b7ab1..fc2f9e6 100644
--- a/src/decoder/stateless.rs
+++ b/src/decoder/stateless.rs
@@ -18,7 +18,9 @@
 pub mod vp9;
 
 use std::os::fd::AsFd;
+use std::os::fd::AsRawFd;
 use std::os::fd::BorrowedFd;
+use std::time::Duration;
 
 use nix::errno::Errno;
 use nix::sys::epoll::Epoll;
@@ -117,6 +119,13 @@
     }
 }
 
+/// Error returned by the [`StatelessVideoDecoder::wait_for_next_event`] method.
+#[derive(Debug, Error)]
+pub enum WaitNextEventError {
+    #[error("timed out while waiting for next decoder event")]
+    TimedOut,
+}
+
 mod private {
     use super::*;
 
@@ -318,6 +327,25 @@
     /// Returns the next event, if there is any pending.
     fn next_event(&mut self) -> Option<DecoderEvent<Self::Handle>>;
 
+    /// Blocks until [`StatelessVideoDecoder::next_event`] is expected to return `Some` or
+    /// `timeout` has elapsed.
+    ///
+    /// Wait for the next event and return it, or return `None` if `timeout` has been reached while
+    /// waiting.
+    fn wait_for_next_event(&mut self, timeout: Duration) -> Result<(), WaitNextEventError> {
+        // Wait until the next event is available.
+        let mut fd = nix::libc::pollfd {
+            fd: self.poll_fd().as_raw_fd(),
+            events: nix::libc::POLLIN,
+            revents: 0,
+        };
+        // SAFETY: `fd` is a valid reference to a properly-filled `pollfd`.
+        match unsafe { nix::libc::poll(&mut fd, 1, timeout.as_millis() as i32) } {
+            0 => Err(WaitNextEventError::TimedOut),
+            _ => Ok(()),
+        }
+    }
+
     /// Returns a file descriptor that signals `POLLIN` whenever an event is pending on this
     /// decoder.
     fn poll_fd(&self) -> BorrowedFd;
diff --git a/src/utils.rs b/src/utils.rs
index d874505..00add8d 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -13,6 +13,7 @@
 use std::io::Write;
 use std::marker::PhantomData;
 use std::os::fd::OwnedFd;
+use std::time::Duration;
 
 use byteorder::ReadBytesExt;
 use byteorder::LE;
@@ -346,6 +347,7 @@
                     }
                 }
                 Err(DecodeError::CheckEvents) | Err(DecodeError::NotEnoughOutputBuffers(_)) => {
+                    decoder.wait_for_next_event(Duration::from_secs(3))?;
                     check_events(decoder)?
                 }
                 Err(e) => anyhow::bail!(e),