Skip to main content

zencodec_io/
work_quota.rs

1use core::num::NonZeroUsize;
2
3/// Maximum codec steps in one uninterrupted async compute turn.
4///
5/// A step is indivisible. This bounds scheduling work units, not wall time,
6/// memory, rows or pending encoded output. Yielding never asks an encoder to
7/// drain: output backpressure needs a separate retention-qualified strategy.
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub struct WorkQuota(NonZeroUsize);
10
11impl WorkQuota {
12    /// A nonzero step quota; zero cannot make progress and returns `None`.
13    pub const fn new(steps: usize) -> Option<Self> {
14        match NonZeroUsize::new(steps) {
15            Some(n) => Some(Self(n)),
16            None => None,
17        }
18    }
19
20    /// Compatibility setting: finish the available batch without a quota yield.
21    pub const fn unlimited() -> Self {
22        Self(NonZeroUsize::MAX)
23    }
24
25    /// Maximum number of complete steps per turn.
26    pub const fn max_steps(self) -> usize {
27        self.0.get()
28    }
29}
30
31impl Default for WorkQuota {
32    fn default() -> Self {
33        Self::unlimited()
34    }
35}
36
37/// Give the calling executor one scheduling opportunity without depending on
38/// a particular runtime or putting I/O backpressure on the worker.
39#[cfg(feature = "tokio")]
40pub(crate) async fn yield_once() {
41    let mut yielded = false;
42    core::future::poll_fn(|cx| {
43        if yielded {
44            core::task::Poll::Ready(())
45        } else {
46            yielded = true;
47            cx.waker().wake_by_ref();
48            core::task::Poll::Pending
49        }
50    })
51    .await;
52}