Skip to main content

dope_runtime/process/lifecycle/
native.rs

1use std::cell::Cell;
2
3use dope_core::platform::thread;
4use o3::alloc;
5
6use crate::process::STACK;
7
8/// Failure before one owned worker starts.
9#[derive(Debug)]
10pub enum Start {
11    Allocation(alloc::Error),
12    Thread(thread::Error),
13}
14
15/// Runs the parent locally and returns the worker's output after its join.
16pub trait Task: thread::Task + Sized {
17    fn with<R>(self, run: impl FnOnce() -> R) -> Result<(R, Self::Output), Start> {
18        scope(self, |join| (run(), join.finish()))
19    }
20}
21
22impl<T: thread::Task> Task for T {}
23
24pub struct Join<'scope, O>(&'scope dyn Fn() -> O);
25
26impl<O> Join<'_, O> {
27    pub fn finish(self) -> O {
28        self.0()
29    }
30}
31
32/// Lends the worker's join authority to the local callback.
33pub fn scope<T: thread::Task, R>(
34    task: T,
35    run: impl for<'scope> FnOnce(Join<'scope, T::Output>) -> R,
36) -> Result<R, Start> {
37    let mut group = thread::Builder::try_new(1).map_err(Start::Allocation)?;
38    group.try_push(task, ()).map_err(Start::Allocation)?;
39    group.scope(|mut workers| {
40        workers
41            .start(STACK)
42            .map_err(|(_, error)| Start::Thread(error))?;
43        let workers = Cell::new(Some(workers));
44        let join = || {
45            let Some(workers) = workers.take() else {
46                unreachable!("joined")
47            };
48            match workers.finish().next() {
49                Some(((), output)) => output,
50                None => unreachable!("worker"),
51            }
52        };
53        Ok(run(Join(&join)))
54    })
55}
56
57/// An entry closure moved only across the worker creation and join boundary.
58pub struct Entry<F>(pub F);
59
60impl<F: FnOnce() -> O + Send, O: Send> thread::Task for Entry<F> {
61    type Output = O;
62
63    fn run(self) -> O {
64        self.0()
65    }
66}