Skip to main content

dope_runtime/
signal.rs

1use std::{cell::Cell, ops, os::fd, task::Poll};
2
3use dope_core::{
4    dope_sys::thread::signal::Signal as _,
5    driver::{
6        life::creation,
7        schedule::ready::wake::{self, store::Slot},
8        settings,
9    },
10    platform::signal,
11};
12
13use crate::{
14    executor::{
15        self, create,
16        drive::{self, clock, turn},
17        root, session,
18    },
19    shutdown,
20};
21
22pub type Error = <signal::Source<'static> as dope_core::dope_sys::thread::signal::Signal>::Error;
23
24/// Owns the process signal across executor generations.
25pub struct Signal<'process> {
26    source: signal::Source<'process>,
27    result: Cell<Option<Result<(), Error>>>,
28}
29
30impl<'process> Signal<'process> {
31    pub const fn new(source: signal::Source<'process>) -> Self {
32        Self {
33            source,
34            result: Cell::new(None),
35        }
36    }
37
38    pub fn result(&self) -> Option<Result<(), Error>> {
39        self.result.get()
40    }
41}
42
43impl fd::AsFd for Signal<'_> {
44    fn as_fd(&self) -> fd::BorrowedFd<'_> {
45        self.source.as_fd()
46    }
47}
48
49impl executor::Startup for &mut Signal<'_> {
50    fn installed(&self) -> Result<(), executor::InstallError> {
51        Ok(())
52    }
53
54    fn observed(&self, _request: shutdown::Requested<Self>) {
55        if self.result.get().is_none() {
56            self.result.set(Some(self.source.wait()));
57        }
58    }
59}
60
61impl<'source, 'process> executor::Build for &'source mut Signal<'process> {
62    type Shutdown = Self;
63
64    fn try_build(
65        self,
66        attempt: &mut creation::Attempt,
67        config: settings::Config,
68    ) -> Result<executor::Executor<(), Self>, create::Error<'_>> {
69        executor::Executor::new(attempt, config)?
70            .shutdown(self)
71            .map_err(create::Error::from)
72    }
73}
74
75/// One reborrowable waiter inside its live application.
76pub struct Wait<'signal, 'd> {
77    result: &'signal Cell<Option<Result<(), Error>>>,
78    wake: &'signal Slot<'d>,
79}
80
81impl<'d> Wait<'_, 'd> {
82    pub fn poll(&mut self, wake: wake::Waker<'d>) -> Poll<Result<(), Error>> {
83        match self.result.get() {
84            Some(result) => Poll::Ready(result),
85            None => {
86                self.wake.set(wake);
87                Poll::Pending
88            }
89        }
90    }
91}
92
93/// Keeps the root alive to perform its own shutdown cleanup.
94pub struct App<'signal, 'app, 'life: 'app, 'd: 'life, D: executor::raw::Layout<'d>, Q> {
95    access: session::Access<'app, 'life, 'd, D, Q>,
96    wake: &'signal Slot<'d>,
97}
98
99impl<'app, 'life: 'app, 'd: 'life, 'source, 'process, D>
100    session::App<'app, 'life, 'd, D, &'source mut Signal<'process>>
101where
102    D: executor::raw::App<'d>,
103{
104    pub fn with_signal<R>(
105        self,
106        run: impl for<'signal> FnOnce(
107            App<'signal, 'app, 'life, 'd, D, &'source mut Signal<'process>>,
108            Wait<'signal, 'd>,
109        ) -> R,
110    ) -> R {
111        let wake = Slot::empty();
112        let result = &self.access.core.lifecycle.result;
113        run(
114            App {
115                access: self.access,
116                wake: &wake,
117            },
118            Wait {
119                result,
120                wake: &wake,
121            },
122        )
123    }
124}
125
126impl<'app, 'life: 'app, 'd: 'life, D, Q> App<'_, 'app, 'life, 'd, D, Q>
127where
128    D: executor::raw::App<'d>,
129    Q: executor::Startup,
130{
131    pub fn drive<R>(&mut self, root: R) -> Result<R::Output, drive::Error>
132    where
133        R: root::Root<'d>,
134    {
135        let source = self.access.core.lifecycle;
136        let wake = self.wake;
137        self.access
138            .reborrow()
139            .drive_with(clock::wall(), root, drive::tick, || {
140                source.observed(shutdown::Requested::new());
141                wake.wake_once();
142                turn::Status::Active
143            })
144    }
145}
146
147impl<'app, 'life: 'app, 'd: 'life, D: executor::raw::Layout<'d>, Q> ops::Deref
148    for App<'_, 'app, 'life, 'd, D, Q>
149{
150    type Target = session::Access<'app, 'life, 'd, D, Q>;
151
152    fn deref(&self) -> &Self::Target {
153        &self.access
154    }
155}
156
157impl<'d, D: executor::raw::Layout<'d>, Q> ops::DerefMut for App<'_, '_, '_, 'd, D, Q> {
158    fn deref_mut(&mut self) -> &mut Self::Target {
159        &mut self.access
160    }
161}