Creates an operator implementation from supplied logic constructor. It returns a shutdown button that when pressed it will cause the logic future to be dropped and input handles to be drained. The button can be converted into a token by using [`Button::press_on_drop`]
(self, constructor: B)
| 569 | /// be drained. The button can be converted into a token by using |
| 570 | /// [`Button::press_on_drop`] |
| 571 | pub fn build<B, L>(self, constructor: B) -> Button |
| 572 | where |
| 573 | B: FnOnce(Vec<Capability<T>>) -> L, |
| 574 | L: Future + 'static, |
| 575 | { |
| 576 | let operator_waker = self.operator_waker; |
| 577 | let mut input_frontiers = self.input_frontiers; |
| 578 | let mut input_queues = self.input_queues; |
| 579 | let mut output_flushes = self.output_flushes; |
| 580 | let mut shutdown_handle = self.shutdown_handle; |
| 581 | self.builder.build_reschedule(move |caps| { |
| 582 | let mut logic_fut = Some(Box::pin(constructor(caps))); |
| 583 | move |new_frontiers| { |
| 584 | operator_waker.active.store(true, Ordering::SeqCst); |
| 585 | for (i, queue) in input_queues.iter_mut().enumerate() { |
| 586 | // First, discover if there are any frontier notifications |
| 587 | let cur = &mut input_frontiers[i]; |
| 588 | let new = new_frontiers[i].frontier(); |
| 589 | if PartialOrder::less_than(&cur.borrow(), &new) { |
| 590 | queue.notify_progress(new.to_owned()); |
| 591 | *cur = new.to_owned(); |
| 592 | } |
| 593 | // Then accept all input into local queues. This step registers the received |
| 594 | // messages with progress tracking. |
| 595 | queue.accept_input(); |
| 596 | } |
| 597 | operator_waker.active.store(false, Ordering::SeqCst); |
| 598 | |
| 599 | // If our worker pressed the button we stop scheduling the logic future and/or |
| 600 | // draining the input handles to stop producing data and frontier updates |
| 601 | // downstream. |
| 602 | if shutdown_handle.local_pressed() { |
| 603 | // When all workers press their buttons we drop the logic future and start |
| 604 | // draining the input handles. |
| 605 | if shutdown_handle.all_pressed() { |
| 606 | logic_fut = None; |
| 607 | for queue in input_queues.iter_mut() { |
| 608 | queue.drain_input(); |
| 609 | } |
| 610 | false |
| 611 | } else { |
| 612 | true |
| 613 | } |
| 614 | } else { |
| 615 | // Schedule the logic future if any of the wakers above marked the task as ready |
| 616 | if let Some(fut) = logic_fut.as_mut() { |
| 617 | if operator_waker.task_ready.load(Ordering::SeqCst) { |
| 618 | let waker = futures_util::task::waker_ref(&operator_waker); |
| 619 | let mut cx = Context::from_waker(&waker); |
| 620 | operator_waker.task_ready.store(false, Ordering::SeqCst); |
| 621 | if Pin::new(fut).poll(&mut cx).is_ready() { |
| 622 | // We're done with logic so deallocate the task |
| 623 | logic_fut = None; |
| 624 | } |
| 625 | // Flush all the outputs before exiting |
| 626 | for flush in output_flushes.iter_mut() { |
| 627 | (flush)(); |
| 628 | } |