Skip to main content

dope_core/io/fd/
handles.rs

1use std::{fmt, mem, ops, process};
2
3use o3::{collections::batch::set, permit};
4
5use crate::{
6    backend::{self, fixed},
7    driver::{self, flight, route, schedule::ready},
8    io,
9};
10
11#[derive(Clone, Copy, Debug)]
12pub(crate) struct FixedSlot(route::SlotIndex, o3::ThreadBound);
13
14const _: () = assert!(mem::size_of::<FixedSlot>() == mem::size_of::<u32>());
15
16#[must_use = "accepted fixed-file authority must be delivered or reclaimed"]
17#[repr(transparent)]
18pub(crate) struct Accepted(FixedSlot);
19
20#[must_use = "created fixed-file authority must be delivered or reclaimed"]
21#[repr(transparent)]
22pub(crate) struct Created(FixedSlot);
23
24const _: () = assert!(mem::size_of::<Accepted>() == mem::size_of::<FixedSlot>());
25
26impl FixedSlot {
27    pub(crate) const fn from_index(index: route::SlotIndex) -> Self {
28        use o3::ThreadBound;
29        Self(index, ThreadBound::NEW)
30    }
31
32    pub(crate) fn raw(self) -> u32 {
33        self.0.raw()
34    }
35
36    pub(crate) fn token_index(self) -> route::SlotIndex {
37        self.0
38    }
39}
40
41impl set::DenseIndex for FixedSlot {
42    fn into_usize(self) -> usize {
43        self.raw() as usize
44    }
45
46    fn from_usize(raw: usize) -> Self {
47        Self::from_index(route::SlotIndex::from_bounded(raw as u32))
48    }
49}
50
51impl Accepted {
52    pub(crate) const fn from_live(slot: FixedSlot) -> Self {
53        Self(slot)
54    }
55
56    pub(crate) fn bind<'d>(self, driver: driver::Reference<'d>) -> Option<AcceptedSlot<'d>> {
57        let slot = self.0;
58        let Some(lease) = FixedLease::claim(slot, driver) else {
59            driver.maintenance().defer_descriptor(slot, None);
60            return None;
61        };
62        Some(AcceptedSlot { lease })
63    }
64
65    pub(crate) fn into_slot(self) -> FixedSlot {
66        self.0
67    }
68}
69
70impl Created {
71    pub(crate) const fn from_live(slot: FixedSlot) -> Self {
72        Self(slot)
73    }
74
75    pub(crate) fn bind<'d>(self, driver: driver::Reference<'d>) -> Option<CreatedSlot<'d>> {
76        match driver.outbound().complete_outbound_create_success(self.0) {
77            driver::CreateSuccess::Deliver(outbound) => Some(CreatedSlot {
78                driver,
79                slot: self.0,
80                outbound,
81            }),
82            driver::CreateSuccess::Close(close) => {
83                driver.maintenance().defer_close(close);
84                None
85            }
86        }
87    }
88}
89
90#[repr(transparent)]
91pub(crate) struct FixedLease<'d> {
92    lease: permit::Lease<FixedReturn<'d>>,
93}
94
95struct FixedReturn<'d> {
96    driver: driver::Reference<'d>,
97}
98
99struct FixedOwned {
100    slot: FixedSlot,
101    epoch: u32,
102}
103
104impl<'d> FixedReturn<'d> {
105    fn key(&self, owned: &FixedOwned) -> ready::FixedKey<'d> {
106        ready::FixedKey::new(owned.slot.raw(), owned.epoch)
107    }
108
109    fn release(&self, owned: FixedOwned) -> Option<ready::FixedRelease<'d>> {
110        self.driver.ready().release_fixed_ready(self.key(&owned))
111    }
112}
113
114impl permit::Return for FixedReturn<'_> {
115    type Item = FixedOwned;
116
117    fn return_item(&self, owned: Self::Item) {
118        if let Some(released) = self.release(owned) {
119            self.driver.maintenance().retire_fixed_release(released);
120        }
121    }
122}
123
124impl<'d> FixedLease<'d> {
125    fn new(driver: driver::Reference<'d>, slot: FixedSlot, epoch: u32) -> Self {
126        Self {
127            lease: permit::Lease::new(FixedReturn { driver }, FixedOwned { slot, epoch }),
128        }
129    }
130
131    fn claim(slot: FixedSlot, driver: driver::Reference<'d>) -> Option<Self> {
132        let key = driver.ready().claim_fixed_ready(slot)?;
133        Some(Self::new(driver, slot, key.epoch()))
134    }
135
136    fn from_reserved(slot: fixed::Slot<'d>, driver: driver::Reference<'d>) -> Option<Self> {
137        let fixed = slot.fixed();
138        let Some(key) = driver.ready().claim_fixed_ready(fixed) else {
139            driver.maintenance().defer_fixed_slot(slot.retire());
140            return None;
141        };
142        Some(Self::new(driver, slot.into_claimed(key), key.epoch()))
143    }
144
145    pub(crate) fn slot(&self) -> FixedSlot {
146        self.lease.item().slot
147    }
148
149    fn slot_ref(&self) -> &FixedSlot {
150        &self.lease.item().slot
151    }
152
153    fn key(&self) -> ready::FixedKey<'d> {
154        self.lease.sink().key(self.lease.item())
155    }
156
157    pub(super) fn driver(&self) -> driver::Reference<'d> {
158        self.lease.sink().driver
159    }
160
161    fn ready_handle(&self) -> ready::Handle<'d> {
162        self.driver().ready().fixed_ready(self.key())
163    }
164
165    fn release(self) -> ready::FixedRelease<'d> {
166        let (return_fixed, owned) = self.lease.into_parts();
167        let Some(released) = return_fixed.release(owned) else {
168            process::abort();
169        };
170        released
171    }
172}
173
174const _: () = {
175    assert!(
176        mem::size_of::<FixedLease<'static>>()
177            == mem::size_of::<(driver::Reference<'static>, FixedSlot, u32)>()
178    );
179    assert!(
180        mem::align_of::<FixedLease<'static>>()
181            == mem::align_of::<(driver::Reference<'static>, FixedSlot, u32)>()
182    );
183};
184
185#[repr(C)]
186pub struct AcceptedSlot<'d> {
187    lease: FixedLease<'d>,
188}
189
190#[must_use = "created socket must be activated or reclaimed"]
191#[repr(C)]
192/// The successful kernel half of one driver-scoped socket creation.
193///
194/// Its invariant driver lifetime cannot be shortened into another scope.
195///
196/// ```compile_fail
197/// use dope_core::io::fd::handles::CreatedSlot;
198///
199/// fn shorten<'long: 'short, 'short>(created: CreatedSlot<'long>) -> CreatedSlot<'short> {
200///     created
201/// }
202/// ```
203pub struct CreatedSlot<'d> {
204    driver: driver::Reference<'d>,
205    slot: FixedSlot,
206    outbound: driver::OutboundKey,
207}
208
209const _: () =
210    assert!(mem::size_of::<AcceptedSlot<'static>>() == mem::size_of::<FixedLease<'static>>());
211const _: () =
212    assert!(mem::size_of::<CreatedSlot<'static>>() == mem::size_of::<Descriptor<'static>>());
213const _: () =
214    assert!(mem::size_of::<AcceptedSlot<'static>>() <= mem::size_of::<io::RecvEvent<'static>>());
215
216pub struct Descriptor<'d> {
217    lease: FixedLease<'d>,
218}
219
220/// A fixed descriptor whose driver's receive pool has been validated for
221/// datagram metadata and at least one payload byte.
222///
223/// This exact-descriptor proof cannot cross driver lifetimes or fixed slots.
224///
225/// ```compile_fail
226/// use dope_core::io::fd::handles::DatagramDescriptor;
227///
228/// fn shorten<'long: 'short, 'short>(
229///     descriptor: DatagramDescriptor<'long>,
230/// ) -> DatagramDescriptor<'short> {
231///     descriptor
232/// }
233/// ```
234#[repr(transparent)]
235pub struct DatagramDescriptor<'d>(Descriptor<'d>);
236
237/// An exclusive outbound fixed slot which does not contain a socket yet.
238pub struct SocketSlot<'d> {
239    lease: FixedLease<'d>,
240}
241
242/// An outbound fixed slot whose socket creation is in flight.
243///
244/// Its invariant driver lifetime cannot be shortened into another scope.
245///
246/// ```compile_fail
247/// use dope_core::io::fd::handles::CreatingSocket;
248///
249/// fn shorten<'long: 'short, 'short>(creating: CreatingSocket<'long>) -> CreatingSocket<'short> {
250///     creating
251/// }
252/// ```
253pub struct CreatingSocket<'d> {
254    lease: FixedLease<'d>,
255    flight: flight::Flight<'d>,
256}
257
258const _: () =
259    assert!(mem::size_of::<Descriptor<'static>>() == mem::size_of::<FixedLease<'static>>());
260const _: () = {
261    assert!(mem::size_of::<DatagramDescriptor<'static>>() == mem::size_of::<Descriptor<'static>>());
262    assert!(
263        mem::align_of::<DatagramDescriptor<'static>>() == mem::align_of::<Descriptor<'static>>()
264    );
265};
266const _: () =
267    assert!(mem::size_of::<SocketSlot<'static>>() == mem::size_of::<Descriptor<'static>>());
268const _: () = assert!(
269    mem::size_of::<CreatingSocket<'static>>()
270        == mem::size_of::<(Descriptor<'static>, flight::Flight<'static>)>()
271);
272
273impl fmt::Debug for Descriptor<'_> {
274    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
275        formatter
276            .debug_tuple("Descriptor")
277            .field(&self.lease.slot().raw())
278            .finish()
279    }
280}
281
282impl fmt::Debug for DatagramDescriptor<'_> {
283    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
284        formatter
285            .debug_tuple("DatagramDescriptor")
286            .field(&self.index())
287            .finish()
288    }
289}
290
291impl<'d> DatagramDescriptor<'d> {
292    pub(crate) fn validated(descriptor: Descriptor<'d>) -> Self {
293        Self(descriptor)
294    }
295
296    pub fn into_descriptor(self) -> Descriptor<'d> {
297        self.0
298    }
299}
300
301impl<'d> From<DatagramDescriptor<'d>> for Descriptor<'d> {
302    fn from(descriptor: DatagramDescriptor<'d>) -> Self {
303        descriptor.into_descriptor()
304    }
305}
306
307impl<'d> ops::Deref for DatagramDescriptor<'d> {
308    type Target = Descriptor<'d>;
309
310    fn deref(&self) -> &Self::Target {
311        &self.0
312    }
313}
314
315impl fmt::Debug for SocketSlot<'_> {
316    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
317        formatter
318            .debug_tuple("SocketSlot")
319            .field(&self.lease.slot().raw())
320            .finish()
321    }
322}
323
324impl fmt::Debug for CreatingSocket<'_> {
325    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
326        formatter
327            .debug_tuple("CreatingSocket")
328            .field(&self.lease.slot().raw())
329            .finish()
330    }
331}
332
333impl<'d> AcceptedSlot<'d> {
334    pub fn bind(self) -> Descriptor<'d> {
335        let Self { lease } = self;
336        Descriptor { lease }
337    }
338}
339
340impl<'d> CreatedSlot<'d> {
341    fn matches(&self, lease: &FixedLease<'d>) -> bool {
342        self.driver.same_driver(lease.driver()) && self.slot.raw() == lease.slot().raw()
343    }
344
345    /// Joins the two affine halves of a completed socket creation.
346    /// Mismatch preserves both authorities for their original owners.
347    pub fn activate(
348        self,
349        socket: CreatingSocket<'d>,
350    ) -> Result<Descriptor<'d>, (CreatingSocket<'d>, Self)> {
351        if !self.matches(&socket.lease) {
352            return Err((socket, self));
353        }
354        let slot = socket.lease.slot();
355        if !socket
356            .lease
357            .driver()
358            .outbound()
359            .activate_outbound(self.outbound, slot)
360        {
361            return Err((socket, self));
362        }
363        let CreatingSocket { lease, flight } = socket;
364        let _created = mem::ManuallyDrop::new(self);
365        let _ = flight.complete();
366        Ok(Descriptor { lease })
367    }
368}
369
370impl<'d> Descriptor<'d> {
371    pub(crate) fn from_reserved_slot(
372        slot: fixed::Slot<'d>,
373        driver: driver::Reference<'d>,
374    ) -> Option<Self> {
375        Some(Self {
376            lease: FixedLease::from_reserved(slot, driver)?,
377        })
378    }
379
380    pub(crate) fn slot(&self) -> FixedSlot {
381        self.lease.slot()
382    }
383
384    pub(crate) fn slot_ref(&self) -> &FixedSlot {
385        self.lease.slot_ref()
386    }
387
388    pub fn index(&self) -> u32 {
389        self.lease.slot().raw()
390    }
391
392    pub fn token_index(&self) -> route::SlotIndex {
393        self.lease.slot().token_index()
394    }
395
396    pub fn driver(&self) -> driver::Reference<'d> {
397        self.lease.driver()
398    }
399
400    pub fn ready_handle(&self) -> ready::Handle<'d> {
401        self.lease.ready_handle()
402    }
403
404    pub(crate) fn into_lease(self) -> FixedLease<'d> {
405        let Self { lease } = self;
406        lease
407    }
408
409    pub(crate) fn close(self, backend: &mut backend::Backend) {
410        use fixed::Lifecycle;
411
412        let lease = self.into_lease();
413        let driver = lease.driver();
414        let released = lease.release();
415        let slot = released.slot();
416        match driver.files().fixed_owner(slot) {
417            driver::FixedOwner::Accepted => {
418                Lifecycle::close(
419                    backend,
420                    driver::Close::untracked(released.into_slot()),
421                    driver,
422                    fixed::Phase::Active,
423                );
424            }
425            driver::FixedOwner::Outbound(outbound) => {
426                match driver
427                    .outbound()
428                    .close_disposition(released.into_slot(), Some(outbound))
429                {
430                    driver::CloseDisposition::Submit(close) => {
431                        Lifecycle::close(backend, close, driver, fixed::Phase::Active);
432                    }
433                    driver::CloseDisposition::NoSubmit(Some(retired)) => {
434                        let slots = driver.outbound().take_retired_slots(retired);
435                        Lifecycle::release_slots(backend, slots);
436                    }
437                    driver::CloseDisposition::NoSubmit(None) => {}
438                }
439            }
440            driver::FixedOwner::Reserved => {
441                let retired = fixed::Retirement::from_release(released);
442                Lifecycle::retire(backend, retired.into_slot(), fixed::Phase::Active);
443            }
444        }
445    }
446}
447
448impl<'d> SocketSlot<'d> {
449    pub(crate) fn from_outbound_slot(
450        slot: FixedSlot,
451        driver: driver::Reference<'d>,
452    ) -> Option<Self> {
453        let Some(lease) = FixedLease::claim(slot, driver) else {
454            if let Some(slots) = driver.outbound().release_outbound_slot_for(slot) {
455                driver.maintenance().defer_outbound_slots(slots);
456            }
457            return None;
458        };
459        Some(Self { lease })
460    }
461
462    pub(crate) fn slot_ref(&self) -> &FixedSlot {
463        self.lease.slot_ref()
464    }
465
466    pub fn index(&self) -> u32 {
467        self.lease.slot().raw()
468    }
469
470    pub(crate) fn into_creating(self, flight: flight::Flight<'d>) -> CreatingSocket<'d> {
471        let slot = self.lease.slot();
472        if !self
473            .lease
474            .driver()
475            .outbound()
476            .begin_outbound_create_for(slot)
477        {
478            process::abort();
479        }
480        let lease = self.lease;
481        CreatingSocket { lease, flight }
482    }
483}
484
485impl<'d> CreatingSocket<'d> {
486    pub fn driver(&self) -> driver::Reference<'d> {
487        self.lease.driver()
488    }
489
490    pub fn ready_handle(&self) -> ready::Handle<'d> {
491        self.lease.ready_handle()
492    }
493}
494impl Drop for CreatedSlot<'_> {
495    fn drop(&mut self) {
496        self.driver
497            .maintenance()
498            .defer_descriptor(self.slot, Some(self.outbound));
499    }
500}