1use std::any::Any;
14use std::cell::{Cell, Ref, RefCell};
15use std::fmt;
16use std::future::poll_fn;
17use std::pin::Pin;
18use std::rc::Rc;
19use std::sync::Arc;
20use std::task::Poll;
21
22use crate::schema::{InputSpec, OutputSpec, Props};
23use crate::signal::{LogicBlock, SignalCursor};
24use crate::stream::{Broadcast, Event, Item, QueueControl, ReaderId};
25use crate::time::{Tick, TickRange, TimeBase};
26
27#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
29pub enum Wait {
30 #[default]
32 Nothing,
33 Input,
35 Output,
37 Budget,
39}
40
41#[derive(Debug, Default)]
43pub struct NodeCx {
44 budget: Cell<u32>,
45 waiting: Cell<Wait>,
46 ops: Cell<u64>,
47}
48
49impl NodeCx {
50 pub fn new() -> Rc<Self> {
52 Rc::new(Self::default())
53 }
54
55 pub fn refill(&self, budget: u32) {
57 self.budget.set(budget);
58 self.waiting.set(Wait::Nothing);
59 }
60
61 pub fn has_budget(&self) -> bool {
63 self.budget.get() > 0
64 }
65
66 pub fn spend(&self) {
68 self.budget.set(self.budget.get().saturating_sub(1));
69 self.ops.set(self.ops.get() + 1);
70 }
71
72 pub fn wait(&self, on: Wait) {
74 self.waiting.set(on);
75 }
76
77 pub fn waiting(&self) -> Wait {
79 self.waiting.get()
80 }
81
82 pub fn ops(&self) -> u64 {
84 self.ops.get()
85 }
86}
87
88#[derive(Clone, Copy, Debug, PartialEq, Eq)]
90pub struct Provenance {
91 pub span: TickRange,
93 pub settled: Tick,
95}
96
97impl Provenance {
98 pub const fn at(tick: Tick) -> Self {
100 Self {
101 span: TickRange::at(tick),
102 settled: tick,
103 }
104 }
105
106 pub const fn over(span: TickRange, settled: Tick) -> Self {
108 Self { span, settled }
109 }
110
111 pub const fn one(tick: Tick) -> Self {
113 Self {
114 span: TickRange::one(tick),
115 settled: tick,
116 }
117 }
118
119 pub fn join(self, other: Provenance) -> Provenance {
122 Provenance {
123 span: self.span.join(other.span),
124 settled: self.settled.max(other.settled),
125 }
126 }
127}
128
129pub(crate) type SharedQueue<T> = Rc<RefCell<Broadcast<T>>>;
131
132pub struct Inbox<T> {
134 queue: SharedQueue<T>,
135 reader: ReaderId,
136 cx: Rc<NodeCx>,
137}
138
139impl<T> fmt::Debug for Inbox<T> {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 f.debug_struct("Inbox")
142 .field("reader", &self.reader)
143 .finish_non_exhaustive()
144 }
145}
146
147impl<T> Inbox<T> {
148 pub(crate) fn new(queue: SharedQueue<T>, cx: Rc<NodeCx>) -> Self {
149 let reader = queue.borrow_mut().add_reader();
150 Self { queue, reader, cx }
151 }
152
153 pub fn cx(&self) -> &Rc<NodeCx> {
155 &self.cx
156 }
157
158 pub fn poll_peek(&self) -> Poll<Ref<'_, Item<T>>> {
161 let reader = self.reader;
162 match Ref::filter_map(self.queue.borrow(), |queue| queue.peek(reader)) {
163 Ok(item) => Poll::Ready(item),
164 Err(_) => {
165 self.cx.wait(Wait::Input);
166 Poll::Pending
167 }
168 }
169 }
170
171 pub fn advance(&self) {
173 self.queue.borrow_mut().advance(self.reader);
174 }
175
176 pub fn peek_with<'a, R, F>(&'a self, f: F) -> impl Future<Output = R> + 'a
179 where
180 F: FnOnce(&Item<T>) -> R + 'a,
181 {
182 let mut f = Some(f);
183 poll_fn(move |_| {
184 if !self.cx.has_budget() {
185 self.cx.wait(Wait::Budget);
186 return Poll::Pending;
187 }
188 let item = match self.poll_peek() {
189 Poll::Pending => return Poll::Pending,
190 Poll::Ready(item) => item,
191 };
192 match f.take() {
193 Some(f) => Poll::Ready(f(&item)),
194 None => Poll::Pending,
195 }
196 })
197 }
198
199 pub fn take(&self) {
201 self.advance();
202 self.cx.spend();
203 }
204
205 pub fn head_time(&self) -> Option<Tick> {
207 match self.poll_peek() {
208 Poll::Ready(item) => Some(item.time()),
209 Poll::Pending => None,
210 }
211 }
212
213 pub fn recv(&mut self) -> impl Future<Output = Item<T>> + '_
216 where
217 T: Clone,
218 {
219 poll_fn(move |_| {
220 if !self.cx.has_budget() {
221 self.cx.wait(Wait::Budget);
222 return Poll::Pending;
223 }
224 let item = match self.poll_peek() {
225 Poll::Pending => return Poll::Pending,
226 Poll::Ready(item) => item.clone(),
227 };
228 self.advance();
229 self.cx.spend();
230 Poll::Ready(item)
231 })
232 }
233}
234
235impl<T> Drop for Inbox<T> {
236 fn drop(&mut self) {
237 self.queue.borrow_mut().remove_reader(self.reader);
238 }
239}
240
241pub trait Readable {
243 fn poll_ready(&self) -> Poll<()>;
246}
247
248impl<T> Readable for Inbox<T> {
249 fn poll_ready(&self) -> Poll<()> {
250 self.poll_peek().map(|_| ())
251 }
252}
253
254pub fn ready_all<'a>(inputs: &'a [&'a dyn Readable]) -> impl Future<Output = ()> + 'a {
259 poll_fn(move |_| {
260 let mut ready = true;
261 for input in inputs {
262 ready &= input.poll_ready().is_ready();
263 }
264 if ready {
265 Poll::Ready(())
266 } else {
267 Poll::Pending
268 }
269 })
270}
271
272pub struct Outbox<T> {
274 queue: SharedQueue<T>,
275 cx: Rc<NodeCx>,
276 sink: Rc<SinkInner>,
277}
278
279impl<T> fmt::Debug for Outbox<T> {
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 f.debug_struct("Outbox").finish_non_exhaustive()
282 }
283}
284
285impl<T: 'static> Outbox<T> {
286 pub(crate) fn new(queue: SharedQueue<T>, cx: Rc<NodeCx>) -> Self {
287 let sink = Rc::new(SinkInner {
288 control: Rc::clone(&queue) as Rc<dyn QueueControl>,
289 cx: Rc::clone(&cx),
290 forwarded: Cell::new(None),
291 held: Cell::new(None),
292 });
293 Self { queue, cx, sink }
294 }
295}
296
297impl<T> Outbox<T> {
298 pub fn cx(&self) -> &Rc<NodeCx> {
300 &self.cx
301 }
302
303 pub fn has_room(&self) -> bool {
305 !self.queue.borrow().is_full()
306 }
307
308 fn poll_push(&mut self, slot: &mut Option<Item<T>>) -> Poll<()> {
309 let Some(item) = slot.take() else {
310 return Poll::Ready(());
311 };
312 if !self.cx.has_budget() {
313 *slot = Some(item);
314 self.cx.wait(Wait::Budget);
315 return Poll::Pending;
316 }
317 match self.queue.borrow_mut().try_push(item) {
318 Ok(()) => {
319 self.cx.spend();
320 Poll::Ready(())
321 }
322 Err(item) => {
323 *slot = Some(item);
324 self.cx.wait(Wait::Output);
325 Poll::Pending
326 }
327 }
328 }
329
330 fn push(&mut self, item: Item<T>) -> impl Future<Output = ()> + '_ {
331 let mut slot = Some(item);
332 poll_fn(move |_| self.poll_push(&mut slot))
333 }
334
335 pub fn emit(&mut self, at: Provenance, payload: T) -> impl Future<Output = ()> + '_ {
337 self.push(Item::Event(Event {
338 seq: 0,
339 span: at.span,
340 settled: at.settled,
341 payload,
342 }))
343 }
344
345 pub fn gap(&mut self, range: TickRange) -> impl Future<Output = ()> + '_ {
347 self.push(Item::Gap(range))
348 }
349
350 pub fn progress(&mut self, tick: Tick) -> impl Future<Output = ()> + '_ {
352 self.push(Item::Progress(tick))
353 }
354
355 pub fn end(&mut self, tick: Tick) -> impl Future<Output = ()> + '_ {
357 self.push(Item::End(tick))
358 }
359
360 pub fn progress_sink(&self) -> ProgressSink {
364 ProgressSink(Rc::clone(&self.sink))
365 }
366}
367
368struct SinkInner {
369 control: Rc<dyn QueueControl>,
370 cx: Rc<NodeCx>,
371 forwarded: Cell<Option<Tick>>,
373 held: Cell<Option<Tick>>,
375}
376
377#[derive(Clone)]
384pub struct ProgressSink(Rc<SinkInner>);
385
386impl fmt::Debug for ProgressSink {
387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388 f.debug_struct("ProgressSink")
389 .field("forwarded", &self.0.forwarded.get())
390 .field("held", &self.0.held.get())
391 .finish()
392 }
393}
394
395impl ProgressSink {
396 pub fn progress(&self, tick: Tick) -> impl Future<Output = ()> + '_ {
399 let inner = &*self.0;
400 let mut pending = !inner.forwarded.get().is_some_and(|last| last >= tick);
401 poll_fn(move |_| {
402 if !pending {
403 return Poll::Ready(());
404 }
405 if !inner.cx.has_budget() {
406 inner.cx.wait(Wait::Budget);
407 return Poll::Pending;
408 }
409 if inner.control.try_progress(tick) {
410 inner.cx.spend();
411 inner.forwarded.set(Some(tick));
412 pending = false;
413 Poll::Ready(())
414 } else {
415 inner.cx.wait(Wait::Output);
416 Poll::Pending
417 }
418 })
419 }
420
421 pub fn forwarded(&self) -> Option<Tick> {
423 self.0.forwarded.get()
424 }
425
426 pub fn hold(&self, from: Option<Tick>) {
431 self.0.held.set(from);
432 }
433
434 pub fn held(&self) -> Option<Tick> {
436 self.0.held.get()
437 }
438}
439
440#[derive(Clone, Debug)]
441pub struct NodeSpec {
442 pub name: String,
444 pub inputs: Vec<InputSpec>,
446 pub outputs: Vec<OutputSpec>,
448}
449
450pub type NodeFuture = Pin<Box<dyn Future<Output = ()>>>;
452
453pub trait Node: 'static {
455 fn spec(&self) -> NodeSpec;
457
458 fn start(self: Box<Self>, io: Io) -> NodeFuture;
460}
461
462impl Node for Box<dyn Node> {
463 fn spec(&self) -> NodeSpec {
464 (**self).spec()
465 }
466
467 fn start(self: Box<Self>, io: Io) -> NodeFuture {
468 (*self).start(io)
469 }
470}
471
472pub struct Io {
476 cx: Rc<NodeCx>,
477 inputs: Vec<Option<Rc<dyn Any>>>,
478 outputs: Vec<Option<Rc<dyn Any>>>,
479 time_base: Option<TimeBase>,
480 input_props: Vec<Props>,
481}
482
483impl fmt::Debug for Io {
484 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
485 f.debug_struct("Io")
486 .field("inputs", &self.inputs.len())
487 .field("outputs", &self.outputs.len())
488 .finish_non_exhaustive()
489 }
490}
491
492impl Io {
493 pub(crate) fn new(
494 cx: Rc<NodeCx>,
495 inputs: Vec<Rc<dyn Any>>,
496 outputs: Vec<Rc<dyn Any>>,
497 time_base: Option<TimeBase>,
498 input_props: Vec<Props>,
499 ) -> Self {
500 Self {
501 cx,
502 inputs: inputs.into_iter().map(Some).collect(),
503 outputs: outputs.into_iter().map(Some).collect(),
504 time_base,
505 input_props,
506 }
507 }
508
509 pub fn time_base(&self) -> Option<TimeBase> {
512 self.time_base
513 }
514
515 pub fn input_props(&self, index: usize) -> &Props {
522 self.input_props
523 .get(index)
524 .unwrap_or_else(|| panic!("the node declared no input port {index}"))
525 }
526
527 pub fn cx(&self) -> &Rc<NodeCx> {
529 &self.cx
530 }
531
532 fn take<T: 'static>(
533 slots: &mut [Option<Rc<dyn Any>>],
534 index: usize,
535 what: &str,
536 ) -> SharedQueue<T> {
537 let slot = slots
538 .get_mut(index)
539 .unwrap_or_else(|| panic!("the node declared no {what} port {index}"))
540 .take()
541 .unwrap_or_else(|| panic!("{what} port {index} was bound twice"));
542 slot.downcast::<RefCell<Broadcast<T>>>()
543 .unwrap_or_else(|_| panic!("{what} port {index} carries another event type"))
544 }
545
546 pub fn input<T: 'static>(&mut self, index: usize) -> Inbox<T> {
553 Inbox::new(
554 Self::take(&mut self.inputs, index, "input"),
555 Rc::clone(&self.cx),
556 )
557 }
558
559 pub fn signal(&mut self, index: usize) -> SignalCursor {
565 SignalCursor::new(self.input::<Arc<LogicBlock>>(index))
566 }
567
568 pub fn output<T: 'static>(&mut self, index: usize) -> Outbox<T> {
574 Outbox::new(
575 Self::take(&mut self.outputs, index, "output"),
576 Rc::clone(&self.cx),
577 )
578 }
579}
580
581#[cfg(test)]
582mod tests {
583 use std::task::{Context, Waker};
584
585 use super::*;
586
587 fn shared<T>(capacity: usize) -> SharedQueue<T> {
588 Rc::new(RefCell::new(Broadcast::new(capacity)))
589 }
590
591 fn poll<F: Future>(future: Pin<&mut F>) -> Poll<F::Output> {
592 future.poll(&mut Context::from_waker(Waker::noop()))
593 }
594
595 #[test]
596 fn reads_wait_for_input_and_writes_for_room() {
597 let queue = shared::<u8>(1);
598 let cx = NodeCx::new();
599 cx.refill(10);
600 let mut inbox = Inbox::new(Rc::clone(&queue), Rc::clone(&cx));
601 let mut outbox = Outbox::new(Rc::clone(&queue), Rc::clone(&cx));
602
603 let mut read = Box::pin(inbox.recv());
604 assert_eq!(poll(read.as_mut()), Poll::Pending);
605 assert_eq!(cx.waiting(), Wait::Input);
606 drop(read);
607
608 let mut write = Box::pin(outbox.emit(Provenance::at(Tick(3)), 7));
609 assert_eq!(poll(write.as_mut()), Poll::Ready(()));
610 drop(write);
611 let mut write = Box::pin(outbox.emit(Provenance::at(Tick(4)), 8));
612 assert_eq!(poll(write.as_mut()), Poll::Pending);
613 assert_eq!(cx.waiting(), Wait::Output);
614 drop(write);
615
616 let mut read = Box::pin(inbox.recv());
617 match poll(read.as_mut()) {
618 Poll::Ready(Item::Event(event)) => {
619 assert_eq!((event.seq, event.payload, event.settled), (0, 7, Tick(3)));
620 }
621 other => panic!("expected the event, got {other:?}"),
622 }
623 drop(read);
624 assert_eq!(cx.ops(), 2);
625 }
626
627 #[test]
628 fn a_spent_budget_pauses_without_consuming() {
629 let queue = shared::<u8>(4);
630 let cx = NodeCx::new();
631 cx.refill(1);
632 let mut inbox = Inbox::new(Rc::clone(&queue), Rc::clone(&cx));
633 let mut outbox = Outbox::new(Rc::clone(&queue), Rc::clone(&cx));
634 let mut write = Box::pin(outbox.emit(Provenance::at(Tick(0)), 1));
635 assert_eq!(poll(write.as_mut()), Poll::Ready(()));
636 drop(write);
637 let mut write = Box::pin(outbox.emit(Provenance::at(Tick(0)), 2));
638 assert_eq!(poll(write.as_mut()), Poll::Pending);
639 assert_eq!(cx.waiting(), Wait::Budget);
640 assert_eq!(queue.borrow().len(), 1);
641 drop(write);
642 cx.refill(1);
643 let mut read = Box::pin(inbox.recv());
644 assert!(matches!(poll(read.as_mut()), Poll::Ready(Item::Event(_))));
645 drop(read);
646 assert!(matches!(
647 poll(Box::pin(inbox.recv()).as_mut()),
648 Poll::Pending
649 ));
650 assert_eq!(cx.waiting(), Wait::Budget);
651 }
652
653 #[test]
654 fn a_sink_passes_progress_on_once_and_waits_for_room() {
655 let queue = shared::<u8>(1);
656 let cx = NodeCx::new();
657 cx.refill(10);
658 let mut inbox = Inbox::new(Rc::clone(&queue), Rc::clone(&cx));
659 let mut outbox = Outbox::new(Rc::clone(&queue), Rc::clone(&cx));
660 let sink = outbox.progress_sink();
661 assert_eq!(
662 poll(Box::pin(sink.progress(Tick(5))).as_mut()),
663 Poll::Ready(())
664 );
665 assert_eq!(sink.forwarded(), Some(Tick(5)));
666 assert_eq!(
668 poll(Box::pin(sink.progress(Tick(3))).as_mut()),
669 Poll::Ready(())
670 );
671 assert_eq!(
672 poll(Box::pin(sink.progress(Tick(7))).as_mut()),
673 Poll::Ready(())
674 );
675 assert_eq!(queue.borrow().len(), 1);
676 assert!(matches!(
677 poll(Box::pin(inbox.recv()).as_mut()),
678 Poll::Ready(Item::Progress(Tick(7)))
679 ));
680 assert_eq!(
682 poll(Box::pin(outbox.emit(Provenance::at(Tick(8)), 1)).as_mut()),
683 Poll::Ready(())
684 );
685 assert_eq!(
686 poll(Box::pin(sink.progress(Tick(9))).as_mut()),
687 Poll::Pending
688 );
689 assert_eq!(cx.waiting(), Wait::Output);
690 assert!(matches!(
691 poll(Box::pin(inbox.recv()).as_mut()),
692 Poll::Ready(Item::Event(_))
693 ));
694 assert_eq!(
695 poll(Box::pin(sink.progress(Tick(9))).as_mut()),
696 Poll::Ready(())
697 );
698 let other = outbox.progress_sink();
700 other.hold(Some(Tick(4)));
701 assert_eq!(sink.held(), Some(Tick(4)));
702 sink.hold(None);
703 assert_eq!(other.held(), None);
704 }
705
706 #[test]
707 fn dropping_an_inbox_detaches_its_reader() {
708 let queue = shared::<u8>(1);
709 let cx = NodeCx::new();
710 cx.refill(10);
711 let inbox = Inbox::new(Rc::clone(&queue), Rc::clone(&cx));
712 let mut outbox = Outbox::new(Rc::clone(&queue), Rc::clone(&cx));
713 assert_eq!(
714 poll(Box::pin(outbox.emit(Provenance::at(Tick(0)), 1)).as_mut()),
715 Poll::Ready(())
716 );
717 assert!(!outbox.has_room());
718 drop(inbox);
719 assert!(outbox.has_room());
720 }
721}