Skip to main content

dope_core/driver/
mod.rs

1use std::{cell::Cell, marker, mem, num, ops::ControlFlow, ptr, task, time};
2
3use dope_sys::{engine, io::reactor::ready::delivery::Quota as _, kernel};
4use o3::{
5    base::cell::region,
6    buffer::{self, storage::provided},
7};
8use route::table;
9
10use crate::{
11    Backend, Engine, driver,
12    driver::{
13        life::{domain::runtime::Runtime, native, native::host::Host},
14        receive::access,
15        result::poll,
16        retained::{flight, flight::cancel},
17        route::{kind, queues},
18        schedule::{
19            clock,
20            clock::{timer, timer::heap},
21            ready,
22            ready::{task::owner, wake::resident::slot},
23            sequence,
24            state::scope,
25        },
26    },
27    io,
28    io::{
29        event::{
30            batch,
31            net::{accept, recv},
32        },
33        fd::{access::Access, outbound},
34    },
35    random,
36};
37
38pub mod life;
39pub mod receive;
40pub mod result;
41pub mod retained;
42pub mod route;
43pub mod schedule;
44pub mod settings;
45pub mod storage;
46
47use result::{IoOperation, Opcode, SubmitError};
48
49type Buffer = <Engine as engine::Buffer>::Token;
50
51/// Keeps every derived capability in the exact driver lifetime that issued it.
52#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
53#[repr(transparent)]
54pub(crate) struct Invariant<'d>(marker::PhantomData<fn(&'d ()) -> &'d ()>);
55
56#[must_use = "application binding must be consumed during startup"]
57pub struct Binding<'d> {
58    timer: timer::Timer<'d>,
59    owners: owner::Capacity,
60}
61
62impl<'d> Binding<'d> {
63    #[doc(hidden)]
64    pub const fn new(timer: timer::Timer<'d>, owners: owner::Capacity) -> Self {
65        Self { timer, owners }
66    }
67
68    pub const fn timer(&self) -> timer::Timer<'d> {
69        self.timer
70    }
71
72    pub const fn owners(&self) -> owner::Capacity {
73        self.owners
74    }
75
76    pub fn sequence(&self) -> sequence::Block<'d> {
77        sequence::Block::new(self.timer.driver)
78    }
79}
80
81#[repr(transparent)]
82pub struct Driver {
83    runtime: Runtime,
84}
85
86#[derive(Clone, Copy)]
87#[repr(transparent)]
88pub struct Reference<'d> {
89    shared: provided::Root<'d, Buffer, Shared, ()>,
90    pub(crate) brand: Invariant<'d>,
91}
92
93impl<'d> Reference<'d> {
94    pub fn outbound_cap(self) -> table::Capacity {
95        let arena = self.ready().arena();
96        arena.descriptors().outbound_cap()
97    }
98
99    pub fn accept_cap(self) -> table::Capacity {
100        let arena = self.ready().arena();
101        arena.descriptors().accept_cap()
102    }
103
104    fn new(shared: provided::Root<'d, Buffer, Shared, ()>) -> Self {
105        Self {
106            shared,
107            brand: Invariant(marker::PhantomData),
108        }
109    }
110
111    pub const fn ready(self) -> ready::Context<'d> {
112        ready::Context::new(self)
113    }
114
115    pub const fn targets<Tag: route::Tag>(self) -> route::Space<'d, Tag> {
116        route::Space {
117            driver: route::Brand::new(self.brand),
118        }
119    }
120
121    pub const fn scheduler(self) -> schedule::Context<'d> {
122        schedule::Context::new(self)
123    }
124
125    /// Issues one identity without reserving a scheduler's local block.
126    pub fn try_next_id(self) -> Result<sequence::Id<'d>, sequence::Exhausted> {
127        let raw = self
128            .shared
129            .context()
130            .sequence
131            .next()
132            .ok_or(sequence::Exhausted)?;
133        Ok(sequence::Id::new(self, raw))
134    }
135
136    pub fn clock(self) -> clock::Clock<'d, 'd> {
137        self.scheduler().clock()
138    }
139
140    pub const fn timer(self) -> timer::Timer<'d> {
141        timer::Timer::new(self)
142    }
143
144    pub fn maintenance(self) -> schedule::Progress<'d> {
145        if self.has_deferred() {
146            schedule::Progress::Runnable
147        } else {
148            schedule::Progress::Quiescent
149        }
150    }
151
152    pub(crate) const fn receive(self) -> access::Access<'d> {
153        access::Access::new(self)
154    }
155
156    pub(crate) fn has_deferred(self) -> bool {
157        self.receive().owners().has_returned()
158            || self.descriptors().retirement().has_pending()
159            || self.shared.context().retire.has_pending()
160    }
161
162    pub(crate) const fn descriptors(self) -> Access<'d> {
163        Access::new(self)
164    }
165
166    pub(crate) const fn outbound(self) -> outbound::Context<'d> {
167        outbound::Context::new(self)
168    }
169
170    #[doc(hidden)]
171    pub fn is_same(self, other: Reference<'_>) -> bool {
172        ptr::from_ref(self.shared.context()).addr() == ptr::from_ref(other.shared.context()).addr()
173    }
174
175    #[doc(hidden)]
176    pub fn random_seed(self) -> &'d random::Seed {
177        self.shared.context().random_seed()
178    }
179}
180
181const _: () = {
182    assert!(mem::size_of::<IoOperation>() == 1);
183    assert!(mem::align_of::<IoOperation>() == 1);
184    assert!(mem::size_of::<Opcode>() == mem::size_of::<<crate::Backend as kernel::Source>::Code>());
185    assert!(
186        mem::align_of::<Opcode>() == mem::align_of::<<crate::Backend as kernel::Source>::Code>()
187    );
188    assert!(mem::size_of::<Driver>() == mem::size_of::<Runtime>());
189    assert!(mem::align_of::<Driver>() == mem::align_of::<Runtime>());
190    assert!(mem::size_of::<Reference<'_>>() == mem::size_of::<usize>());
191    assert!(mem::align_of::<Reference<'_>>() == mem::align_of::<usize>());
192    assert!(mem::size_of::<Invariant<'_>>() == 0);
193    assert!(mem::align_of::<Invariant<'_>>() == 1);
194    assert!(mem::size_of::<Context<'_, '_>>() == mem::size_of::<usize>());
195    assert!(mem::align_of::<Context<'_, '_>>() == mem::align_of::<usize>());
196    assert!(mem::size_of::<SubmitError>() == 1 && mem::align_of::<SubmitError>() == 1);
197    assert!(
198        mem::size_of::<Result<flight::Flight<'static>, SubmitError>>()
199            == mem::size_of::<(flight::Flight<'static>, SubmitError)>()
200    );
201    assert!(
202        mem::align_of::<Result<flight::Flight<'static>, SubmitError>>()
203            == mem::align_of::<flight::Flight<'static>>()
204    );
205};
206
207#[repr(transparent)]
208#[non_exhaustive]
209pub struct Context<'a, 'd> {
210    pub storage: storage::Context<'a, 'd>,
211}
212
213impl<'a, 'd> Context<'a, 'd> {
214    fn new(local: &'a mut life::Local<'d>) -> Self {
215        Self {
216            storage: storage::Context::new(local),
217        }
218    }
219
220    /// Reports one exact cancellation rejection to this scope's mandatory observer.
221    pub fn rejected(&mut self, failure: cancel::Failure) {
222        let report = io::Reclaimed::Cancellation(failure);
223        (self.storage.0.lifecycle.report)(report);
224    }
225
226    /// Reports a consumed accept failure with its original driver-bound target.
227    pub fn accept_error(
228        &mut self,
229        target: route::Erased<'d, kind::Accept>,
230        cause: accept::Failure,
231        flow: batch::Flow,
232    ) {
233        (self.storage.0.lifecycle.report)(io::Reclaimed::AcceptRoute {
234            target: target.into_token(),
235            cause,
236            flow,
237        });
238    }
239
240    /// Reclaims a terminal routed receive and reports its original target and outcome.
241    pub fn retire(&mut self, event: io::Event<'d>) -> ControlFlow<io::Event<'d>> {
242        use io::event::Kind;
243
244        let done = match event.into_kind() {
245            Kind::RetiredRecv(done) => done,
246            kind => return ControlFlow::Break(kind.into()),
247        };
248        self.reclaim(done);
249        ControlFlow::Continue(())
250    }
251
252    /// Consumes one routed receive, releases its owner, and reports the exact result.
253    pub fn reclaim(&mut self, done: recv::Completion<'d>) {
254        use io::transfer::Len;
255        use recv::Outcome;
256
257        let (target, flow, outcome) = done.into_parts();
258        let result = match outcome {
259            Outcome::Data(buffer) => {
260                let length = buffer.len();
261                Ok(length)
262            }
263            Outcome::Eof => Ok(Len::ZERO),
264            Outcome::Cancelled => Err(crate::Errno::CANCELED),
265            Outcome::Exhausted(proof) => Err(proof.retire()),
266            Outcome::Starved(error) => Err(error.into()),
267            Outcome::Failed(error) => Err(error),
268        };
269        (self.storage.0.lifecycle.report)(io::Reclaimed::RecvRoute {
270            target: target.into_token(),
271            result,
272            flow,
273        });
274    }
275
276    pub fn reborrow(&mut self) -> Context<'_, 'd> {
277        Context::new(&mut *self.storage.0)
278    }
279
280    pub(crate) fn native(&mut self) -> native::Access<'_, 'd> {
281        native::Access::new(&mut *self.storage.0)
282    }
283
284    pub fn region_token(&mut self) -> &mut region::Token<region::Identity<'d>> {
285        &mut self.storage.0.region.0
286    }
287
288    pub fn state_token(&mut self) -> &mut region::Token<region::Identity<'d, 1>> {
289        &mut self.storage.0.region.1
290    }
291
292    pub fn into_regions(
293        self,
294    ) -> &'a mut (
295        region::Token<region::Identity<'d>>,
296        region::Token<region::Identity<'d, 1>>,
297    ) {
298        &mut self.storage.0.region
299    }
300
301    pub fn regions(
302        &self,
303    ) -> &(
304        region::Token<region::Identity<'d>>,
305        region::Token<region::Identity<'d, 1>>,
306    ) {
307        &self.storage.0.region
308    }
309
310    pub fn receive(&self) -> settings::Receive {
311        self.storage.receive()
312    }
313
314    pub fn driver_ref(&self) -> Reference<'d> {
315        self.storage.0.driver
316    }
317}
318
319impl<'a, 'd> Context<'a, 'd> {
320    pub fn dispatch(
321        &mut self,
322        work: scope::Reactor<'_, 'd>,
323        emit: impl FnMut(io::Event<'d>, &mut Self) -> ControlFlow<io::Event<'d>>,
324    ) -> poll::Dispatch<'d> {
325        let work = native::Backend::new(work);
326        // SAFETY: Context retains the same Local root, registered storage, and
327        // branded flight owners throughout native resolution and host emission.
328        let turn: <Backend as engine::Dispatch<'_, Host<Backend>, Self>>::Turn<_> =
329            unsafe { engine::raw::Dispatch::<'_, Host<Backend>, Self, _>::new(self, work) };
330        engine::raw::Dispatch::<'_, Host<Backend>, Self, _>::dispatch(turn, emit)
331    }
332
333    pub fn commit(&mut self, work: scope::Reactor<'_, 'd>) -> Result<poll::Commit, poll::Error> {
334        let quota = native::Backend::new(work);
335        quota.reclaim(self);
336        let retry = self.storage.0.retry;
337        let owner = self.driver_ref();
338        let mut native = self.native();
339        // SAFETY: Context retains this driver's registered storage and branded
340        // flight owners throughout maintenance and native completion processing.
341        let state = unsafe {
342            engine::raw::Poll::commit(native.as_mut(), quota, || retry || owner.has_deferred())
343        }
344        .map_err(poll::Error::from_backend)?;
345        self.storage.0.retry = false;
346        Ok(match state {
347            task::Poll::Ready(()) => poll::Commit::Drained,
348            task::Poll::Pending => poll::Commit::Pending,
349        })
350    }
351
352    pub fn wait(
353        &mut self,
354        work: scope::Reactor<'_, 'd>,
355        timeout: Option<time::Duration>,
356    ) -> Result<(), poll::Error> {
357        let quota = native::Backend::new(work);
358        quota.reclaim(self);
359        let owner = self.driver_ref();
360        let busy = self.storage.0.retry
361            || self.storage.0.lifecycle.pending
362            || owner.has_deferred()
363            || owner.ready().has_ready();
364        let mut native = self.native();
365        // SAFETY: Context retains this driver's registered storage and branded
366        // flight owners throughout maintenance and native completion processing.
367        unsafe { engine::raw::Poll::wait(native.as_mut(), quota, timeout, busy) }
368            .map_err(poll::Error::from_backend)?;
369        self.storage.0.retry = false;
370        Ok(())
371    }
372}
373
374pub(crate) struct Shared {
375    random_seed: random::Seed,
376    pub(in crate::driver) schedule: schedule::State,
377    pub(in crate::driver) receive: crate::Permits,
378    buffer: <Engine as engine::Buffer>::Context,
379    pub(in crate::driver) retire: queues::Pending,
380    pub(in crate::driver) generations: [Cell<Option<route::Epoch>>; route::CAPACITY],
381    pub(in crate::driver) sequence: sequence::Owner,
382    timer: heap::raw::Store<time::Instant, slot::Slot, timer::Tag>,
383}
384
385impl Shared {
386    pub(in crate::driver) fn try_new(
387        seed: random::Seed,
388        plan: settings::Plan,
389        failure: &mut Option<result::CreateError>,
390    ) -> Result<(provided::Arena<driver::Buffer, Self, ()>, Engine), &mut result::CreateError> {
391        let config = *plan.config();
392        let initialize = match provided::Arena::<driver::Buffer, Self, ()>::try_allocate(
393            config.receive().count().into(),
394        ) {
395            Ok(initialize) => initialize,
396            Err(error) => return Err(failure.insert(error.into())),
397        };
398        let schedule = match schedule::State::try_new(plan) {
399            Ok(schedule) => schedule,
400            Err(error) => return Err(failure.insert(error.into())),
401        };
402        let timer = match heap::raw::Store::try_new(config.schedule().timers().count().get()) {
403            Ok(timer) => timer,
404            Err(error) => return Err(failure.insert(error.into())),
405        };
406        // SAFETY: all fallible host allocation precedes native creation.
407        // This infallible assembly retains the returned context beside
408        // its backend throughout the same driver generation and teardown.
409        let (backend, buffer, receive) =
410            match unsafe { <Backend as engine::raw::Create<Host<Backend>, _>>::try_new(&config) } {
411                Ok(created) => created,
412                Err(error) => return Err(failure.insert(error.into())),
413            };
414        Ok((
415            initialize(Self {
416                random_seed: seed,
417                schedule,
418                receive,
419                buffer,
420                retire: queues::Pending::new(),
421                generations: [const { Cell::new(Some(route::Epoch::INITIAL)) }; route::CAPACITY],
422                sequence: sequence::Owner::new(),
423                timer,
424            }),
425            backend,
426        ))
427    }
428
429    pub(in crate::driver) const fn random_seed(&self) -> &random::Seed {
430        &self.random_seed
431    }
432
433    pub(crate) fn buffer(&self) -> &<Engine as engine::Buffer>::Context {
434        &self.buffer
435    }
436
437    pub(crate) fn into_buffer(self) -> <Engine as engine::Buffer>::Context {
438        self.buffer
439    }
440
441    pub(in crate::driver) fn timer(
442        &self,
443    ) -> &heap::raw::Store<time::Instant, slot::Slot, timer::Tag> {
444        &self.timer
445    }
446}
447
448impl buffer::raw::Fixed<driver::Buffer> for Shared {
449    fn resident(&self) -> num::NonZero<u32> {
450        buffer::raw::Fixed::resident(&self.buffer)
451    }
452
453    unsafe fn project<'a, 'id>(
454        &'a self,
455        item: &'a mut driver::Buffer,
456        limit: buffer::raw::Limit<'id>,
457    ) -> buffer::raw::Projection<'a, 'id> {
458        // SAFETY: Shared retains this exact native context beside its arena;
459        // the caller supplies its uniquely owned completed item and resident limit.
460        unsafe { buffer::raw::Fixed::project(&self.buffer, item, limit) }
461    }
462}