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, AdmissionRejected, AllocationLifetime, Arc,
6 AtomicU8, BackingPrepareDecision, CapacityAvailabilityEpoch, CapacityEntry, CapacityEpochs,
7 CapacityUnits, CapacityVector, CapacityWaitCondition, CapacityWaitRecheck,
8 DeferredDeviceCleanupDomainId, DeferredDeviceCleanupMaintenanceReceipt,
9 DeferredDeviceCleanupStatus, DeviceCapacityClaim, DeviceCapacitySignal, DeviceId,
10 DeviceRuntime, DynamicBackingDeferred, DynamicDeferredMaintenanceOutcome,
11 DynamicPoolGrowthBatchReceipt, DynamicPoolMaintenanceController, DynamicPoolMaintenanceStatus,
12 DynamicPoolSet, DynamicResourceShape, EvaluatedBackingProjection, EvaluatedBackingRequest,
13 ExecutionLane, 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 try_maintain_for_capacity_pressure(
847 self: &Arc<Self>,
848 deferred: &AdmissionDeferred,
849 ) -> Result<Option<DynamicPoolGrowthBatchReceipt>, VNextError> {
850 let _lifecycle = self.read_lifecycle("maintain foreground capacity pressure")?;
851 self.maintenance_controller
852 .try_maintain_for_capacity_pressure(deferred)
853 }
854
855 pub fn dynamic_pool_status(&self) -> Result<DynamicPoolMaintenanceStatus, VNextError> {
859 let _lifecycle = self.read_lifecycle("observe dynamic pool status")?;
860 self.maintenance_controller.status()
861 }
862
863 pub fn write_dynamic_capacity_availability(
864 &self,
865 out: &mut Vec<CapacityAvailabilityEpoch>,
866 ) -> Result<CapacityEpochs, VNextError> {
867 let _lifecycle = self.read_lifecycle("observe dynamic capacity availability")?;
868 self.dynamic_pools.write_capacity_availability(out)
869 }
870
871 pub fn register_capacity_waiter(
872 self: &Arc<Self>,
873 observed: &CapacityWaitCondition,
874 ) -> Result<PlanCapacityWaitRegistration<R>, VNextError> {
875 let _lifecycle = self.read_lifecycle("register a capacity waiter")?;
876 if observed.coordinator_id() != self.dynamic_pools.logical_admission.id() {
877 return Err(invalid_resource(
878 "capacity wait condition belongs to another plan coordinator",
879 ));
880 }
881 let logical_rx = self.dynamic_pools.logical_admission.subscribe_epochs();
882 let plan_capacity_rx = self.dynamic_pools.budget.subscribe_plan_availability();
883 let process_capacity_rx = self.dynamic_pools.budget.subscribe_process_availability();
884 let lifecycle_rx = self.lifecycle_tx.subscribe();
885 let mut availability = Vec::with_capacity(self.dynamic_pools.domains.len() + 3);
886 self.dynamic_pools
887 .write_capacity_availability(&mut availability)?;
888 let registered = observed.refreshed_from(&availability)?;
889 Ok(PlanCapacityWaitRegistration {
890 observed: observed.clone(),
891 registered,
892 logical_rx,
893 plan_capacity_rx,
894 process_capacity_rx,
895 lifecycle_rx,
896 resources: Arc::clone(self),
897 })
898 }
899
900 pub fn is_closing(&self) -> bool {
901 self.phase.load(Ordering::Acquire) == PLAN_RUNTIME_CLOSING
902 }
903
904 pub fn close(
905 resources: Arc<Self>,
906 ) -> Result<PlanRuntimeCloseOutcome<R>, PlanRuntimeCloseFailure<R>> {
907 {
908 let _lifecycle = resources
909 .lifecycle
910 .write()
911 .unwrap_or_else(std::sync::PoisonError::into_inner);
912 let _ = resources
916 .dynamic_pools
917 .logical_admission
918 .close_checkpoint_admission();
919 match resources.phase.compare_exchange(
920 PLAN_RUNTIME_OPEN,
921 PLAN_RUNTIME_CLOSING,
922 Ordering::AcqRel,
923 Ordering::Acquire,
924 ) {
925 Ok(_) => {
926 resources.lifecycle_tx.send_replace(PLAN_RUNTIME_CLOSING);
927 }
928 Err(PLAN_RUNTIME_CLOSING) => {}
929 Err(_) => unreachable!("plan runtime phase is privately bounded"),
930 }
931 }
932 let resources = match Arc::try_unwrap(resources) {
933 Ok(resources) => resources,
934 Err(resources) => {
935 let strong_count = Arc::strong_count(&resources);
936 let deferred_cleanup = resources.deferred_cleanup_status();
937 return Ok(PlanRuntimeCloseOutcome::Referenced {
938 resources,
939 strong_count,
940 deferred_cleanup,
941 });
942 }
943 };
944 if resources.deferred_cleanup_status().pending() != 0 {
945 let resources = Arc::new(resources);
946 let deferred_cleanup = resources.deferred_cleanup_status();
947 return Ok(PlanRuntimeCloseOutcome::Referenced {
948 resources,
949 strong_count: 1,
950 deferred_cleanup,
951 });
952 }
953 let evidence = resources.evidence();
954 let Self {
955 lifecycle: _,
956 phase: _,
957 lifecycle_tx,
958 maintenance_controller,
959 dynamic_pools,
960 static_resources,
961 runtime,
962 deferred_cleanup_domain,
963 } = resources;
964 debug_assert!(retire_deferred_device_cleanup_domain(
965 deferred_cleanup_domain
966 ));
967 drop(lifecycle_tx);
968 drop(maintenance_controller);
969 drop(dynamic_pools);
970 match static_resources {
971 PlanRuntimeStatic::NoStatic { .. } => {
972 drop(runtime);
973 Ok(PlanRuntimeCloseOutcome::Closed(PlanRuntimeCloseReceipt {
974 evidence,
975 released_static_resources: 0,
976 }))
977 }
978 PlanRuntimeStatic::Static(mut static_resources) => {
979 drop(runtime);
980 let total_static_resources = static_resources.states.len();
981 match static_resources.release_all() {
982 Ok(_) => {
983 drop(static_resources);
984 Ok(PlanRuntimeCloseOutcome::Closed(PlanRuntimeCloseReceipt {
985 evidence,
986 released_static_resources: total_static_resources,
987 }))
988 }
989 Err(failure) => Err(PlanRuntimeCloseFailure {
990 failure: failure.into_failure(),
991 evidence,
992 static_resources: Some(static_resources),
993 }),
994 }
995 }
996 }
997 }
998}
999
1000impl<R> TrustedPlanRuntimeBinding<R>
1001where
1002 R: DeviceRuntime,
1003{
1004 pub(super) fn runtime(&self) -> &Arc<R> {
1005 &self.resources.runtime
1006 }
1007
1008 pub(super) fn logical_admission(&self) -> &LogicalAdmissionCoordinator {
1009 &self.dynamic_pools().logical_admission
1010 }
1011
1012 pub(super) fn dynamic_pools(&self) -> &Arc<DynamicPoolSet<R>> {
1013 &self.resources.dynamic_pools
1014 }
1015
1016 pub(super) fn nodes(&self) -> &[PlanNode] {
1017 &self.dynamic_pools().nodes
1018 }
1019
1020 pub(super) fn reusable_execution_bucket(
1021 &self,
1022 bucket_id: &ReusableExecutionBucketId,
1023 ) -> Option<&ResolvedReusableExecutionBucket> {
1024 self.dynamic_pools()
1025 .reusable_execution
1026 .as_ref()
1027 .and_then(|plan| plan.bucket(bucket_id))
1028 }
1029
1030 pub fn plan_id(&self) -> &PlanId {
1031 match &self.resources.static_resources {
1032 PlanRuntimeStatic::NoStatic { binding } => binding.plan_id(),
1033 PlanRuntimeStatic::Static(source) => source.admission.plan_id(),
1034 }
1035 }
1036
1037 pub fn plan_hash(&self) -> &PlanHash {
1038 match &self.resources.static_resources {
1039 PlanRuntimeStatic::NoStatic { binding } => binding.plan_hash(),
1040 PlanRuntimeStatic::Static(source) => source.admission.plan_hash(),
1041 }
1042 }
1043
1044 pub fn device_id(&self) -> &DeviceId {
1045 match &self.resources.static_resources {
1046 PlanRuntimeStatic::NoStatic { binding } => binding.device_id(),
1047 PlanRuntimeStatic::Static(source) => source.admission.device_id(),
1048 }
1049 }
1050
1051 pub fn runtime_implementation_fingerprint(&self) -> &str {
1052 match &self.resources.static_resources {
1053 PlanRuntimeStatic::NoStatic { binding } => {
1054 binding.device_runtime_implementation_fingerprint()
1055 }
1056 PlanRuntimeStatic::Static(source) => {
1057 source.admission.device_runtime_implementation_fingerprint()
1058 }
1059 }
1060 }
1061
1062 pub fn coordinator_id(&self) -> LogicalAdmissionCoordinatorId {
1063 self.logical_admission().id()
1064 }
1065
1066 pub fn static_provisioning(&self) -> Option<&StaticProvisioningLease<R>> {
1067 match &self.resources.static_resources {
1068 PlanRuntimeStatic::NoStatic { .. } => None,
1069 PlanRuntimeStatic::Static(source) => source.lease.as_ref(),
1070 }
1071 }
1072
1073 pub fn evidence(&self) -> TrustedPlanRuntimeEvidence {
1074 self.resources.evidence()
1075 }
1076
1077 pub(super) fn reject_impossible_plan_fit(
1081 &self,
1082 immediate: &CapacityVector,
1083 fit: &CapacityVector,
1084 ) -> Result<Option<AdmissionRejected>, VNextError> {
1085 let pools = &self.resources.dynamic_pools;
1086 let snapshot = self.logical_admission().snapshot()?;
1087 for entry in fit.entries() {
1088 let domain = snapshot
1089 .domains()
1090 .iter()
1091 .find(|domain| domain.domain() == entry.domain())
1092 .ok_or_else(|| {
1093 invalid_resource("plan fit references an unknown capacity domain")
1094 })?;
1095 if entry.units().get() > domain.maximum_total().get() {
1097 return Ok(None);
1098 }
1099 }
1100 let binding = match &self.resources.static_resources {
1101 PlanRuntimeStatic::NoStatic { binding } => binding,
1102 PlanRuntimeStatic::Static(source) => &source.admission,
1103 };
1104 let minimum_required =
1105 pools
1106 .domains
1107 .iter()
1108 .try_fold(binding.plan_static_bytes(), |total, domain| {
1109 let requested = fit
1110 .units_for(domain.domain_id)
1111 .map_or(0, CapacityUnits::get);
1112 let minimum = domain.pool.provisioning().minimum_resident_bytes();
1113 total
1114 .checked_add(minimum.max(requested))
1115 .ok_or_else(|| invalid_resource("joint plan fit lower bound overflows u64"))
1116 })?;
1117 Ok(
1118 (minimum_required > binding.usable_capacity_bytes()).then(|| {
1119 AdmissionRejected::for_plan_budget(
1120 immediate.clone(),
1121 fit.clone(),
1122 snapshot,
1123 minimum_required,
1124 binding.usable_capacity_bytes(),
1125 )
1126 }),
1127 )
1128 }
1129
1130 pub(super) fn scoped_demand(
1131 &self,
1132 lifetime: AllocationLifetime,
1133 node_id: Option<&NodeId>,
1134 immediate_shape: DynamicResourceShape,
1135 fit_shape: DynamicResourceShape,
1136 reusable_execution_bucket: Option<&ReusableExecutionBucketSpec>,
1137 fit_policy: AdmissionFitPolicy,
1138 pressure_action: AdmissionPressureAction,
1139 ) -> Result<(AdmissionDemand, Vec<EvaluatedBackingRequest<'_>>), VNextError> {
1140 if (lifetime == AllocationLifetime::Invocation) != node_id.is_some() {
1141 return Err(invalid_resource(
1142 "invocation resource demand requires one exact node identity",
1143 ));
1144 }
1145 let capacity_shape = match reusable_execution_bucket {
1146 Some(bucket)
1147 if matches!(
1148 lifetime,
1149 AllocationLifetime::Step | AllocationLifetime::Invocation
1150 ) && bucket.capacity().covers(
1151 immediate_shape.sequences(),
1152 immediate_shape.tokens(),
1153 immediate_shape.pages(),
1154 ) && bucket.capacity().covers(
1155 fit_shape.sequences(),
1156 fit_shape.tokens(),
1157 fit_shape.pages(),
1158 ) =>
1159 {
1160 DynamicResourceShape::from_validated(
1161 bucket.capacity().maximum_sequences(),
1162 bucket.capacity().maximum_tokens(),
1163 bucket.capacity().maximum_pages(),
1164 )
1165 }
1166 Some(_) => {
1167 return Err(invalid_resource(
1168 "reusable execution bucket does not cover this Step or Invocation demand",
1169 ));
1170 }
1171 None => immediate_shape,
1172 };
1173 let node_resources = node_id
1174 .map(|node_id| {
1175 self.nodes()
1176 .iter()
1177 .find(|node| node.id() == node_id)
1178 .map(PlanNode::resources)
1179 .ok_or_else(|| {
1180 invalid_resource("resource admission references an unknown node")
1181 })
1182 })
1183 .transpose()?;
1184 let mut immediate_entries = Vec::new();
1185 let mut fit_entries = Vec::new();
1186 let mut requested_slices = Vec::new();
1187 for domain in &self.dynamic_pools().domains {
1188 let mut immediate_pool_bytes = 0_u64;
1189 let mut fit_pool_bytes = 0_u64;
1190 let mut matched = false;
1191 if lifetime == AllocationLifetime::Step {
1192 for slot in domain.pool.step_resource_slots() {
1193 let mut projections = Vec::with_capacity(slot.resource_ids().len());
1194 let mut immediate_slot_bytes = 0_u64;
1195 let mut fit_slot_bytes = 0_u64;
1196 let mut capacity_slot_bytes = 0_u64;
1197 for resource_id in slot.resource_ids() {
1198 let descriptor = domain
1199 .descriptors
1200 .iter()
1201 .find(|descriptor| descriptor.base_resource_id() == resource_id)
1202 .ok_or_else(|| {
1203 invalid_resource(
1204 "step physical slot references a descriptor outside its pool",
1205 )
1206 })?;
1207 if descriptor.lifetime() != AllocationLifetime::Step {
1208 return Err(invalid_resource(
1209 "step physical slot references a non-Step descriptor",
1210 ));
1211 }
1212 let logical_size_bytes =
1213 descriptor.evaluate_request_bytes_for_shape(immediate_shape)?;
1214 let fit_bytes = descriptor.evaluate_request_bytes_for_shape(fit_shape)?;
1215 let capacity_size_bytes =
1216 descriptor.evaluate_request_bytes_for_shape(capacity_shape)?;
1217 immediate_slot_bytes = immediate_slot_bytes.max(logical_size_bytes);
1218 fit_slot_bytes = fit_slot_bytes.max(fit_bytes);
1219 capacity_slot_bytes = capacity_slot_bytes.max(capacity_size_bytes);
1220 projections.push(EvaluatedBackingProjection {
1221 descriptor,
1222 physical_offset_bytes: 0,
1223 logical_size_bytes,
1224 capacity_size_bytes,
1225 });
1226 }
1227 immediate_pool_bytes = immediate_pool_bytes
1228 .checked_add(immediate_slot_bytes)
1229 .ok_or_else(|| {
1230 invalid_resource("dynamic pool immediate demand overflows u64")
1231 })?;
1232 fit_pool_bytes = fit_pool_bytes
1233 .checked_add(fit_slot_bytes)
1234 .ok_or_else(|| invalid_resource("dynamic pool fit demand overflows u64"))?;
1235 requested_slices.push(EvaluatedBackingRequest {
1236 domain,
1237 claim_identity: PhysicalBackingClaimIdentity::new(
1238 domain.pool_id().clone(),
1239 slot.resource_ids().to_vec(),
1240 )?,
1241 capacity_size_bytes: capacity_slot_bytes,
1242 reusable_execution_bucket_id: reusable_execution_bucket
1243 .map(|bucket| bucket.bucket_id().clone()),
1244 projections,
1245 });
1246 matched = true;
1247 }
1248 } else {
1249 for descriptor in &domain.descriptors {
1250 if descriptor.lifetime() != lifetime
1251 || node_resources.is_some_and(|resources| {
1252 !resources.contains(descriptor.base_resource_id())
1253 })
1254 {
1255 continue;
1256 }
1257 matched = true;
1258 let logical_size_bytes =
1259 descriptor.evaluate_request_bytes_for_shape(immediate_shape)?;
1260 let fit_bytes = descriptor.evaluate_request_bytes_for_shape(fit_shape)?;
1261 let capacity_size_bytes =
1262 descriptor.evaluate_request_bytes_for_shape(capacity_shape)?;
1263 immediate_pool_bytes = immediate_pool_bytes
1264 .checked_add(logical_size_bytes)
1265 .ok_or_else(|| {
1266 invalid_resource("dynamic pool immediate demand overflows u64")
1267 })?;
1268 fit_pool_bytes = fit_pool_bytes
1269 .checked_add(fit_bytes)
1270 .ok_or_else(|| invalid_resource("dynamic pool fit demand overflows u64"))?;
1271 requested_slices.push(EvaluatedBackingRequest {
1272 domain,
1273 claim_identity: PhysicalBackingClaimIdentity::new(
1274 domain.pool_id().clone(),
1275 vec![descriptor.base_resource_id().clone()],
1276 )?,
1277 capacity_size_bytes,
1278 reusable_execution_bucket_id: reusable_execution_bucket
1279 .map(|bucket| bucket.bucket_id().clone()),
1280 projections: vec![EvaluatedBackingProjection {
1281 descriptor,
1282 physical_offset_bytes: 0,
1283 logical_size_bytes,
1284 capacity_size_bytes,
1285 }],
1286 });
1287 }
1288 }
1289 if matched {
1290 immediate_entries.push(CapacityEntry::new(
1291 domain.domain_id(),
1292 CapacityUnits::new(immediate_pool_bytes),
1293 )?);
1294 fit_entries.push(CapacityEntry::new(
1295 domain.domain_id(),
1296 CapacityUnits::new(fit_pool_bytes),
1297 )?);
1298 }
1299 }
1300 let immediate = if immediate_entries.is_empty() {
1301 CapacityVector::empty()
1302 } else {
1303 CapacityVector::new(immediate_entries)?
1304 };
1305 let fit = if fit_entries.is_empty() {
1306 CapacityVector::empty()
1307 } else {
1308 CapacityVector::new(fit_entries)?
1309 };
1310 Ok((
1311 AdmissionDemand::from_plan(immediate, fit, fit_policy, pressure_action)?,
1312 requested_slices,
1313 ))
1314 }
1315
1316 pub(super) fn sequence_extension_demand(
1320 &self,
1321 committed: DynamicResourceShape,
1322 target: DynamicResourceShape,
1323 pressure_action: AdmissionPressureAction,
1324 ) -> Result<(AdmissionDemand, Vec<EvaluatedBackingRequest<'_>>), VNextError> {
1325 if committed.sequences() != 1
1326 || target.sequences() != 1
1327 || target.tokens() < committed.tokens()
1328 || target.pages() < committed.pages()
1329 {
1330 return Err(invalid_resource(
1331 "sequence extension target must monotonically advance one committed sequence",
1332 ));
1333 }
1334
1335 let mut entries = Vec::new();
1336 let mut requested_slices = Vec::new();
1337 for domain in &self.dynamic_pools().domains {
1338 let mut pool_delta = 0_u64;
1339 for descriptor in &domain.descriptors {
1340 if descriptor.lifetime() != AllocationLifetime::Sequence {
1341 continue;
1342 }
1343 let committed_bytes = descriptor.evaluate_request_bytes_for_shape(committed)?;
1344 let target_bytes = descriptor.evaluate_request_bytes_for_shape(target)?;
1345 let delta_bytes = target_bytes.checked_sub(committed_bytes).ok_or_else(|| {
1346 invalid_resource("sequence extension descriptor capacity regressed")
1347 })?;
1348 if delta_bytes == 0 {
1349 continue;
1350 }
1351 if !matches!(
1352 descriptor.storage().profile().view(),
1353 super::DynamicStorageView::PagedRegions { .. }
1354 ) {
1355 return Err(invalid_resource(
1356 "sequence backing extension requires a paged storage profile",
1357 ));
1358 }
1359 pool_delta = pool_delta.checked_add(delta_bytes).ok_or_else(|| {
1360 invalid_resource("sequence extension pool demand overflows u64")
1361 })?;
1362 requested_slices.push(EvaluatedBackingRequest {
1363 domain,
1364 claim_identity: PhysicalBackingClaimIdentity::new(
1365 domain.pool_id().clone(),
1366 vec![descriptor.base_resource_id().clone()],
1367 )?,
1368 capacity_size_bytes: delta_bytes,
1369 reusable_execution_bucket_id: None,
1370 projections: vec![EvaluatedBackingProjection {
1371 descriptor,
1372 physical_offset_bytes: 0,
1373 logical_size_bytes: delta_bytes,
1374 capacity_size_bytes: delta_bytes,
1375 }],
1376 });
1377 }
1378 if pool_delta != 0 {
1379 entries.push(CapacityEntry::new(
1380 domain.domain_id(),
1381 CapacityUnits::new(pool_delta),
1382 )?);
1383 }
1384 }
1385 let delta = if entries.is_empty() {
1386 CapacityVector::empty()
1387 } else {
1388 CapacityVector::new(entries)?
1389 };
1390 Ok((
1391 AdmissionDemand::from_plan(
1392 delta.clone(),
1393 delta,
1394 AdmissionFitPolicy::ImmediateOnly,
1395 pressure_action,
1396 )?,
1397 requested_slices,
1398 ))
1399 }
1400
1401 pub(super) fn submission_wave_demand(
1405 &self,
1406 immediate_shape: DynamicResourceShape,
1407 fit_shape: DynamicResourceShape,
1408 reusable_execution_bucket: Option<&ReusableExecutionBucketSpec>,
1409 fit_policy: AdmissionFitPolicy,
1410 pressure_action: AdmissionPressureAction,
1411 ) -> Result<(AdmissionDemand, Vec<EvaluatedBackingRequest<'_>>), VNextError> {
1412 match reusable_execution_bucket {
1413 Some(bucket)
1414 if bucket.capacity().covers(
1415 immediate_shape.sequences(),
1416 immediate_shape.tokens(),
1417 immediate_shape.pages(),
1418 ) && bucket.capacity().covers(
1419 fit_shape.sequences(),
1420 fit_shape.tokens(),
1421 fit_shape.pages(),
1422 ) =>
1423 {
1424 }
1427 Some(_) => {
1428 return Err(invalid_resource(
1429 "reusable execution bucket does not cover the submission-wave work shape",
1430 ));
1431 }
1432 None => {}
1433 }
1434
1435 let mut immediate_entries = Vec::new();
1436 let mut fit_entries = Vec::new();
1437 let mut requested_slices = Vec::new();
1438 let pools = self.dynamic_pools();
1439 if pools.domains.len() != pools.submission_wave_layouts.len() {
1440 return Err(invalid_resource(
1441 "submission wave layout count differs from immutable plan domains",
1442 ));
1443 }
1444 let reusable_capacity_layouts = reusable_execution_bucket
1445 .map(|bucket| {
1446 pools
1447 .submission_wave_reusable_capacity_layouts
1448 .get(bucket.bucket_id())
1449 .ok_or_else(|| {
1450 invalid_resource(
1451 "reusable execution bucket has no compiled submission-wave capacity layout",
1452 )
1453 })
1454 })
1455 .transpose()?;
1456 if reusable_capacity_layouts.is_some_and(|layouts| layouts.len() != pools.domains.len()) {
1457 return Err(invalid_resource(
1458 "compiled reusable submission-wave capacity layout count differs from immutable plan domains",
1459 ));
1460 }
1461 for (domain_index, (domain, layout)) in pools
1462 .domains
1463 .iter()
1464 .zip(&pools.submission_wave_layouts)
1465 .enumerate()
1466 {
1467 let reusable_capacity_layout = reusable_capacity_layouts
1468 .map(|layouts| {
1469 layouts.get(domain_index).ok_or_else(|| {
1470 invalid_resource(
1471 "compiled reusable submission-wave capacity layout is incomplete",
1472 )
1473 })
1474 })
1475 .transpose()?
1476 .and_then(Option::as_ref);
1477 let Some(layout) = layout else {
1478 if reusable_capacity_layout.is_some() {
1479 return Err(invalid_resource(
1480 "compiled reusable submission-wave capacity exists without a domain layout",
1481 ));
1482 }
1483 continue;
1484 };
1485 if reusable_execution_bucket.is_some() && reusable_capacity_layout.is_none() {
1486 return Err(invalid_resource(
1487 "compiled reusable submission-wave capacity is missing for a domain layout",
1488 ));
1489 }
1490 if reusable_capacity_layout
1491 .is_some_and(|capacity| capacity.projections.len() != layout.projection_count)
1492 {
1493 return Err(invalid_resource(
1494 "compiled reusable submission-wave projection count differs from its domain layout",
1495 ));
1496 }
1497 let mode = domain.pool.invocation_liveness_mode();
1498
1499 let mut projections = vec![None; layout.projection_count];
1500 let mut immediate_pool_bytes = 0_u64;
1501 let mut fit_pool_bytes = 0_u64;
1502 let mut capacity_pool_bytes = 0_u64;
1503 for row in &layout.rows {
1504 let row_base = match mode {
1505 InvocationLivenessMode::TotalOrderReuse => 0,
1506 InvocationLivenessMode::ConservativeConcurrent => capacity_pool_bytes,
1507 InvocationLivenessMode::NoInvocationResources => unreachable!(),
1508 };
1509 let mut immediate_row_bytes = 0_u64;
1510 let mut fit_row_bytes = 0_u64;
1511 let mut capacity_row_bytes = 0_u64;
1512 for projection_layout in &row.projections {
1513 let descriptor = domain
1514 .descriptors
1515 .get(projection_layout.descriptor_index)
1516 .ok_or_else(|| {
1517 invalid_resource(
1518 "submission wave layout references a descriptor outside its pool",
1519 )
1520 })?;
1521 if descriptor.lifetime() != AllocationLifetime::Invocation {
1522 return Err(invalid_resource(
1523 "invocation liveness row references a non-Invocation descriptor",
1524 ));
1525 }
1526 let logical_size_bytes =
1527 descriptor.evaluate_request_bytes_for_shape(immediate_shape)?;
1528 let fit_bytes = if fit_shape == immediate_shape {
1529 logical_size_bytes
1530 } else {
1531 descriptor.evaluate_request_bytes_for_shape(fit_shape)?
1532 };
1533 let (physical_offset_bytes, capacity_size_bytes) =
1534 match reusable_capacity_layout {
1535 Some(capacity_layout) => {
1536 let capacity_projection = capacity_layout
1537 .projections
1538 .get(projection_layout.projection_index)
1539 .ok_or_else(|| {
1540 invalid_resource(
1541 "compiled reusable submission-wave projection is missing",
1542 )
1543 })?;
1544 (
1545 capacity_projection.physical_offset_bytes,
1546 capacity_projection.capacity_size_bytes,
1547 )
1548 }
1549 None => {
1550 let physical_offset_bytes =
1551 row_base.checked_add(capacity_row_bytes).ok_or_else(|| {
1552 invalid_resource(
1553 "invocation wave projection offset overflows u64",
1554 )
1555 })?;
1556 (physical_offset_bytes, logical_size_bytes)
1557 }
1558 };
1559 immediate_row_bytes = immediate_row_bytes
1560 .checked_add(logical_size_bytes)
1561 .ok_or_else(|| {
1562 invalid_resource("invocation wave row demand overflows u64")
1563 })?;
1564 fit_row_bytes = fit_row_bytes.checked_add(fit_bytes).ok_or_else(|| {
1565 invalid_resource("invocation wave fit row demand overflows u64")
1566 })?;
1567 if reusable_capacity_layout.is_none() {
1568 capacity_row_bytes = capacity_row_bytes
1569 .checked_add(capacity_size_bytes)
1570 .ok_or_else(|| {
1571 invalid_resource("invocation wave capacity row overflows u64")
1572 })?;
1573 }
1574 if projections[projection_layout.projection_index]
1575 .replace(EvaluatedBackingProjection {
1576 descriptor,
1577 physical_offset_bytes,
1578 logical_size_bytes,
1579 capacity_size_bytes,
1580 })
1581 .is_some()
1582 {
1583 return Err(invalid_resource(
1584 "submission wave layout repeated a canonical projection",
1585 ));
1586 }
1587 }
1588 match mode {
1589 InvocationLivenessMode::TotalOrderReuse => {
1590 immediate_pool_bytes = immediate_pool_bytes.max(immediate_row_bytes);
1591 fit_pool_bytes = fit_pool_bytes.max(fit_row_bytes);
1592 if reusable_capacity_layout.is_none() {
1593 capacity_pool_bytes = capacity_pool_bytes.max(capacity_row_bytes);
1594 }
1595 }
1596 InvocationLivenessMode::ConservativeConcurrent => {
1597 immediate_pool_bytes = immediate_pool_bytes
1598 .checked_add(immediate_row_bytes)
1599 .ok_or_else(|| {
1600 invalid_resource("invocation wave pool demand overflows u64")
1601 })?;
1602 fit_pool_bytes =
1603 fit_pool_bytes.checked_add(fit_row_bytes).ok_or_else(|| {
1604 invalid_resource("invocation wave pool fit demand overflows u64")
1605 })?;
1606 if reusable_capacity_layout.is_none() {
1607 capacity_pool_bytes = capacity_pool_bytes
1608 .checked_add(capacity_row_bytes)
1609 .ok_or_else(|| {
1610 invalid_resource(
1611 "invocation wave capacity pool demand overflows u64",
1612 )
1613 })?;
1614 }
1615 }
1616 InvocationLivenessMode::NoInvocationResources => unreachable!(),
1617 }
1618 }
1619 if let Some(capacity_layout) = reusable_capacity_layout {
1620 capacity_pool_bytes = capacity_layout.physical_size_bytes;
1621 }
1622 let projections = projections
1623 .into_iter()
1624 .collect::<Option<Vec<_>>>()
1625 .ok_or_else(|| {
1626 invalid_resource("submission wave layout left a projection unevaluated")
1627 })?;
1628 if projections.is_empty() || immediate_pool_bytes == 0 {
1629 return Err(invalid_resource(
1630 "submission wave layout evaluated to empty invocation demand",
1631 ));
1632 }
1633
1634 requested_slices.push(EvaluatedBackingRequest {
1635 domain,
1636 claim_identity: layout.claim_identity.clone(),
1637 capacity_size_bytes: capacity_pool_bytes,
1638 reusable_execution_bucket_id: reusable_execution_bucket
1639 .map(|bucket| bucket.bucket_id().clone()),
1640 projections,
1641 });
1642 immediate_entries.push(CapacityEntry::new(
1643 domain.domain_id(),
1644 CapacityUnits::new(immediate_pool_bytes),
1645 )?);
1646 fit_entries.push(CapacityEntry::new(
1647 domain.domain_id(),
1648 CapacityUnits::new(fit_pool_bytes),
1649 )?);
1650 }
1651
1652 let immediate = if immediate_entries.is_empty() {
1653 CapacityVector::empty()
1654 } else {
1655 CapacityVector::new(immediate_entries)?
1656 };
1657 let fit = if fit_entries.is_empty() {
1658 CapacityVector::empty()
1659 } else {
1660 CapacityVector::new(fit_entries)?
1661 };
1662 Ok((
1663 AdmissionDemand::from_plan(immediate, fit, fit_policy, pressure_action)?,
1664 requested_slices,
1665 ))
1666 }
1667
1668 pub(super) fn prepare_backing_slices(
1669 &self,
1670 requested_slices: Vec<EvaluatedBackingRequest<'_>>,
1671 ) -> Result<BackingPrepareDecision<R>, VNextError> {
1672 self.dynamic_pools().prepare_claim(&requested_slices)
1673 }
1674
1675 pub(super) fn prepare_lane_stable_backing_slices(
1676 &self,
1677 lane: &Arc<ExecutionLane<R>>,
1678 requested_slices: Vec<EvaluatedBackingRequest<'_>>,
1679 ) -> Result<LaneBackingPrepareDecision, VNextError> {
1680 self.dynamic_pools()
1681 .prepare_lane_stable_claim(lane, &requested_slices)
1682 }
1683
1684 pub(super) fn prepare_initial_sequence_backing_slices(
1685 &self,
1686 requested_slices: &[EvaluatedBackingRequest<'_>],
1687 ) -> Result<BackingPrepareDecision<R>, VNextError> {
1688 self.dynamic_pools()
1689 .prepare_initial_sequence_claim(requested_slices)
1690 }
1691
1692 pub(super) fn register_backing_waiter(
1693 &self,
1694 deferred: &DynamicBackingDeferred,
1695 ) -> Result<PlanCapacityWaitRegistration<R>, VNextError> {
1696 self.resources
1697 .register_capacity_waiter(deferred.wait_condition())
1698 }
1699
1700 pub fn register_admission_waiter(
1701 &self,
1702 deferred: &AdmissionDeferred,
1703 ) -> Result<PlanCapacityWaitRegistration<R>, VNextError> {
1704 self.resources
1705 .register_capacity_waiter(deferred.wait_condition())
1706 }
1707}