1use super::{
2 ActivateContext, AppAdmission, AppReadyGate, BTreeMap, CancellationToken, Cell,
3 DeactivationReason, DriverControl, ExecutionAdapterCatalog, ManagedResourceScope,
4 ManagedTaskScope, ModuleDependencies, ModuleDependency, ModuleDependencyHandle,
5 ModuleEventDependencyHandle, ModuleStreamDependencyHandle, NativeApp, NativeAppRuntime,
6 NativeBindingTable, NativeEndpointBinding, NativeEndpointState, NativeEndpointStateTable,
7 NativeEventBindingTable, NativeEventEndpointStateTable, NativeExecutionAdapter,
8 NativeModuleGeneration, NativeModuleRuntime, NativeStreamBindingTable,
9 NativeStreamEndpointBinding, NativeStreamEndpointState, NativeStreamEndpointStateTable,
10 PlanResolutionError, PrepareContext, PreparedBinding, PreparedEventBinding, PreparedNativeApp,
11 PreparedNativeModule, PreparedStreamBinding, Rc, RefCell, RequestAdmission, ResolvedAppPlan,
12 RuntimeDiagnostics, RuntimeDriver, RuntimeFailure, ShutdownCoordinator, Weak,
13 begin_module_supervision, deactivate_in_reverse, event, handle_supervision_schedule_failure,
14 module_supervision, schedule_module_supervision, validate_native_endpoint_set,
15};
16
17#[derive(Clone, Debug, Eq, PartialEq)]
19pub enum PlanValidationError {
20 UnsupportedSchemaVersion { expected: u32, actual: u32 },
22 InvalidResolvedPlan { detail: String },
24}
25
26impl std::fmt::Display for PlanValidationError {
27 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 match self {
29 Self::UnsupportedSchemaVersion { expected, actual } => write!(
30 formatter,
31 "unsupported Resolved App Plan schema {actual}; expected {expected}"
32 ),
33 Self::InvalidResolvedPlan { detail } => {
34 write!(formatter, "invalid Resolved App Plan: {detail}")
35 }
36 }
37 }
38}
39
40impl std::error::Error for PlanValidationError {}
41
42#[derive(Debug)]
44pub struct Kernel;
45
46impl Kernel {
47 pub async fn start_native<D: RuntimeDriver, A: NativeExecutionAdapter>(
49 plan: ResolvedAppPlan,
50 driver: D,
51 adapter: A,
52 ) -> Result<NativeApp, RuntimeFailure> {
53 Self::start_native_with_diagnostics(plan, driver, adapter, RuntimeDiagnostics::new()).await
54 }
55
56 pub async fn start_native_with_diagnostics<D: RuntimeDriver, A: NativeExecutionAdapter>(
58 plan: ResolvedAppPlan,
59 driver: D,
60 adapter: A,
61 diagnostics: RuntimeDiagnostics,
62 ) -> Result<NativeApp, RuntimeFailure> {
63 Self::start_with_diagnostics(
64 plan,
65 driver,
66 ExecutionAdapterCatalog::single(adapter),
67 diagnostics,
68 )
69 .await
70 }
71
72 pub async fn start<D: RuntimeDriver>(
74 plan: ResolvedAppPlan,
75 driver: D,
76 adapters: ExecutionAdapterCatalog,
77 ) -> Result<NativeApp, RuntimeFailure> {
78 Self::start_with_diagnostics(plan, driver, adapters, RuntimeDiagnostics::new()).await
79 }
80
81 #[allow(
83 clippy::too_many_lines,
84 reason = "startup remains linear so validation, preparation, and activation fail closed in order"
85 )]
86 pub async fn start_with_diagnostics<D: RuntimeDriver>(
87 plan: ResolvedAppPlan,
88 driver: D,
89 adapters: ExecutionAdapterCatalog,
90 diagnostics: RuntimeDiagnostics,
91 ) -> Result<NativeApp, RuntimeFailure> {
92 if let Err(error) = plan.validate() {
93 let error = runtime_plan_error(&error);
94 diagnostics.emit_runtime_failure(driver.now(), None, &error);
95 return Err(error);
96 }
97
98 let activation_order = match plan.activation_order() {
99 Ok(order) => order,
100 Err(error) => {
101 let error = runtime_plan_error(&error);
102 diagnostics.emit_runtime_failure(driver.now(), None, &error);
103 return Err(error);
104 }
105 };
106 let adapters = Rc::new(adapters);
107 let PreparedNativeApp {
108 bindings: prepared_bindings,
109 stream_bindings: prepared_stream_bindings,
110 event_bindings: prepared_event_bindings,
111 generations,
112 } = match adapters.prepare(&plan) {
113 Ok(prepared) => prepared,
114 Err(error) => {
115 diagnostics.emit_runtime_failure(driver.now(), None, &error);
116 return Err(error);
117 }
118 };
119 if let Err(error) = validate_prepared_native_app(
120 &plan,
121 &prepared_bindings,
122 &prepared_stream_bindings,
123 &prepared_event_bindings,
124 &generations,
125 ) {
126 diagnostics.emit_runtime_failure(driver.now(), None, &error);
127 return Err(error);
128 }
129 let (bindings, endpoint_states) = native_bindings(&plan, &prepared_bindings);
130 let (stream_bindings, stream_endpoint_states) =
131 native_stream_bindings(&plan, &prepared_stream_bindings);
132 let (event_bindings, event_endpoint_states) =
133 native_event_bindings(&plan, &prepared_event_bindings);
134 let runtime_link = Rc::new(RefCell::new(Weak::new()));
135 let dependencies = module_dependencies(
136 &plan,
137 &bindings,
138 &stream_bindings,
139 &event_bindings,
140 &runtime_link,
141 );
142 let driver_control = DriverControl::new(&driver);
143 let admission = AppAdmission::new();
144 let module_runtimes = native_module_runtimes(&plan, &driver, generations);
145 let ready_gate = AppReadyGate::new();
146 let supervision = module_supervision(&plan);
147 let runtime = Rc::new(NativeAppRuntime {
148 plan,
149 adapters,
150 modules: module_runtimes,
151 dependencies,
152 endpoint_states,
153 stream_endpoint_states,
154 event_endpoint_states,
155 supervision: RefCell::new(supervision),
156 supervision_tasks: RefCell::new(BTreeMap::new()),
157 activation_order,
158 ready_gate,
159 admission,
160 driver: driver_control,
161 diagnostics: diagnostics.clone(),
162 request_ids: Rc::new(Cell::new(1)),
163 supervision_cancellation: CancellationToken::new(),
164 shutdown_started: Cell::new(false),
165 shutdown: ShutdownCoordinator::default(),
166 shutdown_task: RefCell::new(None),
167 terminal_failure: RefCell::new(None),
168 });
169 runtime_link.replace(Rc::downgrade(&runtime));
170 attach_managed_task_failure_handlers(&runtime);
171 runtime.diagnostics.emit(
172 super::DiagnosticSource::Lifecycle,
173 (runtime.driver.now)(),
174 |_| super::DiagnosticEvent::AppStarted {
175 module_count: runtime.plan.module_instances().len(),
176 },
177 );
178 let prepared_instances = prepare_native_modules(&runtime).await?;
179 if let Err(error) = activate_native_modules(&runtime).await {
180 let _ = deactivate_in_reverse(
181 &runtime.modules,
182 &runtime.dependencies,
183 &prepared_instances,
184 DeactivationReason::StartupRollback,
185 &runtime.admission,
186 &runtime.diagnostics,
187 &runtime.driver,
188 )
189 .await;
190 runtime
191 .diagnostics
192 .emit_runtime_failure((runtime.driver.now)(), None, &error);
193 return Err(error);
194 }
195 open_native_readiness(&runtime).await;
196 Ok(NativeApp {
197 bindings,
198 stream_bindings,
199 event_bindings,
200 diagnostics,
201 runtime,
202 })
203 }
204}
205
206pub(super) fn attach_managed_task_failure_handlers(runtime: &Rc<NativeAppRuntime>) {
207 for (instance_key, module) in &runtime.modules {
208 let Some((_, tasks, _)) = module.generation_parts() else {
209 continue;
210 };
211 attach_managed_task_failure_handler(runtime, instance_key, &tasks);
212 }
213}
214
215pub(super) fn attach_managed_task_failure_handler(
216 runtime: &Rc<NativeAppRuntime>,
217 instance_key: &str,
218 tasks: &ManagedTaskScope,
219) {
220 let task_runtime = Rc::downgrade(runtime);
221 let task_instance_key = instance_key.to_owned();
222 let handler: Rc<dyn Fn()> = Rc::new(move || {
223 let Some(runtime) = task_runtime.upgrade() else {
224 return;
225 };
226 if begin_module_supervision(&runtime, &task_instance_key).unwrap_or(false)
227 && let Err(error) = schedule_module_supervision(&runtime, &task_instance_key)
228 {
229 let _ = handle_supervision_schedule_failure(&runtime, &task_instance_key, error);
230 }
231 });
232 tasks.set_failure_handler(&handler);
233}
234
235pub(super) fn runtime_plan_error(error: &PlanResolutionError) -> RuntimeFailure {
236 RuntimeFailure::InvalidResolvedPlan {
237 detail: error.to_string(),
238 }
239}
240
241#[allow(
242 clippy::too_many_lines,
243 reason = "one fail-closed pass keeps request, stream, event, and generation validation aligned"
244)]
245pub(super) fn validate_prepared_native_app(
246 plan: &ResolvedAppPlan,
247 bindings: &[PreparedBinding],
248 stream_bindings: &[PreparedStreamBinding],
249 event_bindings: &[PreparedEventBinding],
250 generations: &BTreeMap<String, PreparedNativeModule>,
251) -> Result<(), RuntimeFailure> {
252 if generations.len() != plan.module_instances().len() {
253 return Err(RuntimeFailure::InvalidResolvedPlan {
254 detail: format!(
255 "Execution Adapters prepared {} Module generations; expected {}",
256 generations.len(),
257 plan.module_instances().len()
258 ),
259 });
260 }
261 for instance in plan.module_instances() {
262 let generation = generations.get(instance.instance_key()).ok_or_else(|| {
263 RuntimeFailure::InvalidResolvedPlan {
264 detail: format!(
265 "Execution Adapters did not prepare Module Instance `{}`",
266 instance.instance_key()
267 ),
268 }
269 })?;
270 validate_native_endpoint_set(
271 instance.instance_key(),
272 instance,
273 generation.endpoints(),
274 generation.stream_endpoints(),
275 generation.event_endpoints(),
276 )?;
277 }
278 if let Some(instance_key) = generations.keys().find(|instance_key| {
279 !plan
280 .module_instances()
281 .iter()
282 .any(|instance| instance.instance_key() == instance_key.as_str())
283 }) {
284 return Err(RuntimeFailure::InvalidResolvedPlan {
285 detail: format!("Execution Adapter prepared unknown Module Instance `{instance_key}`"),
286 });
287 }
288
289 let expected_request_bindings = plan
290 .capability_bindings()
291 .iter()
292 .filter(|binding| {
293 plan.module_instance(binding.provider_instance())
294 .and_then(|provider| {
295 provider
296 .provided_capabilities()
297 .iter()
298 .find(|endpoint| endpoint.capability_id() == binding.capability_id())
299 })
300 .is_some_and(|endpoint| !endpoint.request_operations().is_empty())
301 })
302 .count();
303 let expected_stream_bindings = plan
304 .capability_bindings()
305 .iter()
306 .filter(|binding| {
307 plan.module_instance(binding.provider_instance())
308 .and_then(|provider| {
309 provider
310 .provided_capabilities()
311 .iter()
312 .find(|endpoint| endpoint.capability_id() == binding.capability_id())
313 })
314 .is_some_and(|endpoint| !endpoint.stream_operations().is_empty())
315 })
316 .count();
317 let expected_event_bindings = plan
318 .capability_bindings()
319 .iter()
320 .filter(|binding| {
321 plan.module_instance(binding.provider_instance())
322 .and_then(|provider| {
323 provider
324 .provided_capabilities()
325 .iter()
326 .find(|endpoint| endpoint.capability_id() == binding.capability_id())
327 })
328 .is_some_and(|endpoint| !endpoint.event_operations().is_empty())
329 })
330 .count();
331 if bindings.len() != expected_request_bindings {
332 return Err(RuntimeFailure::InvalidResolvedPlan {
333 detail: if expected_stream_bindings == 0 && stream_bindings.is_empty() {
334 format!(
335 "Execution Adapters prepared {} bindings; expected {}",
336 bindings.len(),
337 expected_request_bindings
338 )
339 } else {
340 format!(
341 "Execution Adapters prepared {} request bindings; expected {}",
342 bindings.len(),
343 expected_request_bindings
344 )
345 },
346 });
347 }
348 if stream_bindings.len() != expected_stream_bindings {
349 return Err(RuntimeFailure::InvalidResolvedPlan {
350 detail: format!(
351 "Execution Adapters prepared {} stream bindings; expected {}",
352 stream_bindings.len(),
353 expected_stream_bindings
354 ),
355 });
356 }
357 if event_bindings.len() != expected_event_bindings {
358 return Err(RuntimeFailure::InvalidResolvedPlan {
359 detail: format!(
360 "Execution Adapters prepared {} Event bindings; expected {}",
361 event_bindings.len(),
362 expected_event_bindings
363 ),
364 });
365 }
366 for planned in plan.capability_bindings() {
367 let provider = generations
368 .get(planned.provider_instance())
369 .expect("the resolved Plan references one validated provider generation");
370 let descriptor = plan
371 .module_instance(planned.provider_instance())
372 .and_then(|provider| {
373 provider
374 .provided_capabilities()
375 .iter()
376 .find(|endpoint| endpoint.capability_id() == planned.capability_id())
377 })
378 .expect("the resolved Plan references one validated provider endpoint");
379 if !descriptor.request_operations().is_empty() {
380 let matching: Vec<_> = bindings
381 .iter()
382 .filter(|prepared| {
383 prepared.consumer_instance == planned.consumer_instance()
384 && prepared.provider_instance == planned.provider_instance()
385 && prepared.endpoint.capability_id() == planned.capability_id()
386 && prepared.endpoint.descriptor_version() == planned.descriptor_version()
387 })
388 .collect();
389 if matching.len() != 1 {
390 return Err(RuntimeFailure::InvalidResolvedPlan {
391 detail: format!(
392 "Execution Adapters prepared {} request bindings for `{}:{}:{}`; expected 1",
393 matching.len(),
394 planned.consumer_instance(),
395 planned.capability_id(),
396 planned.provider_instance()
397 ),
398 });
399 }
400 if !provider
401 .endpoints()
402 .iter()
403 .any(|endpoint| Rc::ptr_eq(endpoint, &matching[0].endpoint))
404 {
405 return Err(RuntimeFailure::InvalidResolvedPlan {
406 detail: format!(
407 "request binding `{}:{}:{}` does not reference its provider generation endpoint",
408 planned.consumer_instance(),
409 planned.capability_id(),
410 planned.provider_instance()
411 ),
412 });
413 }
414 }
415 if !descriptor.stream_operations().is_empty() {
416 let matching: Vec<_> = stream_bindings
417 .iter()
418 .filter(|prepared| {
419 prepared.consumer_instance == planned.consumer_instance()
420 && prepared.provider_instance == planned.provider_instance()
421 && prepared.endpoint.capability_id() == planned.capability_id()
422 && prepared.endpoint.descriptor_version() == planned.descriptor_version()
423 })
424 .collect();
425 if matching.len() != 1 {
426 return Err(RuntimeFailure::InvalidResolvedPlan {
427 detail: format!(
428 "Execution Adapters prepared {} stream bindings for `{}:{}:{}`; expected 1",
429 matching.len(),
430 planned.consumer_instance(),
431 planned.capability_id(),
432 planned.provider_instance()
433 ),
434 });
435 }
436 if !provider
437 .stream_endpoints()
438 .iter()
439 .any(|endpoint| Rc::ptr_eq(endpoint, &matching[0].endpoint))
440 {
441 return Err(RuntimeFailure::InvalidResolvedPlan {
442 detail: format!(
443 "stream binding `{}:{}:{}` does not reference its provider generation endpoint",
444 planned.consumer_instance(),
445 planned.capability_id(),
446 planned.provider_instance()
447 ),
448 });
449 }
450 }
451 if !descriptor.event_operations().is_empty() {
452 let matching: Vec<_> = event_bindings
453 .iter()
454 .filter(|prepared| {
455 prepared.consumer_instance == planned.consumer_instance()
456 && prepared.provider_instance == planned.provider_instance()
457 && prepared.endpoint.capability_id() == planned.capability_id()
458 && prepared.endpoint.descriptor_version() == planned.descriptor_version()
459 })
460 .collect();
461 if matching.len() != 1 {
462 return Err(RuntimeFailure::InvalidResolvedPlan {
463 detail: format!(
464 "Execution Adapters prepared {} Event bindings for `{}:{}:{}`; expected 1",
465 matching.len(),
466 planned.consumer_instance(),
467 planned.capability_id(),
468 planned.provider_instance()
469 ),
470 });
471 }
472 if !provider
473 .event_endpoints()
474 .iter()
475 .any(|endpoint| Rc::ptr_eq(endpoint, &matching[0].endpoint))
476 {
477 return Err(RuntimeFailure::InvalidResolvedPlan {
478 detail: format!(
479 "Event binding `{}:{}:{}` does not reference its provider generation endpoint",
480 planned.consumer_instance(),
481 planned.capability_id(),
482 planned.provider_instance()
483 ),
484 });
485 }
486 }
487 }
488 Ok(())
489}
490
491pub(super) fn native_module_runtimes<D: RuntimeDriver>(
492 plan: &ResolvedAppPlan,
493 driver: &D,
494 mut generations: BTreeMap<String, PreparedNativeModule>,
495) -> BTreeMap<String, NativeModuleRuntime> {
496 let mut runtimes = BTreeMap::new();
497 for instance in plan.module_instances() {
498 let lifecycle = generations
499 .remove(instance.instance_key())
500 .map(|generation| generation.lifecycle())
501 .expect("prepared App validation requires one generation per planned Instance");
502 runtimes.insert(
503 instance.instance_key().to_owned(),
504 NativeModuleRuntime {
505 generation: RefCell::new(Some(NativeModuleGeneration {
506 lifecycle,
507 tasks: ManagedTaskScope::new(driver),
508 resources: ManagedResourceScope::new(),
509 })),
510 },
511 );
512 }
513 runtimes
514}
515
516pub(super) async fn prepare_native_modules(
517 runtime: &Rc<NativeAppRuntime>,
518) -> Result<Vec<String>, RuntimeFailure> {
519 let mut prepared_instances = Vec::with_capacity(runtime.activation_order.len());
520 for instance_key in &runtime.activation_order {
521 let instance = runtime
522 .plan
523 .module_instances()
524 .iter()
525 .find(|instance| instance.instance_key() == instance_key)
526 .expect("activation order only contains planned Module Instances");
527 let module = runtime
528 .modules
529 .get(instance_key)
530 .expect("activation order only contains planned Module Instances");
531 let (lifecycle, tasks, resources) = module
532 .generation_parts()
533 .expect("every startup Module Instance has a generation");
534 let cancellation = tasks.cancellation();
535 prepared_instances.push(instance_key.clone());
536 let started_at = (runtime.driver.now)();
537 runtime
538 .diagnostics
539 .emit(super::DiagnosticSource::Lifecycle, started_at, |_| {
540 super::DiagnosticEvent::LifecycleStarted {
541 instance: instance_key.clone(),
542 generation: 1,
543 phase: super::ModuleLifecyclePhase::Prepare,
544 }
545 });
546 let context = PrepareContext {
547 instance_key: instance_key.clone(),
548 entrypoint: instance.entrypoint().to_owned(),
549 configuration: instance.configuration().to_owned(),
550 dependencies: runtime
551 .dependencies
552 .get(instance_key)
553 .cloned()
554 .unwrap_or_default(),
555 resources,
556 cancellation,
557 admission: runtime.admission.clone(),
558 };
559 let result = lifecycle.prepare(context).await;
560 let outcome = result.as_ref().map_or_else(
561 |error| super::DiagnosticOutcome::RuntimeFailure(error.into()),
562 |()| super::DiagnosticOutcome::Succeeded,
563 );
564 runtime.diagnostics.emit(
565 super::DiagnosticSource::Lifecycle,
566 (runtime.driver.now)(),
567 |_| super::DiagnosticEvent::LifecycleCompleted {
568 instance: instance_key.clone(),
569 generation: 1,
570 phase: super::ModuleLifecyclePhase::Prepare,
571 outcome,
572 elapsed: (runtime.driver.now)().saturating_sub(started_at),
573 },
574 );
575 if let Err(error) = result {
576 let _ = deactivate_in_reverse(
577 &runtime.modules,
578 &runtime.dependencies,
579 &prepared_instances,
580 DeactivationReason::StartupRollback,
581 &runtime.admission,
582 &runtime.diagnostics,
583 &runtime.driver,
584 )
585 .await;
586 runtime.diagnostics.emit_runtime_failure(
587 (runtime.driver.now)(),
588 Some(instance_key),
589 &error,
590 );
591 return Err(error);
592 }
593 }
594 Ok(prepared_instances)
595}
596
597pub(super) async fn activate_native_modules(
598 runtime: &Rc<NativeAppRuntime>,
599) -> Result<(), RuntimeFailure> {
600 for instance_key in &runtime.activation_order {
601 let module = runtime
602 .modules
603 .get(instance_key)
604 .expect("activation order only contains planned Module Instances");
605 let (lifecycle, tasks, resources) = module
606 .generation_parts()
607 .expect("every startup Module Instance has a generation");
608 let cancellation = tasks.cancellation();
609 let started_at = (runtime.driver.now)();
610 runtime
611 .diagnostics
612 .emit(super::DiagnosticSource::Lifecycle, started_at, |_| {
613 super::DiagnosticEvent::LifecycleStarted {
614 instance: instance_key.clone(),
615 generation: 1,
616 phase: super::ModuleLifecyclePhase::Activate,
617 }
618 });
619 let context = ActivateContext {
620 instance_key: instance_key.clone(),
621 dependencies: runtime
622 .dependencies
623 .get(instance_key)
624 .cloned()
625 .unwrap_or_default(),
626 ready_gate: runtime.ready_gate.clone(),
627 tasks,
628 resources,
629 cancellation,
630 admission: runtime.admission.clone(),
631 };
632 let result = lifecycle.activate(context).await;
633 let outcome = result.as_ref().map_or_else(
634 |error| super::DiagnosticOutcome::RuntimeFailure(error.into()),
635 |()| super::DiagnosticOutcome::Succeeded,
636 );
637 runtime.diagnostics.emit(
638 super::DiagnosticSource::Lifecycle,
639 (runtime.driver.now)(),
640 |_| super::DiagnosticEvent::LifecycleCompleted {
641 instance: instance_key.clone(),
642 generation: 1,
643 phase: super::ModuleLifecyclePhase::Activate,
644 outcome,
645 elapsed: (runtime.driver.now)().saturating_sub(started_at),
646 },
647 );
648 if let Err(error) = result {
649 runtime.diagnostics.emit_runtime_failure(
650 (runtime.driver.now)(),
651 Some(instance_key),
652 &error,
653 );
654 return Err(error);
655 }
656 }
657 Ok(())
658}
659
660pub(super) async fn open_native_readiness(runtime: &Rc<NativeAppRuntime>) {
661 runtime.ready_gate.open();
662 runtime.admission.open();
663 runtime.diagnostics.emit(
664 super::DiagnosticSource::Lifecycle,
665 (runtime.driver.now)(),
666 |_| super::DiagnosticEvent::AppReady,
667 );
668 (runtime.driver.yield_now)().await;
669}
670
671pub(super) fn native_bindings(
672 plan: &ResolvedAppPlan,
673 prepared: &[PreparedBinding],
674) -> (NativeBindingTable, NativeEndpointStateTable) {
675 let mut bindings = BTreeMap::new();
676 let mut endpoint_states = BTreeMap::new();
677 for binding in plan.capability_bindings() {
678 let Some(descriptor) =
679 plan.module_instance(binding.provider_instance())
680 .and_then(|provider| {
681 provider
682 .provided_capabilities()
683 .iter()
684 .find(|endpoint| endpoint.capability_id() == binding.capability_id())
685 })
686 else {
687 continue;
688 };
689 if descriptor.request_operations().is_empty() {
690 continue;
691 }
692 let Some(endpoint) = prepared.iter().find_map(|prepared| {
693 (prepared.consumer_instance == binding.consumer_instance()
694 && prepared.provider_instance == binding.provider_instance()
695 && prepared.endpoint.capability_id() == binding.capability_id())
696 .then_some(&prepared.endpoint)
697 }) else {
698 continue;
699 };
700 let state = endpoint_states
701 .entry((
702 binding.provider_instance().to_owned(),
703 endpoint.capability_id().to_owned(),
704 ))
705 .or_insert_with(|| Rc::new(NativeEndpointState::new(endpoint.clone(), 1)))
706 .clone();
707 let admissions = endpoint
708 .operations()
709 .iter()
710 .map(|operation| {
711 (
712 (*operation).to_owned(),
713 RequestAdmission::new(plan.request_admission_for(binding, operation)),
714 )
715 })
716 .collect();
717 bindings
718 .entry((
719 binding.consumer_instance().to_owned(),
720 endpoint.capability_id(),
721 ))
722 .or_insert_with(Vec::new)
723 .push(NativeEndpointBinding {
724 module_instance: binding.provider_instance().to_owned(),
725 state,
726 admissions,
727 });
728 }
729 (bindings, endpoint_states)
730}
731
732pub(super) fn native_stream_bindings(
733 plan: &ResolvedAppPlan,
734 prepared: &[PreparedStreamBinding],
735) -> (NativeStreamBindingTable, NativeStreamEndpointStateTable) {
736 let mut bindings = BTreeMap::new();
737 let mut endpoint_states = BTreeMap::new();
738 for binding in plan.capability_bindings() {
739 let Some(descriptor) =
740 plan.module_instance(binding.provider_instance())
741 .and_then(|provider| {
742 provider
743 .provided_capabilities()
744 .iter()
745 .find(|endpoint| endpoint.capability_id() == binding.capability_id())
746 })
747 else {
748 continue;
749 };
750 if descriptor.stream_operations().is_empty() {
751 continue;
752 }
753 let Some(endpoint) = prepared.iter().find_map(|prepared| {
754 (prepared.consumer_instance == binding.consumer_instance()
755 && prepared.provider_instance == binding.provider_instance()
756 && prepared.endpoint.capability_id() == binding.capability_id())
757 .then_some(&prepared.endpoint)
758 }) else {
759 continue;
760 };
761 let state = endpoint_states
762 .entry((
763 binding.provider_instance().to_owned(),
764 endpoint.capability_id().to_owned(),
765 ))
766 .or_insert_with(|| Rc::new(NativeStreamEndpointState::new(endpoint.clone(), 1)))
767 .clone();
768 let admissions = endpoint
769 .operations()
770 .iter()
771 .map(|operation| {
772 (
773 (*operation).to_owned(),
774 RequestAdmission::new(plan.request_admission_for(binding, operation)),
775 )
776 })
777 .collect();
778 bindings
779 .entry((
780 binding.consumer_instance().to_owned(),
781 endpoint.capability_id(),
782 ))
783 .or_insert_with(Vec::new)
784 .push(NativeStreamEndpointBinding {
785 module_instance: binding.provider_instance().to_owned(),
786 state,
787 admissions,
788 });
789 }
790 (bindings, endpoint_states)
791}
792
793pub(super) fn native_event_bindings(
794 plan: &ResolvedAppPlan,
795 prepared: &[PreparedEventBinding],
796) -> (NativeEventBindingTable, NativeEventEndpointStateTable) {
797 let mut bindings = BTreeMap::new();
798 let mut endpoint_states = BTreeMap::new();
799 for binding in plan.capability_bindings() {
800 let Some(descriptor) =
801 plan.module_instance(binding.provider_instance())
802 .and_then(|provider| {
803 provider
804 .provided_capabilities()
805 .iter()
806 .find(|endpoint| endpoint.capability_id() == binding.capability_id())
807 })
808 else {
809 continue;
810 };
811 if descriptor.event_operations().is_empty() {
812 continue;
813 }
814 let Some(endpoint) = prepared.iter().find_map(|prepared| {
815 (prepared.consumer_instance == binding.consumer_instance()
816 && prepared.provider_instance == binding.provider_instance()
817 && prepared.endpoint.capability_id() == binding.capability_id())
818 .then_some(&prepared.endpoint)
819 }) else {
820 continue;
821 };
822 let state = endpoint_states
823 .entry((
824 binding.provider_instance().to_owned(),
825 endpoint.capability_id().to_owned(),
826 ))
827 .or_insert_with(|| Rc::new(event::NativeEventEndpointState::new(endpoint.clone(), 1)))
828 .clone();
829 let queue = event::NativeEventQueue::new(plan.event_admission_for(binding));
830 state.register_queue(&queue);
831 bindings
832 .entry((
833 binding.consumer_instance().to_owned(),
834 endpoint.capability_id(),
835 ))
836 .or_insert_with(Vec::new)
837 .push(event::NativeEventEndpointBinding {
838 module_instance: binding.provider_instance().to_owned(),
839 state,
840 queue,
841 });
842 }
843 (bindings, endpoint_states)
844}
845
846pub(super) fn module_dependencies(
847 plan: &ResolvedAppPlan,
848 endpoints: &BTreeMap<(String, &'static str), Vec<NativeEndpointBinding>>,
849 stream_endpoints: &NativeStreamBindingTable,
850 event_endpoints: &NativeEventBindingTable,
851 runtime: &Rc<RefCell<Weak<NativeAppRuntime>>>,
852) -> BTreeMap<String, ModuleDependencies> {
853 let mut dependencies: BTreeMap<String, ModuleDependencies> = plan
854 .module_instances()
855 .iter()
856 .map(|instance| {
857 (
858 instance.instance_key().to_owned(),
859 ModuleDependencies::new(instance.instance_key(), runtime.clone()),
860 )
861 })
862 .collect();
863 for binding in plan.capability_bindings() {
864 dependencies
865 .get_mut(binding.consumer_instance())
866 .expect("every resolved binding consumer has Module dependencies")
867 .bindings
868 .push(ModuleDependency::new(
869 binding.capability_id(),
870 binding.provider_instance(),
871 binding.provider_order(),
872 endpoints
873 .iter()
874 .find(|((consumer, capability), _)| {
875 consumer == binding.consumer_instance()
876 && *capability == binding.capability_id()
877 })
878 .and_then(|(_, endpoints)| endpoints.get(binding.provider_order()))
879 .map(|endpoint| ModuleDependencyHandle {
880 binding: endpoint.clone(),
881 caller_instance: binding.consumer_instance().to_owned(),
882 runtime: runtime.clone(),
883 }),
884 stream_endpoints
885 .iter()
886 .find(|((consumer, capability), _)| {
887 consumer == binding.consumer_instance()
888 && *capability == binding.capability_id()
889 })
890 .and_then(|(_, endpoints)| endpoints.get(binding.provider_order()))
891 .map(|endpoint| ModuleStreamDependencyHandle {
892 binding: endpoint.clone(),
893 caller_instance: binding.consumer_instance().to_owned(),
894 runtime: runtime.clone(),
895 }),
896 event_endpoints
897 .iter()
898 .find(|((consumer, capability), _)| {
899 consumer == binding.consumer_instance()
900 && *capability == binding.capability_id()
901 })
902 .and_then(|(_, endpoints)| endpoints.get(binding.provider_order()))
903 .map(|endpoint| ModuleEventDependencyHandle {
904 binding: endpoint.clone(),
905 caller_instance: binding.consumer_instance().to_owned(),
906 runtime: runtime.clone(),
907 }),
908 ));
909 }
910 dependencies
911}