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
24pub 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
75pub 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
93pub 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}