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