1use alloc::boxed::Box;
16use alloc::sync::Arc;
17use alloc::vec::Vec;
18use core::future::Future;
19use core::sync::atomic::{AtomicUsize, Ordering};
20
21use super::autoplug::PadRequest;
22use crate::bus::BusHandle;
23use crate::caps::{Caps, CapsSet};
24use crate::clock::{ClockCandidate, ClockPriority};
25use crate::clock::{DynAsyncClock, PipelineClock};
26use crate::element::{
27 AsyncElement, BoxFuture, ConfigureOutcome, DynAsyncElement, ElementBound, OutputSink,
28 OutputSinkExt, PushOutcome, Reconfigure,
29};
30use crate::error::G2gError;
31use crate::fanout::{
32 DuplexInbound, Merger, MultiDuplexSession, MultiInputElement, MultiOutputSink, MultiSenderSink,
33};
34use crate::format_element::{CapsConstraint, CapsPreferences};
35use crate::frame::PipelinePacket;
36use crate::graph::Graph;
37use crate::memory::{DomainSet, MemoryDomainKind};
38use crate::property::{ElementMetadata, PropError, PropValue, PropertySpec};
39use crate::query::{AllocationParams, LatencyReport};
40use crate::runtime::channel::{
41 bounded, packet_bytes, ProbeAction, ProbeSlot, Receiver, SendError, Sender, SenderSink,
42};
43use crate::runtime::coordinator::log_caps_rejected;
44use crate::runtime::graph_runner::{run_graph_inner, GraphNodeRef};
45use crate::runtime::instrument::{EdgeCounters, ElementProbe, Probe};
46use crate::runtime::join::{dynamic_join, join_all, select2, Either};
47use crate::runtime::observe::{link_tapped, register_runner_tap, EdgeTap, TapEdge, TapNode};
48use crate::runtime::runner::{LinkCapacity, NullSink, RunStats, SourceLoop};
49use crate::runtime::{NodeRole, Observer};
50use spin::Mutex;
51
52pub trait DynSourceLoop: ElementBound {
57 fn intercept_caps<'a>(&'a mut self) -> BoxFuture<'a, Result<Caps, G2gError>>;
58
59 fn produced_caps<'a>(&'a mut self) -> BoxFuture<'a, Result<CapsSet, G2gError>> {
65 Box::pin(async move { Ok(CapsSet::one(self.intercept_caps().await?)) })
66 }
67
68 fn caps_preferences(&self) -> Option<CapsPreferences> {
72 None
73 }
74
75 fn configure_pipeline(&mut self, absolute_caps: &Caps) -> Result<ConfigureOutcome, G2gError>;
76
77 fn run<'a>(&'a mut self, out: &'a mut dyn OutputSink) -> BoxFuture<'a, Result<u64, G2gError>>;
78
79 fn reconfigure(&mut self, request: Reconfigure) -> Result<Caps, G2gError>;
80
81 fn latency(&self) -> LatencyReport;
84
85 fn output_memory(&self) -> MemoryDomainKind {
88 MemoryDomainKind::System
89 }
90
91 fn output_domains(&self) -> DomainSet {
94 DomainSet::only(self.output_memory())
95 }
96
97 fn query_duration(&self) -> Option<u64> {
100 None
101 }
102
103 fn provide_clock(&self) -> Option<ClockCandidate>;
106
107 fn configure_allocation(&mut self, params: &AllocationParams);
110
111 fn configured_output_caps(&self) -> Option<Caps> {
114 None
115 }
116
117 fn probe_output_caps(&mut self) -> Option<Caps> {
120 self.configured_output_caps()
121 }
122
123 fn properties(&self) -> &'static [PropertySpec] {
126 &[]
127 }
128
129 fn metadata(&self) -> ElementMetadata {
132 ElementMetadata::default()
133 }
134
135 fn log_category(&self) -> &'static str {
138 "source"
139 }
140
141 fn set_instance_name(&mut self, _name: alloc::string::String) {}
143
144 fn set_log_category(&mut self, _category: alloc::string::String) {}
146
147 fn set_property(&mut self, _name: &str, _value: PropValue) -> Result<(), PropError> {
150 Err(PropError::Unknown)
151 }
152
153 fn get_property(&self, _name: &str) -> Option<PropValue> {
156 None
157 }
158}
159
160impl<T: SourceLoop> DynSourceLoop for T {
165 fn intercept_caps<'a>(&'a mut self) -> BoxFuture<'a, Result<Caps, G2gError>> {
166 Box::pin(SourceLoop::intercept_caps(self))
167 }
168
169 fn produced_caps<'a>(&'a mut self) -> BoxFuture<'a, Result<CapsSet, G2gError>> {
174 Box::pin(async move {
175 match SourceLoop::caps_constraint(self).await? {
176 CapsConstraint::Produces(set) => Ok(set),
177 CapsConstraint::LegacySource(caps) => Ok(CapsSet::one(caps)),
178 _ => Err(G2gError::CapsMismatch),
179 }
180 })
181 }
182
183 fn caps_preferences(&self) -> Option<CapsPreferences> {
184 SourceLoop::caps_preferences(self)
185 }
186
187 fn configure_pipeline(&mut self, absolute_caps: &Caps) -> Result<ConfigureOutcome, G2gError> {
188 SourceLoop::configure_pipeline(self, absolute_caps)
189 }
190
191 fn run<'a>(&'a mut self, out: &'a mut dyn OutputSink) -> BoxFuture<'a, Result<u64, G2gError>> {
192 Box::pin(SourceLoop::run(self, out))
193 }
194
195 fn reconfigure(&mut self, request: Reconfigure) -> Result<Caps, G2gError> {
196 SourceLoop::reconfigure(self, request)
197 }
198
199 fn latency(&self) -> LatencyReport {
200 SourceLoop::latency(self)
201 }
202
203 fn output_memory(&self) -> MemoryDomainKind {
204 SourceLoop::output_memory(self)
205 }
206
207 fn output_domains(&self) -> DomainSet {
208 SourceLoop::output_domains(self)
209 }
210
211 fn query_duration(&self) -> Option<u64> {
212 SourceLoop::query_duration(self)
213 }
214
215 fn provide_clock(&self) -> Option<ClockCandidate> {
216 SourceLoop::provide_clock(self)
217 }
218
219 fn configure_allocation(&mut self, params: &AllocationParams) {
220 SourceLoop::configure_allocation(self, params)
221 }
222
223 fn configured_output_caps(&self) -> Option<Caps> {
224 SourceLoop::configured_output_caps(self)
225 }
226
227 fn probe_output_caps(&mut self) -> Option<Caps> {
228 SourceLoop::probe_output_caps(self)
229 }
230
231 fn properties(&self) -> &'static [PropertySpec] {
232 SourceLoop::properties(self)
233 }
234
235 fn metadata(&self) -> ElementMetadata {
236 SourceLoop::metadata(self)
237 }
238
239 fn log_category(&self) -> &'static str {
240 crate::log::short_type_name::<T>()
241 }
242
243 fn set_instance_name(&mut self, name: alloc::string::String) {
244 SourceLoop::set_instance_name(self, name)
245 }
246
247 fn set_log_category(&mut self, category: alloc::string::String) {
248 SourceLoop::set_log_category(self, category)
249 }
250
251 fn set_property(&mut self, name: &str, value: PropValue) -> Result<(), PropError> {
252 SourceLoop::set_property(self, name, value)
253 }
254
255 fn get_property(&self, name: &str) -> Option<PropValue> {
256 SourceLoop::get_property(self, name)
257 }
258}
259
260impl<'b> DynSourceLoop for &'b mut (dyn DynSourceLoop + 'b) {
265 fn intercept_caps<'a>(&'a mut self) -> BoxFuture<'a, Result<Caps, G2gError>> {
266 (**self).intercept_caps()
267 }
268
269 fn produced_caps<'a>(&'a mut self) -> BoxFuture<'a, Result<CapsSet, G2gError>> {
270 (**self).produced_caps()
271 }
272
273 fn caps_preferences(&self) -> Option<CapsPreferences> {
274 (**self).caps_preferences()
275 }
276
277 fn configure_pipeline(&mut self, absolute_caps: &Caps) -> Result<ConfigureOutcome, G2gError> {
278 (**self).configure_pipeline(absolute_caps)
279 }
280
281 fn run<'a>(&'a mut self, out: &'a mut dyn OutputSink) -> BoxFuture<'a, Result<u64, G2gError>> {
282 (**self).run(out)
283 }
284
285 fn reconfigure(&mut self, request: Reconfigure) -> Result<Caps, G2gError> {
286 (**self).reconfigure(request)
287 }
288
289 fn latency(&self) -> LatencyReport {
290 (**self).latency()
291 }
292
293 fn output_memory(&self) -> MemoryDomainKind {
294 (**self).output_memory()
295 }
296
297 fn output_domains(&self) -> DomainSet {
298 (**self).output_domains()
299 }
300
301 fn query_duration(&self) -> Option<u64> {
302 (**self).query_duration()
303 }
304
305 fn provide_clock(&self) -> Option<ClockCandidate> {
306 (**self).provide_clock()
307 }
308
309 fn configure_allocation(&mut self, params: &AllocationParams) {
310 (**self).configure_allocation(params)
311 }
312
313 fn configured_output_caps(&self) -> Option<Caps> {
314 (**self).configured_output_caps()
315 }
316
317 fn properties(&self) -> &'static [PropertySpec] {
318 (**self).properties()
319 }
320
321 fn metadata(&self) -> ElementMetadata {
322 (**self).metadata()
323 }
324
325 fn log_category(&self) -> &'static str {
326 (**self).log_category()
327 }
328
329 fn set_instance_name(&mut self, name: alloc::string::String) {
330 (**self).set_instance_name(name)
331 }
332
333 fn set_log_category(&mut self, category: alloc::string::String) {
334 (**self).set_log_category(category)
335 }
336
337 fn set_property(&mut self, name: &str, value: PropValue) -> Result<(), PropError> {
338 (**self).set_property(name, value)
339 }
340
341 fn get_property(&self, name: &str) -> Option<PropValue> {
342 (**self).get_property(name)
343 }
344}
345
346fn select_branch_caps(produced: &CapsSet, pad: &CapsConstraint<'_>) -> Result<Caps, G2gError> {
355 let accepted = produced
356 .alternatives()
357 .iter()
358 .filter_map(|alt| alt.fixate().ok())
359 .find(|fixed| pad.accepts(fixed));
360 match accepted {
361 Some(caps) => Ok(caps),
362 None => produced.fixate().ok_or(G2gError::CapsMismatch),
363 }
364}
365
366pub trait DynMultiInputElement: ElementBound {
373 fn input_count(&self) -> usize;
374 fn input_pts_ordered(&self) -> bool;
377 fn output_follows_input(&self) -> Option<usize>;
380 fn tick_interval_ns(&self) -> Option<u64> {
383 None
384 }
385 fn input_pad_index(&self, req: &PadRequest, ordinal: usize) -> Option<usize>;
388 fn caps_constraint_as_input(&self, input: usize) -> CapsConstraint<'_>;
389 fn caps_constraint_for_output(&self) -> Result<CapsConstraint<'_>, G2gError>;
390 fn input_domains(&self) -> DomainSet {
393 DomainSet::ALL
394 }
395 fn propose_allocation_for_input(&self, input: usize, caps: &Caps) -> Option<AllocationParams>;
397 fn propose_allocation_for_output(&self, caps: &Caps) -> Option<AllocationParams>;
399 fn configure_allocation_for_output(&mut self, params: &AllocationParams);
401 fn output_caps(&self) -> Result<Caps, G2gError>;
403 fn configure_pipeline(
404 &mut self,
405 input: usize,
406 absolute_caps: &Caps,
407 ) -> Result<ConfigureOutcome, G2gError>;
408 fn process<'a>(
409 &'a mut self,
410 input: usize,
411 packet: PipelinePacket,
412 out: &'a mut dyn OutputSink,
413 ) -> BoxFuture<'a, Result<(), G2gError>>;
414 fn properties(&self) -> &'static [PropertySpec];
415 fn set_property(&mut self, name: &str, value: PropValue) -> Result<(), PropError>;
416 fn get_property(&self, name: &str) -> Option<PropValue>;
417 fn metadata(&self) -> ElementMetadata;
420
421 fn reverse_channel(&self, _input: usize) -> Option<crate::fanout::ReverseChannel> {
425 None
426 }
427
428 fn is_terminal(&self) -> bool {
430 false
431 }
432
433 fn accepts_runtime_input(&self, _pad: usize, _caps: &Caps) -> bool {
436 true
437 }
438
439 fn set_instance_name(&mut self, _name: alloc::string::String) {}
442
443 fn set_log_category(&mut self, _category: alloc::string::String) {}
445
446 #[doc(hidden)]
456 fn drive_muxer_arm<'s>(
457 self: Box<Self>,
458 io: crate::runtime::MuxerArmIo<'s>,
459 ) -> BoxFuture<'s, Result<u64, G2gError>>
460 where
461 Self: 's;
462
463 #[cfg(feature = "multi-thread")]
466 #[doc(hidden)]
467 fn drive_muxer_arm_owned_tick(
468 self: Box<Self>,
469 io: crate::runtime::MuxerArmOwnedTickIo,
470 ) -> BoxFuture<'static, Result<u64, G2gError>>
471 where
472 Self: 'static;
473
474 #[doc(hidden)]
476 fn drive_fanin_sink_arm<'s>(
477 self: Box<Self>,
478 io: crate::runtime::FaninSinkArmIo,
479 ) -> BoxFuture<'s, Result<u64, G2gError>>
480 where
481 Self: 's;
482}
483
484impl<T: MultiInputElement> DynMultiInputElement for T {
485 fn input_count(&self) -> usize {
486 MultiInputElement::input_count(self)
487 }
488
489 fn input_pts_ordered(&self) -> bool {
490 MultiInputElement::input_pts_ordered(self)
491 }
492
493 fn output_follows_input(&self) -> Option<usize> {
494 MultiInputElement::output_follows_input(self)
495 }
496
497 fn tick_interval_ns(&self) -> Option<u64> {
498 MultiInputElement::tick_interval_ns(self)
499 }
500
501 fn input_pad_index(&self, req: &PadRequest, ordinal: usize) -> Option<usize> {
502 MultiInputElement::input_pad_index(self, req, ordinal)
503 }
504
505 fn caps_constraint_as_input(&self, input: usize) -> CapsConstraint<'_> {
506 MultiInputElement::caps_constraint_as_input(self, input)
507 }
508
509 fn caps_constraint_for_output(&self) -> Result<CapsConstraint<'_>, G2gError> {
510 MultiInputElement::caps_constraint_for_output(self)
511 }
512
513 fn input_domains(&self) -> DomainSet {
514 MultiInputElement::input_domains(self)
515 }
516
517 fn propose_allocation_for_input(&self, input: usize, caps: &Caps) -> Option<AllocationParams> {
518 MultiInputElement::propose_allocation_for_input(self, input, caps)
519 }
520
521 fn propose_allocation_for_output(&self, caps: &Caps) -> Option<AllocationParams> {
522 MultiInputElement::propose_allocation_for_output(self, caps)
523 }
524
525 fn configure_allocation_for_output(&mut self, params: &AllocationParams) {
526 MultiInputElement::configure_allocation_for_output(self, params)
527 }
528
529 fn output_caps(&self) -> Result<Caps, G2gError> {
530 MultiInputElement::output_caps(self)
531 }
532
533 fn configure_pipeline(
534 &mut self,
535 input: usize,
536 absolute_caps: &Caps,
537 ) -> Result<ConfigureOutcome, G2gError> {
538 MultiInputElement::configure_pipeline(self, input, absolute_caps)
539 }
540
541 fn process<'a>(
542 &'a mut self,
543 input: usize,
544 packet: PipelinePacket,
545 out: &'a mut dyn OutputSink,
546 ) -> BoxFuture<'a, Result<(), G2gError>> {
547 Box::pin(MultiInputElement::process(self, input, packet, out))
548 }
549
550 fn properties(&self) -> &'static [PropertySpec] {
551 MultiInputElement::properties(self)
552 }
553
554 fn set_property(&mut self, name: &str, value: PropValue) -> Result<(), PropError> {
555 MultiInputElement::set_property(self, name, value)
556 }
557
558 fn get_property(&self, name: &str) -> Option<PropValue> {
559 MultiInputElement::get_property(self, name)
560 }
561
562 fn metadata(&self) -> ElementMetadata {
563 MultiInputElement::metadata(self)
564 }
565
566 fn reverse_channel(&self, input: usize) -> Option<crate::fanout::ReverseChannel> {
567 MultiInputElement::reverse_channel(self, input)
568 }
569
570 fn is_terminal(&self) -> bool {
571 MultiInputElement::is_terminal(self)
572 }
573
574 fn accepts_runtime_input(&self, pad: usize, caps: &Caps) -> bool {
575 MultiInputElement::accepts_runtime_input(self, pad, caps)
576 }
577
578 fn set_instance_name(&mut self, name: alloc::string::String) {
579 MultiInputElement::set_instance_name(self, name)
580 }
581
582 fn set_log_category(&mut self, category: alloc::string::String) {
583 MultiInputElement::set_log_category(self, category)
584 }
585
586 fn drive_muxer_arm<'s>(
587 self: Box<Self>,
588 io: crate::runtime::MuxerArmIo<'s>,
589 ) -> BoxFuture<'s, Result<u64, G2gError>>
590 where
591 Self: 's,
592 {
593 if MultiInputElement::input_pts_ordered(&*self) {
594 Box::pin(crate::runtime::graph_runner::muxer_arm_pts(*self, io))
595 } else {
596 Box::pin(crate::runtime::graph_runner::muxer_arm(*self, io))
597 }
598 }
599
600 #[cfg(feature = "multi-thread")]
601 fn drive_muxer_arm_owned_tick(
602 self: Box<Self>,
603 io: crate::runtime::MuxerArmOwnedTickIo,
604 ) -> BoxFuture<'static, Result<u64, G2gError>>
605 where
606 Self: 'static,
607 {
608 Box::pin(crate::runtime::graph_runner::muxer_arm_owned_tick(
609 *self, io,
610 ))
611 }
612
613 fn drive_fanin_sink_arm<'s>(
614 self: Box<Self>,
615 io: crate::runtime::FaninSinkArmIo,
616 ) -> BoxFuture<'s, Result<u64, G2gError>>
617 where
618 Self: 's,
619 {
620 Box::pin(crate::runtime::graph_runner::fanin_sink_arm(*self, io))
621 }
622}
623
624struct MuxRef<'b>(&'b mut (dyn DynMultiInputElement + 'b));
629
630impl MultiInputElement for MuxRef<'_> {
631 type ProcessFuture<'a>
632 = BoxFuture<'a, Result<(), G2gError>>
633 where
634 Self: 'a;
635
636 fn input_count(&self) -> usize {
637 self.0.input_count()
638 }
639
640 fn input_pts_ordered(&self) -> bool {
641 self.0.input_pts_ordered()
642 }
643
644 fn tick_interval_ns(&self) -> Option<u64> {
645 self.0.tick_interval_ns()
646 }
647
648 fn intercept_caps(&self, input: usize, upstream_caps: &Caps) -> Result<Caps, G2gError> {
652 let upstream = CapsConstraint::LegacySource(upstream_caps.clone());
653 let pad = self.0.caps_constraint_as_input(input);
654 crate::runtime::solver::solve_linear(&[&upstream, &pad])
655 .map_err(|_| G2gError::CapsMismatch)?
656 .last()
657 .cloned()
658 .ok_or(G2gError::CapsMismatch)
659 }
660
661 fn caps_constraint_as_input(&self, input: usize) -> CapsConstraint<'_> {
662 self.0.caps_constraint_as_input(input)
663 }
664
665 fn caps_constraint_for_output(&self) -> Result<CapsConstraint<'_>, G2gError> {
666 self.0.caps_constraint_for_output()
667 }
668
669 fn configure_pipeline(
670 &mut self,
671 input: usize,
672 absolute_caps: &Caps,
673 ) -> Result<ConfigureOutcome, G2gError> {
674 self.0.configure_pipeline(input, absolute_caps)
675 }
676
677 fn output_caps(&self) -> Result<Caps, G2gError> {
678 self.0.output_caps()
679 }
680
681 fn output_follows_input(&self) -> Option<usize> {
682 self.0.output_follows_input()
683 }
684
685 fn input_pad_index(&self, req: &PadRequest, ordinal: usize) -> Option<usize> {
686 self.0.input_pad_index(req, ordinal)
687 }
688
689 fn accepts_runtime_input(&self, pad: usize, caps: &Caps) -> bool {
690 self.0.accepts_runtime_input(pad, caps)
691 }
692
693 fn reverse_channel(&self, input: usize) -> Option<crate::fanout::ReverseChannel> {
694 self.0.reverse_channel(input)
695 }
696
697 fn is_terminal(&self) -> bool {
698 self.0.is_terminal()
699 }
700
701 fn input_domains(&self) -> DomainSet {
702 self.0.input_domains()
703 }
704
705 fn propose_allocation_for_input(&self, input: usize, caps: &Caps) -> Option<AllocationParams> {
706 self.0.propose_allocation_for_input(input, caps)
707 }
708
709 fn propose_allocation_for_output(&self, caps: &Caps) -> Option<AllocationParams> {
710 self.0.propose_allocation_for_output(caps)
711 }
712
713 fn configure_allocation_for_output(&mut self, params: &AllocationParams) {
714 self.0.configure_allocation_for_output(params)
715 }
716
717 fn process<'a>(
718 &'a mut self,
719 input: usize,
720 packet: PipelinePacket,
721 out: &'a mut dyn OutputSink,
722 ) -> Self::ProcessFuture<'a> {
723 self.0.process(input, packet, out)
724 }
725
726 fn properties(&self) -> &'static [PropertySpec] {
727 self.0.properties()
728 }
729
730 fn set_property(&mut self, name: &str, value: PropValue) -> Result<(), PropError> {
731 self.0.set_property(name, value)
732 }
733
734 fn get_property(&self, name: &str) -> Option<PropValue> {
735 self.0.get_property(name)
736 }
737
738 fn metadata(&self) -> ElementMetadata {
739 self.0.metadata()
740 }
741
742 fn set_instance_name(&mut self, name: alloc::string::String) {
743 self.0.set_instance_name(name)
744 }
745
746 fn set_log_category(&mut self, category: alloc::string::String) {
747 self.0.set_log_category(category)
748 }
749}
750
751impl<'b> DynMultiInputElement for &'b mut (dyn DynMultiInputElement + 'b) {
756 fn input_count(&self) -> usize {
757 (**self).input_count()
758 }
759
760 fn input_pts_ordered(&self) -> bool {
761 (**self).input_pts_ordered()
762 }
763
764 fn output_follows_input(&self) -> Option<usize> {
765 (**self).output_follows_input()
766 }
767
768 fn tick_interval_ns(&self) -> Option<u64> {
769 (**self).tick_interval_ns()
770 }
771
772 fn input_pad_index(&self, req: &PadRequest, ordinal: usize) -> Option<usize> {
773 (**self).input_pad_index(req, ordinal)
774 }
775
776 fn caps_constraint_as_input(&self, input: usize) -> CapsConstraint<'_> {
777 (**self).caps_constraint_as_input(input)
778 }
779
780 fn caps_constraint_for_output(&self) -> Result<CapsConstraint<'_>, G2gError> {
781 (**self).caps_constraint_for_output()
782 }
783
784 fn input_domains(&self) -> DomainSet {
785 (**self).input_domains()
786 }
787
788 fn propose_allocation_for_input(&self, input: usize, caps: &Caps) -> Option<AllocationParams> {
789 (**self).propose_allocation_for_input(input, caps)
790 }
791
792 fn propose_allocation_for_output(&self, caps: &Caps) -> Option<AllocationParams> {
793 (**self).propose_allocation_for_output(caps)
794 }
795
796 fn configure_allocation_for_output(&mut self, params: &AllocationParams) {
797 (**self).configure_allocation_for_output(params)
798 }
799
800 fn output_caps(&self) -> Result<Caps, G2gError> {
801 (**self).output_caps()
802 }
803
804 fn configure_pipeline(
805 &mut self,
806 input: usize,
807 absolute_caps: &Caps,
808 ) -> Result<ConfigureOutcome, G2gError> {
809 (**self).configure_pipeline(input, absolute_caps)
810 }
811
812 fn process<'a>(
813 &'a mut self,
814 input: usize,
815 packet: PipelinePacket,
816 out: &'a mut dyn OutputSink,
817 ) -> BoxFuture<'a, Result<(), G2gError>> {
818 (**self).process(input, packet, out)
819 }
820
821 fn properties(&self) -> &'static [PropertySpec] {
822 (**self).properties()
823 }
824
825 fn set_property(&mut self, name: &str, value: PropValue) -> Result<(), PropError> {
826 (**self).set_property(name, value)
827 }
828
829 fn get_property(&self, name: &str) -> Option<PropValue> {
830 (**self).get_property(name)
831 }
832
833 fn metadata(&self) -> ElementMetadata {
834 (**self).metadata()
835 }
836
837 fn reverse_channel(&self, input: usize) -> Option<crate::fanout::ReverseChannel> {
838 (**self).reverse_channel(input)
839 }
840
841 fn is_terminal(&self) -> bool {
842 (**self).is_terminal()
843 }
844
845 fn accepts_runtime_input(&self, pad: usize, caps: &Caps) -> bool {
846 (**self).accepts_runtime_input(pad, caps)
847 }
848
849 fn set_instance_name(&mut self, name: alloc::string::String) {
850 (**self).set_instance_name(name)
851 }
852
853 fn set_log_category(&mut self, category: alloc::string::String) {
854 (**self).set_log_category(category)
855 }
856
857 fn drive_muxer_arm<'s>(
858 self: Box<Self>,
859 io: crate::runtime::MuxerArmIo<'s>,
860 ) -> BoxFuture<'s, Result<u64, G2gError>>
861 where
862 Self: 's,
863 {
864 let mux = MuxRef(*self);
865 if MultiInputElement::input_pts_ordered(&mux) {
866 Box::pin(crate::runtime::graph_runner::muxer_arm_pts(mux, io))
867 } else {
868 Box::pin(crate::runtime::graph_runner::muxer_arm(mux, io))
869 }
870 }
871
872 #[cfg(feature = "multi-thread")]
873 fn drive_muxer_arm_owned_tick(
874 self: Box<Self>,
875 io: crate::runtime::MuxerArmOwnedTickIo,
876 ) -> BoxFuture<'static, Result<u64, G2gError>>
877 where
878 Self: 'static,
879 {
880 Box::pin(crate::runtime::graph_runner::muxer_arm_owned_tick(
881 MuxRef(*self),
882 io,
883 ))
884 }
885
886 fn drive_fanin_sink_arm<'s>(
887 self: Box<Self>,
888 io: crate::runtime::FaninSinkArmIo,
889 ) -> BoxFuture<'s, Result<u64, G2gError>>
890 where
891 Self: 's,
892 {
893 Box::pin(crate::runtime::graph_runner::fanin_sink_arm(
894 MuxRef(*self),
895 io,
896 ))
897 }
898}
899
900pub async fn run_fanin_sink<Snk, Clk>(
909 sources: Vec<&mut dyn DynSourceLoop>,
910 merger: &mut Merger,
911 sink: &mut Snk,
912 clock: &Clk,
913 link_capacity: impl Into<LinkCapacity>,
914) -> Result<RunStats, G2gError>
915where
916 Snk: AsyncElement,
917 Clk: PipelineClock,
918{
919 run_fanin_sink_inner(sources, merger, sink, clock, link_capacity, None).await
920}
921
922pub async fn run_fanin_sink_observed<Snk, Clk>(
928 sources: Vec<&mut dyn DynSourceLoop>,
929 merger: &mut Merger,
930 sink: &mut Snk,
931 clock: &Clk,
932 link_capacity: impl Into<LinkCapacity>,
933 observer: &Observer,
934) -> Result<RunStats, G2gError>
935where
936 Snk: AsyncElement,
937 Clk: PipelineClock,
938{
939 run_fanin_sink_inner(sources, merger, sink, clock, link_capacity, Some(observer)).await
940}
941
942async fn run_fanin_sink_inner<Snk, Clk>(
943 sources: Vec<&mut dyn DynSourceLoop>,
944 merger: &mut Merger,
945 sink: &mut Snk,
946 _clock: &Clk,
947 link_capacity: impl Into<LinkCapacity>,
948 observer: Option<&Observer>,
949) -> Result<RunStats, G2gError>
950where
951 Snk: AsyncElement,
952 Clk: PipelineClock,
953{
954 let link_capacity: usize = link_capacity.into().get();
955 let input_count = sources.len();
956 assert!(input_count > 0, "fan-in needs at least one source");
957 assert!(
958 merger.input_count() == input_count,
959 "merger input count must match the number of sources"
960 );
961
962 let mut sources = sources;
963 let mut namer = crate::log::InstanceNamer::new();
967 let mut source_names: Vec<alloc::string::String> = Vec::with_capacity(input_count);
968 for source in sources.iter_mut() {
969 let name = namer.add(source.log_category(), None);
970 source.set_instance_name(name.clone());
971 source_names.push(name);
972 }
973 let sink_name = namer.add(crate::log::short_type_name::<Snk>(), None);
974 AsyncElement::set_instance_name(sink, sink_name.clone());
975 let sink_probe = ElementProbe::new(sink_name);
978
979 let mut fixated_caps: Vec<Caps> = Vec::with_capacity(input_count);
984 for source in sources.iter_mut() {
985 let produced = source.produced_caps().await?;
986 let fixated = {
987 let sink_constraint = sink.caps_constraint_as_sink();
988 select_branch_caps(&produced, &sink_constraint)?
989 };
990 source.configure_pipeline(&fixated)?.reject_refixate()?;
991 fixated_caps.push(fixated);
992 }
993 let merged_caps = fixated_caps[0].clone();
994 sink.configure_pipeline(&merged_caps)?.reject_refixate()?;
995
996 let tap = observer.is_some();
998 let mut input_senders = Vec::with_capacity(input_count);
999 let mut input_receivers = Vec::with_capacity(input_count);
1000 let mut taps = Vec::with_capacity(input_count + 1);
1001 for _ in 0..input_count {
1002 let (tx, rx, edge) = link_tapped(link_capacity, tap);
1003 input_senders.push(tx);
1004 input_receivers.push(rx);
1005 taps.push(edge);
1006 }
1007 let (out_tx, out_rx, out_tap) = link_tapped(link_capacity, tap);
1008 taps.push(out_tap);
1009 let live_inputs = Arc::new(AtomicUsize::new(input_count));
1010
1011 if let Some(obs) = observer {
1014 let merger_id = input_count;
1015 let sink_id = input_count + 1;
1016 let mut nodes: Vec<TapNode> = source_names
1017 .iter()
1018 .map(|n| (n.clone(), NodeRole::Source, None))
1019 .collect();
1020 nodes.push((alloc::string::String::new(), NodeRole::Muxer, None));
1021 nodes.push((
1022 alloc::string::String::from(sink_probe.name()),
1023 NodeRole::Sink,
1024 Some(sink_probe.clone()),
1025 ));
1026 let mut edges: Vec<TapEdge> = Vec::with_capacity(input_count + 1);
1027 let mut taps = taps.into_iter();
1028 for (i, caps) in fixated_caps.iter().enumerate() {
1029 edges.push((i, merger_id, caps.clone(), taps.next().expect("input tap")));
1030 }
1031 edges.push((
1032 merger_id,
1033 sink_id,
1034 merged_caps.clone(),
1035 taps.next().expect("output tap"),
1036 ));
1037 register_runner_tap(obs, nodes, edges);
1038 }
1039
1040 let mut source_arms: Vec<BoxFuture<'_, Result<u64, G2gError>>> =
1041 Vec::with_capacity(input_count);
1042 for (source, in_tx) in sources.into_iter().zip(input_senders) {
1043 source_arms.push(Box::pin(async move {
1044 let mut adapter = SenderSink::new(in_tx);
1045 source.run(&mut adapter).await
1046 }));
1047 }
1048
1049 let mut forwarder_arms: Vec<BoxFuture<'_, Result<u64, G2gError>>> =
1050 Vec::with_capacity(input_count);
1051 for (idx, in_rx) in input_receivers.into_iter().enumerate() {
1052 let handle = merger.handle();
1053 let out_tx_i = out_tx.clone();
1054 let live = live_inputs.clone();
1055 forwarder_arms.push(Box::pin(async move {
1056 let mut out = SenderSink::new(out_tx_i);
1057 loop {
1058 match in_rx.recv().await {
1059 Some(PipelinePacket::Eos) | None => {
1060 if live.fetch_sub(1, Ordering::SeqCst) == 1 {
1062 out.push(PipelinePacket::Eos).await?;
1063 }
1064 return Ok::<u64, G2gError>(0);
1065 }
1066 Some(packet) => {
1067 if handle.selected() == idx {
1068 out.push(packet).await?;
1069 }
1070 }
1073 }
1074 }
1075 }));
1076 }
1077 drop(out_tx);
1080
1081 let probe_for_sink = sink_probe.clone();
1082 let sink_arm: BoxFuture<'_, Result<u64, G2gError>> = Box::pin(async move {
1083 let mut null = NullSink;
1084 let mut consumed: u64 = 0;
1085 loop {
1086 match out_rx.recv().await {
1087 Some(PipelinePacket::Eos) => {
1088 sink.process(PipelinePacket::Eos, &mut null).await?;
1089 return Ok::<u64, G2gError>(consumed);
1090 }
1091 Some(PipelinePacket::CapsChanged(new_caps)) => {
1092 match log_caps_rejected(
1093 Some(probe_for_sink.name()),
1094 &new_caps,
1095 sink.configure_pipeline(&new_caps),
1096 )? {
1097 ConfigureOutcome::Accepted => {
1098 sink.process(PipelinePacket::CapsChanged(new_caps), &mut null)
1099 .await?;
1100 }
1101 ConfigureOutcome::ReFixate(counter) => {
1102 out_rx.request_reconfigure(Reconfigure::Propose(counter));
1103 }
1104 }
1105 }
1106 Some(packet) => {
1107 let is_data = matches!(packet, PipelinePacket::DataFrame(_));
1108 if is_data {
1109 consumed += 1;
1110 probe_for_sink.record_fill(out_rx.fill_percent());
1111 }
1112 let t0 = is_data.then(ElementProbe::mark).flatten();
1113 sink.process(packet, &mut null).await?;
1114 if is_data {
1115 probe_for_sink.record_proc_since(t0);
1116 }
1117 }
1118 None => return Ok(consumed),
1119 }
1120 }
1121 });
1122
1123 let mut arms = Vec::with_capacity(2 * input_count + 1);
1125 arms.extend(source_arms);
1126 arms.extend(forwarder_arms);
1127 arms.push(sink_arm);
1128
1129 let results = join_all(arms).await;
1130 let mut counts = Vec::with_capacity(results.len());
1131 for r in results {
1132 counts.push(r?);
1133 }
1134 let emitted: u64 = counts[0..input_count].iter().copied().sum();
1135 let consumed = counts[2 * input_count];
1136 Ok(RunStats {
1140 frames_emitted: emitted,
1141 frames_consumed: consumed,
1142 frames_dropped: 0,
1143 latency: LatencyReport::ZERO,
1144 allocation: None,
1145 clock_priority: ClockPriority::SystemFallback,
1146 base_time_ns: 0,
1147 coordinator_events: 0,
1148 per_element: alloc::vec![sink_probe.snapshot()],
1149 })
1150}
1151
1152struct TaggingSink {
1156 idx: usize,
1157 tx: Sender<(usize, PipelinePacket)>,
1158 reverse: Option<crate::fanout::ReverseChannel>,
1162 counters: Option<Arc<EdgeCounters>>,
1166 probe: Option<ProbeSlot>,
1171 staged: Option<(usize, PipelinePacket)>,
1175 staged_bytes: u64,
1177 delivered_data_frames: u64,
1181 channel_closed: bool,
1184}
1185
1186impl OutputSink for TaggingSink {
1187 fn begin_push(&mut self) {
1188 self.staged = None;
1190 }
1191
1192 fn poll_push(
1193 &mut self,
1194 cx: &mut core::task::Context<'_>,
1195 packet_slot: &mut Option<PipelinePacket>,
1196 ) -> core::task::Poll<Result<PushOutcome, G2gError>> {
1197 use core::task::Poll;
1198 if self.staged.is_none() {
1199 let packet = packet_slot
1200 .take()
1201 .expect("poll_push called without a packet");
1202 if let Some(p) = &self.probe {
1205 if p.action(&packet) == ProbeAction::Drop {
1206 return Poll::Ready(Ok(PushOutcome::Accepted));
1207 }
1208 }
1209 self.staged_bytes = packet_bytes(&packet);
1210 self.staged = Some((self.idx, packet));
1211 }
1212 let is_data = matches!(self.staged, Some((_, PipelinePacket::DataFrame(_))));
1213 match self.tx.poll_send(cx, &mut self.staged) {
1214 Poll::Pending => Poll::Pending,
1215 Poll::Ready(Ok(())) => {
1216 if is_data {
1217 self.delivered_data_frames += 1;
1218 }
1219 if let Some(c) = &self.counters {
1220 c.record_packet(self.staged_bytes, 0);
1221 }
1222 Poll::Ready(Ok(self
1223 .reverse
1224 .as_ref()
1225 .and_then(|rc| rc.take())
1226 .unwrap_or(PushOutcome::Accepted)))
1227 }
1228 Poll::Ready(Err(_)) => {
1229 self.staged = None;
1230 self.channel_closed = true;
1231 Poll::Ready(Err(G2gError::Shutdown))
1232 }
1233 }
1234 }
1235}
1236
1237impl TaggingSink {
1238 fn new(
1239 idx: usize,
1240 tx: Sender<(usize, PipelinePacket)>,
1241 reverse: Option<crate::fanout::ReverseChannel>,
1242 counters: Option<Arc<EdgeCounters>>,
1243 probe: Option<ProbeSlot>,
1244 ) -> Self {
1245 Self {
1246 idx,
1247 tx,
1248 reverse,
1249 counters,
1250 probe,
1251 staged: None,
1252 staged_bytes: 0,
1253 delivered_data_frames: 0,
1254 channel_closed: false,
1255 }
1256 }
1257}
1258
1259fn wind_down_when_session_ended(
1265 result: Result<u64, G2gError>,
1266 adapter: &TaggingSink,
1267) -> Result<u64, G2gError> {
1268 match result {
1269 Err(G2gError::Shutdown) if adapter.channel_closed => Ok(adapter.delivered_data_frames),
1270 other => other,
1271 }
1272}
1273
1274pub async fn run_fanin_session<Sess, Clk>(
1292 sources: Vec<&mut dyn DynSourceLoop>,
1293 session: &mut Sess,
1294 clock: &Clk,
1295 link_capacity: impl Into<LinkCapacity>,
1296) -> Result<RunStats, G2gError>
1297where
1298 Sess: MultiInputElement,
1299 Clk: PipelineClock,
1300{
1301 run_fanin_session_inner(sources, session, clock, link_capacity, None).await
1302}
1303
1304pub async fn run_fanin_session_observed<Sess, Clk>(
1309 sources: Vec<&mut dyn DynSourceLoop>,
1310 session: &mut Sess,
1311 clock: &Clk,
1312 link_capacity: impl Into<LinkCapacity>,
1313 observer: &Observer,
1314) -> Result<RunStats, G2gError>
1315where
1316 Sess: MultiInputElement,
1317 Clk: PipelineClock,
1318{
1319 run_fanin_session_inner(sources, session, clock, link_capacity, Some(observer)).await
1320}
1321
1322async fn run_fanin_session_inner<Sess, Clk>(
1323 sources: Vec<&mut dyn DynSourceLoop>,
1324 session: &mut Sess,
1325 _clock: &Clk,
1326 link_capacity: impl Into<LinkCapacity>,
1327 observer: Option<&Observer>,
1328) -> Result<RunStats, G2gError>
1329where
1330 Sess: MultiInputElement,
1331 Clk: PipelineClock,
1332{
1333 let link_capacity: usize = link_capacity.into().get();
1334 let input_count = sources.len();
1335 assert!(input_count > 0, "fan-in session needs at least one source");
1336 assert!(
1337 session.input_count() == input_count,
1338 "session input count must match the number of sources"
1339 );
1340
1341 let mut sources = sources;
1344 let mut namer = crate::log::InstanceNamer::new();
1345 let mut source_names: Vec<alloc::string::String> = Vec::with_capacity(input_count);
1346 for source in sources.iter_mut() {
1347 let name = namer.add(source.log_category(), None);
1348 source.set_instance_name(name.clone());
1349 source_names.push(name);
1350 }
1351 let session_probe = ElementProbe::new(namer.add(crate::log::short_type_name::<Sess>(), None));
1352
1353 let mut fixated_caps: Vec<Caps> = Vec::with_capacity(input_count);
1357 for (i, source) in sources.iter_mut().enumerate() {
1358 let produced = source.produced_caps().await?;
1359 let fixated = {
1360 let pad_constraint = MultiInputElement::caps_constraint_as_input(session, i);
1361 select_branch_caps(&produced, &pad_constraint)?
1362 };
1363 source.configure_pipeline(&fixated)?.reject_refixate()?;
1364 MultiInputElement::configure_pipeline(session, i, &fixated)?.reject_refixate()?;
1365 fixated_caps.push(fixated);
1366 }
1367
1368 let (tx, rx) = bounded::<(usize, PipelinePacket)>(link_capacity);
1370 let live_inputs = Arc::new(AtomicUsize::new(input_count));
1371 let counters: Vec<Option<Arc<EdgeCounters>>> = (0..input_count)
1375 .map(|_| observer.map(|_| Arc::new(EdgeCounters::default())))
1376 .collect();
1377 let probes: Vec<Option<ProbeSlot>> = (0..input_count)
1378 .map(|_| observer.map(|_| ProbeSlot::default()))
1379 .collect();
1380
1381 if let Some(obs) = observer {
1382 let session_id = input_count;
1383 let mut nodes: Vec<TapNode> = source_names
1384 .iter()
1385 .map(|n| (n.clone(), NodeRole::Source, None))
1386 .collect();
1387 nodes.push((
1388 alloc::string::String::from(session_probe.name()),
1389 NodeRole::Muxer,
1390 Some(session_probe.clone()),
1391 ));
1392 let edges: Vec<TapEdge> = fixated_caps
1393 .iter()
1394 .zip(counters.iter())
1395 .zip(probes.iter())
1396 .enumerate()
1397 .map(|(i, ((caps, c), p))| {
1398 (
1399 i,
1400 session_id,
1401 caps.clone(),
1402 EdgeTap {
1403 probe: p.clone().unwrap_or_default(),
1404 counters: c.clone(),
1405 },
1406 )
1407 })
1408 .collect();
1409 register_runner_tap(obs, nodes, edges);
1410 }
1411
1412 let reverse: Vec<Option<crate::fanout::ReverseChannel>> = (0..input_count)
1415 .map(|i| session.reverse_channel(i))
1416 .collect();
1417
1418 let mut source_arms: Vec<BoxFuture<'_, Result<u64, G2gError>>> =
1419 Vec::with_capacity(input_count);
1420 for (i, source) in sources.into_iter().enumerate() {
1421 let tx_i = tx.clone();
1422 let reverse_i = reverse[i].clone();
1423 let counters_i = counters[i].clone();
1424 let probe_i = probes[i].clone();
1425 source_arms.push(Box::pin(async move {
1426 let mut adapter = TaggingSink::new(i, tx_i, reverse_i, counters_i, probe_i);
1427 let result = source.run(&mut adapter).await;
1428 wind_down_when_session_ended(result, &adapter)
1429 }));
1430 }
1431 drop(tx);
1433
1434 let probe_for_session = session_probe.clone();
1435 let session_arm: BoxFuture<'_, Result<u64, G2gError>> = Box::pin(async move {
1436 let mut null = NullSink;
1437 let mut consumed: u64 = 0;
1438 loop {
1439 match rx.recv().await {
1440 Some((idx, PipelinePacket::Eos)) => {
1441 session.process(idx, PipelinePacket::Eos, &mut null).await?;
1445 if live_inputs.fetch_sub(1, Ordering::SeqCst) == 1 {
1446 return Ok::<u64, G2gError>(consumed);
1447 }
1448 }
1449 Some((idx, PipelinePacket::CapsChanged(new_caps))) => {
1450 MultiInputElement::configure_pipeline(session, idx, &new_caps)?
1454 .reject_refixate()?;
1455 session
1456 .process(idx, PipelinePacket::CapsChanged(new_caps), &mut null)
1457 .await?;
1458 }
1459 Some((idx, packet)) => {
1460 let is_data = matches!(packet, PipelinePacket::DataFrame(_));
1461 if is_data {
1462 consumed += 1;
1463 probe_for_session.record_fill(rx.fill_percent());
1464 }
1465 let t0 = is_data.then(ElementProbe::mark).flatten();
1466 session.process(idx, packet, &mut null).await?;
1467 if is_data {
1468 probe_for_session.record_proc_since(t0);
1469 }
1470 }
1471 None => return Ok(consumed),
1472 }
1473 }
1474 });
1475
1476 let mut arms = Vec::with_capacity(input_count + 1);
1477 arms.extend(source_arms);
1478 arms.push(session_arm);
1479
1480 let results = join_all(arms).await;
1481 let mut counts = Vec::with_capacity(results.len());
1482 for r in results {
1483 counts.push(r?);
1484 }
1485 let emitted: u64 = counts[0..input_count].iter().copied().sum();
1486 let consumed = counts[input_count];
1487 Ok(RunStats {
1488 frames_emitted: emitted,
1489 frames_consumed: consumed,
1490 frames_dropped: 0,
1491 latency: LatencyReport::ZERO,
1492 allocation: None,
1493 clock_priority: ClockPriority::SystemFallback,
1494 base_time_ns: 0,
1495 coordinator_events: 0,
1496 per_element: alloc::vec![session_probe.snapshot()],
1497 })
1498}
1499
1500type ReverseMap = Arc<Mutex<Vec<Option<crate::fanout::ReverseChannel>>>>;
1505
1506struct InboundReceiver {
1510 rx: Receiver<(usize, PipelinePacket)>,
1511 reverse: ReverseMap,
1512}
1513
1514impl DuplexInbound for InboundReceiver {
1515 fn recv(&mut self) -> BoxFuture<'_, Option<(usize, PipelinePacket)>> {
1516 Box::pin(async move { self.rx.recv().await })
1517 }
1518
1519 fn reverse_channel(&self, input: usize) -> Option<crate::fanout::ReverseChannel> {
1520 self.reverse.lock().get(input).cloned().flatten()
1521 }
1522}
1523
1524async fn duplex_sink_arm(
1528 sink: &mut dyn DynAsyncElement,
1529 rx: crate::runtime::channel::LinkReceiver,
1530 probe: &ElementProbe,
1531) -> Result<u64, G2gError> {
1532 let mut null = NullSink;
1533 let mut consumed: u64 = 0;
1534 loop {
1535 match rx.recv().await {
1536 Some(PipelinePacket::Eos) => {
1537 sink.process(PipelinePacket::Eos, &mut null).await?;
1538 return Ok(consumed);
1539 }
1540 Some(PipelinePacket::CapsChanged(new_caps)) => {
1541 match log_caps_rejected(
1542 Some(probe.name()),
1543 &new_caps,
1544 sink.configure_pipeline(&new_caps),
1545 )? {
1546 ConfigureOutcome::Accepted => {
1547 sink.process(PipelinePacket::CapsChanged(new_caps), &mut null)
1548 .await?;
1549 }
1550 ConfigureOutcome::ReFixate(counter) => {
1551 rx.request_reconfigure(Reconfigure::Propose(counter));
1552 }
1553 }
1554 }
1555 Some(packet) => {
1556 let is_data = matches!(packet, PipelinePacket::DataFrame(_));
1557 if is_data {
1558 consumed += 1;
1559 probe.record_fill(rx.fill_percent());
1560 }
1561 let t0 = is_data.then(ElementProbe::mark).flatten();
1562 sink.process(packet, &mut null).await?;
1563 if is_data {
1564 probe.record_proc_since(t0);
1565 }
1566 }
1567 None => return Ok(consumed),
1568 }
1569 }
1570}
1571
1572pub async fn run_duplex_session<Sess, Clk>(
1597 sources: Vec<&mut dyn DynSourceLoop>,
1598 session: &mut Sess,
1599 sinks: Vec<&mut dyn DynAsyncElement>,
1600 clock: &Clk,
1601 link_capacity: impl Into<LinkCapacity>,
1602) -> Result<RunStats, G2gError>
1603where
1604 Sess: MultiDuplexSession,
1605 Clk: PipelineClock,
1606{
1607 run_duplex_session_inner(sources, session, sinks, clock, link_capacity, None).await
1608}
1609
1610pub async fn run_duplex_session_observed<Sess, Clk>(
1615 sources: Vec<&mut dyn DynSourceLoop>,
1616 session: &mut Sess,
1617 sinks: Vec<&mut dyn DynAsyncElement>,
1618 clock: &Clk,
1619 link_capacity: impl Into<LinkCapacity>,
1620 observer: &Observer,
1621) -> Result<RunStats, G2gError>
1622where
1623 Sess: MultiDuplexSession,
1624 Clk: PipelineClock,
1625{
1626 run_duplex_session_inner(
1627 sources,
1628 session,
1629 sinks,
1630 clock,
1631 link_capacity,
1632 Some(observer),
1633 )
1634 .await
1635}
1636
1637#[allow(clippy::too_many_arguments)]
1638async fn run_duplex_session_inner<Sess, Clk>(
1639 sources: Vec<&mut dyn DynSourceLoop>,
1640 session: &mut Sess,
1641 sinks: Vec<&mut dyn DynAsyncElement>,
1642 _clock: &Clk,
1643 link_capacity: impl Into<LinkCapacity>,
1644 observer: Option<&Observer>,
1645) -> Result<RunStats, G2gError>
1646where
1647 Sess: MultiDuplexSession,
1648 Clk: PipelineClock,
1649{
1650 let link_capacity: usize = link_capacity.into().get();
1651 let input_count = sources.len();
1652 let output_count = sinks.len();
1653 assert!(
1654 input_count > 0,
1655 "duplex session needs at least one send source"
1656 );
1657 assert!(
1658 output_count > 0,
1659 "duplex session needs at least one recv sink"
1660 );
1661 assert!(
1662 session.input_count() == input_count,
1663 "session input count must match the number of send sources"
1664 );
1665 assert!(
1666 session.output_count() == output_count,
1667 "session output count must match the number of recv sinks"
1668 );
1669
1670 let mut sources = sources;
1673 let mut sinks = sinks;
1674 let mut namer = crate::log::InstanceNamer::new();
1675 let mut source_names: Vec<alloc::string::String> = Vec::with_capacity(input_count);
1676 for source in sources.iter_mut() {
1677 let name = namer.add(source.log_category(), None);
1678 source.set_instance_name(name.clone());
1679 source_names.push(name);
1680 }
1681 let session_name = namer.add(crate::log::short_type_name::<Sess>(), None);
1682 let mut sink_probes = Vec::with_capacity(output_count);
1683 for sink in sinks.iter_mut() {
1684 let name = namer.add(sink.log_category(), None);
1685 sink.set_instance_name(name.clone());
1686 sink_probes.push(ElementProbe::new(name));
1687 }
1688
1689 let mut input_caps: Vec<Caps> = Vec::with_capacity(input_count);
1693 for (i, source) in sources.iter_mut().enumerate() {
1694 let produced = source.produced_caps().await?;
1695 let fixated = {
1696 let pad_constraint = MultiDuplexSession::caps_constraint_as_input(session, i);
1697 select_branch_caps(&produced, &pad_constraint)?
1698 };
1699 source.configure_pipeline(&fixated)?.reject_refixate()?;
1700 session.configure_input(i, &fixated)?.reject_refixate()?;
1701 input_caps.push(fixated);
1702 }
1703 let mut output_caps: Vec<Caps> = Vec::with_capacity(output_count);
1706 for (o, sink) in sinks.iter_mut().enumerate() {
1707 let fixated = session.output_caps(o)?.fixate()?;
1708 sink.configure_pipeline(&fixated)?.reject_refixate()?;
1709 output_caps.push(fixated);
1710 }
1711
1712 let (in_tx, in_rx) = bounded::<(usize, PipelinePacket)>(link_capacity);
1714 let in_counters: Vec<Option<Arc<EdgeCounters>>> = (0..input_count)
1717 .map(|_| observer.map(|_| Arc::new(EdgeCounters::default())))
1718 .collect();
1719 let in_probes: Vec<Option<ProbeSlot>> = (0..input_count)
1720 .map(|_| observer.map(|_| ProbeSlot::default()))
1721 .collect();
1722 let tap = observer.is_some();
1724 let mut branch_senders = Vec::with_capacity(output_count);
1725 let mut branch_receivers = Vec::with_capacity(output_count);
1726 let mut branch_taps = Vec::with_capacity(output_count);
1727 for _ in 0..output_count {
1728 let (tx, rx, edge) = link_tapped(link_capacity, tap);
1729 branch_senders.push(SenderSink::new(tx));
1730 branch_receivers.push(rx);
1731 branch_taps.push(edge);
1732 }
1733
1734 if let Some(obs) = observer {
1735 let session_id = input_count;
1736 let mut nodes: Vec<TapNode> = source_names
1737 .iter()
1738 .map(|n| (n.clone(), NodeRole::Source, None))
1739 .collect();
1740 nodes.push((session_name, NodeRole::Muxer, None));
1741 for probe in &sink_probes {
1742 nodes.push((
1743 alloc::string::String::from(probe.name()),
1744 NodeRole::Sink,
1745 Some(probe.clone()),
1746 ));
1747 }
1748 let mut edges: Vec<TapEdge> = Vec::with_capacity(input_count + output_count);
1749 for (i, ((caps, c), p)) in input_caps
1750 .iter()
1751 .zip(in_counters.iter())
1752 .zip(in_probes.iter())
1753 .enumerate()
1754 {
1755 edges.push((
1756 i,
1757 session_id,
1758 caps.clone(),
1759 EdgeTap {
1760 probe: p.clone().unwrap_or_default(),
1761 counters: c.clone(),
1762 },
1763 ));
1764 }
1765 for (o, (caps, edge)) in output_caps
1766 .iter()
1767 .zip(core::mem::take(&mut branch_taps))
1768 .enumerate()
1769 {
1770 edges.push((session_id, session_id + 1 + o, caps.clone(), edge));
1771 }
1772 register_runner_tap(obs, nodes, edges);
1773 }
1774
1775 let reverse: Vec<Option<crate::fanout::ReverseChannel>> = (0..input_count)
1780 .map(|i| session.reverse_channel(i))
1781 .collect();
1782
1783 let mut source_arms: Vec<BoxFuture<'_, Result<u64, G2gError>>> =
1784 Vec::with_capacity(input_count);
1785 for (i, source) in sources.into_iter().enumerate() {
1786 let tx_i = in_tx.clone();
1787 let reverse_i = reverse[i].clone();
1788 let counters_i = in_counters[i].clone();
1789 let probe_i = in_probes[i].clone();
1790 source_arms.push(Box::pin(async move {
1791 let mut adapter = TaggingSink::new(i, tx_i, reverse_i, counters_i, probe_i);
1792 let result = source.run(&mut adapter).await;
1793 wind_down_when_session_ended(result, &adapter)
1794 }));
1795 }
1796 drop(in_tx);
1799
1800 let session_arm: BoxFuture<'_, Result<u64, G2gError>> = Box::pin(async move {
1801 let mut inbound = InboundReceiver {
1802 rx: in_rx,
1803 reverse: Arc::new(Mutex::new(Vec::new())),
1804 };
1805 let mut multi = MultiSenderSink::new(branch_senders);
1806 session.run(&mut inbound, &mut multi).await
1807 });
1808
1809 let mut sink_arms: Vec<BoxFuture<'_, Result<u64, G2gError>>> = Vec::with_capacity(output_count);
1810 for ((sink, rx), probe) in sinks
1811 .into_iter()
1812 .zip(branch_receivers)
1813 .zip(sink_probes.iter().cloned())
1814 {
1815 sink_arms.push(Box::pin(
1816 async move { duplex_sink_arm(sink, rx, &probe).await },
1817 ));
1818 }
1819
1820 let mut arms = Vec::with_capacity(input_count + 1 + output_count);
1822 arms.extend(source_arms);
1823 arms.push(session_arm);
1824 arms.extend(sink_arms);
1825
1826 let results = join_all(arms).await;
1827 let mut counts = Vec::with_capacity(results.len());
1828 for r in results {
1829 counts.push(r?);
1830 }
1831 let emitted: u64 = counts[0..input_count].iter().copied().sum();
1832 let consumed: u64 = counts[input_count + 1..].iter().copied().sum();
1833 Ok(RunStats {
1834 frames_emitted: emitted,
1835 frames_consumed: consumed,
1836 frames_dropped: 0,
1837 latency: LatencyReport::ZERO,
1838 allocation: None,
1839 clock_priority: ClockPriority::SystemFallback,
1840 base_time_ns: 0,
1841 coordinator_events: 0,
1842 per_element: crate::runtime::snapshot_all(
1843 &sink_probes.into_iter().map(Some).collect::<Vec<_>>(),
1844 ),
1845 })
1846}
1847
1848pub async fn run_muxer_sink<Mux, Snk, Clk>(
1866 sources: Vec<&mut dyn DynSourceLoop>,
1867 mux: &mut Mux,
1868 sink: &mut Snk,
1869 clock: &Clk,
1870 link_capacity: impl Into<LinkCapacity>,
1871) -> Result<RunStats, G2gError>
1872where
1873 Mux: MultiInputElement,
1874 Snk: AsyncElement,
1875 Clk: PipelineClock,
1876{
1877 run_muxer_sink_inner(sources, mux, sink, clock, link_capacity, None, None).await
1878}
1879
1880pub async fn run_muxer_sink_with_bus<Mux, Snk, Clk>(
1884 sources: Vec<&mut dyn DynSourceLoop>,
1885 mux: &mut Mux,
1886 sink: &mut Snk,
1887 clock: &Clk,
1888 link_capacity: impl Into<LinkCapacity>,
1889 bus: &BusHandle,
1890) -> Result<RunStats, G2gError>
1891where
1892 Mux: MultiInputElement,
1893 Snk: AsyncElement,
1894 Clk: PipelineClock,
1895{
1896 run_muxer_sink_inner(sources, mux, sink, clock, link_capacity, Some(bus), None).await
1897}
1898
1899#[allow(clippy::too_many_arguments)]
1900async fn run_muxer_sink_inner<Mux, Snk, Clk>(
1901 sources: Vec<&mut dyn DynSourceLoop>,
1902 mux: &mut Mux,
1903 sink: &mut Snk,
1904 clock: &Clk,
1905 link_capacity: impl Into<LinkCapacity>,
1906 bus: Option<&BusHandle>,
1907 ticker: Option<&dyn DynAsyncClock>,
1908) -> Result<RunStats, G2gError>
1909where
1910 Mux: MultiInputElement,
1911 Snk: AsyncElement,
1912 Clk: PipelineClock,
1913{
1914 let n = sources.len();
1915 assert!(n > 0, "muxer needs at least one source");
1916 assert!(
1917 mux.input_count() == n,
1918 "muxer input count must match the number of sources"
1919 );
1920
1921 let mut g: Graph<GraphNodeRef<'_>> = Graph::new();
1925 let mux_node = g.add_muxer(GraphNodeRef::muxer_ref(mux), n as u8);
1926 let snk = g.add_sink(GraphNodeRef::element_ref(sink));
1927 for (i, source) in sources.into_iter().enumerate() {
1928 let s = g.add_source(GraphNodeRef::source_ref(source));
1929 g.link(s, mux_node.input(i as u8))
1930 .map_err(|_| G2gError::CapsMismatch)?;
1931 }
1932 g.link(mux_node.output(), snk)
1933 .map_err(|_| G2gError::CapsMismatch)?;
1934
1935 run_graph_inner(
1936 g,
1937 clock,
1938 link_capacity,
1939 bus,
1940 None,
1941 None,
1942 None,
1943 None,
1944 None,
1945 ticker,
1946 )
1947 .await
1948}
1949
1950#[derive(Debug, Clone, Copy)]
1954enum FaninArmOut {
1955 Aggregator(u64),
1957 Source(u64),
1959 Sink(u64),
1962}
1963
1964#[cfg(feature = "std")]
1967const DYN_FANIN_NODE: usize = 0;
1968
1969#[cfg(feature = "std")]
1971const DYN_FANIN_SINK_NODE: usize = 1;
1972
1973#[cfg(feature = "std")]
1979#[derive(Debug)]
1980struct FaninTap {
1981 obs: Option<Observer>,
1982 namer: crate::log::InstanceNamer,
1983}
1984
1985#[cfg(feature = "std")]
1989const FANIN_CATEGORY: &str = "fanin";
1990
1991#[cfg(feature = "std")]
1994struct InputRequest<'a> {
1995 pad: usize,
1996 source: Box<dyn DynSourceLoop + 'a>,
1997 verdict: Sender<Result<(), G2gError>>,
1998}
1999
2000#[cfg(feature = "std")]
2006#[derive(Debug)]
2007pub struct PendingInput {
2008 pad: usize,
2009 verdict: Receiver<Result<(), G2gError>>,
2010}
2011
2012#[cfg(feature = "std")]
2013impl PendingInput {
2014 pub fn pad(&self) -> usize {
2016 self.pad
2017 }
2018
2019 pub async fn accepted(self) -> Result<(), G2gError> {
2024 self.verdict.recv().await.unwrap_or(Err(G2gError::Shutdown))
2025 }
2026}
2027
2028#[derive(Clone)]
2043#[allow(missing_debug_implementations)]
2044pub struct DynamicFaninHandle<'a> {
2045 new_input_tx: Sender<InputRequest<'a>>,
2046 next_pad: Arc<AtomicUsize>,
2050 max_inputs: usize,
2051}
2052
2053#[cfg(feature = "std")]
2054impl<'a> DynamicFaninHandle<'a> {
2055 pub fn add_input(&self, source: Box<dyn DynSourceLoop + 'a>) -> Result<PendingInput, G2gError> {
2068 let pad = self.next_pad.fetch_add(1, Ordering::SeqCst);
2072 if pad >= self.max_inputs {
2073 return Err(G2gError::Shutdown);
2074 }
2075 let (verdict_tx, verdict_rx) = bounded::<Result<(), G2gError>>(1);
2076 match self.new_input_tx.try_send(InputRequest {
2077 pad,
2078 source,
2079 verdict: verdict_tx,
2080 }) {
2081 Ok(()) => Ok(PendingInput {
2082 pad,
2083 verdict: verdict_rx,
2084 }),
2085 Err((_, SendError::Closed)) => Err(G2gError::Shutdown),
2086 Err((_, SendError::Full)) => {
2087 let _ = self.next_pad.compare_exchange(
2091 pad + 1,
2092 pad,
2093 Ordering::SeqCst,
2094 Ordering::SeqCst,
2095 );
2096 Err(G2gError::PoolExhausted)
2097 }
2098 }
2099 }
2100}
2101
2102#[cfg(feature = "std")]
2125pub fn run_aggregator_dynamic<'a, Agg>(
2126 aggregator: &'a mut Agg,
2127 link_capacity: impl Into<LinkCapacity>,
2128) -> (
2129 DynamicFaninHandle<'a>,
2130 impl Future<Output = Result<RunStats, G2gError>> + 'a,
2131)
2132where
2133 Agg: MultiInputElement + 'a,
2134{
2135 run_aggregator_dynamic_inner(aggregator, link_capacity, None)
2136}
2137
2138#[cfg(feature = "std")]
2144pub fn run_aggregator_dynamic_observed<'a, Agg>(
2145 aggregator: &'a mut Agg,
2146 link_capacity: impl Into<LinkCapacity>,
2147 observer: &Observer,
2148) -> (
2149 DynamicFaninHandle<'a>,
2150 impl Future<Output = Result<RunStats, G2gError>> + 'a,
2151)
2152where
2153 Agg: MultiInputElement + 'a,
2154{
2155 run_aggregator_dynamic_inner(aggregator, link_capacity, Some(observer.clone()))
2156}
2157
2158#[cfg(feature = "std")]
2159fn run_aggregator_dynamic_inner<'a, Agg>(
2160 aggregator: &'a mut Agg,
2161 link_capacity: impl Into<LinkCapacity>,
2162 observer: Option<Observer>,
2163) -> (
2164 DynamicFaninHandle<'a>,
2165 impl Future<Output = Result<RunStats, G2gError>> + 'a,
2166)
2167where
2168 Agg: MultiInputElement + 'a,
2169{
2170 let link_capacity: usize = link_capacity.into().get();
2171 let max_inputs = aggregator.input_count();
2172
2173 let (new_input_tx, new_input_rx) = bounded::<InputRequest<'a>>(link_capacity);
2175 let (new_arm_tx, new_arm_rx) =
2177 bounded::<BoxFuture<'a, Result<FaninArmOut, G2gError>>>(link_capacity);
2178
2179 let handle = DynamicFaninHandle {
2180 new_input_tx,
2181 next_pad: Arc::new(AtomicUsize::new(0)),
2182 max_inputs,
2183 };
2184
2185 let run = async move {
2186 let (tagged_tx, tagged_rx) = bounded::<(usize, PipelinePacket)>(link_capacity);
2188
2189 let mut namer = crate::log::InstanceNamer::new();
2192 let agg_probe = ElementProbe::new(namer.add(crate::log::short_type_name::<Agg>(), None));
2193 if let Some(obs) = &observer {
2194 register_runner_tap(
2195 obs,
2196 alloc::vec![(
2197 alloc::string::String::from(agg_probe.name()),
2198 NodeRole::Muxer,
2199 Some(agg_probe.clone()),
2200 )],
2201 Vec::new(),
2202 );
2203 }
2204 let mut tap = FaninTap {
2205 obs: observer,
2206 namer,
2207 };
2208 let probe_for_agg = agg_probe.clone();
2209
2210 let aggregator_arm: BoxFuture<'a, Result<FaninArmOut, G2gError>> = Box::pin(async move {
2211 let aggregator: &mut dyn DynMultiInputElement = aggregator;
2214 let mut null = NullSink;
2215 let mut consumed = 0u64;
2216 let mut accepting = true;
2217 let mut keepalive: Option<Sender<(usize, PipelinePacket)>> = Some(tagged_tx);
2221 loop {
2222 while let Some(request) = new_input_rx.try_recv() {
2227 let tx = keepalive.as_ref().expect("keepalive held while accepting");
2228 attach_input(request, aggregator, tx, &new_arm_tx, &mut tap).await?;
2229 }
2230
2231 if accepting {
2232 match select2(tagged_rx.recv(), new_input_rx.recv()).await {
2233 Either::Left(Some((pad, PipelinePacket::Eos))) => {
2234 aggregator
2237 .process(pad, PipelinePacket::Eos, &mut null)
2238 .await?;
2239 }
2240 Either::Left(Some((pad, packet))) => {
2241 let is_data = matches!(packet, PipelinePacket::DataFrame(_));
2242 if is_data {
2243 consumed += 1;
2244 probe_for_agg.record_fill(tagged_rx.fill_percent());
2245 }
2246 let t0 = is_data.then(ElementProbe::mark).flatten();
2247 aggregator.process(pad, packet, &mut null).await?;
2248 if is_data {
2249 probe_for_agg.record_proc_since(t0);
2250 }
2251 }
2252 Either::Left(None) => return Ok(FaninArmOut::Aggregator(consumed)),
2255 Either::Right(Some(request)) => {
2256 let tx = keepalive.as_ref().expect("keepalive held while accepting");
2257 attach_input(request, aggregator, tx, &new_arm_tx, &mut tap).await?;
2258 }
2259 Either::Right(None) => {
2263 accepting = false;
2264 keepalive = None;
2265 }
2266 }
2267 } else {
2268 match tagged_rx.recv().await {
2269 Some((pad, PipelinePacket::Eos)) => {
2270 aggregator
2271 .process(pad, PipelinePacket::Eos, &mut null)
2272 .await?;
2273 }
2274 Some((pad, packet)) => {
2275 let is_data = matches!(packet, PipelinePacket::DataFrame(_));
2276 if is_data {
2277 consumed += 1;
2278 probe_for_agg.record_fill(tagged_rx.fill_percent());
2279 }
2280 let t0 = is_data.then(ElementProbe::mark).flatten();
2281 aggregator.process(pad, packet, &mut null).await?;
2282 if is_data {
2283 probe_for_agg.record_proc_since(t0);
2284 }
2285 }
2286 None => return Ok(FaninArmOut::Aggregator(consumed)),
2287 }
2288 }
2289 }
2290 });
2291
2292 let arms: Vec<BoxFuture<'a, Result<FaninArmOut, G2gError>>> = alloc::vec![aggregator_arm];
2295 let results = dynamic_join(arms, new_arm_rx).await;
2296
2297 let mut emitted = 0u64;
2298 let mut consumed = 0u64;
2299 for r in results {
2300 match r? {
2301 FaninArmOut::Source(n) => emitted += n,
2302 FaninArmOut::Aggregator(n) => consumed = n,
2303 FaninArmOut::Sink(_) => {}
2305 }
2306 }
2307 Ok(RunStats {
2308 frames_emitted: emitted,
2309 frames_consumed: consumed,
2310 frames_dropped: 0,
2311 latency: LatencyReport::ZERO,
2312 allocation: None,
2313 clock_priority: ClockPriority::SystemFallback,
2314 base_time_ns: 0,
2315 coordinator_events: 0,
2316 per_element: alloc::vec![agg_probe.snapshot()],
2317 })
2318 };
2319
2320 (handle, run)
2321}
2322
2323#[cfg(feature = "std")]
2339pub fn run_muxer_sink_dynamic<'a, Mux, Snk>(
2340 mux: &'a mut Mux,
2341 sink: &'a mut Snk,
2342 link_capacity: impl Into<LinkCapacity>,
2343) -> (
2344 DynamicFaninHandle<'a>,
2345 impl Future<Output = Result<RunStats, G2gError>> + 'a,
2346)
2347where
2348 Mux: MultiInputElement + 'a,
2349 Snk: AsyncElement + 'a,
2350{
2351 run_muxer_sink_dynamic_inner(mux, sink, link_capacity, None)
2352}
2353
2354#[cfg(feature = "std")]
2360pub fn run_muxer_sink_dynamic_observed<'a, Mux, Snk>(
2361 mux: &'a mut Mux,
2362 sink: &'a mut Snk,
2363 link_capacity: impl Into<LinkCapacity>,
2364 observer: &Observer,
2365) -> (
2366 DynamicFaninHandle<'a>,
2367 impl Future<Output = Result<RunStats, G2gError>> + 'a,
2368)
2369where
2370 Mux: MultiInputElement + 'a,
2371 Snk: AsyncElement + 'a,
2372{
2373 run_muxer_sink_dynamic_inner(mux, sink, link_capacity, Some(observer.clone()))
2374}
2375
2376#[cfg(feature = "std")]
2377fn run_muxer_sink_dynamic_inner<'a, Mux, Snk>(
2378 mux: &'a mut Mux,
2379 sink: &'a mut Snk,
2380 link_capacity: impl Into<LinkCapacity>,
2381 observer: Option<Observer>,
2382) -> (
2383 DynamicFaninHandle<'a>,
2384 impl Future<Output = Result<RunStats, G2gError>> + 'a,
2385)
2386where
2387 Mux: MultiInputElement + 'a,
2388 Snk: AsyncElement + 'a,
2389{
2390 let link_capacity: usize = link_capacity.into().get();
2391 let max_inputs = mux.input_count();
2392
2393 let (new_input_tx, new_input_rx) = bounded::<InputRequest<'a>>(link_capacity);
2394 let (new_arm_tx, new_arm_rx) =
2395 bounded::<BoxFuture<'a, Result<FaninArmOut, G2gError>>>(link_capacity);
2396
2397 let handle = DynamicFaninHandle {
2398 new_input_tx,
2399 next_pad: Arc::new(AtomicUsize::new(0)),
2400 max_inputs,
2401 };
2402
2403 let run = async move {
2404 let (tagged_tx, tagged_rx) = bounded::<(usize, PipelinePacket)>(link_capacity);
2405 let (out_tx, out_rx, out_tap) = link_tapped(link_capacity, observer.is_some());
2407
2408 let mut namer = crate::log::InstanceNamer::new();
2411 let mux_probe = ElementProbe::new(namer.add(crate::log::short_type_name::<Mux>(), None));
2412 let sink_name = namer.add(crate::log::short_type_name::<Snk>(), None);
2413 AsyncElement::set_instance_name(sink, sink_name.clone());
2414 let sink_probe = ElementProbe::new(sink_name.clone());
2415 if let Some(obs) = &observer {
2416 register_runner_tap(
2419 obs,
2420 alloc::vec![
2421 (
2422 alloc::string::String::from(mux_probe.name()),
2423 NodeRole::Muxer,
2424 Some(mux_probe.clone()),
2425 ),
2426 (sink_name, NodeRole::Sink, Some(sink_probe.clone())),
2427 ],
2428 Vec::new(),
2429 );
2430 }
2431 let mut tap = FaninTap {
2432 obs: observer,
2433 namer,
2434 };
2435 let probe_for_mux = mux_probe.clone();
2436 let probe_for_sink = sink_probe.clone();
2437
2438 let sink_arm: BoxFuture<'a, Result<FaninArmOut, G2gError>> = Box::pin(async move {
2441 let sink: &mut dyn DynAsyncElement = sink;
2442 let mut null = NullSink;
2443 let mut consumed = 0u64;
2444 while let Some(pkt) = out_rx.recv().await {
2445 match pkt {
2446 PipelinePacket::CapsChanged(caps) => {
2447 log_caps_rejected(
2448 Some(probe_for_sink.name()),
2449 &caps,
2450 sink.configure_pipeline(&caps),
2451 )?
2452 .reject_refixate()?;
2453 sink.process(PipelinePacket::CapsChanged(caps), &mut null)
2454 .await?;
2455 }
2456 PipelinePacket::Eos => break,
2457 other => {
2458 let is_data = matches!(other, PipelinePacket::DataFrame(_));
2459 if is_data {
2460 consumed += 1;
2461 probe_for_sink.record_fill(out_rx.fill_percent());
2462 }
2463 let t0 = is_data.then(ElementProbe::mark).flatten();
2464 sink.process(other, &mut null).await?;
2465 if is_data {
2466 probe_for_sink.record_proc_since(t0);
2467 }
2468 }
2469 }
2470 }
2471 Ok(FaninArmOut::Sink(consumed))
2472 });
2473
2474 let muxer_arm: BoxFuture<'a, Result<FaninArmOut, G2gError>> = Box::pin(async move {
2475 let mux: &mut dyn DynMultiInputElement = mux;
2476 let mut out = SenderSink::new(out_tx);
2477 out.set_push_wait_probe(Some(probe_for_mux.clone()));
2479 let mut merged_tap = Some(out_tap);
2480 let mut current_output: Option<Caps> = None;
2481 let mut consumed = 0u64;
2482 let mut accepting = true;
2483 let mut keepalive: Option<Sender<(usize, PipelinePacket)>> = Some(tagged_tx);
2484 loop {
2485 while let Some(request) = new_input_rx.try_recv() {
2486 let tx = keepalive.as_ref().expect("keepalive held while accepting");
2487 attach_input(request, mux, tx, &new_arm_tx, &mut tap).await?;
2488 }
2489
2490 let next = if accepting {
2491 match select2(tagged_rx.recv(), new_input_rx.recv()).await {
2492 Either::Left(packet) => packet,
2493 Either::Right(Some(request)) => {
2494 let tx = keepalive.as_ref().expect("keepalive held while accepting");
2495 attach_input(request, mux, tx, &new_arm_tx, &mut tap).await?;
2496 continue;
2497 }
2498 Either::Right(None) => {
2499 accepting = false;
2502 keepalive = None;
2503 continue;
2504 }
2505 }
2506 } else {
2507 tagged_rx.recv().await
2508 };
2509
2510 match next {
2511 Some((pad, PipelinePacket::Eos)) => {
2512 mux.process(pad, PipelinePacket::Eos, &mut out).await?;
2515 }
2516 Some((pad, packet)) => {
2517 let is_data = matches!(packet, PipelinePacket::DataFrame(_));
2518 if is_data {
2519 if let Ok(oc) = mux.output_caps() {
2524 if current_output.as_ref() != Some(&oc) {
2525 if let Some(obs) = &tap.obs {
2526 if let Some(t) = merged_tap.take() {
2529 obs.add_edge(
2530 DYN_FANIN_NODE,
2531 DYN_FANIN_SINK_NODE,
2532 oc.clone(),
2533 t,
2534 );
2535 }
2536 }
2537 out.push(PipelinePacket::CapsChanged(oc.clone())).await?;
2538 current_output = Some(oc);
2539 }
2540 }
2541 consumed += 1;
2542 probe_for_mux.record_fill(tagged_rx.fill_percent());
2543 }
2544 let t0 = is_data.then(ElementProbe::mark).flatten();
2545 mux.process(pad, packet, &mut out).await?;
2546 if is_data {
2547 probe_for_mux.record_proc_since(t0);
2548 }
2549 }
2550 None => break,
2551 }
2552 }
2553 out.push(PipelinePacket::Eos).await?;
2555 Ok(FaninArmOut::Aggregator(consumed))
2556 });
2557
2558 let arms: Vec<BoxFuture<'a, Result<FaninArmOut, G2gError>>> =
2559 alloc::vec![muxer_arm, sink_arm];
2560 let results = dynamic_join(arms, new_arm_rx).await;
2561
2562 let mut emitted = 0u64;
2563 let mut consumed = 0u64;
2564 for r in results {
2565 match r? {
2566 FaninArmOut::Source(n) => emitted += n,
2567 FaninArmOut::Sink(n) => consumed = n,
2568 FaninArmOut::Aggregator(_) => {}
2569 }
2570 }
2571 Ok(RunStats {
2572 frames_emitted: emitted,
2573 frames_consumed: consumed,
2574 frames_dropped: 0,
2575 latency: LatencyReport::ZERO,
2576 allocation: None,
2577 clock_priority: ClockPriority::SystemFallback,
2578 base_time_ns: 0,
2579 coordinator_events: 0,
2580 per_element: alloc::vec![mux_probe.snapshot(), sink_probe.snapshot()],
2581 })
2582 };
2583
2584 (handle, run)
2585}
2586
2587#[cfg(feature = "std")]
2592async fn negotiate_new_input(
2593 pad: usize,
2594 source: &mut dyn DynSourceLoop,
2595 aggregator: &mut dyn DynMultiInputElement,
2596) -> Result<Caps, G2gError> {
2597 let produced = source.produced_caps().await?;
2598 let fixated = {
2599 let pad_constraint = aggregator.caps_constraint_as_input(pad);
2600 let fixated = select_branch_caps(&produced, &pad_constraint)?;
2601 if !pad_constraint.accepts(&fixated) {
2604 return Err(G2gError::CapsMismatch);
2605 }
2606 fixated
2607 };
2608 if !aggregator.accepts_runtime_input(pad, &fixated) {
2609 return Err(G2gError::InputRefused);
2610 }
2611 source.configure_pipeline(&fixated)?.reject_refixate()?;
2612 aggregator
2613 .configure_pipeline(pad, &fixated)?
2614 .reject_refixate()?;
2615 Ok(fixated)
2616}
2617
2618#[cfg(feature = "std")]
2627async fn attach_input<'a>(
2628 request: InputRequest<'a>,
2629 aggregator: &mut dyn DynMultiInputElement,
2630 tagged_tx: &Sender<(usize, PipelinePacket)>,
2631 new_arm_tx: &Sender<BoxFuture<'a, Result<FaninArmOut, G2gError>>>,
2632 tap: &mut FaninTap,
2633) -> Result<(), G2gError> {
2634 let InputRequest {
2635 pad,
2636 mut source,
2637 verdict,
2638 } = request;
2639 let fixated = match negotiate_new_input(pad, source.as_mut(), aggregator).await {
2640 Ok(caps) => caps,
2641 Err(e) => {
2642 crate::g2g_error!(
2643 crate::log::Target::category(FANIN_CATEGORY),
2644 "runtime input on pad {pad} rejected: {e:?}"
2645 );
2646 let _ = verdict.try_send(Err(e));
2647 return Ok(());
2648 }
2649 };
2650
2651 let name = tap.namer.add(source.log_category(), None);
2656 source.set_instance_name(name.clone());
2657 let (counters, slot) = match &tap.obs {
2658 Some(obs) => {
2659 let counters = Arc::new(EdgeCounters::default());
2660 let slot = ProbeSlot::default();
2661 let id = obs.add_node(name, NodeRole::Source, None);
2662 obs.add_edge(
2663 id,
2664 DYN_FANIN_NODE,
2665 fixated,
2666 EdgeTap {
2667 probe: slot.clone(),
2668 counters: Some(counters.clone()),
2669 },
2670 );
2671 (Some(counters), Some(slot))
2672 }
2673 None => (None, None),
2674 };
2675
2676 let tx = tagged_tx.clone();
2677 let arm: BoxFuture<'a, Result<FaninArmOut, G2gError>> = Box::pin(async move {
2678 let mut sink = TaggingSink::new(pad, tx, None, counters, slot);
2679 let mut source = source;
2680 let result = source.run(&mut sink).await;
2681 wind_down_when_session_ended(result, &sink).map(FaninArmOut::Source)
2682 });
2683 new_arm_tx.try_send(arm).map_err(|_| G2gError::Shutdown)?;
2684 let _ = verdict.try_send(Ok(()));
2685 Ok(())
2686}
2687
2688#[cfg(feature = "std")]
2692const DUPLEX_CATEGORY: &str = "duplex";
2693
2694#[cfg(feature = "std")]
2698#[derive(Debug, Clone, Copy)]
2699enum DuplexArmOut {
2700 Source(u64),
2702 Session,
2705 Sink(u64),
2707 Dropped(u64),
2709 Control,
2711}
2712
2713#[cfg(feature = "std")]
2717struct SendTrackRequest<'a> {
2718 input: usize,
2719 source: Box<dyn DynSourceLoop + 'a>,
2720}
2721
2722#[cfg(feature = "std")]
2725#[derive(Debug)]
2726struct GrownPort {
2727 port: usize,
2728 caps: Caps,
2729 rx: crate::runtime::channel::LinkReceiver,
2730 edge: EdgeTap,
2731}
2732
2733#[cfg(feature = "std")]
2738#[derive(Debug)]
2739struct GrowableSenderSink {
2740 ports: MultiSenderSink,
2741 link_capacity: usize,
2742 tap: bool,
2745 grown_tx: Sender<GrownPort>,
2746}
2747
2748#[cfg(feature = "std")]
2749impl MultiOutputSink for GrowableSenderSink {
2750 fn begin_push_to(&mut self, port: usize) {
2751 self.ports.begin_push_to(port);
2752 }
2753
2754 fn poll_push_to(
2755 &mut self,
2756 cx: &mut core::task::Context<'_>,
2757 port: usize,
2758 packet: &mut Option<PipelinePacket>,
2759 ) -> core::task::Poll<Result<PushOutcome, G2gError>> {
2760 self.ports.poll_push_to(cx, port, packet)
2761 }
2762
2763 fn port_count(&self) -> usize {
2764 self.ports.port_count()
2765 }
2766
2767 fn add_port(&mut self, caps: &Caps) -> Option<usize> {
2768 let port = self.ports.port_count();
2769 let (tx, rx, edge) = link_tapped(self.link_capacity, self.tap);
2770 let request = GrownPort {
2771 port,
2772 caps: caps.clone(),
2773 rx,
2774 edge,
2775 };
2776 match self.grown_tx.try_send(request) {
2779 Ok(()) => {
2780 self.ports.push_port(SenderSink::new(tx));
2781 Some(port)
2782 }
2783 Err(_) => None,
2784 }
2785 }
2786}
2787
2788#[cfg(feature = "std")]
2792#[derive(Debug)]
2793struct DuplexTap {
2794 obs: Option<Observer>,
2795 namer: crate::log::InstanceNamer,
2796 probes: Arc<Mutex<Vec<Probe>>>,
2799 session_id: usize,
2801}
2802
2803#[cfg(feature = "std")]
2813#[derive(Clone)]
2814#[allow(missing_debug_implementations)]
2815pub struct DynamicDuplexHandle<'a> {
2816 new_track_tx: Sender<SendTrackRequest<'a>>,
2817 next_input: Arc<Mutex<usize>>,
2823}
2824
2825#[cfg(feature = "std")]
2826impl<'a> DynamicDuplexHandle<'a> {
2827 pub fn add_send_track(&self, source: Box<dyn DynSourceLoop + 'a>) -> Result<usize, G2gError> {
2841 let mut next_input = self.next_input.lock();
2845 let input = *next_input;
2846 match self
2847 .new_track_tx
2848 .try_send(SendTrackRequest { input, source })
2849 {
2850 Ok(()) => {
2851 *next_input = input + 1;
2852 Ok(input)
2853 }
2854 Err((_, SendError::Closed)) => Err(G2gError::Shutdown),
2855 Err((_, SendError::Full)) => Err(G2gError::PoolExhausted),
2858 }
2859 }
2860}
2861
2862#[cfg(feature = "std")]
2887pub fn run_duplex_session_dynamic<'a, Sess, Clk, Factory>(
2888 sources: Vec<&'a mut dyn DynSourceLoop>,
2889 session: &'a mut Sess,
2890 sinks: Vec<&'a mut dyn DynAsyncElement>,
2891 clock: &'a Clk,
2892 link_capacity: impl Into<LinkCapacity>,
2893 sink_factory: Factory,
2894) -> (
2895 DynamicDuplexHandle<'a>,
2896 impl Future<Output = Result<RunStats, G2gError>> + 'a,
2897)
2898where
2899 Sess: MultiDuplexSession + 'a,
2900 Clk: PipelineClock + 'a,
2901 Factory: FnMut(usize, &Caps) -> Option<Box<dyn DynAsyncElement + 'a>> + 'a,
2902{
2903 run_duplex_session_dynamic_inner(
2904 sources,
2905 session,
2906 sinks,
2907 clock,
2908 link_capacity,
2909 sink_factory,
2910 None,
2911 )
2912}
2913
2914#[cfg(feature = "std")]
2919pub fn run_duplex_session_dynamic_observed<'a, Sess, Clk, Factory>(
2920 sources: Vec<&'a mut dyn DynSourceLoop>,
2921 session: &'a mut Sess,
2922 sinks: Vec<&'a mut dyn DynAsyncElement>,
2923 clock: &'a Clk,
2924 link_capacity: impl Into<LinkCapacity>,
2925 sink_factory: Factory,
2926 observer: &Observer,
2927) -> (
2928 DynamicDuplexHandle<'a>,
2929 impl Future<Output = Result<RunStats, G2gError>> + 'a,
2930)
2931where
2932 Sess: MultiDuplexSession + 'a,
2933 Clk: PipelineClock + 'a,
2934 Factory: FnMut(usize, &Caps) -> Option<Box<dyn DynAsyncElement + 'a>> + 'a,
2935{
2936 run_duplex_session_dynamic_inner(
2937 sources,
2938 session,
2939 sinks,
2940 clock,
2941 link_capacity,
2942 sink_factory,
2943 Some(observer.clone()),
2944 )
2945}
2946
2947#[cfg(feature = "std")]
2948#[allow(clippy::too_many_arguments)]
2949fn run_duplex_session_dynamic_inner<'a, Sess, Clk, Factory>(
2950 sources: Vec<&'a mut dyn DynSourceLoop>,
2951 session: &'a mut Sess,
2952 sinks: Vec<&'a mut dyn DynAsyncElement>,
2953 _clock: &'a Clk,
2954 link_capacity: impl Into<LinkCapacity>,
2955 mut sink_factory: Factory,
2956 observer: Option<Observer>,
2957) -> (
2958 DynamicDuplexHandle<'a>,
2959 impl Future<Output = Result<RunStats, G2gError>> + 'a,
2960)
2961where
2962 Sess: MultiDuplexSession + 'a,
2963 Clk: PipelineClock + 'a,
2964 Factory: FnMut(usize, &Caps) -> Option<Box<dyn DynAsyncElement + 'a>> + 'a,
2965{
2966 let link_capacity: usize = link_capacity.into().get();
2967 let input_count = sources.len();
2968 let output_count = sinks.len();
2969 let (new_track_tx, new_track_rx) = bounded::<SendTrackRequest<'a>>(link_capacity);
2971 let handle = DynamicDuplexHandle {
2972 new_track_tx,
2973 next_input: Arc::new(Mutex::new(input_count)),
2974 };
2975
2976 let run = async move {
2977 assert!(
2978 input_count > 0,
2979 "duplex session needs at least one send source"
2980 );
2981 assert!(
2982 output_count > 0,
2983 "duplex session needs at least one recv sink"
2984 );
2985 assert!(
2986 session.input_count() == input_count,
2987 "session input count must match the number of send sources"
2988 );
2989 assert!(
2990 session.output_count() == output_count,
2991 "session output count must match the number of recv sinks"
2992 );
2993
2994 let mut sources = sources;
2995 let mut sinks = sinks;
2996 let mut namer = crate::log::InstanceNamer::new();
2997 let mut source_names: Vec<alloc::string::String> = Vec::with_capacity(input_count);
2998 for source in sources.iter_mut() {
2999 let name = namer.add(source.log_category(), None);
3000 source.set_instance_name(name.clone());
3001 source_names.push(name);
3002 }
3003 let session_name = namer.add(crate::log::short_type_name::<Sess>(), None);
3004 let mut sink_probes = Vec::with_capacity(output_count);
3005 for sink in sinks.iter_mut() {
3006 let name = namer.add(sink.log_category(), None);
3007 sink.set_instance_name(name.clone());
3008 sink_probes.push(ElementProbe::new(name));
3009 }
3010
3011 let mut input_caps: Vec<Caps> = Vec::with_capacity(input_count);
3014 for (i, source) in sources.iter_mut().enumerate() {
3015 let produced = source.produced_caps().await?;
3016 let fixated = {
3017 let pad_constraint = MultiDuplexSession::caps_constraint_as_input(session, i);
3018 select_branch_caps(&produced, &pad_constraint)?
3019 };
3020 source.configure_pipeline(&fixated)?.reject_refixate()?;
3021 session.configure_input(i, &fixated)?.reject_refixate()?;
3022 input_caps.push(fixated);
3023 }
3024 let mut output_caps: Vec<Caps> = Vec::with_capacity(output_count);
3025 for (o, sink) in sinks.iter_mut().enumerate() {
3026 let fixated = session.output_caps(o)?.fixate()?;
3027 sink.configure_pipeline(&fixated)?.reject_refixate()?;
3028 output_caps.push(fixated);
3029 }
3030
3031 let (in_tx, in_rx) = bounded::<(usize, PipelinePacket)>(link_capacity);
3032 let in_counters: Vec<Option<Arc<EdgeCounters>>> = (0..input_count)
3033 .map(|_| observer.as_ref().map(|_| Arc::new(EdgeCounters::default())))
3034 .collect();
3035 let in_probes: Vec<Option<ProbeSlot>> = (0..input_count)
3036 .map(|_| observer.as_ref().map(|_| ProbeSlot::default()))
3037 .collect();
3038 let tap = observer.is_some();
3039 let mut branch_senders = Vec::with_capacity(output_count);
3040 let mut branch_receivers = Vec::with_capacity(output_count);
3041 let mut branch_taps = Vec::with_capacity(output_count);
3042 for _ in 0..output_count {
3043 let (tx, rx, edge) = link_tapped(link_capacity, tap);
3044 branch_senders.push(SenderSink::new(tx));
3045 branch_receivers.push(rx);
3046 branch_taps.push(edge);
3047 }
3048
3049 let session_id = input_count;
3050 if let Some(obs) = &observer {
3051 let mut nodes: Vec<TapNode> = source_names
3052 .iter()
3053 .map(|n| (n.clone(), NodeRole::Source, None))
3054 .collect();
3055 nodes.push((session_name, NodeRole::Muxer, None));
3056 for probe in &sink_probes {
3057 nodes.push((
3058 alloc::string::String::from(probe.name()),
3059 NodeRole::Sink,
3060 Some(probe.clone()),
3061 ));
3062 }
3063 let mut edges: Vec<TapEdge> = Vec::with_capacity(input_count + output_count);
3064 for (i, ((caps, c), p)) in input_caps
3065 .iter()
3066 .zip(in_counters.iter())
3067 .zip(in_probes.iter())
3068 .enumerate()
3069 {
3070 edges.push((
3071 i,
3072 session_id,
3073 caps.clone(),
3074 EdgeTap {
3075 probe: p.clone().unwrap_or_default(),
3076 counters: c.clone(),
3077 },
3078 ));
3079 }
3080 for (o, (caps, edge)) in output_caps
3081 .iter()
3082 .zip(core::mem::take(&mut branch_taps))
3083 .enumerate()
3084 {
3085 edges.push((session_id, session_id + 1 + o, caps.clone(), edge));
3086 }
3087 register_runner_tap(obs, nodes, edges);
3088 }
3089
3090 let reverse: Vec<Option<crate::fanout::ReverseChannel>> = (0..input_count)
3094 .map(|i| session.reverse_channel(i))
3095 .collect();
3096 let reverse_map: ReverseMap = Arc::new(Mutex::new(reverse.clone()));
3097
3098 let mut source_arms: Vec<BoxFuture<'a, Result<DuplexArmOut, G2gError>>> =
3099 Vec::with_capacity(input_count);
3100 for (i, source) in sources.into_iter().enumerate() {
3101 let tx_i = in_tx.clone();
3102 let reverse_i = reverse[i].clone();
3103 let counters_i = in_counters[i].clone();
3104 let probe_i = in_probes[i].clone();
3105 source_arms.push(Box::pin(async move {
3106 let mut adapter = TaggingSink::new(i, tx_i, reverse_i, counters_i, probe_i);
3107 let result = source.run(&mut adapter).await;
3108 wind_down_when_session_ended(result, &adapter).map(DuplexArmOut::Source)
3109 }));
3110 }
3111
3112 let (grown_tx, grown_rx) = bounded::<GrownPort>(link_capacity);
3116 let (new_arm_tx, new_arm_rx) =
3118 bounded::<BoxFuture<'a, Result<DuplexArmOut, G2gError>>>(link_capacity);
3119
3120 let session_reverse = reverse_map.clone();
3121 let session_arm: BoxFuture<'a, Result<DuplexArmOut, G2gError>> = Box::pin(async move {
3122 let mut inbound = InboundReceiver {
3123 rx: in_rx,
3124 reverse: session_reverse,
3125 };
3126 let mut multi = GrowableSenderSink {
3127 ports: MultiSenderSink::new(branch_senders),
3128 link_capacity,
3129 tap,
3130 grown_tx,
3131 };
3132 session
3133 .run(&mut inbound, &mut multi)
3134 .await
3135 .map(|_| DuplexArmOut::Session)
3136 });
3137
3138 let mut sink_arms: Vec<BoxFuture<'a, Result<DuplexArmOut, G2gError>>> =
3139 Vec::with_capacity(output_count);
3140 for ((sink, rx), probe) in sinks
3141 .into_iter()
3142 .zip(branch_receivers)
3143 .zip(sink_probes.iter().cloned())
3144 {
3145 sink_arms.push(Box::pin(async move {
3146 duplex_sink_arm(sink, rx, &probe)
3147 .await
3148 .map(DuplexArmOut::Sink)
3149 }));
3150 }
3151
3152 let probes: Arc<Mutex<Vec<Probe>>> =
3153 Arc::new(Mutex::new(sink_probes.into_iter().map(Some).collect()));
3154 let mut tap_state = DuplexTap {
3155 obs: observer,
3156 namer,
3157 probes: probes.clone(),
3158 session_id,
3159 };
3160 let control_arm: BoxFuture<'a, Result<DuplexArmOut, G2gError>> = Box::pin(async move {
3161 let mut keepalive: Option<Sender<(usize, PipelinePacket)>> = Some(in_tx);
3165 loop {
3166 if let Some(tx) = keepalive.clone() {
3170 while let Some(request) = new_track_rx.try_recv() {
3171 attach_send_track(request, &tx, &new_arm_tx, &reverse_map, &mut tap_state)
3172 .await?;
3173 }
3174 }
3175 while let Some(grown) = grown_rx.try_recv() {
3176 attach_recv_port(grown, &mut sink_factory, &new_arm_tx, &mut tap_state).await?;
3177 }
3178
3179 match keepalive {
3180 Some(ref tx) => {
3181 let tx = tx.clone();
3182 match select2(new_track_rx.recv(), grown_rx.recv()).await {
3183 Either::Left(Some(request)) => {
3184 attach_send_track(
3185 request,
3186 &tx,
3187 &new_arm_tx,
3188 &reverse_map,
3189 &mut tap_state,
3190 )
3191 .await?;
3192 }
3193 Either::Left(None) => keepalive = None,
3196 Either::Right(Some(grown)) => {
3197 attach_recv_port(
3198 grown,
3199 &mut sink_factory,
3200 &new_arm_tx,
3201 &mut tap_state,
3202 )
3203 .await?;
3204 }
3205 Either::Right(None) => return Ok(DuplexArmOut::Control),
3206 }
3207 }
3208 None => match grown_rx.recv().await {
3211 Some(grown) => {
3212 attach_recv_port(grown, &mut sink_factory, &new_arm_tx, &mut tap_state)
3213 .await?;
3214 }
3215 None => return Ok(DuplexArmOut::Control),
3216 },
3217 }
3218 }
3219 });
3220
3221 let mut arms: Vec<BoxFuture<'a, Result<DuplexArmOut, G2gError>>> =
3222 Vec::with_capacity(input_count + output_count + 2);
3223 arms.extend(source_arms);
3224 arms.push(session_arm);
3225 arms.extend(sink_arms);
3226 arms.push(control_arm);
3227
3228 let results = dynamic_join(arms, new_arm_rx).await;
3229 let mut emitted = 0u64;
3230 let mut consumed = 0u64;
3231 let mut dropped = 0u64;
3232 for r in results {
3233 match r? {
3234 DuplexArmOut::Source(n) => emitted += n,
3235 DuplexArmOut::Sink(n) => consumed += n,
3236 DuplexArmOut::Dropped(n) => dropped += n,
3237 DuplexArmOut::Session | DuplexArmOut::Control => {}
3238 }
3239 }
3240 let per_element = crate::runtime::snapshot_all(&probes.lock());
3241 Ok(RunStats {
3242 frames_emitted: emitted,
3243 frames_consumed: consumed,
3244 frames_dropped: dropped,
3245 latency: LatencyReport::ZERO,
3246 allocation: None,
3247 clock_priority: ClockPriority::SystemFallback,
3248 base_time_ns: 0,
3249 coordinator_events: 0,
3250 per_element,
3251 })
3252 };
3253
3254 (handle, run)
3255}
3256
3257#[cfg(feature = "std")]
3262async fn negotiate_new_send_track(source: &mut dyn DynSourceLoop) -> Result<Caps, G2gError> {
3263 let produced = source.produced_caps().await?;
3264 let fixated = produced.fixate().ok_or(G2gError::CapsMismatch)?;
3265 source.configure_pipeline(&fixated)?.reject_refixate()?;
3266 Ok(fixated)
3267}
3268
3269#[cfg(feature = "std")]
3277async fn attach_send_track<'a>(
3278 request: SendTrackRequest<'a>,
3279 in_tx: &Sender<(usize, PipelinePacket)>,
3280 new_arm_tx: &Sender<BoxFuture<'a, Result<DuplexArmOut, G2gError>>>,
3281 reverse: &ReverseMap,
3282 tap: &mut DuplexTap,
3283) -> Result<(), G2gError> {
3284 let SendTrackRequest { input, mut source } = request;
3285 let fixated = match negotiate_new_send_track(source.as_mut()).await {
3286 Ok(caps) => caps,
3287 Err(e) => {
3288 crate::g2g_error!(
3289 crate::log::Target::category(DUPLEX_CATEGORY),
3290 "runtime send track on input {input} rejected: {e:?}"
3291 );
3292 return Ok(());
3293 }
3294 };
3295
3296 let name = tap.namer.add(source.log_category(), None);
3297 source.set_instance_name(name.clone());
3298 let (counters, slot) = match &tap.obs {
3299 Some(obs) => {
3300 let counters = Arc::new(EdgeCounters::default());
3301 let slot = ProbeSlot::default();
3302 let id = obs.add_node(name, NodeRole::Source, None);
3303 obs.add_edge(
3304 id,
3305 tap.session_id,
3306 fixated.clone(),
3307 EdgeTap {
3308 probe: slot.clone(),
3309 counters: Some(counters.clone()),
3310 },
3311 );
3312 (Some(counters), Some(slot))
3313 }
3314 None => (None, None),
3315 };
3316
3317 let channel = crate::fanout::ReverseChannel::new();
3318 {
3319 let mut map = reverse.lock();
3320 if map.len() <= input {
3321 map.resize(input + 1, None);
3322 }
3323 map[input] = Some(channel.clone());
3324 }
3325
3326 let tx = in_tx.clone();
3327 let arm: BoxFuture<'a, Result<DuplexArmOut, G2gError>> = Box::pin(async move {
3328 let mut adapter = TaggingSink::new(input, tx, Some(channel), counters, slot);
3329 let announced = adapter.push(PipelinePacket::CapsChanged(fixated)).await;
3332 let mut source = source;
3333 let result = match announced {
3334 Ok(_) => source.run(&mut adapter).await,
3335 Err(e) => Err(e),
3336 };
3337 wind_down_when_session_ended(result, &adapter).map(DuplexArmOut::Source)
3338 });
3339 new_arm_tx.send(arm).await.map_err(|_| G2gError::Shutdown)
3343}
3344
3345#[cfg(feature = "std")]
3350fn prepare_grown_sink<'a>(
3351 mut sink: Box<dyn DynAsyncElement + 'a>,
3352 port: usize,
3353 caps: &Caps,
3354 edge: EdgeTap,
3355 tap: &mut DuplexTap,
3356) -> Option<(Box<dyn DynAsyncElement + 'a>, Arc<ElementProbe>)> {
3357 let name = tap.namer.add(sink.log_category(), None);
3358 sink.set_instance_name(name.clone());
3359 let probe = ElementProbe::new(name.clone());
3360 if let Some(obs) = &tap.obs {
3361 let id = obs.add_node(name, NodeRole::Sink, Some(probe.clone()));
3362 obs.add_edge(tap.session_id, id, caps.clone(), edge);
3363 }
3364 match log_caps_rejected(Some(probe.name()), caps, sink.configure_pipeline(caps)) {
3365 Ok(ConfigureOutcome::Accepted) => {
3366 tap.probes.lock().push(Some(probe.clone()));
3367 Some((sink, probe))
3368 }
3369 _ => {
3370 crate::g2g_error!(
3371 crate::log::Target::category(DUPLEX_CATEGORY),
3372 "sink for grown recv port {port} refused its caps: draining the port"
3373 );
3374 None
3375 }
3376 }
3377}
3378
3379#[cfg(feature = "std")]
3384async fn attach_recv_port<'a, Factory>(
3385 grown: GrownPort,
3386 factory: &mut Factory,
3387 new_arm_tx: &Sender<BoxFuture<'a, Result<DuplexArmOut, G2gError>>>,
3388 tap: &mut DuplexTap,
3389) -> Result<(), G2gError>
3390where
3391 Factory: FnMut(usize, &Caps) -> Option<Box<dyn DynAsyncElement + 'a>>,
3392{
3393 let GrownPort {
3394 port,
3395 caps,
3396 rx,
3397 edge,
3398 } = grown;
3399 let prepared = match factory(port, &caps) {
3400 Some(sink) => prepare_grown_sink(sink, port, &caps, edge, tap),
3401 None => {
3402 crate::g2g_error!(
3403 crate::log::Target::category(DUPLEX_CATEGORY),
3404 "no sink for grown recv port {port}: draining it"
3405 );
3406 None
3407 }
3408 };
3409 let arm: BoxFuture<'a, Result<DuplexArmOut, G2gError>> = match prepared {
3410 Some((mut sink, probe)) => Box::pin(async move {
3411 duplex_sink_arm(sink.as_mut(), rx, &probe)
3412 .await
3413 .map(DuplexArmOut::Sink)
3414 }),
3415 None => Box::pin(async move { drain_grown_port(rx).await.map(DuplexArmOut::Dropped) }),
3416 };
3417 new_arm_tx.send(arm).await.map_err(|_| G2gError::Shutdown)
3419}
3420
3421#[cfg(feature = "std")]
3424async fn drain_grown_port(rx: crate::runtime::channel::LinkReceiver) -> Result<u64, G2gError> {
3425 let mut dropped = 0u64;
3426 loop {
3427 match rx.recv().await {
3428 Some(PipelinePacket::DataFrame(_)) => dropped += 1,
3429 Some(PipelinePacket::Eos) | None => return Ok(dropped),
3430 Some(_) => {}
3431 }
3432 }
3433}