49 }
50
51 pub fn reserve(&mut self, units: u64, clocks: ClockReading) -> Result<Ticket, RiskError> {
52 if units == 0 {
53 return Err(RiskError::ZeroUnits);
54 }
55 let next_total = self.reserved.checked_add(units).ok_or(RiskError::Capacity)?;
56 if next_total > self.cap {
57 return Err(RiskError::Capacity);
58 }
59 let slot_index = self.slots.iter().position(|slot| {
60 slot.units == 0 && clocks.monotonic_ns >= slot.reusable_at
61 }).ok_or(RiskError::NoSlot)?;
62 let slot = &mut self.slots[slot_index];
63 // PHC buckets are reused if the configured servo steps backward.
64 let generation = clocks.nic_ptp_ns / crate::clock::PTP_BUCKET_NS;
65 slot.generation = generation;
66 slot.units = units;
V12Run #8Reuse waits on monotonic time, but identity comes from a quantized NIC PTP clock. After quarantine expires, a configured backward servo step can give B the exact slot and generation previously assigned to A.
67 self.reserved = next_total;
68 Ok(Ticket { slot: slot_index, generation })
69 }
70
71 /// A confirmed venue cancel, including a later FIX replay of that report.
72 pub fn cancel_ack(&mut self, ticket: Ticket, clocks: ClockReading) -> Result<bool, RiskError> {
73 let Some(slot) = self.slots.get_mut(ticket.slot) else {
74 return Ok(false);
75 };
76 if slot.units == 0 || slot.generation != ticket.generation {
77 return Ok(false);
78 }
79 let reusable_at = clocks.monotonic_ns.checked_add(QUARANTINE_NS)
80 .ok_or(RiskError::ClockOverflow)?;
81 self.reserved -= slot.units;
82 slot.units = 0;
83 slot.reusable_at = reusable_at;
V12Run #8A replayed cancel for A now matches live B. It releases B’s reservation without cancelling B at the venue. C can consume the freed budget: two 1m live orders against a 1m cap. Exclusive access does not prevent identity reuse.
84 Ok(true)
85 }
86}