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 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 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
240pub 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 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}