Skip to main content

indicatrix_dispatch/pool/
feed.rs

1//! Lanes that join a run already in progress: [`LaneFeed`] and the thread that polls it.
2//!
3//! A late lane claims from the same [`crate::SampleCursor`] as the lanes that started
4//! with the run (a requeued range first, fresh samples after) and merges into the same
5//! [`crate::Merger`], so it takes part in the epoch exactly like any other lane. The
6//! merger folds chunks in ascending `first_sample` order whichever lane traced them, so a
7//! lane's start time cannot change which samples end up in the image, only which lane
8//! traced which chunk.
9
10use super::{LaneSlot, epoch::Epoch, lane_loop};
11use crate::{RateModel, WorkerLane};
12use std::{
13    sync::{Arc, Mutex},
14    thread,
15    time::Duration,
16};
17
18/// How long the feeder thread waits inside one [`LaneFeed::next`] call: also the longest a
19/// finished run waits for the feeder to notice it is over.
20const FEED_WAIT: Duration = Duration::from_millis(50);
21
22/// A source of lanes that join a run in progress (see
23/// [`LanePool::run_into_fed`](super::LanePool::run_into_fed)).
24pub trait LaneFeed: Sync {
25    /// Waits up to `wait` for lanes to add and returns them with their starting rate
26    /// models, or an empty list. The first returned lane gets the event index
27    /// `first_index`, the next one `first_index + 1`, and so on, so an implementation
28    /// can set up whatever it keys by lane index before the lane starts.
29    ///
30    /// Called repeatedly from one thread until the run ends; lanes returned after the
31    /// last lane has left are dropped unused.
32    fn next(&self, first_index: usize, wait: Duration) -> Vec<(Arc<dyn WorkerLane>, RateModel)>;
33}
34
35/// Starts lane `index` on its own scoped thread; the roster counts it out when it ends.
36pub(super) fn spawn_lane<'scope, 'env>(
37    scope: &'scope thread::Scope<'scope, 'env>,
38    epoch: &'env Epoch<'env>,
39    index: usize,
40    slot: Arc<LaneSlot>,
41) {
42    scope.spawn(move || {
43        let left = lane_loop::run_lane(epoch, index, &*slot.lane, &slot.rate);
44        if !left {
45            epoch.roster.leave();
46        }
47    });
48}
49
50/// The feeder thread's body: asks `feed` for lanes until the run ends or is cancelled and
51/// starts each one that arrives while the roster is still open.
52pub(super) fn feed_lanes<'scope, 'env>(
53    scope: &'scope thread::Scope<'scope, 'env>,
54    epoch: &'env Epoch<'env>,
55    feed: &'env dyn LaneFeed,
56) {
57    while !epoch.roster.is_closed() && !epoch.cancel.is_cancelled() {
58        let late = feed.next(epoch.lane_count(), FEED_WAIT);
59        for (lane, rate) in late {
60            if !epoch.roster.try_join() {
61                return;
62            }
63            let slot = Arc::new(LaneSlot {
64                lane,
65                rate: Mutex::new(rate),
66            });
67            let index = epoch.push_lane(Arc::clone(&slot));
68            spawn_lane(scope, epoch, index, slot);
69        }
70    }
71}