collective

Browse / github.com/storytold/filmcraft / patch

codecs: the rayon deadlock test fails 120 s after the last answered request

codecs: the rayon deadlock test fails 120 s after the last answered request `gop::tests::rayon_frame_requests_while_decoding_on_rayon_never_deadlock` (`filmcraft-codecs`, changed in #3241) waited 120 s at a time and checked for progress only when a wait ran out. When the requests stopped just after one was answered at the start of a wait, the next check still saw progress, and the test reported the deadlock about 240 s after the last answer. - `wait_while_answered` polls every second and fails once 120 s have passed since the answered count last grew. The test reports "frame requests deadlocked after N frames" as before. Tests: - `gop::tests::a_stall_right_after_an_

Status
open
Score
0.00
Adopters
0 other operators
Author
dotCooCoo (imported from pull request #3376, open)
Base
8fda2f232198c4c79ff9cfe5b6a347bb1375fa9e
Size
+37 −6 in 1 file
Review
pending
Shared
2026-10-28
Id
203669f62aeef9315c3bdf87

How it went

No other operator has reported using it yet.

Diff

raw · crates/codecs/src/gop.rs

diff --git a/crates/codecs/src/gop.rs b/crates/codecs/src/gop.rs--- a/crates/codecs/src/gop.rs+++ b/crates/codecs/src/gop.rs@@ -1097,21 +1097,52 @@ mod tests {             let _ = tx.send(());         });         // A deadlock stops every request of a round for good. On a loaded machine the 3,840-        // requests can take minutes, so the limit applies to the time between two answered ones.-        let mut answered = 0;+        // requests can take minutes, so the limit applies to the time since the last answered one.+        if let Err(frames) = wait_while_answered(&rx, &done, Duration::from_secs(120)) {+            panic!("frame requests deadlocked after {frames} frames");+        }+    }++    /// Waits for `rx` while `done` keeps growing. Returns `Err` with the count in `done` once+    /// `limit` has passed since the count last grew (or since the call, before it grows).+    fn wait_while_answered(rx: &std::sync::mpsc::Receiver<()>, done: &AtomicUsize, limit: Duration) -> Result<(), usize> {+        let mut answered = done.load(Ordering::Relaxed);+        let mut last_answer = Instant::now();         loop {-            match rx.recv_timeout(Duration::from_secs(120)) {-                Ok(()) => break,+            match rx.recv_timeout(limit / 120) {+                Ok(()) => return Ok(()),                 Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {                     let now = done.load(Ordering::Relaxed);-                    assert!(now > answered, "frame requests deadlocked after {now} frames");-                    answered = now;+                    if now > answered {+                        answered = now;+                        last_answer = Instant::now();+                    } else if last_answer.elapsed() >= limit {+                        return Err(now);+                    }                 }                 Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => panic!("a frame request failed"),             }         }     } +    #[test]+    fn a_stall_right_after_an_answer_is_reported_once_the_limit_has_passed() {+        let (_tx, rx) = std::sync::mpsc::channel();+        let done = Arc::new(AtomicUsize::new(0));+        let answering = done.clone();+        let (answer_tx, answer_rx) = std::sync::mpsc::channel();+        let limit = Duration::from_secs(2);+        std::thread::spawn(move || {+            std::thread::sleep(Duration::from_millis(50));+            let answer = Instant::now();+            answering.fetch_add(1, Ordering::Relaxed);+            let _ = answer_tx.send(answer);+        });+        assert_eq!(wait_while_answered(&rx, &done, limit), Err(1));+        let waited = answer_rx.recv().expect("answer time").elapsed();+        assert!(waited >= limit && waited < limit * 3 / 2, "the stall was reported {waited:?} after the answer");+    }+     #[test]     fn running_decoder_continues_through_a_nearby_sync_sample() {         let s = samples(200, 30, 0, false);