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        Executor {
175            storage: storage::Value::new(value),
176            generation: self.generation,
177            teardown: self.teardown,
178        }
179    }
180
181    pub fn with_plan<T: storage::Plan>(self, plan: T) -> Executor<T, Q> {
182        Executor {
183            storage: plan,
184            generation: self.generation,
185            teardown: self.teardown,
186        }
187    }
188}
189
190pub trait Build: Sized {
191    type Shutdown: Startup;
192
193    fn try_build(
194        self,
195        attempt: &mut creation::Attempt,
196        config: settings::Config,
197    ) -> Result<Executor<(), Self::Shutdown>, create::Error<'_>>;
198}
199
200impl Build for () {
201    type Shutdown = ();
202
203    fn try_build(
204        self,
205        attempt: &mut creation::Attempt,
206        config: settings::Config,
207    ) -> Result<Executor<(), Self::Shutdown>, create::Error<'_>> {
208        Executor::new(attempt, config)
209    }
210}
211
212impl<S> Executor<S, ()> {
213    /// Installs and owns a process-local shutdown source for this driver domain.
214    pub fn shutdown<Q: fd::AsFd>(self, source: Q) -> Result<Executor<S, Q>, domain::Register> {
215        let Self {
216            storage: store,
217            generation: epoch,
218            teardown,
219        } = self;
220        Ok(Executor {
221            storage: store,
222            generation: epoch.fd(source)?,
223            teardown,
224        })
225    }
226}
227
228type ExecResult<'owner, S, O, Q> = Result<
229    <O as Op<S, Q>>::Output,
230    domain::Error<
231        'owner,
232        domain::Rejected<<S as storage::Build>::Error, O>,
233        <O as Op<S, Q>>::Output,
234        Q,
235    >,
236>;
237type CreateResult<'owner, S, F, R, Q> =
238    Result<R, domain::Error<'owner, domain::Rejected<<S as storage::Create>::Error, F>, R, Q>>;
239
240/// One execution borrows its exact persistent native retirement owner.
241pub struct Execution<'owner, S, Q> {
242    storage: S,
243    domain: domain::Session<'owner, Q>,
244}
245
246impl<S, Q> Executor<S, Q> {
247    pub fn run<R>(self, run: impl for<'owner> FnOnce(Execution<'owner, S, Q>) -> R) -> R {
248        let Self {
249            storage,
250            generation: epoch,
251            mut teardown,
252        } = self;
253        let session = epoch.start(&mut teardown);
254        run(Execution {
255            storage,
256            domain: session,
257        })
258    }
259}
260
261impl<'owner, S: storage::Build, Q: Startup> Execution<'owner, S, Q> {
262    pub fn try_enter<O>(self, op: O, report: impl recovery::Observe) -> ExecResult<'owner, S, O, Q>
263    where
264        O: Op<S, Q>,
265    {
266        let Self {
267            storage: store,
268            domain,
269        } = self;
270        domain
271            .try_enter(store, Entry(op), report)
272            .map_err(|error| error.map_storage(|rejected| rejected.map_op(|entry| entry.0)))
273    }
274}
275
276impl<'owner, S: storage::Create, Q: Startup> Execution<'owner, S, Q> {
277    pub fn try_create<F, R>(
278        self,
279        op: F,
280        report: impl recovery::Observe,
281    ) -> CreateResult<'owner, S, F, R, Q>
282    where
283        F: for<'scope, 'life, 'p, 'd> FnOnce(session::Scope<'scope, 'life, 'p, 'd, S, Q>) -> R,
284    {
285        self.try_scope(Scoped(op), report)
286            .map_err(|error| error.map_storage(|rejected| rejected.map_op(|entry| entry.0)))
287    }
288
289    pub(crate) fn try_scope<O>(
290        self,
291        op: O,
292        report: impl recovery::Observe,
293    ) -> CreateResult<'owner, S, O, O::Output, Q>
294    where
295        O: Op<S, Q>,
296    {
297        let Self {
298            storage: store,
299            domain,
300        } = self;
301        domain
302            .try_create(store, Entry(op), report)
303            .map_err(|error| error.map_storage(|rejected| rejected.map_op(|entry| entry.0)))
304    }
305}
306
307impl<'owner, S, Q> Execution<'owner, S, Q>
308where
309    S: storage::Direct,
310    Q: Startup,
311{
312    pub fn enter<O>(
313        self,
314        op: O,
315        report: impl recovery::Observe,
316    ) -> Result<O::Output, domain::Error<'owner, convert::Infallible, O::Output, Q>>
317    where
318        O: Op<S, Q>,
319    {
320        let Self {
321            storage: store,
322            domain,
323        } = self;
324        domain.enter(store, Entry(op), report)
325    }
326
327    pub fn enter_with<R>(
328        self,
329        op: impl for<'scope, 'life, 'p, 'd> FnOnce(session::Scope<'scope, 'life, 'p, 'd, S, Q>) -> R,
330        report: impl recovery::Observe,
331    ) -> Result<R, domain::Error<'owner, convert::Infallible, R, Q>> {
332        self.enter(Scoped(op), report)
333    }
334}
335
336impl<'owner, A: buffer::raw::Arenas, Q: Startup> Execution<'owner, storage::Scope<A>, Q> {
337    /// Lends exact arena roots from the same generation as the driver.
338    pub fn scope<R>(
339        self,
340        op: impl for<'scope, 'life, 'p, 'd> FnOnce(
341            session::Scope<'scope, 'life, 'p, 'd, storage::Scope<A>, Q>,
342        ) -> R,
343        report: impl recovery::Observe,
344    ) -> Result<R, domain::Error<'owner, convert::Infallible, R, Q>> {
345        let Self {
346            storage: store,
347            domain,
348        } = self;
349        domain.scope(store, Entry(Scoped(op)), report)
350    }
351}