1use core::cmp;
4use std::mem;
5
6use o3::{
7 base::time::Positive,
8 depot::{
9 capacity,
10 heap::{self, resident},
11 },
12};
13
14use crate::manifold;
15
16pub mod config;
17mod exact;
18pub mod timeline;
19pub use exact::Exact;
20#[repr(u8)]
22#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
23pub enum Phase {
24 Head,
25 BodyIdle,
26 Total,
27 Handler,
28 ResponseAdmission,
29}
30
31impl Phase {
32 const fn rank(self) -> u8 {
33 match self {
34 Self::Total => 0,
35 _ => self as u8 + 1,
36 }
37 }
38
39 const fn lane(self) -> usize {
40 match self {
41 Self::Total => 0,
42 Self::Head | Self::BodyIdle => 1,
43 Self::Handler | Self::ResponseAdmission => 2,
44 }
45 }
46}
47
48#[repr(u8)]
51#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
52pub enum WakeReason {
53 DeferredOutput,
54}
55
56#[derive(Clone, Copy, Eq, PartialEq)]
58pub enum Action<W = WakeReason> {
59 Expire(Phase),
60 Wake(W),
61}
62
63impl<W: Ord> Ord for Action<W> {
64 fn cmp(&self, other: &Self) -> cmp::Ordering {
65 match (self, other) {
66 (Self::Expire(left), Self::Expire(right)) => left.rank().cmp(&right.rank()),
67 (Self::Expire(_), Self::Wake(_)) => cmp::Ordering::Less,
68 (Self::Wake(_), Self::Expire(_)) => cmp::Ordering::Greater,
69 (Self::Wake(left), Self::Wake(right)) => left.cmp(right),
70 }
71 }
72}
73
74impl<W: Ord> PartialOrd for Action<W> {
75 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
76 Some(self.cmp(other))
77 }
78}
79
80#[derive(Clone, Copy, Eq, PartialEq)]
82pub struct Scheduled<P, W = WakeReason> {
83 pub at: P,
84 pub action: Action<W>,
85}
86
87impl<P, W> Scheduled<P, W> {
88 pub const fn expire(at: P, phase: Phase) -> Self {
89 Self {
90 at,
91 action: Action::Expire(phase),
92 }
93 }
94
95 pub const fn wake(at: P, reason: W) -> Self {
96 Self {
97 at,
98 action: Action::Wake(reason),
99 }
100 }
101}
102
103impl<P: Ord, W: Ord> Ord for Scheduled<P, W> {
104 fn cmp(&self, other: &Self) -> cmp::Ordering {
105 self.at
106 .cmp(&other.at)
107 .then_with(|| self.action.cmp(&other.action))
108 }
109}
110
111impl<P: Ord, W: Ord> PartialOrd for Scheduled<P, W> {
112 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
113 Some(self.cmp(other))
114 }
115}
116
117#[derive(Clone, Copy, Eq, PartialEq)]
119pub enum Mutation {
120 Unchanged,
121 Contents,
122 Earliest,
123}
124
125impl Mutation {
126 pub const fn is_earliest(self) -> bool {
127 matches!(self, Self::Earliest)
128 }
129
130 fn between<P: PartialEq>(before: Option<P>, after: Option<P>) -> Self {
131 if before == after {
132 Self::Contents
133 } else {
134 Self::Earliest
135 }
136 }
137}
138
139impl From<heap::Change> for Mutation {
140 fn from(change: heap::Change) -> Self {
141 match change {
142 heap::Change::Contents => Self::Contents,
143 heap::Change::Minimum => Self::Earliest,
144 }
145 }
146}
147
148#[derive(Clone, Copy, Debug, Eq, PartialEq)]
150pub enum Error<K> {
151 OutOfRange { key: K, capacity: usize },
152 Conflict { requested: K, current: K },
153}
154
155#[derive(Clone, Copy, Eq, PartialEq)]
157pub struct Expired<K, P, W = WakeReason> {
158 pub key: K,
159 pub scheduled: Scheduled<P, W>,
160}
161
162#[derive(Clone, Copy, Debug, Eq, PartialEq)]
164#[must_use = "account for renewal work and preserve the current minimum"]
165pub enum Poll<T> {
166 Idle,
167 Renewed,
170 Expired(T),
171}
172
173pub trait Book<K: manifold::raw::Key, P: Copy + Ord, W: Copy + Ord = WakeReason> {
175 fn schedule(&mut self, key: K, item: Scheduled<P, W>) -> Result<(), Error<K>>;
176 fn cancel(&mut self, key: K) -> Mutation;
177 fn cancel_one(&mut self, key: K, action: Action<W>) -> Mutation;
178 fn peek(&self) -> Option<(K, Scheduled<P, W>)>;
179 fn poll_due(&mut self, now: P) -> Poll<Expired<K, P, W>>;
180 fn pop_due(&mut self, now: P) -> Option<Expired<K, P, W>> {
184 loop {
185 match self.poll_due(now) {
186 Poll::Idle => return None,
187 Poll::Renewed => {}
188 Poll::Expired(item) => return Some(item),
189 }
190 }
191 }
192 fn len(&self) -> usize;
193 fn is_empty(&self) -> bool {
194 self.len() == 0
195 }
196}
197
198pub struct Agenda<K: manifold::raw::Key, P: Copy + Ord, W: Copy + Ord = WakeReason> {
201 heap: resident::Min<K, Scheduled<P, W>, Lane<P, W>>,
202}
203
204#[derive(Clone, Copy)]
205struct Lane<P, W> {
206 phases: timeline::Timeline<P>,
207 wake: Option<Wake<P, W>>,
208}
209
210#[derive(Clone, Copy, Eq, PartialEq)]
211struct Wake<P, W = WakeReason> {
212 at: P,
213 reason: W,
214}
215
216const _: () = {
217 assert!(mem::size_of::<Wake<u64>>() <= mem::size_of::<Scheduled<u64>>());
218 assert!(mem::align_of::<Wake<u64>>() == mem::align_of::<Scheduled<u64>>());
219};
220
221enum Plan<'a, K: manifold::raw::Key, P: Copy + Ord, W: Copy + Ord> {
222 Vacant(resident::Vacant<'a, K, Scheduled<P, W>, Lane<P, W>>),
223 Occupied(resident::Occupied<'a, K, Scheduled<P, W>, Lane<P, W>>),
224}
225
226impl<P: Copy + Ord, W: Copy + Ord> Lane<P, W> {
227 const EMPTY: Self = Self {
228 phases: timeline::Timeline::EMPTY,
229 wake: None,
230 };
231
232 fn get(&self, action: Action<W>) -> Option<Scheduled<P, W>> {
233 self.resident(action).filter(|item| item.action == action)
234 }
235
236 fn resident(&self, action: Action<W>) -> Option<Scheduled<P, W>> {
237 match action {
238 Action::Expire(phase) => self
239 .phases
240 .resident(phase)
241 .map(|item| Scheduled::expire(item.at, item.phase)),
242 Action::Wake(_) => self.wake.map(|item| Scheduled::wake(item.at, item.reason)),
243 }
244 }
245
246 fn set(&mut self, item: Scheduled<P, W>) -> Scheduled<P, W> {
249 match item.action {
250 Action::Expire(phase) => {
251 self.phases.set(item.at, phase);
252 }
253 Action::Wake(reason) => {
254 self.wake = Some(Wake {
255 at: item.at,
256 reason,
257 });
258 }
259 };
260 self.next().unwrap_or(item)
261 }
262
263 fn retire(&mut self, action: Action<W>) {
264 match action {
265 Action::Expire(phase) => self.phases.clear_phase(phase),
266 Action::Wake(reason) => {
267 if self.wake.is_some_and(|item| item.reason == reason) {
268 self.wake = None;
269 }
270 }
271 }
272 }
273
274 fn next(&self) -> Option<Scheduled<P, W>> {
275 self.phases
276 .next()
277 .map(|item| Scheduled::expire(item.at, item.phase))
278 .into_iter()
279 .chain(self.wake.map(|item| Scheduled::wake(item.at, item.reason)))
280 .min()
281 }
282
283 fn clear_wake(&mut self) -> Mutation {
284 if self.wake.is_none() {
285 return Mutation::Unchanged;
286 }
287 self.wake = None;
288 Mutation::Contents
289 }
290
291 fn transition<N, F>(
292 &mut self,
293 config: config::Config,
294 change: timeline::Transition,
295 now: N,
296 add: F,
297 ) -> Mutation
298 where
299 N: Copy,
300 F: FnMut(N, Positive) -> P,
301 {
302 let before = self.next();
303 let phases = self.phases.apply(config, change, now, add);
304 let wake = if change == timeline::Transition::Retired {
305 self.clear_wake()
306 } else {
307 Mutation::Unchanged
308 };
309 if phases == timeline::Change::Unchanged && wake == Mutation::Unchanged {
310 return Mutation::Unchanged;
311 }
312 Mutation::between(before, self.next())
313 }
314}
315
316impl<K: manifold::raw::Key, P: Copy + Ord, W: Copy + Ord> Agenda<K, P, W> {
317 fn try_entry(&mut self, key: K) -> Result<Plan<'_, K, P, W>, Error<K>> {
318 match self.heap.entry(key) {
319 Err(error) => Err(Error::OutOfRange {
320 key: error.key(),
321 capacity: error.capacity(),
322 }),
323 Ok(resident::Entry::Vacant(entry)) => Ok(Plan::Vacant(entry)),
324 Ok(resident::Entry::Occupied(entry)) => Ok(Plan::Occupied(entry)),
325 Ok(resident::Entry::Conflict(entry)) => Err(Error::Conflict {
326 requested: entry.requested(),
327 current: entry.current(),
328 }),
329 }
330 }
331
332 pub fn try_new(capacity: capacity::Count) -> Result<Self, heap::Error> {
333 Ok(Self {
334 heap: resident::Min::try_repeat(capacity, Lane::EMPTY)?,
335 })
336 }
337
338 pub fn transition<N, F>(
339 &mut self,
340 key: K,
341 config: config::Config,
342 change: timeline::Transition,
343 now: N,
344 add: F,
345 ) -> Result<(), Error<K>>
346 where
347 N: Copy,
348 F: FnMut(N, Positive) -> P,
349 {
350 if change == timeline::Transition::BodyProgress {
351 let Plan::Occupied(mut entry) = self.try_entry(key)? else {
352 return Ok(());
353 };
354 entry.resident_mut().phases.progress(config, now, add);
355 return Ok(());
356 }
357 let plan = self.try_entry(key)?;
358 match plan {
359 Plan::Vacant(mut entry) => {
360 let mut lane = Lane::EMPTY;
361 if lane.transition(config, change, now, add) == Mutation::Unchanged {
362 return Ok(());
363 }
364 let Some(next) = lane.next() else {
365 return Ok(());
366 };
367 *entry.resident_mut() = lane;
368 entry.insert(next);
369 }
370 Plan::Occupied(mut entry) => {
371 let mutation = entry.resident_mut().transition(config, change, now, add);
372 if mutation != Mutation::Earliest {
373 return Ok(());
374 }
375 match entry.resident().next() {
376 Some(next) => entry.set(next),
377 None => entry.retire(),
378 }
379 }
380 }
381 Ok(())
382 }
383}
384
385impl<K: manifold::raw::Key, P: Copy + Ord, W: Copy + Ord> Book<K, P, W> for Agenda<K, P, W> {
386 fn schedule(&mut self, key: K, item: Scheduled<P, W>) -> Result<(), Error<K>> {
387 let plan = self.try_entry(key)?;
388 match plan {
389 Plan::Vacant(mut entry) => {
390 let mut lane = Lane::EMPTY;
391 let minimum = lane.set(item);
392 *entry.resident_mut() = lane;
393 entry.insert(minimum);
394 }
395 Plan::Occupied(mut entry) => {
396 if entry.resident().resident(item.action) == Some(item) {
397 return Ok(());
398 }
399 let minimum = entry.resident_mut().set(item);
400 entry.set(minimum);
401 }
402 }
403 Ok(())
404 }
405
406 fn cancel(&mut self, key: K) -> Mutation {
407 let heap::Probe::Occupied(entry) = self.heap.probe(key) else {
408 return Mutation::Unchanged;
409 };
410 entry.retire_min().into()
411 }
412
413 fn cancel_one(&mut self, key: K, action: Action<W>) -> Mutation {
414 let heap::Probe::Occupied(mut entry) = self.heap.probe(key) else {
415 return Mutation::Unchanged;
416 };
417 if entry.resident().get(action).is_none() {
418 return Mutation::Unchanged;
419 }
420 let next = {
421 let lane = entry.resident_mut();
422 lane.retire(action);
423 lane.next()
424 };
425 match next {
426 Some(next) => entry.set_min(next).into(),
427 None => entry.retire_min().into(),
428 }
429 }
430
431 fn peek(&self) -> Option<(K, Scheduled<P, W>)> {
432 self.heap.peek().map(|(key, item)| (key, *item))
433 }
434
435 fn poll_due(&mut self, now: P) -> Poll<Expired<K, P, W>> {
436 let Some(mut entry) = self.heap.first_entry_if(|priority| !(priority.at > now)) else {
437 return Poll::Idle;
438 };
439 let key = entry.key();
440 let item = *entry.priority();
441 let (next, renewed) = {
442 let lane = entry.resident_mut();
443 let renewed = item.action == Action::Expire(Phase::BodyIdle)
444 && lane.phases.refresh(item.at) == timeline::Change::Changed;
445 if !renewed {
446 lane.retire(item.action);
447 }
448 (lane.next(), renewed)
449 };
450 match next {
451 Some(next) => entry.set(next),
452 None => entry.retire(),
453 }
454 if renewed {
455 Poll::Renewed
456 } else {
457 Poll::Expired(Expired {
458 key,
459 scheduled: item,
460 })
461 }
462 }
463
464 fn len(&self) -> usize {
465 self.heap.len()
466 }
467}