1use std::{cell, marker, mem, os::fd, time};
2
3use dope_sys::io::{
4 fd::Fd as _,
5 pipe,
6 poll::{self, Poll as _},
7};
8
9use crate::{Backend, Errno, platform::Flags};
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum CreateError {
13 Open(Errno),
14 CloseOnExec { end: pipe::End, failure: Flags },
15 Nonblocking { end: pipe::End, failure: Flags },
16 Fork(Errno),
17}
18
19#[derive(Clone, Copy, PartialEq, Eq, Debug)]
20pub enum WaitError {
21 Read(Errno),
22 Record {
23 expected: usize,
24 actual: isize,
25 },
26 Poll(Errno),
27 Timeout {
28 timeout: time::Duration,
29 elapsed: time::Duration,
30 },
31 PollResult {
32 result: i32,
33 revents: i16,
34 },
35}
36
37#[derive(Clone, Copy, PartialEq, Eq, Debug)]
38pub enum NotifyError {
39 Write(Errno),
40 Record { expected: usize, actual: isize },
41}
42
43#[repr(transparent)]
45pub struct Wait(fd::OwnedFd, marker::PhantomData<cell::Cell<()>>);
46
47#[repr(transparent)]
48#[derive(Debug)]
49pub struct Notify(fd::OwnedFd, marker::PhantomData<cell::Cell<()>>);
50
51pub struct Ends<R> {
52 read: R,
53 notify: Notify,
54}
55
56impl Ends<Wait> {
57 pub fn blocking() -> Result<Self, CreateError> {
58 let (read, write) = <Backend as pipe::Pipe>::open().map_err(CreateError::from_native)?;
59 Ok(Self {
60 read: Wait(read, marker::PhantomData),
61 notify: Notify(write, marker::PhantomData),
62 })
63 }
64}
65
66impl Ends<fd::OwnedFd> {
67 pub fn event() -> Result<Self, CreateError> {
68 let (read, write) =
69 <Backend as pipe::Pipe>::nonblocking().map_err(CreateError::from_native)?;
70 Ok(Self {
71 read,
72 notify: Notify(write, marker::PhantomData),
73 })
74 }
75}
76
77impl<R> Ends<R> {
78 pub fn split(self) -> (R, Notify) {
79 (self.read, self.notify)
80 }
81}
82
83impl Wait {
84 pub fn wait(&self) -> Result<(), WaitError> {
85 let mut byte = [0u8];
86 loop {
87 match Backend::read::<Errno>(fd::AsFd::as_fd(&self.0), &mut byte) {
88 Ok([_]) => return Ok(()),
89 Ok(bytes) => {
90 return Err(WaitError::Record {
91 expected: 1,
92 actual: bytes.len() as isize,
93 });
94 }
95 Err(error) if error.is_intr() => {}
96 Err(error) => return Err(WaitError::Read(error)),
97 }
98 }
99 }
100
101 pub fn wait_timeout(&self, timeout: time::Duration) -> Result<(), WaitError> {
102 let started = time::Instant::now();
103 let total = timeout.as_nanos();
104 let mut delay = total.saturating_sub(started.elapsed().as_nanos());
105 loop {
106 let nanos = delay.min(i32::MAX as u128 * 1_000_000) as u64;
107 let millis = nanos.div_ceil(1_000_000) as i32;
108 let mut entries = [poll::Entry::readable(fd::AsFd::as_fd(&self.0))];
109 match Backend::wait::<_, Errno>(&mut entries, millis) {
110 Err(error) if error.is_intr() => {}
111 Err(error) => return Err(WaitError::Poll(error)),
112 Ok(events) => {
113 let result = events.count() as i32;
114 let entry = events.entries()[0];
115 match (result, entry.revents()) {
116 (1, _) if entry.is_readable() => return self.wait(),
117 (0, 0) => {}
118 (_, revents) => return Err(WaitError::PollResult { result, revents }),
119 }
120 }
121 }
122 let elapsed = started.elapsed();
123 delay = total.saturating_sub(elapsed.as_nanos());
124 if delay == 0 {
125 return Err(WaitError::Timeout { timeout, elapsed });
126 }
127 }
128 }
129}
130
131impl Notify {
132 pub fn notify(&self) -> Result<(), NotifyError> {
133 loop {
134 match Backend::write::<Errno>(fd::AsFd::as_fd(&self.0), &[1]) {
135 Ok([_]) => return Ok(()),
136 Ok(bytes) => {
137 return Err(NotifyError::Record {
138 expected: 1,
139 actual: bytes.len() as isize,
140 });
141 }
142 Err(error) if error.is_intr() => {}
143 Err(error) => return Err(NotifyError::Write(error)),
144 }
145 }
146 }
147
148 pub fn fork(self) -> Result<(Self, Self), CreateError> {
149 let guard =
150 Backend::duplicate::<Errno>(fd::AsFd::as_fd(&self.0)).map_err(CreateError::Fork)?;
151 Ok((Self(guard, marker::PhantomData), self))
152 }
153}
154
155const _: () = {
156 assert!(mem::size_of::<Wait>() == mem::size_of::<fd::OwnedFd>());
157 assert!(mem::align_of::<Wait>() == mem::align_of::<fd::OwnedFd>());
158 assert!(mem::size_of::<Notify>() == mem::size_of::<fd::OwnedFd>());
159 assert!(mem::align_of::<Notify>() == mem::align_of::<fd::OwnedFd>());
160};
161
162impl CreateError {
163 fn from_native(error: pipe::Error) -> Self {
164 match error {
165 pipe::Error::Open(error) => Self::Open(error),
166 pipe::Error::CloseOnExec { end, failure } => Self::CloseOnExec {
167 end,
168 failure: Flags::cloexec(failure),
169 },
170 pipe::Error::Nonblocking { end, failure } => Self::Nonblocking {
171 end,
172 failure: Flags::nonblock(failure),
173 },
174 }
175 }
176}
177
178impl fd::AsFd for Wait {
179 fn as_fd(&self) -> fd::BorrowedFd<'_> {
180 fd::AsFd::as_fd(&self.0)
181 }
182}
183impl fd::AsFd for Notify {
184 fn as_fd(&self) -> fd::BorrowedFd<'_> {
185 fd::AsFd::as_fd(&self.0)
186 }
187}