Skip to main content

dope_runtime/executor/
mod.rs

1use std::{io, os::fd, pin, task};
2
3use dope_core::driver::{
4    self, lifecycle, retained,
5    schedule::{self, ready},
6    settings, storage,
7};
8
9use crate::{random, shutdown};
10
11mod application;
12#[doc(hidden)]
13pub mod raw;
14pub mod session;
15mod startup;
16
17pub use application::Application;
18pub use startup::Startup;
19
20/// Runtime poll capability tying one exact root and driver to a poll.
21/// ```compile_fail,E0308
22/// fn retag<'a, 'd: 'a, A, B>(value: dope_runtime::executor::RootContext<'a, 'd, A>) -> dope_runtime::executor::RootContext<'a, 'd, B> { value }
23/// ```
24/// ```compile_fail,E0624
25/// let _ = dope_runtime::executor::RootContext::<()>::new(todo!(), todo!(), todo!());
26/// ```
27/// ```compile_fail
28/// fn escape<'a, 'd: 'a, R>(value: dope_runtime::executor::RootContext<'a, 'd, R>) -> std::pin::Pin<&'d mut R> { value.into_parts().0 }
29/// ```
30pub struct RootContext<'poll, 'd: 'poll, R> {
31    root: pin::Pin<&'poll mut R>,
32    wake: ready::Target<'d>,
33    work: schedule::Application<'poll, 'd>,
34    driver: retained::Context<'poll, 'poll, 'd>,
35}
36
37impl<'poll, 'd: 'poll, R> RootContext<'poll, 'd, R> {
38    pub(crate) fn new(
39        root: pin::Pin<&'poll mut R>,
40        wake: ready::Target<'d>,
41        work: schedule::Application<'poll, 'd>,
42        driver: retained::Context<'poll, 'poll, 'd>,
43    ) -> Self {
44        Self {
45            root,
46            wake,
47            work,
48            driver,
49        }
50    }
51
52    pub fn into_parts(
53        self,
54    ) -> (
55        pin::Pin<&'poll mut R>,
56        ready::Target<'d>,
57        schedule::Application<'poll, 'd>,
58        retained::Context<'poll, 'poll, 'd>,
59    ) {
60        (self.root, self.wake, self.work, self.driver)
61    }
62}
63
64/// Transient pinned control-flow root.
65/// Its driver authority retains the installed application through terminal
66/// completion and quiescence; backends must not retain root-owned fields.
67pub trait Root<'d>: Sized {
68    type Output;
69    fn poll(context: RootContext<'_, 'd, Self>) -> task::Poll<Self::Output>;
70}
71
72pub struct Executor<S = (), Q = ()> {
73    storage: S,
74    domain: lifecycle::Domain<Q>,
75    seed: random::Seed,
76}
77
78impl Executor<(), ()> {
79    pub fn new(cfg: settings::Config) -> io::Result<Self> {
80        Self::with_seed(cfg, random::Seed::random()?)
81    }
82
83    pub(crate) fn with_seed(cfg: settings::Config, seed: random::Seed) -> io::Result<Self> {
84        let driver = driver::Driver::new(cfg)?;
85        Ok(Self {
86            storage: (),
87            domain: lifecycle::Domain::new(driver),
88            seed,
89        })
90    }
91}
92
93impl<S, Q> Executor<S, Q> {
94    pub fn with_storage<T: 'static>(self, value: T) -> Executor<storage::Value<T>, Q> {
95        Executor {
96            storage: storage::Value::new(value),
97            domain: self.domain,
98            seed: self.seed,
99        }
100    }
101
102    pub fn with_factory<T: storage::Factory>(self, storage: T) -> Executor<T, Q> {
103        Executor {
104            storage,
105            domain: self.domain,
106            seed: self.seed,
107        }
108    }
109}
110
111pub trait Factory: Sized {
112    type Shutdown: Startup;
113
114    fn executor(self, config: settings::Config) -> io::Result<Executor<(), Self::Shutdown>>;
115}
116
117impl<S> Executor<S, ()> {
118    /// Installs and owns a process-local shutdown source for this driver domain.
119    pub fn with_shutdown(
120        self,
121        source: shutdown::Source,
122    ) -> io::Result<Executor<S, shutdown::Source>> {
123        self.with_source(source, |source| fd::AsFd::as_fd(&source.event))
124    }
125
126    pub(crate) fn with_source<Q>(
127        self,
128        source: Q,
129        select: impl for<'a> FnOnce(&'a Q) -> fd::BorrowedFd<'a>,
130    ) -> io::Result<Executor<S, Q>> {
131        let Self {
132            storage,
133            domain,
134            seed,
135        } = self;
136        let domain = domain.fd(source, select)?;
137        Ok(Executor {
138            storage,
139            domain,
140            seed,
141        })
142    }
143}
144
145impl<S: storage::Factory, Q: Startup> Executor<S, Q> {
146    pub fn try_enter<R>(
147        self,
148        f: impl for<'scope, 'd> FnOnce(session::Session<'scope, 'd, S::Output<'d>, Q>) -> R,
149    ) -> Result<R, S::Error> {
150        let Self {
151            storage,
152            domain,
153            seed,
154        } = self;
155        let owner = crate::Owner::acquire().into_inner();
156        domain.enter(owner, storage, move |scope, storage, source| {
157            let mut core = session::Core::new(scope, seed);
158            f(session::Session::new(storage, &mut core, source))
159        })
160    }
161}
162
163impl<S, Q> Executor<S, Q>
164where
165    S: storage::Factory,
166    S::Error: storage::Never,
167    Q: Startup,
168{
169    pub fn enter<R>(
170        self,
171        f: impl for<'scope, 'd> FnOnce(session::Session<'scope, 'd, S::Output<'d>, Q>) -> R,
172    ) -> R {
173        match self.try_enter(f) {
174            Ok(output) => output,
175            Err(error) => storage::Never::never(error),
176        }
177    }
178}