1use std::{cell::Cell, marker, mem, os::fd, time};
2
3use dope_core::{
4 driver::{life::creation, settings},
5 platform::wake::{CreateError, Ends, Notify, NotifyError, Wait, WaitError},
6};
7use o3::base::marker::ThreadBound;
8
9use crate::executor::{self, create};
10
11#[derive(Debug)]
12pub struct FireError {
13 pub trigger: Trigger,
14 pub cause: NotifyError,
15}
16
17#[must_use]
18pub struct Pair {
19 source: Source,
20 trigger: Trigger,
21}
22
23#[must_use]
24pub struct Source {
25 pub(crate) event: (fd::OwnedFd, Notify),
26}
27
28impl fd::AsFd for Source {
29 fn as_fd(&self) -> fd::BorrowedFd<'_> {
30 fd::AsFd::as_fd(&self.event.0)
31 }
32}
33
34#[must_use]
35#[derive(Debug)]
36pub struct Trigger(Notify);
37
38#[must_use]
40pub struct ReadySource {
41 shutdown: Source,
42 ready: Cell<Option<Notify>>,
43}
44
45impl fd::AsFd for ReadySource {
46 fn as_fd(&self) -> fd::BorrowedFd<'_> {
47 fd::AsFd::as_fd(&self.shutdown)
48 }
49}
50
51#[must_use]
53pub struct Readiness(Wait);
54
55#[must_use]
57pub struct ReadyPair {
58 source: ReadySource,
59 trigger: Trigger,
60 readiness: Readiness,
61}
62
63type Invariant<Q> = marker::PhantomData<(fn(Q) -> Q, ThreadBound)>;
64
65#[must_use]
66pub struct Requested<Q = Source>(Invariant<Q>);
67
68impl Pair {
69 pub fn new() -> Result<Self, CreateError> {
70 let (event, notify) = Ends::event()?.split();
71 let (guard, notify) = notify.fork()?;
72 Ok(Self {
73 source: Source {
74 event: (event, guard),
75 },
76 trigger: Trigger(notify),
77 })
78 }
79
80 pub fn split(self) -> (Source, Trigger) {
81 (self.source, self.trigger)
82 }
83
84 pub fn run<R>(self, execute: impl FnOnce(Source) -> R) -> R {
85 execute(self.source)
86 }
87}
88
89impl ReadyPair {
90 pub fn new() -> Result<Self, CreateError> {
91 let (stop, trig) = Pair::new()?.split();
92 let (ready, notify) = Ends::blocking()?.split();
93 Ok(Self {
94 source: ReadySource {
95 shutdown: stop,
96 ready: Cell::new(Some(notify)),
97 },
98 trigger: trig,
99 readiness: Readiness(ready),
100 })
101 }
102
103 pub fn split(self) -> (ReadySource, Trigger, Readiness) {
104 (self.source, self.trigger, self.readiness)
105 }
106}
107
108impl executor::Build for Source {
109 type Shutdown = Source;
110
111 fn try_build(
112 self,
113 attempt: &mut creation::Attempt,
114 config: settings::Config,
115 ) -> Result<executor::Executor<(), Self::Shutdown>, create::Error<'_>> {
116 executor::Executor::new(attempt, config)?
117 .shutdown(self)
118 .map_err(create::Error::from)
119 }
120}
121
122impl executor::Build for ReadySource {
123 type Shutdown = ReadySource;
124
125 fn try_build(
126 self,
127 attempt: &mut creation::Attempt,
128 config: settings::Config,
129 ) -> Result<executor::Executor<(), Self::Shutdown>, create::Error<'_>> {
130 executor::Executor::new(attempt, config)?
131 .shutdown(self)
132 .map_err(create::Error::from)
133 }
134}
135
136impl executor::Startup for Source {
137 fn installed(&self) -> Result<(), executor::InstallError> {
138 Ok(())
139 }
140}
141
142impl executor::Startup for ReadySource {
143 fn installed(&self) -> Result<(), executor::InstallError> {
144 let Some(notify) = self.ready.take() else {
145 return Ok(());
146 };
147 match notify.notify() {
148 Ok(()) => Ok(()),
149 Err(NotifyError::Write(error)) if error.is_pipe() => Ok(()),
150 Err(error) => {
151 self.ready.set(Some(notify));
152 Err(error.into())
153 }
154 }
155 }
156}
157
158impl Readiness {
159 pub fn wait(self) -> Result<(), WaitError> {
160 self.0.wait()
161 }
162
163 pub fn wait_timeout(self, limit: time::Duration) -> Result<(), WaitError> {
164 self.0.wait_timeout(limit)
165 }
166}
167
168impl Trigger {
169 pub fn fire(self) -> Result<(), FireError> {
170 match self.0.notify() {
171 Ok(()) => Ok(()),
172 Err(cause) => Err(FireError {
173 trigger: self,
174 cause,
175 }),
176 }
177 }
178}
179
180impl<Q> Requested<Q> {
181 pub(crate) const fn new() -> Self {
182 Self(marker::PhantomData)
183 }
184}
185
186const _: () = assert!(std::mem::size_of::<Requested>() == 0);
187const _: () = assert!(mem::size_of::<Option<Notify>>() == mem::size_of::<fd::OwnedFd>());