Skip to main content

dope_runtime/executor/session/
scope.rs

1use std::{mem, pin, time};
2
3use dope_core::driver::{
4    self,
5    schedule::{clock, sequence},
6    storage,
7};
8
9use crate::{
10    executor::{
11        self, boot, drive,
12        session::{self, App, Manual, run},
13    },
14    random, shutdown,
15};
16
17pub struct Scope<'scope, 'life: 'scope, 'p: 'life, 'd: 'p, S: storage::Plan + 'd = (), Q = ()> {
18    storage: pin::Pin<&'life S::Output<'p, 'd>>,
19    pub(crate) core: &'scope mut session::Core<'life, 'd, Q>,
20}
21
22const fn assert_layout<'scope, 'life: 'scope, 'p: 'life, 'd: 'p>() {
23    assert!(mem::size_of::<Scope<'scope, 'life, 'p, 'd>>() == mem::size_of::<usize>() * 2);
24    assert!(mem::align_of::<Scope<'scope, 'life, 'p, 'd>>() == mem::align_of::<usize>());
25}
26
27const _: () = assert_layout();
28
29impl<'scope, 'life: 'scope, 'p: 'life, 'd: 'p, S: storage::Plan + 'd, Q>
30    Scope<'scope, 'life, 'p, 'd, S, Q>
31{
32    pub(in crate::executor) fn new(
33        storage: storage::Loan<'life, 'p, 'd, S>,
34        core: &'scope mut session::Core<'life, 'd, Q>,
35    ) -> Self {
36        Self {
37            storage: storage.pinned(),
38            core,
39        }
40    }
41
42    pub(crate) fn reborrow(&mut self) -> Scope<'_, 'life, 'p, 'd, S, Q> {
43        Scope {
44            storage: self.storage,
45            core: &mut *self.core,
46        }
47    }
48
49    /// Borrows storage while preserving its pinned address.
50    pub fn pinned(&self) -> pin::Pin<&'life S::Output<'p, 'd>> {
51        self.storage
52    }
53
54    pub fn storage(&self) -> &'life S::Output<'p, 'd> {
55        self.storage.get_ref()
56    }
57
58    pub fn driver(&mut self) -> driver::Context<'_, 'd> {
59        self.core.scope.context()
60    }
61
62    pub fn hash_state(&mut self, domain: random::Domain) -> random::HashState<'d> {
63        let driver = self.core.scope.context();
64        random::HashState::for_seed(driver.driver_ref().random_seed(), domain)
65    }
66
67    pub fn try_root<P: random::Purpose>(
68        &mut self,
69    ) -> Result<random::Root<'d, P>, sequence::Exhausted> {
70        let driver = self.core.scope.context();
71        random::Root::try_acquire(&driver)
72    }
73
74    pub(crate) fn finish<'report, D>(
75        self,
76        report: &'report mut session::Completion,
77        app: D,
78    ) -> Result<(), session::Error<'report, ()>>
79    where
80        D: executor::raw::App<'d>,
81        Q: executor::Startup,
82    {
83        self.with_run(app, None, report, |scope, report| scope.finish(report, ()))
84    }
85
86    pub fn with_app<'report, D, R>(
87        &mut self,
88        report: &'report mut session::Completion,
89        app: D,
90        f: impl for<'app> FnOnce(App<'app, 'life, 'd, D, Q>) -> R,
91    ) -> session::Result<'report, R>
92    where
93        D: executor::raw::App<'d>,
94        Q: executor::Startup,
95    {
96        self.reborrow()
97            .with_run(app, None, report, |mut scope, report| {
98                let output = f(scope.app());
99                scope.finish(report, output)
100            })
101    }
102
103    /// Runs to shutdown while retaining the exact request and separate drive result.
104    pub fn request<'report, D>(
105        &mut self,
106        report: &'report mut session::Completion,
107        app: D,
108    ) -> session::Result<'report, Result<shutdown::Requested<Q>, drive::Error>>
109    where
110        D: executor::raw::App<'d>,
111        Q: executor::Startup,
112    {
113        self.with_app(report, app, |app| session::App::run(app))
114    }
115
116    pub fn run<'report, D>(
117        &mut self,
118        report: &'report mut session::Completion,
119        app: D,
120    ) -> session::Result<'report, Result<(), drive::Error>>
121    where
122        D: executor::raw::App<'d>,
123        Q: executor::Startup,
124    {
125        self.with_app(report, app, |app| app.run().map(|_| ()))
126    }
127
128    /// Bounds application quiescence after the callback returns.
129    /// Use `App::drive_for` or `App::run_for` to bound execution itself.
130    pub fn with_quiesce<'report, D, R>(
131        &mut self,
132        report: &'report mut session::Completion,
133        app: D,
134        timeout: time::Duration,
135        f: impl for<'app> FnOnce(App<'app, 'life, 'd, D, Q>) -> R,
136    ) -> session::Result<'report, R>
137    where
138        D: executor::raw::App<'d>,
139        Q: executor::Startup,
140    {
141        use drive::clock::{Bound, wall};
142
143        self.reborrow()
144            .with_run(app, None, report, |mut scope, report| {
145                let output = f(scope.app());
146                scope.finish_for(report, Bound::for_duration(wall(), timeout), output)
147            })
148    }
149
150    /// Serves until shutdown, then bounds application quiescence.
151    pub fn run_quiesce<'report, D>(
152        &mut self,
153        report: &'report mut session::Completion,
154        app: D,
155        timeout: time::Duration,
156    ) -> session::Result<'report, Result<(), drive::Error>>
157    where
158        D: executor::raw::App<'d>,
159        Q: executor::Startup,
160    {
161        self.with_quiesce(report, app, timeout, |app| app.run().map(|_| ()))
162    }
163
164    /// Runs with caller-controlled protocol time and bounds quiescence afterward.
165    /// The timeout starts after the callback; `Manual::drive_for` bounds a drive.
166    /// Later wall-clock applications reject entry until wall time catches up.
167    pub fn with_at_for<'report, D, R>(
168        &mut self,
169        report: &'report mut session::Completion,
170        app: D,
171        start: clock::Deadline<'d>,
172        timeout: time::Duration,
173        f: impl for<'app> FnOnce(Manual<'app, 'life, 'd, D, Q>) -> R,
174    ) -> session::Result<'report, R>
175    where
176        D: executor::raw::App<'d>,
177        Q: executor::Startup,
178    {
179        use drive::clock::{Bound, at};
180
181        self.reborrow()
182            .with_run(app, Some(start), report, |scope, report| {
183                let output = f(Manual {
184                    access: session::Access::new(&mut *scope.core, scope.owner.view()),
185                });
186                let now = scope.core.scope.context().driver_ref().clock().now().into();
187                scope.finish_for(report, Bound::for_duration(at(now), timeout), output)
188            })
189    }
190
191    pub(crate) fn drive<R>(&mut self, root: R) -> Result<R::Output, drive::Error>
192    where
193        R: boot::Root<'d>,
194    {
195        use drive::{block, clock::wall, turn};
196        use turn::scope;
197
198        let core = &mut *self.core;
199        let admitted = core.scope.try_boot();
200        let pending = &mut core.pending;
201        core.scope.with_driver(|mut driver, turn| {
202            let block = scope::Boot::try_new(&mut driver, turn, pending, admitted)
203                .and_then(|state| block::Block::new(state.driver_ref(), state, wall()));
204            block::Block::run_boot(block, root)
205        })
206    }
207
208    /// Keeps the pinned application in this frame through the aborting leaf.
209    fn with_run<'report, D, R>(
210        self,
211        app: D,
212        start: Option<clock::Deadline<'d>>,
213        report: &'report mut session::Completion,
214        run: impl for<'app> FnOnce(
215            run::Run<'app, 'life, 'd, D, Q>,
216            &'report mut session::Completion,
217        ) -> session::Result<'report, R>,
218    ) -> session::Result<'report, R>
219    where
220        D: executor::raw::App<'d>,
221        Q: executor::Startup,
222    {
223        let start = self.core.admit(start).map_err(session::Error::Drive)?;
224        let cell = pin::pin!(executor::raw::Backing::new(self.core.scope.token(), app));
225        let app = cell.as_ref().pin();
226        let owner = self.core.scope.with_turn(|token, driver, mut turn| {
227            turn.sample(start);
228            executor::raw::Owner::install(app, token, driver.driver_ref())
229        });
230        let scope = run::Run {
231            core: &mut *self.core,
232            owner,
233        }
234        .publish()
235        .map_err(|(cause, disarm)| session::Error::Install { cause, disarm })?;
236        run(scope, report)
237    }
238}