dope_core/platform/
signal.rs1use std::{cell, marker, mem, os::fd};
2
3use dope_sys::thread::{
4 Thread,
5 signal::{self, AcquireFault, ReleaseFault, Signal},
6};
7use o3::{base::marker::ThreadBound, mem::slot};
8
9use crate::{Backend, Errno};
10
11pub type Receiver = <Backend as Thread>::Receiver;
12
13#[derive(Debug)]
14pub enum Error<'owner, E> {
15 Acquire(Acquire),
16 Run(E),
17 Release {
18 run: Option<E>,
19 cause: Release<'owner>,
20 },
21}
22
23thread_local! {
24 static CLAIMED: cell::Cell<bool> = const { cell::Cell::new(false) };
25}
26
27#[derive(Debug)]
29#[must_use = "the signal policy must be restored before another scope can start"]
30pub struct Release<'owner> {
31 pub fault: ReleaseFault,
32 receiver: slot::Occupied<'owner, Receiver>,
33}
34
35impl Release<'_> {
36 pub fn retry(self) -> Result<(), Self> {
37 release(self.receiver)
38 }
39}
40
41fn release(receiver: slot::Occupied<'_, Receiver>) -> Result<(), Release<'_>> {
42 match signal::raw::Receiver::restore(&*receiver) {
43 Ok(()) => {
44 drop(receiver);
45 CLAIMED.with(|claimed| claimed.set(false));
46 Ok(())
47 }
48 Err(fault) => Err(Release { fault, receiver }),
49 }
50}
51
52type State<'process> = (
53 <Receiver as signal::raw::Receiver>::Source<'process>,
54 marker::PhantomData<(cell::Cell<&'process ()>, ThreadBound)>,
55);
56
57#[must_use]
59pub struct Source<'process> {
60 read: State<'process>,
61}
62
63impl Signal for Source<'_> {
64 type Error = <Receiver as signal::raw::Receiver>::Wait;
65
66 fn wait(&self) -> Result<(), Self::Error> {
67 self.read.0.wait()
68 }
69}
70
71impl Source<'_> {
72 pub fn scope<'owner, R, E>(
77 retained: slot::Vacant<'owner, Receiver>,
78 run: impl for<'process> FnOnce(Source<'process>) -> Result<R, E>,
79 ) -> Result<R, Error<'owner, E>> {
80 if !<Backend as Thread>::is_main() {
81 return Err(Error::Acquire(Acquire::Thread));
82 }
83 if CLAIMED.with(|claimed| claimed.replace(true)) {
84 return Err(Error::Acquire(Acquire::Busy));
85 }
86 let receiver = match unsafe { <Receiver as signal::raw::Receiver>::try_new() } {
89 Ok(receiver) => retained.insert(receiver),
90 Err(error) => {
91 CLAIMED.with(|claimed| claimed.set(false));
92 return Err(Error::Acquire(Acquire::from_native(error)));
93 }
94 };
95 let source = Source {
96 read: (
97 signal::raw::Receiver::source(&*receiver),
98 marker::PhantomData,
99 ),
100 };
101 let outcome = run(source);
102 match release(receiver) {
103 Ok(()) => outcome.map_err(Error::Run),
104 Err(cause) => Err(Error::Release {
105 run: outcome.err(),
106 cause,
107 }),
108 }
109 }
110}
111
112impl fd::AsFd for Source<'_> {
113 fn as_fd(&self) -> fd::BorrowedFd<'_> {
114 self.read.0.as_fd()
115 }
116}
117
118const _: () = {
119 assert!(mem::size_of::<Source<'static>>() == mem::size_of::<fd::BorrowedFd<'static>>());
120 assert!(mem::align_of::<Source<'static>>() == mem::align_of::<fd::BorrowedFd<'static>>());
121 assert!(
122 mem::size_of::<Option<Source<'static>>>()
123 == mem::size_of::<Option<fd::BorrowedFd<'static>>>()
124 );
125};
126
127#[derive(Debug)]
128pub enum Acquire {
129 Thread,
130 Busy,
131 MaskBlock(Errno),
132 SignalFd(Errno),
133 QueueOpen(Errno),
134 QueueCloexec(Errno),
135 Register(Errno),
136 Ignore(Errno),
137}
138
139impl Acquire {
140 fn from_native(error: AcquireFault) -> Self {
141 match error {
142 AcquireFault::MaskBlock(error) => Self::MaskBlock(error),
143 AcquireFault::SignalFd(error) => Self::SignalFd(error),
144 AcquireFault::QueueOpen(error) => Self::QueueOpen(error),
145 AcquireFault::QueueCloexec(error) => Self::QueueCloexec(error),
146 AcquireFault::Register(error) => Self::Register(error),
147 AcquireFault::Ignore(error) => Self::Ignore(error),
148 }
149 }
150}