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;
25pub 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
99pub trait Startup: Sized {
102 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 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
236pub 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 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}