almost_enough/
stop_token.rs1use alloc::sync::Arc;
30use core::any::{Any, TypeId};
31
32use crate::{Stop, StopReason};
33
34pub struct StopToken {
59 inner: Option<Arc<dyn Stop + Send + Sync>>,
63}
64
65impl StopToken {
66 #[inline]
71 pub fn new<T: Stop + 'static>(stop: T) -> Self {
72 if !stop.may_stop() {
74 return Self { inner: None };
75 }
76 if TypeId::of::<T>() == TypeId::of::<StopToken>() {
78 let any_ref: &dyn Any = &stop;
79 let inner = any_ref.downcast_ref::<StopToken>().unwrap();
80 let result = inner.clone();
81 drop(stop);
82 return result;
83 }
84 if TypeId::of::<T>() == TypeId::of::<crate::Stopper>() {
86 let any_ref: &dyn Any = &stop;
87 let stopper = any_ref.downcast_ref::<crate::Stopper>().unwrap();
88 let result = Self {
89 inner: Some(stopper.inner.clone() as Arc<dyn Stop + Send + Sync>),
90 };
91 drop(stop);
92 return result;
93 }
94 if TypeId::of::<T>() == TypeId::of::<crate::SyncStopper>() {
96 let any_ref: &dyn Any = &stop;
97 let stopper = any_ref.downcast_ref::<crate::SyncStopper>().unwrap();
98 let result = Self {
99 inner: Some(stopper.inner.clone() as Arc<dyn Stop + Send + Sync>),
100 };
101 drop(stop);
102 return result;
103 }
104 Self {
105 inner: Some(Arc::new(stop)),
106 }
107 }
108
109 #[inline]
131 pub fn from_arc<T: Stop + 'static>(arc: Arc<T>) -> Self {
132 if !arc.may_stop() {
133 return Self { inner: None };
134 }
135 if TypeId::of::<T>() == TypeId::of::<StopToken>() {
137 let any_ref: &dyn Any = &*arc;
138 let inner = any_ref.downcast_ref::<StopToken>().unwrap();
139 return inner.clone();
140 }
141 Self {
142 inner: Some(arc as Arc<dyn Stop + Send + Sync>),
143 }
144 }
145}
146
147impl Clone for StopToken {
148 #[inline]
149 fn clone(&self) -> Self {
150 Self {
151 inner: self.inner.clone(),
152 }
153 }
154}
155
156impl Stop for StopToken {
157 #[inline(always)]
158 fn check(&self) -> Result<(), StopReason> {
159 match &self.inner {
160 Some(inner) => inner.check(),
161 None => Ok(()),
162 }
163 }
164
165 #[inline(always)]
166 fn should_stop(&self) -> bool {
167 match &self.inner {
168 Some(inner) => inner.should_stop(),
169 None => false,
170 }
171 }
172
173 #[inline(always)]
174 fn may_stop(&self) -> bool {
175 self.inner.is_some()
176 }
177}
178
179impl From<crate::Stopper> for StopToken {
182 #[inline]
183 fn from(stopper: crate::Stopper) -> Self {
184 Self {
185 inner: Some(stopper.inner as Arc<dyn Stop + Send + Sync>),
186 }
187 }
188}
189
190impl From<crate::SyncStopper> for StopToken {
192 #[inline]
193 fn from(stopper: crate::SyncStopper) -> Self {
194 Self {
195 inner: Some(stopper.inner as Arc<dyn Stop + Send + Sync>),
196 }
197 }
198}
199
200const _: () = assert!(
203 core::mem::size_of::<StopToken>() == core::mem::size_of::<Arc<dyn Stop + Send + Sync>>()
204);
205
206impl core::fmt::Debug for StopToken {
207 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
208 f.debug_tuple("StopToken").finish()
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215 use crate::{FnStop, StopSource, Stopper, Unstoppable};
216
217 #[test]
218 fn from_unstoppable() {
219 let stop = StopToken::new(Unstoppable);
220 assert!(!stop.should_stop());
221 assert!(stop.check().is_ok());
222 }
223
224 #[test]
225 fn from_stopper() {
226 let stopper = Stopper::new();
227 let stop = StopToken::new(stopper.clone());
228
229 assert!(!stop.should_stop());
230
231 stopper.cancel();
232
233 assert!(stop.should_stop());
234 assert_eq!(stop.check(), Err(StopReason::Cancelled));
235 }
236
237 #[test]
238 fn clone_is_cheap() {
239 let stopper = Stopper::new();
240 let stop = StopToken::new(stopper.clone());
241 let stop2 = stop.clone();
242
243 stopper.cancel();
244
245 assert!(stop.should_stop());
247 assert!(stop2.should_stop());
248 }
249
250 #[cfg(feature = "std")]
251 #[test]
252 fn clone_send_to_thread() {
253 let stopper = Stopper::new();
254 let stop = StopToken::new(stopper.clone());
255
256 let handle = std::thread::spawn({
257 let stop = stop.clone();
258 move || stop.should_stop()
259 });
260
261 stopper.cancel();
262 let _ = handle.join().unwrap();
264 }
265
266 #[test]
267 fn is_send_sync() {
268 fn assert_send_sync<T: Send + Sync>() {}
269 assert_send_sync::<StopToken>();
270 }
271
272 #[test]
273 fn debug_format() {
274 let stop = StopToken::new(Unstoppable);
275 let debug = alloc::format!("{:?}", stop);
276 assert!(debug.contains("StopToken"));
277 }
278
279 #[test]
280 fn may_stop_delegates() {
281 assert!(!StopToken::new(Unstoppable).may_stop());
282 assert!(StopToken::new(Stopper::new()).may_stop());
283 }
284
285 #[test]
286 fn unstoppable_is_none_internally() {
287 let stop = StopToken::new(Unstoppable);
288 assert!(!stop.may_stop());
289 assert!(stop.check().is_ok());
290 }
291
292 #[test]
293 fn collapses_nested_dyn_stop() {
294 let inner = StopToken::new(Unstoppable);
295 let outer = StopToken::new(inner);
296 assert!(!outer.may_stop());
297 }
298
299 #[test]
300 fn collapses_nested_stopper() {
301 let stopper = Stopper::new();
302 let inner = StopToken::new(stopper.clone());
303 let outer = StopToken::new(inner.clone());
304
305 stopper.cancel();
307 assert!(outer.should_stop());
308 assert!(inner.should_stop());
309 }
310
311 #[test]
312 fn from_arc() {
313 let stopper = Arc::new(Stopper::new());
314 let cancel_handle = stopper.clone();
315 let stop = StopToken::from_arc(stopper);
316
317 assert!(!stop.should_stop());
318 cancel_handle.cancel();
319 assert!(stop.should_stop());
320 }
321
322 #[test]
323 fn from_non_clone_fn_stop() {
324 let flag = Arc::new(core::sync::atomic::AtomicBool::new(false));
326 let flag2 = flag.clone();
327 let stop = StopToken::new(FnStop::new(move || {
328 flag2.load(core::sync::atomic::Ordering::Relaxed)
329 }));
330
331 assert!(!stop.should_stop());
332
333 let stop2 = stop.clone();
335 flag.store(true, core::sync::atomic::Ordering::Relaxed);
336
337 assert!(stop.should_stop());
338 assert!(stop2.should_stop());
339 }
340
341 #[test]
342 fn avoids_monomorphization() {
343 fn process(stop: StopToken) -> bool {
344 stop.should_stop()
345 }
346
347 assert!(!process(StopToken::new(Unstoppable)));
348 assert!(!process(StopToken::new(StopSource::new())));
349 assert!(!process(StopToken::new(Stopper::new())));
350 }
351
352 #[test]
353 fn hot_loop_pattern() {
354 let stop = StopToken::new(Unstoppable);
355 for _ in 0..1000 {
356 assert!(stop.check().is_ok()); }
358 }
359
360 #[test]
361 fn from_stopper_zero_cost() {
362 let stopper = Stopper::new();
363 let cancel = stopper.clone();
364 let stop: StopToken = stopper.into(); assert!(!stop.should_stop());
367 cancel.cancel();
368 assert!(stop.should_stop()); }
370
371 #[test]
372 fn from_sync_stopper_zero_cost() {
373 let stopper = crate::SyncStopper::new();
374 let cancel = stopper.clone();
375 let stop: StopToken = stopper.into();
376
377 assert!(!stop.should_stop());
378 cancel.cancel();
379 assert!(stop.should_stop());
380 }
381
382 #[test]
383 fn new_stopper_flattens() {
384 let stopper = Stopper::new();
387 let cancel = stopper.clone();
388 let stop = StopToken::new(stopper);
389
390 cancel.cancel();
391 assert!(stop.should_stop());
392 }
393
394 #[test]
395 fn new_sync_stopper_flattens() {
396 let stopper = crate::SyncStopper::new();
397 let cancel = stopper.clone();
398 let stop = StopToken::new(stopper);
399
400 cancel.cancel();
401 assert!(stop.should_stop());
402 }
403
404 #[test]
405 fn from_arc_collapses_dynstop() {
406 let inner = StopToken::new(Stopper::new());
408 let arc = alloc::sync::Arc::new(inner);
409 let stop = StopToken::from_arc(arc);
410 assert!(!stop.should_stop());
411 }
412
413 #[test]
414 fn stopper_inner_debug() {
415 let stop = Stopper::new();
416 let debug = alloc::format!("{:?}", stop);
417 assert!(debug.contains("cancelled"));
418 }
419
420 #[test]
421 fn sync_stopper_inner_debug() {
422 let stop = crate::SyncStopper::new();
423 let debug = alloc::format!("{:?}", stop);
424 assert!(debug.contains("cancelled"));
425 }
426
427 #[test]
428 fn from_stopper_clone_shares_state() {
429 let stopper = Stopper::new();
430 let stop: StopToken = stopper.clone().into();
431 let stop2 = stop.clone(); stopper.cancel();
434 assert!(stop.should_stop());
436 assert!(stop2.should_stop());
437 }
438}