1#![forbid(unsafe_code)]
3#![deny(missing_docs)]
4
5use std::{
6 collections::BTreeMap,
7 fmt,
8 future::Future,
9 pin::Pin,
10 sync::{Arc, Mutex, Weak},
11 task::{Context, Poll, Waker},
12};
13
14pub const MAX_REASON_BYTES: usize = 256;
16
17pub static RECIPES: sim_cookbook::EmbeddedDir =
19 include!(concat!(env!("OUT_DIR"), "/cookbook_recipes.rs"));
20
21#[derive(Clone, Debug, Eq, PartialEq)]
23pub struct CancellationReason(Arc<str>);
24
25impl CancellationReason {
26 pub fn new(reason: impl Into<String>) -> Result<Self, InvalidReason> {
28 let reason = reason.into();
29 if reason.trim().is_empty() {
30 return Err(InvalidReason::Empty);
31 }
32 if reason.len() > MAX_REASON_BYTES {
33 return Err(InvalidReason::TooLong {
34 actual: reason.len(),
35 maximum: MAX_REASON_BYTES,
36 });
37 }
38 Ok(Self(reason.into()))
39 }
40 #[must_use]
42 pub fn as_str(&self) -> &str {
43 &self.0
44 }
45}
46
47#[derive(Clone, Debug, Eq, PartialEq)]
49pub enum InvalidReason {
50 Empty,
52 TooLong {
54 actual: usize,
56 maximum: usize,
58 },
59}
60impl fmt::Display for InvalidReason {
61 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
62 write!(output, "invalid cancellation reason: {self:?}")
63 }
64}
65impl std::error::Error for InvalidReason {}
66
67#[derive(Debug)]
68struct Inner {
69 state: Mutex<State>,
70}
71#[derive(Debug, Default)]
72struct State {
73 reason: Option<CancellationReason>,
74 next_waiter: u64,
75 waiters: BTreeMap<u64, Waker>,
76 children: Vec<Weak<Inner>>,
77}
78
79#[derive(Clone, Debug)]
84pub struct Cancellation {
85 inner: Arc<Inner>,
86}
87impl Default for Cancellation {
88 fn default() -> Self {
89 Self::new()
90 }
91}
92
93impl Cancellation {
94 #[must_use]
96 pub fn new() -> Self {
97 Self {
98 inner: Arc::new(Inner {
99 state: Mutex::new(State::default()),
100 }),
101 }
102 }
103 #[must_use]
105 pub fn child(&self) -> Self {
106 let child = Self::new();
107 let inherited = {
108 let mut state = self
109 .inner
110 .state
111 .lock()
112 .expect("cancellation mutex poisoned");
113 match state.reason.clone() {
114 Some(reason) => Some(reason),
115 None => {
116 state.children.retain(|entry| entry.strong_count() > 0);
117 state.children.push(Arc::downgrade(&child.inner));
118 None
119 }
120 }
121 };
122 if let Some(reason) = inherited {
123 child.cancel(reason);
124 }
125 child
126 }
127 pub fn cancel(&self, reason: CancellationReason) -> bool {
129 let (waiters, children) = {
130 let mut state = self
131 .inner
132 .state
133 .lock()
134 .expect("cancellation mutex poisoned");
135 if state.reason.is_some() {
136 return false;
137 }
138 state.reason = Some(reason.clone());
139 let waiters = std::mem::take(&mut state.waiters)
140 .into_values()
141 .collect::<Vec<_>>();
142 let children = std::mem::take(&mut state.children)
143 .into_iter()
144 .filter_map(|child| child.upgrade())
145 .collect::<Vec<_>>();
146 (waiters, children)
147 };
148 for child in children {
149 Self { inner: child }.cancel(reason.clone());
150 }
151 for waiter in waiters {
152 waiter.wake();
153 }
154 true
155 }
156 #[must_use]
158 pub fn reason(&self) -> Option<CancellationReason> {
159 self.inner
160 .state
161 .lock()
162 .expect("cancellation mutex poisoned")
163 .reason
164 .clone()
165 }
166 #[must_use]
168 pub fn is_cancelled(&self) -> bool {
169 self.reason().is_some()
170 }
171 #[must_use]
173 pub fn cancelled(&self) -> CancellationWaiter {
174 CancellationWaiter {
175 inner: Arc::downgrade(&self.inner),
176 registration: None,
177 }
178 }
179}
180
181#[derive(Debug)]
185pub struct CancellationWaiter {
186 inner: Weak<Inner>,
187 registration: Option<u64>,
188}
189impl Future for CancellationWaiter {
190 type Output = CancellationReason;
191 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
192 let Some(inner) = self.inner.upgrade() else {
193 return Poll::Pending;
194 };
195 let mut state = inner.state.lock().expect("cancellation mutex poisoned");
196 if let Some(reason) = state.reason.clone() {
197 if let Some(id) = self.registration.take() {
198 state.waiters.remove(&id);
199 }
200 return Poll::Ready(reason);
201 }
202 match self.registration {
203 Some(id) => {
204 if !state
205 .waiters
206 .get(&id)
207 .is_some_and(|old| old.will_wake(cx.waker()))
208 {
209 state.waiters.insert(id, cx.waker().clone());
210 }
211 }
212 None => {
213 let id = state.next_waiter;
214 state.next_waiter = state.next_waiter.wrapping_add(1);
215 state.waiters.insert(id, cx.waker().clone());
216 self.registration = Some(id);
217 }
218 }
219 Poll::Pending
220 }
221}
222impl Drop for CancellationWaiter {
223 fn drop(&mut self) {
224 let Some(id) = self.registration else { return };
225 if let Some(inner) = self.inner.upgrade()
226 && let Ok(mut state) = inner.state.lock()
227 {
228 state.waiters.remove(&id);
229 }
230 }
231}
232#[cfg(test)]
233mod tests;