1use super::{
2 core_resource_failure, deferred_device_cleanup_status, invalid_resource,
3 maintain_deferred_device_cleanups, new_deferred_device_cleanup_domain,
4 retire_deferred_device_cleanup_domain, watch, AdmissionDeferred, AdmissionDemand,
5 AdmissionFitPolicy, AdmissionPressureAction, AllocationLifetime, Arc, AtomicU8,
6 BackingPrepareDecision, CapacityAvailabilityEpoch, CapacityEntry, CapacityEpochs,
7 CapacityUnits, CapacityVector, CapacityWaitCondition, CapacityWaitRecheck,
8 DeferredDeviceCleanupDomainId, DeferredDeviceCleanupMaintenanceReceipt,
9 DeferredDeviceCleanupStatus, DeviceCapacityClaim, DeviceCapacitySignal, DeviceId,
10 DeviceRuntime, DynamicBackingDeferred, DynamicDeferredMaintenanceOutcome,
11 DynamicPoolMaintenanceController, DynamicPoolMaintenanceStatus, DynamicPoolSet,
12 DynamicResourceShape, EvaluatedBackingProjection, EvaluatedBackingRequest, ExecutionLane,
13 ExecutionLaneCreationError, FailureEnvelope, InvocationLivenessMode,
14 LaneBackingPrepareDecision, LogicalAdmissionCoordinator, LogicalAdmissionCoordinatorId, Mutex,
15 NoStatic, NodeId, Ordering, PhysicalBackingClaimIdentity, PlanHash, PlanId, PlanNode,
16 ResourceAbandonSignal, ResourceActionCursor, ResourceDriverFailure,
17 ResourceLedgerEntrySnapshot, ResourceOwnershipReason, ResourceOwnershipTransferFailure,
18 ResourcePoolIdentity, ResourcePoolOwnership, ResourceReservation, ResourceReservationBatch,
19 ResourceTransactionAction, ResourceTransactionContext, ResourceTransactionDriver,
20 ResourceTransactionIdentity, ResourceTransactionState, RwLock, RwLockReadGuard, Serialize,
21 StaticProvisioningBinding, StaticProvisioningLease, VNextError,
22};
23use crate::vnext::{
24 ResolvedReusableExecutionBucket, ReusableExecutionBucketId, ReusableExecutionBucketSpec,
25};
26
27pub(super) const PLAN_RUNTIME_OPEN: u8 = 0;
28const PLAN_RUNTIME_CLOSING: u8 = 1;
29
30pub(super) trait ErasedPlanStaticDriver<R>: Send
31where
32 R: DeviceRuntime,
33{
34 fn release_resource(
35 &mut self,
36 context: &ResourceTransactionContext<'_, R>,
37 reservation: &ResourceReservation,
38 buffer: &R::Buffer,
39 ) -> Result<(), ResourceDriverFailure>;
40
41 fn quarantine_transaction(
42 &mut self,
43 context: &ResourceTransactionContext<'_, R>,
44 ownership: ResourcePoolOwnership<R>,
45 ) -> Result<(), ResourceOwnershipTransferFailure<R>>;
46
47 fn abandon_transaction(&mut self, ownership: ResourcePoolOwnership<R>);
48}
49
50impl<D> ErasedPlanStaticDriver<D::Runtime> for D
51where
52 D: ResourceTransactionDriver,
53{
54 fn release_resource(
55 &mut self,
56 context: &ResourceTransactionContext<'_, D::Runtime>,
57 reservation: &ResourceReservation,
58 buffer: &D::Buffer,
59 ) -> Result<(), ResourceDriverFailure> {
60 ResourceTransactionDriver::release_resource(self, context, reservation, buffer)
61 }
62
63 fn quarantine_transaction(
64 &mut self,
65 context: &ResourceTransactionContext<'_, D::Runtime>,
66 ownership: ResourcePoolOwnership<D::Runtime>,
67 ) -> Result<(), ResourceOwnershipTransferFailure<D::Runtime>> {
68 ResourceTransactionDriver::quarantine_transaction(self, context, ownership)
69 }
70
71 fn abandon_transaction(&mut self, ownership: ResourcePoolOwnership<D::Runtime>) {
72 ResourceTransactionDriver::abandon_transaction(self, ownership);
73 }
74}
75
76pub(super) struct PlanStaticResources<R>
77where
78 R: DeviceRuntime,
79{
80 pub(super) driver: Mutex<Option<Box<dyn ErasedPlanStaticDriver<R>>>>,
81 pub(super) identity: ResourceTransactionIdentity,
82 pub(super) admission: StaticProvisioningBinding,
83 pub(super) reservations: ResourceReservationBatch,
84 pub(super) states: Vec<ResourceTransactionState>,
85 pub(super) capacity_claim: Option<DeviceCapacityClaim>,
86 pub(super) lease: Option<StaticProvisioningLease<R>>,
87 pub(super) finalized: bool,
88}
89
90impl<R> PlanStaticResources<R>
91where
92 R: DeviceRuntime,
93{
94 fn ledger_snapshot_entries(&self) -> Vec<ResourceLedgerEntrySnapshot> {
95 self.lease
96 .as_ref()
97 .expect("open plan runtime owns its static lease")
98 .slots
99 .iter()
100 .zip(&self.states)
101 .map(|(slot, &transaction_state)| ResourceLedgerEntrySnapshot {
102 entry: slot.entry.clone(),
103 transaction_state,
104 buffer_present: slot.buffer.is_some(),
105 actual_resource_id: slot.actual_resource_id.clone(),
106 actual_generation: slot.actual_generation,
107 actual_descriptor: slot.descriptor.clone(),
108 })
109 .collect()
110 }
111
112 fn release_all(&mut self) -> Result<usize, ResourceDriverFailure> {
113 let mut released = 0;
114 for order in 0..self.states.len() {
115 if self.states[order] == ResourceTransactionState::Released {
116 continue;
117 }
118 if self.states[order] != ResourceTransactionState::Committed {
119 return Err(ResourceDriverFailure::new(core_resource_failure(
120 "plan_runtime_close_ledger_diverged",
121 "plan runtime close found a non-committed static resource",
122 false,
123 ))
124 .expect("core failure has resource domain"));
125 }
126 let lease = self
127 .lease
128 .as_ref()
129 .expect("open plan runtime owns its static lease");
130 let reservation = self.reservations.reservations[order].clone();
131 let buffer = lease
132 .buffer(order)
133 .expect("committed plan runtime owns its static buffer");
134 let context = ResourceTransactionContext {
135 runtime: &lease.runtime,
136 identity: &self.identity,
137 binding: &self.admission,
138 reservations: &self.reservations,
139 cursor: Some(ResourceActionCursor {
140 order,
141 action: ResourceTransactionAction::Release,
142 before: self.states[order],
143 allocation_authorized: false,
144 }),
145 allocation_authority: None,
146 pending_allocation: None,
147 };
148 let driver = match self.driver.get_mut() {
149 Ok(driver) => driver,
150 Err(poisoned) => poisoned.into_inner(),
151 };
152 driver
153 .as_mut()
154 .expect("open plan runtime owns its static driver")
155 .release_resource(&context, &reservation, buffer)?;
156 self.lease
157 .as_mut()
158 .expect("open plan runtime owns its static lease")
159 .clear(order);
160 self.states[order] = ResourceTransactionState::Released;
161 self.capacity_claim
162 .as_mut()
163 .expect("open plan runtime owns its static capacity claim")
164 .release_bytes(reservation.size_bytes());
165 released += 1;
166 }
167 if let Some(mut claim) = self.capacity_claim.take() {
168 claim.release();
169 }
170 self.finalized = true;
171 Ok(released)
172 }
173
174 fn quarantine_remaining(&mut self) -> Result<usize, ResourceDriverFailure> {
175 let quarantined = self
176 .states
177 .iter()
178 .filter(|state| **state == ResourceTransactionState::Committed)
179 .count();
180 if self.states.iter().any(|state| {
181 !matches!(
182 state,
183 ResourceTransactionState::Committed | ResourceTransactionState::Released
184 )
185 }) {
186 return Err(ResourceDriverFailure::new(core_resource_failure(
187 "plan_runtime_quarantine_ledger_diverged",
188 "plan runtime quarantine found an invalid static resource state",
189 false,
190 ))
191 .expect("core failure has resource domain"));
192 }
193 if quarantined == 0 {
194 self.finalized = true;
195 return Ok(0);
196 }
197 let lease = self
198 .lease
199 .as_mut()
200 .expect("failed plan runtime close owns its static lease");
201 let buffers = lease.take_owned_buffers(&self.reservations);
202 let ownership = ResourcePoolOwnership {
203 runtime: Arc::clone(&lease.runtime),
204 pool_identity: self.admission.pool_identity.clone(),
205 reason: ResourceOwnershipReason::Quarantine,
206 signal: None,
207 buffers,
208 capacity_claim: self.capacity_claim.take(),
209 };
210 let result = {
211 let context = ResourceTransactionContext {
212 runtime: &lease.runtime,
213 identity: &self.identity,
214 binding: &self.admission,
215 reservations: &self.reservations,
216 cursor: None,
217 allocation_authority: None,
218 pending_allocation: None,
219 };
220 let driver = match self.driver.get_mut() {
221 Ok(driver) => driver,
222 Err(poisoned) => poisoned.into_inner(),
223 };
224 driver
225 .as_mut()
226 .expect("failed plan runtime close owns its static driver")
227 .quarantine_transaction(&context, ownership)
228 };
229 if let Err(failure) = result {
230 let (failure, mut ownership) = failure.into_parts();
231 let expected_claimed_bytes = self
232 .states
233 .iter()
234 .zip(self.reservations.reservations())
235 .filter(|(state, _)| state.is_live())
236 .map(|(_, reservation)| reservation.size_bytes())
237 .sum::<u64>();
238 if ownership.pool_identity != self.admission.pool_identity
239 || ownership.claimed_bytes() != expected_claimed_bytes
240 {
241 std::mem::forget(ownership);
242 return Err(ResourceDriverFailure::new(core_resource_failure(
243 "ownership_transfer_identity_mismatch",
244 "plan runtime quarantine failure returned foreign ownership",
245 false,
246 ))
247 .expect("core failure has resource domain"));
248 }
249 self.lease
250 .as_mut()
251 .expect("failed plan runtime close owns its static lease")
252 .restore_owned_buffers(std::mem::take(&mut ownership.buffers));
253 self.capacity_claim = ownership.capacity_claim.take();
254 return Err(failure);
255 }
256 for state in &mut self.states {
257 if *state == ResourceTransactionState::Committed {
258 *state = ResourceTransactionState::Quarantined;
259 }
260 }
261 self.finalized = true;
262 Ok(quarantined)
263 }
264}
265
266impl<R> Drop for PlanStaticResources<R>
267where
268 R: DeviceRuntime,
269{
270 fn drop(&mut self) {
271 if self.finalized {
272 return;
273 }
274 let signal = ResourceAbandonSignal {
275 identity: self.identity.clone(),
276 admission: self.admission.clone(),
277 state: if self
278 .states
279 .iter()
280 .all(|state| *state == ResourceTransactionState::Committed)
281 {
282 ResourceTransactionState::Committed
283 } else {
284 ResourceTransactionState::Released
285 },
286 pending_action: None,
287 ledger: self.ledger_snapshot_entries(),
288 active_sequence_slots: Vec::new(),
289 poisoned_sequence_slots: Vec::new(),
290 undrained_sequence_slots: Vec::new(),
291 failure: None,
292 };
293 let mut lease = self
294 .lease
295 .take()
296 .expect("open plan runtime owns its static lease");
297 let buffers = lease.take_owned_buffers(&self.reservations);
298 let StaticProvisioningLease {
299 slots: _,
300 identity: _,
301 admission: _,
302 runtime,
303 } = lease;
304 let ownership = ResourcePoolOwnership {
305 runtime,
306 pool_identity: self.admission.pool_identity.clone(),
307 reason: ResourceOwnershipReason::Abandon,
308 signal: Some(signal),
309 buffers,
310 capacity_claim: self.capacity_claim.take(),
311 };
312 let driver = match self.driver.get_mut() {
313 Ok(driver) => driver,
314 Err(poisoned) => poisoned.into_inner(),
315 };
316 if let Some(driver) = driver.as_mut() {
317 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
318 driver.abandon_transaction(ownership);
319 }));
320 } else {
321 std::mem::forget(ownership);
322 }
323 self.finalized = true;
324 }
325}
326
327pub(super) enum PlanRuntimeStatic<R>
328where
329 R: DeviceRuntime,
330{
331 NoStatic { binding: StaticProvisioningBinding },
332 Static(PlanStaticResources<R>),
333}
334
335#[must_use = "plan runtime resources must be explicitly closed or safely abandoned"]
338pub struct PlanRuntimeResources<R>
339where
340 R: DeviceRuntime,
341{
342 pub(super) lifecycle: RwLock<()>,
343 pub(super) phase: AtomicU8,
344 pub(super) lifecycle_tx: watch::Sender<u8>,
345 pub(super) maintenance_controller: DynamicPoolMaintenanceController<R>,
346 pub(super) dynamic_pools: Arc<DynamicPoolSet<R>>,
347 pub(super) static_resources: PlanRuntimeStatic<R>,
348 pub(super) runtime: Arc<R>,
349 pub(super) deferred_cleanup_domain: DeferredDeviceCleanupDomainId,
350}
351
352#[must_use = "a trusted plan/runtime binding must be consumed by logical admission"]
356pub struct TrustedPlanRuntimeBinding<R>
357where
358 R: DeviceRuntime,
359{
360 pub(super) resources: Arc<PlanRuntimeResources<R>>,
361}
362
363pub(super) struct PlanBackingDeferral<R>
367where
368 R: DeviceRuntime,
369{
370 evidence: DynamicBackingDeferred,
371 resources: Arc<PlanRuntimeResources<R>>,
372}
373
374impl<R> PlanBackingDeferral<R>
375where
376 R: DeviceRuntime,
377{
378 pub(super) fn new(
379 resources: Arc<PlanRuntimeResources<R>>,
380 evidence: DynamicBackingDeferred,
381 ) -> Result<Self, VNextError> {
382 {
383 let _lifecycle = resources.read_lifecycle("bind deferred backing to its plan")?;
384 if evidence.wait_condition().coordinator_id()
385 != resources.dynamic_pools.logical_admission.id()
386 {
387 return Err(invalid_resource(
388 "deferred backing belongs to another plan coordinator",
389 ));
390 }
391 }
392 Ok(Self {
393 evidence,
394 resources,
395 })
396 }
397
398 pub(super) fn evidence(&self) -> &DynamicBackingDeferred {
399 &self.evidence
400 }
401
402 pub(super) fn maintain(&self) -> Result<DynamicDeferredMaintenanceOutcome, VNextError> {
403 let _lifecycle = self
404 .resources
405 .read_lifecycle("maintain plan-owned deferred backing")?;
406 if self
410 .resources
411 .dynamic_pools
412 .try_reclaim_expired_lane_slots()?
413 {
414 return self.retry_admission();
415 }
416 let outcome = self
417 .resources
418 .maintenance_controller
419 .maintain_for_live_deferred(&self.evidence)?;
420 if matches!(
421 &outcome,
422 DynamicDeferredMaintenanceOutcome::WaitForRelease { .. }
423 ) && self
424 .resources
425 .dynamic_pools
426 .try_reclaim_one_idle_lane_slot()?
427 {
428 return self.retry_admission();
429 }
430 Ok(outcome)
431 }
432
433 pub(super) fn retry_admission(&self) -> Result<DynamicDeferredMaintenanceOutcome, VNextError> {
434 let mut availability = Vec::with_capacity(self.resources.dynamic_pools.domains.len() + 3);
435 let current_epochs = self
436 .resources
437 .dynamic_pools
438 .write_capacity_availability(&mut availability)?;
439 Ok(DynamicDeferredMaintenanceOutcome::RetryAdmission { current_epochs })
440 }
441
442 pub(super) fn register_waiter(&self) -> Result<PlanCapacityWaitRegistration<R>, VNextError> {
443 self.resources
444 .register_capacity_waiter(self.evidence.wait_condition())
445 }
446}
447
448#[must_use = "capacity wait registrations must be awaited, rechecked, or dropped"]
451pub struct PlanCapacityWaitRegistration<R>
452where
453 R: DeviceRuntime,
454{
455 observed: CapacityWaitCondition,
456 registered: CapacityWaitCondition,
457 logical_rx: watch::Receiver<CapacityEpochs>,
458 plan_capacity_rx: watch::Receiver<DeviceCapacitySignal>,
459 process_capacity_rx: watch::Receiver<DeviceCapacitySignal>,
460 lifecycle_rx: watch::Receiver<u8>,
461 resources: Arc<PlanRuntimeResources<R>>,
462}
463
464impl<R> PlanCapacityWaitRegistration<R>
465where
466 R: DeviceRuntime,
467{
468 pub fn recheck(&self) -> Result<CapacityWaitRecheck, VNextError> {
469 let _lifecycle = self.resources.read_lifecycle("recheck a capacity waiter")?;
470 let mut availability = Vec::with_capacity(self.resources.dynamic_pools.domains.len() + 3);
471 let current = self
472 .resources
473 .dynamic_pools
474 .write_capacity_availability(&mut availability)?;
475 Ok(CapacityWaitRecheck::new(
476 current,
477 self.observed.changed_since(&availability)?,
478 self.registered.changed_since(&availability)?,
479 ))
480 }
481
482 pub async fn wait_for_change(self) -> Result<CapacityEpochs, VNextError> {
483 let Self {
484 observed,
485 registered,
486 mut logical_rx,
487 mut plan_capacity_rx,
488 mut process_capacity_rx,
489 mut lifecycle_rx,
490 resources,
491 } = self;
492 loop {
493 let recheck = {
494 let _lifecycle = match resources.read_lifecycle("recheck a capacity waiter") {
495 Ok(lifecycle) => lifecycle,
496 Err(_) if resources.is_closing() => {
497 return Err(invalid_resource(
498 "closing plan runtime cancelled its capacity waiter",
499 ));
500 }
501 Err(error) => return Err(error),
502 };
503 let mut availability =
504 Vec::with_capacity(resources.dynamic_pools.domains.len() + 3);
505 let current = resources
506 .dynamic_pools
507 .write_capacity_availability(&mut availability)?;
508 CapacityWaitRecheck::new(
509 current,
510 observed.changed_since(&availability)?,
511 registered.changed_since(&availability)?,
512 )
513 };
514 if recheck.should_retry() {
515 return Ok(recheck.current());
516 }
517 tokio::select! {
518 biased;
519 changed = lifecycle_rx.changed() => {
520 changed.map_err(|_| invalid_resource(
521 "plan runtime lifecycle signal closed while a capacity waiter was live",
522 ))?;
523 if *lifecycle_rx.borrow_and_update() == PLAN_RUNTIME_CLOSING {
524 return Err(invalid_resource(
525 "closing plan runtime cancelled its capacity waiter",
526 ));
527 }
528 return Err(invalid_resource(
529 "capacity waiter observed an invalid plan runtime lifecycle transition",
530 ));
531 }
532 changed = logical_rx.changed() => {
533 changed.map_err(|_| invalid_resource(
534 "logical capacity signal closed while a plan waiter was live",
535 ))?;
536 logical_rx.borrow_and_update();
537 }
538 changed = plan_capacity_rx.changed() => {
539 changed.map_err(|_| invalid_resource(
540 "plan device-capacity signal closed while a waiter was live",
541 ))?;
542 plan_capacity_rx.borrow_and_update();
543 }
544 changed = process_capacity_rx.changed() => {
545 changed.map_err(|_| invalid_resource(
546 "process device-capacity signal closed while a waiter was live",
547 ))?;
548 process_capacity_rx.borrow_and_update();
549 }
550 }
551 }
552 }
553}
554
555#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
556pub struct PlanRuntimeCloseReceipt {
557 evidence: TrustedPlanRuntimeEvidence,
558 released_static_resources: usize,
559}
560
561#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
562pub struct PlanRuntimeQuarantineReceipt {
563 evidence: TrustedPlanRuntimeEvidence,
564 released_static_resources: usize,
565 quarantined_static_resources: usize,
566}
567
568impl PlanRuntimeQuarantineReceipt {
569 pub fn evidence(&self) -> &TrustedPlanRuntimeEvidence {
570 &self.evidence
571 }
572
573 pub const fn released_static_resources(&self) -> usize {
574 self.released_static_resources
575 }
576
577 pub const fn quarantined_static_resources(&self) -> usize {
578 self.quarantined_static_resources
579 }
580}
581
582impl PlanRuntimeCloseReceipt {
583 pub fn evidence(&self) -> &TrustedPlanRuntimeEvidence {
584 &self.evidence
585 }
586
587 pub const fn released_static_resources(&self) -> usize {
588 self.released_static_resources
589 }
590}
591
592pub enum PlanRuntimeCloseOutcome<R>
593where
594 R: DeviceRuntime,
595{
596 Closed(PlanRuntimeCloseReceipt),
597 Referenced {
598 resources: Arc<PlanRuntimeResources<R>>,
599 strong_count: usize,
600 deferred_cleanup: DeferredDeviceCleanupStatus,
601 },
602}
603
604#[must_use = "failed plan runtime close retains static cleanup ownership"]
605pub struct PlanRuntimeCloseFailure<R>
606where
607 R: DeviceRuntime,
608{
609 failure: FailureEnvelope,
610 evidence: TrustedPlanRuntimeEvidence,
611 static_resources: Option<PlanStaticResources<R>>,
612}
613
614impl<R> PlanRuntimeCloseFailure<R>
615where
616 R: DeviceRuntime,
617{
618 pub fn failure(&self) -> &FailureEnvelope {
619 &self.failure
620 }
621
622 pub fn retry(mut self) -> Result<PlanRuntimeCloseReceipt, Self> {
623 let static_resources = self
624 .static_resources
625 .as_mut()
626 .expect("plan runtime close failure owns static cleanup authority");
627 let total_static_resources = static_resources.states.len();
628 match static_resources.release_all() {
629 Ok(_) => {
630 self.static_resources.take();
631 Ok(PlanRuntimeCloseReceipt {
632 evidence: self.evidence,
633 released_static_resources: total_static_resources,
634 })
635 }
636 Err(failure) => {
637 self.failure = failure.into_failure();
638 Err(self)
639 }
640 }
641 }
642
643 pub fn quarantine(mut self) -> Result<PlanRuntimeQuarantineReceipt, Self> {
644 let static_resources = self
645 .static_resources
646 .as_mut()
647 .expect("plan runtime close failure owns static cleanup authority");
648 let released_static_resources = static_resources
649 .states
650 .iter()
651 .filter(|state| **state == ResourceTransactionState::Released)
652 .count();
653 match static_resources.quarantine_remaining() {
654 Ok(quarantined_static_resources) => {
655 self.static_resources.take();
656 Ok(PlanRuntimeQuarantineReceipt {
657 evidence: self.evidence,
658 released_static_resources,
659 quarantined_static_resources,
660 })
661 }
662 Err(failure) => {
663 self.failure = failure.into_failure();
664 Err(self)
665 }
666 }
667 }
668}
669
670#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
671pub struct TrustedPlanRuntimeEvidence {
672 plan_id: PlanId,
673 plan_hash: PlanHash,
674 device_id: DeviceId,
675 runtime_implementation_fingerprint: String,
676 coordinator_id: LogicalAdmissionCoordinatorId,
677 static_provisioning_binding: Option<StaticProvisioningBinding>,
678 static_pool_identity: Option<ResourcePoolIdentity>,
679 static_provisioning_identity: Option<ResourceTransactionIdentity>,
680}
681
682impl TrustedPlanRuntimeEvidence {
683 pub fn plan_id(&self) -> &PlanId {
684 &self.plan_id
685 }
686
687 pub fn plan_hash(&self) -> &PlanHash {
688 &self.plan_hash
689 }
690
691 pub fn device_id(&self) -> &DeviceId {
692 &self.device_id
693 }
694
695 pub fn runtime_implementation_fingerprint(&self) -> &str {
696 &self.runtime_implementation_fingerprint
697 }
698
699 pub const fn coordinator_id(&self) -> LogicalAdmissionCoordinatorId {
700 self.coordinator_id
701 }
702
703 pub fn static_pool_identity(&self) -> Option<&ResourcePoolIdentity> {
704 self.static_pool_identity.as_ref()
705 }
706
707 pub fn static_provisioning_binding(&self) -> Option<&StaticProvisioningBinding> {
708 self.static_provisioning_binding.as_ref()
709 }
710
711 pub fn static_provisioning_identity(&self) -> Option<&ResourceTransactionIdentity> {
712 self.static_provisioning_identity.as_ref()
713 }
714}
715
716impl<R> NoStatic<R>
717where
718 R: DeviceRuntime,
719{
720 pub fn into_plan_runtime(self) -> Arc<PlanRuntimeResources<R>> {
721 let Self {
722 maintenance_controller,
723 dynamic_pools,
724 binding,
725 runtime,
726 } = self;
727 let (lifecycle_tx, _) = watch::channel(PLAN_RUNTIME_OPEN);
728 Arc::new(PlanRuntimeResources {
729 lifecycle: RwLock::new(()),
730 phase: AtomicU8::new(PLAN_RUNTIME_OPEN),
731 lifecycle_tx,
732 maintenance_controller,
733 dynamic_pools,
734 static_resources: PlanRuntimeStatic::NoStatic { binding },
735 runtime,
736 deferred_cleanup_domain: new_deferred_device_cleanup_domain(),
737 })
738 }
739}
740
741impl<R> PlanRuntimeResources<R>
742where
743 R: DeviceRuntime,
744{
745 fn evidence(&self) -> TrustedPlanRuntimeEvidence {
746 let (binding, identity) = match &self.static_resources {
747 PlanRuntimeStatic::NoStatic { binding } => (binding, None),
748 PlanRuntimeStatic::Static(source) => (&source.admission, Some(&source.identity)),
749 };
750 let has_static = matches!(&self.static_resources, PlanRuntimeStatic::Static(_));
751 TrustedPlanRuntimeEvidence {
752 plan_id: binding.plan_id().clone(),
753 plan_hash: binding.plan_hash().clone(),
754 device_id: binding.device_id().clone(),
755 runtime_implementation_fingerprint: binding
756 .device_runtime_implementation_fingerprint()
757 .to_owned(),
758 coordinator_id: self.dynamic_pools.logical_admission.id(),
759 static_provisioning_binding: has_static.then(|| binding.clone()),
760 static_pool_identity: has_static.then(|| binding.pool_identity().clone()),
761 static_provisioning_identity: identity.cloned(),
762 }
763 }
764
765 pub(super) fn read_lifecycle(
766 &self,
767 action: &'static str,
768 ) -> Result<RwLockReadGuard<'_, ()>, VNextError> {
769 let lifecycle = self
770 .lifecycle
771 .read()
772 .map_err(|_| invalid_resource("plan runtime lifecycle gate is poisoned"))?;
773 if self.phase.load(Ordering::Acquire) != PLAN_RUNTIME_OPEN {
774 return Err(invalid_resource(format!(
775 "closing plan runtime cannot {action}"
776 )));
777 }
778 let cleanup = self.deferred_cleanup_status();
779 if cleanup.is_saturated() {
780 return Err(invalid_resource(format!(
781 "plan runtime cannot {action} while {} deferred device cleanup owners await recovery",
782 cleanup.pending()
783 )));
784 }
785 Ok(lifecycle)
786 }
787
788 pub fn deferred_cleanup_status(&self) -> DeferredDeviceCleanupStatus {
789 deferred_device_cleanup_status(self.deferred_cleanup_domain)
790 }
791
792 pub fn create_execution_lane(
793 &self,
794 ) -> Result<Arc<ExecutionLane<R>>, ExecutionLaneCreationError<R::Error>> {
795 let _lifecycle = self
796 .read_lifecycle("create an execution lane")
797 .map_err(ExecutionLaneCreationError::Contract)?;
798 ExecutionLane::create(Arc::clone(&self.runtime))
799 }
800
801 pub fn maintain_deferred_cleanups(
805 &self,
806 maximum_tasks: usize,
807 ) -> Result<DeferredDeviceCleanupMaintenanceReceipt, VNextError> {
808 if maximum_tasks == 0
809 || maximum_tasks > super::MAX_DEFERRED_DEVICE_CLEANUP_MAINTENANCE_TASKS
810 {
811 return Err(invalid_resource(format!(
812 "deferred device cleanup maintenance size must be in 1..={}",
813 super::MAX_DEFERRED_DEVICE_CLEANUP_MAINTENANCE_TASKS
814 )));
815 }
816 Ok(maintain_deferred_device_cleanups(
817 self.deferred_cleanup_domain,
818 maximum_tasks,
819 ))
820 }
821
822 pub fn trusted_runtime_binding(
823 self: &Arc<Self>,
824 ) -> Result<TrustedPlanRuntimeBinding<R>, VNextError> {
825 let _lifecycle = self.read_lifecycle("mint a trusted runtime binding")?;
826 Ok(TrustedPlanRuntimeBinding {
827 resources: Arc::clone(self),
828 })
829 }
830
831 pub fn maintain_for_admission_deferred(
832 self: &Arc<Self>,
833 deferred: &AdmissionDeferred,
834 ) -> Result<DynamicDeferredMaintenanceOutcome, VNextError> {
835 let _lifecycle = self.read_lifecycle("maintain deferred logical backing growth")?;
836 self.maintenance_controller
837 .maintain_for_admission_deferred(deferred)
838 }
839
840 pub fn dynamic_pool_status(&self) -> Result<DynamicPoolMaintenanceStatus, VNextError> {
844 let _lifecycle = self.read_lifecycle("observe dynamic pool status")?;
845 self.maintenance_controller.status()
846 }
847
848 pub fn write_dynamic_capacity_availability(
849 &self,
850 out: &mut Vec<CapacityAvailabilityEpoch>,
851 ) -> Result<CapacityEpochs, VNextError> {
852 let _lifecycle = self.read_lifecycle("observe dynamic capacity availability")?;
853 self.dynamic_pools.write_capacity_availability(out)
854 }
855
856 pub fn register_capacity_waiter(
857 self: &Arc<Self>,
858 observed: &CapacityWaitCondition,
859 ) -> Result<PlanCapacityWaitRegistration<R>, VNextError> {
860 let _lifecycle = self.read_lifecycle("register a capacity waiter")?;
861 if observed.coordinator_id() != self.dynamic_pools.logical_admission.id() {
862 return Err(invalid_resource(
863 "capacity wait condition belongs to another plan coordinator",
864 ));
865 }
866 let logical_rx = self.dynamic_pools.logical_admission.subscribe_epochs();
867 let plan_capacity_rx = self.dynamic_pools.budget.subscribe_plan_availability();
868 let process_capacity_rx = self.dynamic_pools.budget.subscribe_process_availability();
869 let lifecycle_rx = self.lifecycle_tx.subscribe();
870 let mut availability = Vec::with_capacity(self.dynamic_pools.domains.len() + 3);
871 self.dynamic_pools
872 .write_capacity_availability(&mut availability)?;
873 let registered = observed.refreshed_from(&availability)?;
874 Ok(PlanCapacityWaitRegistration {
875 observed: observed.clone(),
876 registered,
877 logical_rx,
878 plan_capacity_rx,
879 process_capacity_rx,
880 lifecycle_rx,
881 resources: Arc::clone(self),
882 })
883 }
884
885 pub fn is_closing(&self) -> bool {
886 self.phase.load(Ordering::Acquire) == PLAN_RUNTIME_CLOSING
887 }
888
889 pub fn close(
890 resources: Arc<Self>,
891 ) -> Result<PlanRuntimeCloseOutcome<R>, PlanRuntimeCloseFailure<R>> {
892 {
893 let _lifecycle = resources
894 .lifecycle
895 .write()
896 .unwrap_or_else(std::sync::PoisonError::into_inner);
897 match resources.phase.compare_exchange(
898 PLAN_RUNTIME_OPEN,
899 PLAN_RUNTIME_CLOSING,
900 Ordering::AcqRel,
901 Ordering::Acquire,
902 ) {
903 Ok(_) => {
904 resources.lifecycle_tx.send_replace(PLAN_RUNTIME_CLOSING);
905 }
906 Err(PLAN_RUNTIME_CLOSING) => {}
907 Err(_) => unreachable!("plan runtime phase is privately bounded"),
908 }
909 }
910 let resources = match Arc::try_unwrap(resources) {
911 Ok(resources) => resources,
912 Err(resources) => {
913 let strong_count = Arc::strong_count(&resources);
914 let deferred_cleanup = resources.deferred_cleanup_status();
915 return Ok(PlanRuntimeCloseOutcome::Referenced {
916 resources,
917 strong_count,
918 deferred_cleanup,
919 });
920 }
921 };
922 if resources.deferred_cleanup_status().pending() != 0 {
923 let resources = Arc::new(resources);
924 let deferred_cleanup = resources.deferred_cleanup_status();
925 return Ok(PlanRuntimeCloseOutcome::Referenced {
926 resources,
927 strong_count: 1,
928 deferred_cleanup,
929 });
930 }
931 let evidence = resources.evidence();
932 let Self {
933 lifecycle: _,
934 phase: _,
935 lifecycle_tx,
936 maintenance_controller,
937 dynamic_pools,
938 static_resources,
939 runtime,
940 deferred_cleanup_domain,
941 } = resources;
942 debug_assert!(retire_deferred_device_cleanup_domain(
943 deferred_cleanup_domain
944 ));
945 drop(lifecycle_tx);
946 drop(maintenance_controller);
947 drop(dynamic_pools);
948 match static_resources {
949 PlanRuntimeStatic::NoStatic { .. } => {
950 drop(runtime);
951 Ok(PlanRuntimeCloseOutcome::Closed(PlanRuntimeCloseReceipt {
952 evidence,
953 released_static_resources: 0,
954 }))
955 }
956 PlanRuntimeStatic::Static(mut static_resources) => {
957 drop(runtime);
958 let total_static_resources = static_resources.states.len();
959 match static_resources.release_all() {
960 Ok(_) => {
961 drop(static_resources);
962 Ok(PlanRuntimeCloseOutcome::Closed(PlanRuntimeCloseReceipt {
963 evidence,
964 released_static_resources: total_static_resources,
965 }))
966 }
967 Err(failure) => Err(PlanRuntimeCloseFailure {
968 failure: failure.into_failure(),
969 evidence,
970 static_resources: Some(static_resources),
971 }),
972 }
973 }
974 }
975 }
976}
977
978impl<R> TrustedPlanRuntimeBinding<R>
979where
980 R: DeviceRuntime,
981{
982 pub(super) fn runtime(&self) -> &Arc<R> {
983 &self.resources.runtime
984 }
985
986 pub(super) fn logical_admission(&self) -> &LogicalAdmissionCoordinator {
987 &self.dynamic_pools().logical_admission
988 }
989
990 pub(super) fn dynamic_pools(&self) -> &Arc<DynamicPoolSet<R>> {
991 &self.resources.dynamic_pools
992 }
993
994 pub(super) fn nodes(&self) -> &[PlanNode] {
995 &self.dynamic_pools().nodes
996 }
997
998 pub(super) fn reusable_execution_bucket(
999 &self,
1000 bucket_id: &ReusableExecutionBucketId,
1001 ) -> Option<&ResolvedReusableExecutionBucket> {
1002 self.dynamic_pools()
1003 .reusable_execution
1004 .as_ref()
1005 .and_then(|plan| plan.bucket(bucket_id))
1006 }
1007
1008 pub fn plan_id(&self) -> &PlanId {
1009 match &self.resources.static_resources {
1010 PlanRuntimeStatic::NoStatic { binding } => binding.plan_id(),
1011 PlanRuntimeStatic::Static(source) => source.admission.plan_id(),
1012 }
1013 }
1014
1015 pub fn plan_hash(&self) -> &PlanHash {
1016 match &self.resources.static_resources {
1017 PlanRuntimeStatic::NoStatic { binding } => binding.plan_hash(),
1018 PlanRuntimeStatic::Static(source) => source.admission.plan_hash(),
1019 }
1020 }
1021
1022 pub fn device_id(&self) -> &DeviceId {
1023 match &self.resources.static_resources {
1024 PlanRuntimeStatic::NoStatic { binding } => binding.device_id(),
1025 PlanRuntimeStatic::Static(source) => source.admission.device_id(),
1026 }
1027 }
1028
1029 pub fn runtime_implementation_fingerprint(&self) -> &str {
1030 match &self.resources.static_resources {
1031 PlanRuntimeStatic::NoStatic { binding } => {
1032 binding.device_runtime_implementation_fingerprint()
1033 }
1034 PlanRuntimeStatic::Static(source) => {
1035 source.admission.device_runtime_implementation_fingerprint()
1036 }
1037 }
1038 }
1039
1040 pub fn coordinator_id(&self) -> LogicalAdmissionCoordinatorId {
1041 self.logical_admission().id()
1042 }
1043
1044 pub fn static_provisioning(&self) -> Option<&StaticProvisioningLease<R>> {
1045 match &self.resources.static_resources {
1046 PlanRuntimeStatic::NoStatic { .. } => None,
1047 PlanRuntimeStatic::Static(source) => source.lease.as_ref(),
1048 }
1049 }
1050
1051 pub fn evidence(&self) -> TrustedPlanRuntimeEvidence {
1052 self.resources.evidence()
1053 }
1054
1055 pub(super) fn scoped_demand(
1056 &self,
1057 lifetime: AllocationLifetime,
1058 node_id: Option<&NodeId>,
1059 immediate_shape: DynamicResourceShape,
1060 fit_shape: DynamicResourceShape,
1061 reusable_execution_bucket: Option<&ReusableExecutionBucketSpec>,
1062 fit_policy: AdmissionFitPolicy,
1063 pressure_action: AdmissionPressureAction,
1064 ) -> Result<(AdmissionDemand, Vec<EvaluatedBackingRequest<'_>>), VNextError> {
1065 if (lifetime == AllocationLifetime::Invocation) != node_id.is_some() {
1066 return Err(invalid_resource(
1067 "invocation resource demand requires one exact node identity",
1068 ));
1069 }
1070 let capacity_shape = match reusable_execution_bucket {
1071 Some(bucket)
1072 if matches!(
1073 lifetime,
1074 AllocationLifetime::Step | AllocationLifetime::Invocation
1075 ) && bucket.capacity().covers(
1076 immediate_shape.sequences(),
1077 immediate_shape.tokens(),
1078 immediate_shape.pages(),
1079 ) && bucket.capacity().covers(
1080 fit_shape.sequences(),
1081 fit_shape.tokens(),
1082 fit_shape.pages(),
1083 ) =>
1084 {
1085 DynamicResourceShape::from_validated(
1086 bucket.capacity().maximum_sequences(),
1087 bucket.capacity().maximum_tokens(),
1088 bucket.capacity().maximum_pages(),
1089 )
1090 }
1091 Some(_) => {
1092 return Err(invalid_resource(
1093 "reusable execution bucket does not cover this Step or Invocation demand",
1094 ));
1095 }
1096 None => immediate_shape,
1097 };
1098 let node_resources = node_id
1099 .map(|node_id| {
1100 self.nodes()
1101 .iter()
1102 .find(|node| node.id() == node_id)
1103 .map(PlanNode::resources)
1104 .ok_or_else(|| {
1105 invalid_resource("resource admission references an unknown node")
1106 })
1107 })
1108 .transpose()?;
1109 let mut immediate_entries = Vec::new();
1110 let mut fit_entries = Vec::new();
1111 let mut requested_slices = Vec::new();
1112 for domain in &self.dynamic_pools().domains {
1113 let mut immediate_pool_bytes = 0_u64;
1114 let mut fit_pool_bytes = 0_u64;
1115 let mut matched = false;
1116 if lifetime == AllocationLifetime::Step {
1117 for slot in domain.pool.step_resource_slots() {
1118 let mut projections = Vec::with_capacity(slot.resource_ids().len());
1119 let mut immediate_slot_bytes = 0_u64;
1120 let mut fit_slot_bytes = 0_u64;
1121 let mut capacity_slot_bytes = 0_u64;
1122 for resource_id in slot.resource_ids() {
1123 let descriptor = domain
1124 .descriptors
1125 .iter()
1126 .find(|descriptor| descriptor.base_resource_id() == resource_id)
1127 .ok_or_else(|| {
1128 invalid_resource(
1129 "step physical slot references a descriptor outside its pool",
1130 )
1131 })?;
1132 if descriptor.lifetime() != AllocationLifetime::Step {
1133 return Err(invalid_resource(
1134 "step physical slot references a non-Step descriptor",
1135 ));
1136 }
1137 let logical_size_bytes =
1138 descriptor.evaluate_request_bytes_for_shape(immediate_shape)?;
1139 let fit_bytes = descriptor.evaluate_request_bytes_for_shape(fit_shape)?;
1140 let capacity_size_bytes =
1141 descriptor.evaluate_request_bytes_for_shape(capacity_shape)?;
1142 immediate_slot_bytes = immediate_slot_bytes.max(logical_size_bytes);
1143 fit_slot_bytes = fit_slot_bytes.max(fit_bytes);
1144 capacity_slot_bytes = capacity_slot_bytes.max(capacity_size_bytes);
1145 projections.push(EvaluatedBackingProjection {
1146 descriptor,
1147 physical_offset_bytes: 0,
1148 logical_size_bytes,
1149 capacity_size_bytes,
1150 });
1151 }
1152 immediate_pool_bytes = immediate_pool_bytes
1153 .checked_add(immediate_slot_bytes)
1154 .ok_or_else(|| {
1155 invalid_resource("dynamic pool immediate demand overflows u64")
1156 })?;
1157 fit_pool_bytes = fit_pool_bytes
1158 .checked_add(fit_slot_bytes)
1159 .ok_or_else(|| invalid_resource("dynamic pool fit demand overflows u64"))?;
1160 requested_slices.push(EvaluatedBackingRequest {
1161 domain,
1162 claim_identity: PhysicalBackingClaimIdentity::new(
1163 domain.pool_id().clone(),
1164 slot.resource_ids().to_vec(),
1165 )?,
1166 capacity_size_bytes: capacity_slot_bytes,
1167 reusable_execution_bucket_id: reusable_execution_bucket
1168 .map(|bucket| bucket.bucket_id().clone()),
1169 projections,
1170 });
1171 matched = true;
1172 }
1173 } else {
1174 for descriptor in &domain.descriptors {
1175 if descriptor.lifetime() != lifetime
1176 || node_resources.is_some_and(|resources| {
1177 !resources.contains(descriptor.base_resource_id())
1178 })
1179 {
1180 continue;
1181 }
1182 matched = true;
1183 let logical_size_bytes =
1184 descriptor.evaluate_request_bytes_for_shape(immediate_shape)?;
1185 let fit_bytes = descriptor.evaluate_request_bytes_for_shape(fit_shape)?;
1186 let capacity_size_bytes =
1187 descriptor.evaluate_request_bytes_for_shape(capacity_shape)?;
1188 immediate_pool_bytes = immediate_pool_bytes
1189 .checked_add(logical_size_bytes)
1190 .ok_or_else(|| {
1191 invalid_resource("dynamic pool immediate demand overflows u64")
1192 })?;
1193 fit_pool_bytes = fit_pool_bytes
1194 .checked_add(fit_bytes)
1195 .ok_or_else(|| invalid_resource("dynamic pool fit demand overflows u64"))?;
1196 requested_slices.push(EvaluatedBackingRequest {
1197 domain,
1198 claim_identity: PhysicalBackingClaimIdentity::new(
1199 domain.pool_id().clone(),
1200 vec![descriptor.base_resource_id().clone()],
1201 )?,
1202 capacity_size_bytes,
1203 reusable_execution_bucket_id: reusable_execution_bucket
1204 .map(|bucket| bucket.bucket_id().clone()),
1205 projections: vec![EvaluatedBackingProjection {
1206 descriptor,
1207 physical_offset_bytes: 0,
1208 logical_size_bytes,
1209 capacity_size_bytes,
1210 }],
1211 });
1212 }
1213 }
1214 if matched {
1215 immediate_entries.push(CapacityEntry::new(
1216 domain.domain_id(),
1217 CapacityUnits::new(immediate_pool_bytes),
1218 )?);
1219 fit_entries.push(CapacityEntry::new(
1220 domain.domain_id(),
1221 CapacityUnits::new(fit_pool_bytes),
1222 )?);
1223 }
1224 }
1225 let immediate = if immediate_entries.is_empty() {
1226 CapacityVector::empty()
1227 } else {
1228 CapacityVector::new(immediate_entries)?
1229 };
1230 let fit = if fit_entries.is_empty() {
1231 CapacityVector::empty()
1232 } else {
1233 CapacityVector::new(fit_entries)?
1234 };
1235 Ok((
1236 AdmissionDemand::from_plan(immediate, fit, fit_policy, pressure_action)?,
1237 requested_slices,
1238 ))
1239 }
1240
1241 pub(super) fn sequence_extension_demand(
1245 &self,
1246 committed: DynamicResourceShape,
1247 target: DynamicResourceShape,
1248 pressure_action: AdmissionPressureAction,
1249 ) -> Result<(AdmissionDemand, Vec<EvaluatedBackingRequest<'_>>), VNextError> {
1250 if committed.sequences() != 1
1251 || target.sequences() != 1
1252 || target.tokens() < committed.tokens()
1253 || target.pages() < committed.pages()
1254 {
1255 return Err(invalid_resource(
1256 "sequence extension target must monotonically advance one committed sequence",
1257 ));
1258 }
1259
1260 let mut entries = Vec::new();
1261 let mut requested_slices = Vec::new();
1262 for domain in &self.dynamic_pools().domains {
1263 let mut pool_delta = 0_u64;
1264 for descriptor in &domain.descriptors {
1265 if descriptor.lifetime() != AllocationLifetime::Sequence {
1266 continue;
1267 }
1268 let committed_bytes = descriptor.evaluate_request_bytes_for_shape(committed)?;
1269 let target_bytes = descriptor.evaluate_request_bytes_for_shape(target)?;
1270 let delta_bytes = target_bytes.checked_sub(committed_bytes).ok_or_else(|| {
1271 invalid_resource("sequence extension descriptor capacity regressed")
1272 })?;
1273 if delta_bytes == 0 {
1274 continue;
1275 }
1276 if !matches!(
1277 descriptor.storage().profile().view(),
1278 super::DynamicStorageView::PagedRegions { .. }
1279 ) {
1280 return Err(invalid_resource(
1281 "sequence backing extension requires a paged storage profile",
1282 ));
1283 }
1284 pool_delta = pool_delta.checked_add(delta_bytes).ok_or_else(|| {
1285 invalid_resource("sequence extension pool demand overflows u64")
1286 })?;
1287 requested_slices.push(EvaluatedBackingRequest {
1288 domain,
1289 claim_identity: PhysicalBackingClaimIdentity::new(
1290 domain.pool_id().clone(),
1291 vec![descriptor.base_resource_id().clone()],
1292 )?,
1293 capacity_size_bytes: delta_bytes,
1294 reusable_execution_bucket_id: None,
1295 projections: vec![EvaluatedBackingProjection {
1296 descriptor,
1297 physical_offset_bytes: 0,
1298 logical_size_bytes: delta_bytes,
1299 capacity_size_bytes: delta_bytes,
1300 }],
1301 });
1302 }
1303 if pool_delta != 0 {
1304 entries.push(CapacityEntry::new(
1305 domain.domain_id(),
1306 CapacityUnits::new(pool_delta),
1307 )?);
1308 }
1309 }
1310 let delta = if entries.is_empty() {
1311 CapacityVector::empty()
1312 } else {
1313 CapacityVector::new(entries)?
1314 };
1315 Ok((
1316 AdmissionDemand::from_plan(
1317 delta.clone(),
1318 delta,
1319 AdmissionFitPolicy::ImmediateOnly,
1320 pressure_action,
1321 )?,
1322 requested_slices,
1323 ))
1324 }
1325
1326 pub(super) fn submission_wave_demand(
1330 &self,
1331 immediate_shape: DynamicResourceShape,
1332 fit_shape: DynamicResourceShape,
1333 reusable_execution_bucket: Option<&ReusableExecutionBucketSpec>,
1334 fit_policy: AdmissionFitPolicy,
1335 pressure_action: AdmissionPressureAction,
1336 ) -> Result<(AdmissionDemand, Vec<EvaluatedBackingRequest<'_>>), VNextError> {
1337 match reusable_execution_bucket {
1338 Some(bucket)
1339 if bucket.capacity().covers(
1340 immediate_shape.sequences(),
1341 immediate_shape.tokens(),
1342 immediate_shape.pages(),
1343 ) && bucket.capacity().covers(
1344 fit_shape.sequences(),
1345 fit_shape.tokens(),
1346 fit_shape.pages(),
1347 ) =>
1348 {
1349 }
1352 Some(_) => {
1353 return Err(invalid_resource(
1354 "reusable execution bucket does not cover the submission-wave work shape",
1355 ));
1356 }
1357 None => {}
1358 }
1359
1360 let mut immediate_entries = Vec::new();
1361 let mut fit_entries = Vec::new();
1362 let mut requested_slices = Vec::new();
1363 let pools = self.dynamic_pools();
1364 if pools.domains.len() != pools.submission_wave_layouts.len() {
1365 return Err(invalid_resource(
1366 "submission wave layout count differs from immutable plan domains",
1367 ));
1368 }
1369 let reusable_capacity_layouts = reusable_execution_bucket
1370 .map(|bucket| {
1371 pools
1372 .submission_wave_reusable_capacity_layouts
1373 .get(bucket.bucket_id())
1374 .ok_or_else(|| {
1375 invalid_resource(
1376 "reusable execution bucket has no compiled submission-wave capacity layout",
1377 )
1378 })
1379 })
1380 .transpose()?;
1381 if reusable_capacity_layouts.is_some_and(|layouts| layouts.len() != pools.domains.len()) {
1382 return Err(invalid_resource(
1383 "compiled reusable submission-wave capacity layout count differs from immutable plan domains",
1384 ));
1385 }
1386 for (domain_index, (domain, layout)) in pools
1387 .domains
1388 .iter()
1389 .zip(&pools.submission_wave_layouts)
1390 .enumerate()
1391 {
1392 let reusable_capacity_layout = reusable_capacity_layouts
1393 .map(|layouts| {
1394 layouts.get(domain_index).ok_or_else(|| {
1395 invalid_resource(
1396 "compiled reusable submission-wave capacity layout is incomplete",
1397 )
1398 })
1399 })
1400 .transpose()?
1401 .and_then(Option::as_ref);
1402 let Some(layout) = layout else {
1403 if reusable_capacity_layout.is_some() {
1404 return Err(invalid_resource(
1405 "compiled reusable submission-wave capacity exists without a domain layout",
1406 ));
1407 }
1408 continue;
1409 };
1410 if reusable_execution_bucket.is_some() && reusable_capacity_layout.is_none() {
1411 return Err(invalid_resource(
1412 "compiled reusable submission-wave capacity is missing for a domain layout",
1413 ));
1414 }
1415 if reusable_capacity_layout
1416 .is_some_and(|capacity| capacity.projections.len() != layout.projection_count)
1417 {
1418 return Err(invalid_resource(
1419 "compiled reusable submission-wave projection count differs from its domain layout",
1420 ));
1421 }
1422 let mode = domain.pool.invocation_liveness_mode();
1423
1424 let mut projections = vec![None; layout.projection_count];
1425 let mut immediate_pool_bytes = 0_u64;
1426 let mut fit_pool_bytes = 0_u64;
1427 let mut capacity_pool_bytes = 0_u64;
1428 for row in &layout.rows {
1429 let row_base = match mode {
1430 InvocationLivenessMode::TotalOrderReuse => 0,
1431 InvocationLivenessMode::ConservativeConcurrent => capacity_pool_bytes,
1432 InvocationLivenessMode::NoInvocationResources => unreachable!(),
1433 };
1434 let mut immediate_row_bytes = 0_u64;
1435 let mut fit_row_bytes = 0_u64;
1436 let mut capacity_row_bytes = 0_u64;
1437 for projection_layout in &row.projections {
1438 let descriptor = domain
1439 .descriptors
1440 .get(projection_layout.descriptor_index)
1441 .ok_or_else(|| {
1442 invalid_resource(
1443 "submission wave layout references a descriptor outside its pool",
1444 )
1445 })?;
1446 if descriptor.lifetime() != AllocationLifetime::Invocation {
1447 return Err(invalid_resource(
1448 "invocation liveness row references a non-Invocation descriptor",
1449 ));
1450 }
1451 let logical_size_bytes =
1452 descriptor.evaluate_request_bytes_for_shape(immediate_shape)?;
1453 let fit_bytes = if fit_shape == immediate_shape {
1454 logical_size_bytes
1455 } else {
1456 descriptor.evaluate_request_bytes_for_shape(fit_shape)?
1457 };
1458 let (physical_offset_bytes, capacity_size_bytes) =
1459 match reusable_capacity_layout {
1460 Some(capacity_layout) => {
1461 let capacity_projection = capacity_layout
1462 .projections
1463 .get(projection_layout.projection_index)
1464 .ok_or_else(|| {
1465 invalid_resource(
1466 "compiled reusable submission-wave projection is missing",
1467 )
1468 })?;
1469 (
1470 capacity_projection.physical_offset_bytes,
1471 capacity_projection.capacity_size_bytes,
1472 )
1473 }
1474 None => {
1475 let physical_offset_bytes =
1476 row_base.checked_add(capacity_row_bytes).ok_or_else(|| {
1477 invalid_resource(
1478 "invocation wave projection offset overflows u64",
1479 )
1480 })?;
1481 (physical_offset_bytes, logical_size_bytes)
1482 }
1483 };
1484 immediate_row_bytes = immediate_row_bytes
1485 .checked_add(logical_size_bytes)
1486 .ok_or_else(|| {
1487 invalid_resource("invocation wave row demand overflows u64")
1488 })?;
1489 fit_row_bytes = fit_row_bytes.checked_add(fit_bytes).ok_or_else(|| {
1490 invalid_resource("invocation wave fit row demand overflows u64")
1491 })?;
1492 if reusable_capacity_layout.is_none() {
1493 capacity_row_bytes = capacity_row_bytes
1494 .checked_add(capacity_size_bytes)
1495 .ok_or_else(|| {
1496 invalid_resource("invocation wave capacity row overflows u64")
1497 })?;
1498 }
1499 if projections[projection_layout.projection_index]
1500 .replace(EvaluatedBackingProjection {
1501 descriptor,
1502 physical_offset_bytes,
1503 logical_size_bytes,
1504 capacity_size_bytes,
1505 })
1506 .is_some()
1507 {
1508 return Err(invalid_resource(
1509 "submission wave layout repeated a canonical projection",
1510 ));
1511 }
1512 }
1513 match mode {
1514 InvocationLivenessMode::TotalOrderReuse => {
1515 immediate_pool_bytes = immediate_pool_bytes.max(immediate_row_bytes);
1516 fit_pool_bytes = fit_pool_bytes.max(fit_row_bytes);
1517 if reusable_capacity_layout.is_none() {
1518 capacity_pool_bytes = capacity_pool_bytes.max(capacity_row_bytes);
1519 }
1520 }
1521 InvocationLivenessMode::ConservativeConcurrent => {
1522 immediate_pool_bytes = immediate_pool_bytes
1523 .checked_add(immediate_row_bytes)
1524 .ok_or_else(|| {
1525 invalid_resource("invocation wave pool demand overflows u64")
1526 })?;
1527 fit_pool_bytes =
1528 fit_pool_bytes.checked_add(fit_row_bytes).ok_or_else(|| {
1529 invalid_resource("invocation wave pool fit demand overflows u64")
1530 })?;
1531 if reusable_capacity_layout.is_none() {
1532 capacity_pool_bytes = capacity_pool_bytes
1533 .checked_add(capacity_row_bytes)
1534 .ok_or_else(|| {
1535 invalid_resource(
1536 "invocation wave capacity pool demand overflows u64",
1537 )
1538 })?;
1539 }
1540 }
1541 InvocationLivenessMode::NoInvocationResources => unreachable!(),
1542 }
1543 }
1544 if let Some(capacity_layout) = reusable_capacity_layout {
1545 capacity_pool_bytes = capacity_layout.physical_size_bytes;
1546 }
1547 let projections = projections
1548 .into_iter()
1549 .collect::<Option<Vec<_>>>()
1550 .ok_or_else(|| {
1551 invalid_resource("submission wave layout left a projection unevaluated")
1552 })?;
1553 if projections.is_empty() || immediate_pool_bytes == 0 {
1554 return Err(invalid_resource(
1555 "submission wave layout evaluated to empty invocation demand",
1556 ));
1557 }
1558
1559 requested_slices.push(EvaluatedBackingRequest {
1560 domain,
1561 claim_identity: layout.claim_identity.clone(),
1562 capacity_size_bytes: capacity_pool_bytes,
1563 reusable_execution_bucket_id: reusable_execution_bucket
1564 .map(|bucket| bucket.bucket_id().clone()),
1565 projections,
1566 });
1567 immediate_entries.push(CapacityEntry::new(
1568 domain.domain_id(),
1569 CapacityUnits::new(immediate_pool_bytes),
1570 )?);
1571 fit_entries.push(CapacityEntry::new(
1572 domain.domain_id(),
1573 CapacityUnits::new(fit_pool_bytes),
1574 )?);
1575 }
1576
1577 let immediate = if immediate_entries.is_empty() {
1578 CapacityVector::empty()
1579 } else {
1580 CapacityVector::new(immediate_entries)?
1581 };
1582 let fit = if fit_entries.is_empty() {
1583 CapacityVector::empty()
1584 } else {
1585 CapacityVector::new(fit_entries)?
1586 };
1587 Ok((
1588 AdmissionDemand::from_plan(immediate, fit, fit_policy, pressure_action)?,
1589 requested_slices,
1590 ))
1591 }
1592
1593 pub(super) fn prepare_backing_slices(
1594 &self,
1595 requested_slices: Vec<EvaluatedBackingRequest<'_>>,
1596 ) -> Result<BackingPrepareDecision<R>, VNextError> {
1597 self.dynamic_pools().prepare_claim(&requested_slices)
1598 }
1599
1600 pub(super) fn prepare_lane_stable_backing_slices(
1601 &self,
1602 lane: &Arc<ExecutionLane<R>>,
1603 requested_slices: Vec<EvaluatedBackingRequest<'_>>,
1604 ) -> Result<LaneBackingPrepareDecision, VNextError> {
1605 self.dynamic_pools()
1606 .prepare_lane_stable_claim(lane, &requested_slices)
1607 }
1608
1609 pub(super) fn prepare_initial_sequence_backing_slices(
1610 &self,
1611 requested_slices: &[EvaluatedBackingRequest<'_>],
1612 ) -> Result<BackingPrepareDecision<R>, VNextError> {
1613 self.dynamic_pools()
1614 .prepare_initial_sequence_claim(requested_slices)
1615 }
1616
1617 pub(super) fn register_backing_waiter(
1618 &self,
1619 deferred: &DynamicBackingDeferred,
1620 ) -> Result<PlanCapacityWaitRegistration<R>, VNextError> {
1621 self.resources
1622 .register_capacity_waiter(deferred.wait_condition())
1623 }
1624
1625 pub fn register_admission_waiter(
1626 &self,
1627 deferred: &AdmissionDeferred,
1628 ) -> Result<PlanCapacityWaitRegistration<R>, VNextError> {
1629 self.resources
1630 .register_capacity_waiter(deferred.wait_condition())
1631 }
1632}