Skip to main content

dope_runtime/executor/
mod.rs

1use std::{convert, num::NonZero, os::fd};
2
3use dope_core::{
4    driver::{
5        life,
6        life::{creation, domain, recovery},
7        settings, storage,
8    },
9    platform::wake,
10    random,
11};
12use o3::buffer;
13
14use crate::process::failure::bus;
15
16pub mod boot;
17pub mod create;
18pub mod drain;
19pub mod drive;
20pub mod raw;
21pub mod root;
22pub mod session;
23#[doc(hidden)]
24pub mod work;
25/// Runs once inside an executor generation with its exact storage family.
26pub trait Op<S: storage::Plan, Q>: Sized {
27    type Output;
28
29    fn run<'scope, 'life, 'p, 'd>(
30        self,
31        scope: session::Scope<'scope, 'life, 'p, 'd, S, Q>,
32    ) -> Self::Output
33    where
34        'd: 'p,
35        'p: 'life,
36        'd: 'life,
37        'life: 'scope,
38        Self: 'd,
39        S: 'd,
40        Q: 'd;
41}
42
43#[repr(transparent)]
44struct Scoped<F>(F);
45
46impl<S, Q, F, R> Op<S, Q> for Scoped<F>
47where
48    S: storage::Plan,
49    F: for<'scope, 'life, 'p, 'd> FnOnce(session::Scope<'scope, 'life, 'p, 'd, S, Q>) -> R,
50{
51    type Output = R;
52
53    fn run<'scope, 'life, 'p, 'd>(
54        self,
55        scope: session::Scope<'scope, 'life, 'p, 'd, S, Q>,
56    ) -> Self::Output
57    where
58        'd: 'p,
59        'p: 'life,
60        'd: 'life,
61        'life: 'scope,
62        Self: 'd,
63        S: 'd,
64        Q: 'd,
65    {
66        (self.0)(scope)
67    }
68}
69
70struct Entry<O>(O);
71
72impl<S, Q, O> life::Operation<S, Q> for Entry<O>
73where
74    S: storage::Plan,
75    O: Op<S, Q>,
76{
77    type Output = O::Output;
78
79    fn run<'life, 'p, 'd>(
80        self,
81        scope: life::Scope<'life, 'd>,
82        store: storage::Loan<'life, 'p, 'd, S>,
83        source: &'life Q,
84    ) -> Self::Output
85    where
86        'd: 'p,
87        'p: 'life,
88        'd: 'life,
89        Self: 'd,
90        S: 'd,
91        Q: 'd,
92    {
93        let mut core = session::Core::new(scope, source);
94        let scope = session::Scope::new(store, &mut core);
95        Op::run(self.0, scope)
96    }
97}
98
99/// Compile-time behavior selected by an executor's shutdown source after its
100/// pinned application is installed.
101pub trait Startup: Sized {
102    /// Publishes installation readiness or preserves its exact notification failure.
103    fn installed(&self) -> Result<(), InstallError>;
104}
105
106impl Startup for () {
107    fn installed(&self) -> Result<(), InstallError> {
108        Ok(())
109    }
110}
111
112#[derive(Clone, Copy, Debug, Eq, PartialEq)]
113pub enum InstallError {
114    Claim(settings::errors::Claim),
115    Coordinators { count: NonZero<usize>, limit: usize },
116    CoordinatorCountOverflow,
117    Notify(wake::NotifyError),
118    Process(bus::Fault),
119}
120
121impl From<settings::errors::Claim> for InstallError {
122    fn from(error: settings::errors::Claim) -> Self {
123        Self::Claim(error)
124    }
125}
126
127impl From<wake::NotifyError> for InstallError {
128    fn from(error: wake::NotifyError) -> Self {
129        Self::Notify(error)
130    }
131}
132
133impl From<bus::Fault> for InstallError {
134    fn from(error: bus::Fault) -> Self {
135        Self::Process(error)
136    }
137}
138
139pub struct Executor<S = (), Q = ()> {
140    storage: S,
141    generation: domain::Generation<Q>,
142    teardown: domain::Teardown,
143}
144
145impl Executor<(), ()> {
146    pub fn new(
147        attempt: &mut creation::Attempt,
148        cfg: settings::Config,
149    ) -> Result<Self, create::Error<'_>> {
150        let driver = attempt.try_driver(cfg)?;
151        Ok(Self {
152            storage: (),
153            generation: domain::Generation::new(driver),
154            teardown: domain::Teardown::default(),
155        })
156    }
157
158    pub(crate) fn with_seed(
159        attempt: &mut creation::Attempt,
160        cfg: settings::Config,
161        seed: random::Seed,
162    ) -> Result<Self, create::Error<'_>> {
163        let driver = attempt.try_seed(cfg, seed)?;
164        Ok(Self {
165            storage: (),
166            generation: domain::Generation::new(driver),
167            teardown: domain::Teardown::default(),
168        })
169    }
170}
171
172impl<S, Q> Executor<S, Q> {
173    pub fn with_storage<T>(self, value: T) -> Executor<storage::Value<T>, Q> {
174        self.with_plan(storage::Value::new(value))
175    }
176
177    pub fn with_plan<T: storage::Plan>(self, plan: T) -> Executor<T, Q> {
178        Executor {
179            storage: plan,
180            generation: self.generation,
181            teardown: self.teardown,
182        }
183    }
184}
185
186pub trait Build: Sized {
187    type Shutdown: Startup;
188
189    fn try_build(
190        self,
191        attempt: &mut creation::Attempt,
192        config: settings::Config,
193    ) -> Result<Executor<(), Self::Shutdown>, create::Error<'_>>;
194}
195
196impl Build for () {
197    type Shutdown = ();
198
199    fn try_build(
200        self,
201        attempt: &mut creation::Attempt,
202        config: settings::Config,
203    ) -> Result<Executor<(), Self::Shutdown>, create::Error<'_>> {
204        Executor::new(attempt, config)
205    }
206}
207
208impl<S> Executor<S, ()> {
209    /// Installs and owns a process-local shutdown source for this driver domain.
210    pub fn shutdown<Q: fd::AsFd>(self, source: Q) -> Result<Executor<S, Q>, domain::Register> {
211        let Self {
212            storage: store,
213            generation: epoch,
214            teardown,
215        } = self;
216        Ok(Executor {
217            storage: store,
218            generation: epoch.fd(source)?,
219            teardown,
220        })
221    }
222}
223
224type ExecResult<'owner, S, O, Q> = Result<
225    <O as Op<S, Q>>::Output,
226    domain::Error<
227        'owner,
228        domain::Rejected<<S as storage::Build>::Error, O>,
229        <O as Op<S, Q>>::Output,
230        Q,
231    >,
232>;
233type CreateResult<'owner, S, F, R, Q> =
234    Result<R, domain::Error<'owner, domain::Rejected<<S as storage::Create>::Error, F>, R, Q>>;
235
236/// One execution borrows its exact persistent native retirement owner.
237pub struct Execution<'owner, S, Q> {
238    storage: S,
239    domain: domain::Session<'owner, Q>,
240}
241
242impl<S, Q> Executor<S, Q> {
243    pub fn run<R>(self, run: impl for<'owner> FnOnce(Execution<'owner, S, Q>) -> R) -> R {
244        let Self {
245            storage,
246            generation: epoch,
247            mut teardown,
248        } = self;
249        let session = epoch.start(&mut teardown);
250        run(Execution {
251            storage,
252            domain: session,
253        })
254    }
255}
256
257impl<'owner, S: storage::Build, Q: Startup> Execution<'owner, S, Q> {
258    pub fn try_enter<O>(self, op: O, report: impl recovery::Observe) -> ExecResult<'owner, S, O, Q>
259    where
260        O: Op<S, Q>,
261    {
262        let Self {
263            storage: store,
264            domain,
265        } = self;
266        domain
267            .try_enter(store, Entry(op), report)
268            .map_err(|error| error.map_storage(|rejected| rejected.map_op(|entry| entry.0)))
269    }
270}
271
272impl<'owner, S: storage::Create, Q: Startup> Execution<'owner, S, Q> {
273    pub fn try_create<F, R>(
274        self,
275        op: F,
276        report: impl recovery::Observe,
277    ) -> CreateResult<'owner, S, F, R, Q>
278    where
279        F: for<'scope, 'life, 'p, 'd> FnOnce(session::Scope<'scope, 'life, 'p, 'd, S, Q>) -> R,
280    {
281        self.try_scope(Scoped(op), report)
282            .map_err(|error| error.map_storage(|rejected| rejected.map_op(|entry| entry.0)))
283    }
284
285    pub(crate) fn try_scope<O>(
286        self,
287        op: O,
288        report: impl recovery::Observe,
289    ) -> CreateResult<'owner, S, O, O::Output, Q>
290    where
291        O: Op<S, Q>,
292    {
293        let Self {
294            storage: store,
295            domain,
296        } = self;
297        domain
298            .try_create(store, Entry(op), report)
299            .map_err(|error| error.map_storage(|rejected| rejected.map_op(|entry| entry.0)))
300    }
301}
302
303impl<'owner, S, Q> Execution<'owner, S, Q>
304where
305    S: storage::Direct,
306    Q: Startup,
307{
308    pub fn enter<O>(
309        self,
310        op: O,
311        report: impl recovery::Observe,
312    ) -> Result<O::Output, domain::Error<'owner, convert::Infallible, O::Output, Q>>
313    where
314        O: Op<S, Q>,
315    {
316        let Self {
317            storage: store,
318            domain,
319        } = self;
320        domain.enter(store, Entry(op), report)
321    }
322
323    pub fn enter_with<R>(
324        self,
325        op: impl for<'scope, 'life, 'p, 'd> FnOnce(session::Scope<'scope, 'life, 'p, 'd, S, Q>) -> R,
326        report: impl recovery::Observe,
327    ) -> Result<R, domain::Error<'owner, convert::Infallible, R, Q>> {
328        self.enter(Scoped(op), report)
329    }
330}
331
332impl<'owner, A: buffer::raw::Arenas, Q: Startup> Execution<'owner, storage::Scope<A>, Q> {
333    /// Lends exact arena roots from the same generation as the driver.
334    pub fn scope<R>(
335        self,
336        op: impl for<'scope, 'life, 'p, 'd> FnOnce(
337            session::Scope<'scope, 'life, 'p, 'd, storage::Scope<A>, Q>,
338        ) -> R,
339        report: impl recovery::Observe,
340    ) -> Result<R, domain::Error<'owner, convert::Infallible, R, Q>> {
341        let Self {
342            storage: store,
343            domain,
344        } = self;
345        domain.scope(store, Entry(Scoped(op)), report)
346    }
347}