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