almost_enough/
stop_token.rs1use alloc::sync::Arc;
30use core::any::{Any, TypeId};
31
32use crate::{Stop, StopReason};
33
34pub struct StopToken {
62 inner: StopTokenInner,
63}
64
65enum StopTokenInner {
70 None,
72 Relaxed(Arc<crate::stopper::StopperInner>),
74 Dyn(Arc<dyn Stop + Send + Sync>),
76}
77
78impl StopToken {
79 #[inline]
84 pub fn new<T: Stop + 'static>(stop: T) -> Self {
85 if !stop.may_stop() {
87 return Self {
88 inner: StopTokenInner::None,
89 };
90 }
91 if TypeId::of::<T>() == TypeId::of::<StopToken>() {
93 let any_ref: &dyn Any = &stop;
94 let inner = any_ref.downcast_ref::<StopToken>().unwrap();
95 let result = inner.clone();
96 drop(stop);
97 return result;
98 }
99 if let Some(token) = boxed_token(&stop) {
101 return token;
102 }
103 if TypeId::of::<T>() == TypeId::of::<crate::Stopper>() {
105 let any_ref: &dyn Any = &stop;
106 let stopper = any_ref.downcast_ref::<crate::Stopper>().unwrap();
107 let result = Self {
108 inner: StopTokenInner::Relaxed(stopper.inner.clone()),
109 };
110 drop(stop);
111 return result;
112 }
113 if TypeId::of::<T>() == TypeId::of::<crate::SyncStopper>() {
115 let any_ref: &dyn Any = &stop;
116 let stopper = any_ref.downcast_ref::<crate::SyncStopper>().unwrap();
117 let result = Self {
118 inner: StopTokenInner::Dyn(stopper.inner.clone()),
119 };
120 drop(stop);
121 return result;
122 }
123 Self {
124 inner: StopTokenInner::Dyn(Arc::new(stop)),
125 }
126 }
127
128 #[inline]
144 pub fn from_arc<T: Stop + 'static>(arc: Arc<T>) -> Self {
145 if !arc.may_stop() {
146 return Self {
147 inner: StopTokenInner::None,
148 };
149 }
150 if TypeId::of::<T>() == TypeId::of::<StopToken>() {
151 let any_ref: &dyn Any = &*arc;
152 let inner = any_ref.downcast_ref::<StopToken>().unwrap();
153 return inner.clone();
154 }
155 if TypeId::of::<T>() == TypeId::of::<crate::Stopper>() {
158 let any_ref: &dyn Any = &*arc;
159 let stopper = any_ref.downcast_ref::<crate::Stopper>().unwrap();
160 return Self {
161 inner: StopTokenInner::Relaxed(stopper.inner.clone()),
162 };
163 }
164 Self {
165 inner: StopTokenInner::Dyn(arc as Arc<dyn Stop + Send + Sync>),
166 }
167 }
168}
169
170#[allow(deprecated)]
172#[inline]
173fn boxed_token<T: Stop + 'static>(stop: &T) -> Option<StopToken> {
174 let any_ref: &dyn Any = stop;
175 any_ref
176 .downcast_ref::<crate::BoxedStop>()
177 .map(|boxed| boxed.0.clone())
178}
179
180impl Clone for StopTokenInner {
181 #[inline]
182 fn clone(&self) -> Self {
183 match self {
184 Self::None => Self::None,
185 Self::Relaxed(arc) => Self::Relaxed(Arc::clone(arc)),
186 Self::Dyn(arc) => Self::Dyn(Arc::clone(arc)),
187 }
188 }
189}
190
191impl Clone for StopToken {
192 #[inline]
193 fn clone(&self) -> Self {
194 Self {
195 inner: self.inner.clone(),
196 }
197 }
198}
199
200impl Stop for StopToken {
201 #[inline(always)]
202 #[track_caller]
203 fn check(&self) -> Result<(), StopReason> {
204 match &self.inner {
205 StopTokenInner::None => Ok(()),
206 StopTokenInner::Relaxed(inner) => inner.check(),
207 StopTokenInner::Dyn(inner) => inner.check(),
208 }
209 }
210
211 #[inline(always)]
212 #[track_caller]
213 fn should_stop(&self) -> bool {
214 match &self.inner {
215 StopTokenInner::None => false,
216 StopTokenInner::Relaxed(inner) => inner.should_stop(),
217 StopTokenInner::Dyn(inner) => inner.should_stop(),
218 }
219 }
220
221 #[inline(always)]
222 fn may_stop(&self) -> bool {
223 !matches!(self.inner, StopTokenInner::None)
224 }
225}
226
227impl From<crate::Stopper> for StopToken {
229 #[inline]
230 fn from(stopper: crate::Stopper) -> Self {
231 Self {
232 inner: StopTokenInner::Relaxed(stopper.inner),
233 }
234 }
235}
236
237impl From<crate::SyncStopper> for StopToken {
239 #[inline]
240 fn from(stopper: crate::SyncStopper) -> Self {
241 Self {
242 inner: StopTokenInner::Dyn(stopper.inner),
243 }
244 }
245}
246
247impl core::fmt::Debug for StopToken {
248 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
249 f.debug_tuple("StopToken").finish()
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256 use crate::{FnStop, StopSource, Stopper, Unstoppable};
257
258 #[test]
259 fn boxed_and_arced_dyn_stops_are_stops_with_almost_enough_alone() {
260 fn takes(stop: impl Stop + 'static) -> StopToken {
262 StopToken::new(stop)
263 }
264 let arc: Arc<dyn Stop> = Arc::new(Stopper::new());
265 assert!(takes(arc).check().is_ok());
266 let boxed: alloc::boxed::Box<dyn Stop> = alloc::boxed::Box::new(Stopper::cancelled());
267 assert!(takes(boxed).should_stop());
268 }
269
270 #[test]
271 fn from_arc_checks_a_stopper_directly_and_a_sync_stopper_through_the_vtable() {
272 let stopper = Stopper::new();
273 let token = StopToken::from_arc(Arc::new(stopper.clone()));
274 assert_eq!(direct(&token), "relaxed");
275 assert!(!token.should_stop());
276 stopper.cancel();
277 assert!(token.should_stop());
278
279 let sync = crate::SyncStopper::new();
280 let token = StopToken::from_arc(Arc::new(sync.clone()));
281 assert_eq!(direct(&token), "dyn");
282 sync.cancel();
283 assert!(token.should_stop());
284 }
285
286 fn direct(stop: &StopToken) -> &'static str {
287 match &stop.inner {
288 StopTokenInner::None => "none",
289 StopTokenInner::Relaxed(_) => "relaxed",
290 StopTokenInner::Dyn(_) => "dyn",
291 }
292 }
293
294 #[test]
295 #[allow(deprecated)] fn boxed_stop_takes_the_same_fast_paths() {
297 use crate::{BoxedStop, SyncStopper};
298 assert_eq!(direct(&BoxedStop::new(Unstoppable).0), "none");
299 let stopper = Stopper::new();
300 let boxed = BoxedStop::new(stopper.clone());
301 assert_eq!(direct(&boxed.0), "relaxed");
302 let StopTokenInner::Relaxed(inner) = &boxed.0.inner else {
303 unreachable!()
304 };
305 assert!(
306 Arc::ptr_eq(inner, &stopper.inner),
307 "reuses the Stopper's Arc"
308 );
309 let sync = SyncStopper::new();
310 let boxed = BoxedStop::new(sync.clone());
311 assert_eq!(direct(&boxed.0), "dyn");
312 let StopTokenInner::Dyn(inner) = &boxed.0.inner else {
313 unreachable!()
314 };
315 assert_eq!(
316 Arc::as_ptr(inner).cast::<()>(),
317 Arc::as_ptr(&sync.inner).cast::<()>(),
318 "reuses the SyncStopper's Arc"
319 );
320 assert_eq!(direct(&BoxedStop::new(StopSource::new()).0), "dyn");
321 }
322
323 #[test]
324 #[allow(deprecated)] fn boxed_stop_and_stop_token_nest_without_wrapping() {
326 use crate::BoxedStop;
327 let token = StopToken::new(FnStop::new(|| false));
328 let StopTokenInner::Dyn(original) = &token.inner else {
329 unreachable!()
330 };
331 let boxed = BoxedStop::new(token.clone());
332 let again = BoxedStop::new(boxed);
333 let back = StopToken::new(again);
334 let StopTokenInner::Dyn(inner) = &back.inner else {
335 unreachable!()
336 };
337 assert!(Arc::ptr_eq(inner, original));
338 }
339
340 #[test]
341 #[allow(deprecated)] fn boxed_stop_sees_cancellation_through_every_path() {
343 use crate::{BoxedStop, SyncStopper};
344 let stopper = Stopper::new();
345 let sync = SyncStopper::new();
346 let boxed = [
347 BoxedStop::new(stopper.clone()),
348 BoxedStop::new(sync.clone()),
349 BoxedStop::new(BoxedStop::new(StopToken::new(stopper.clone()))),
350 ];
351 assert!(boxed.iter().all(|stop| stop.check().is_ok()));
352 stopper.cancel();
353 sync.cancel();
354 assert!(boxed.iter().all(|stop| stop.should_stop()));
355 assert_eq!(boxed[0].check(), Err(StopReason::Cancelled));
356 }
357
358 #[test]
359 fn from_unstoppable() {
360 let stop = StopToken::new(Unstoppable);
361 assert!(!stop.should_stop());
362 assert!(stop.check().is_ok());
363 }
364
365 #[test]
366 fn from_stopper() {
367 let stopper = Stopper::new();
368 let stop = StopToken::new(stopper.clone());
369
370 assert!(!stop.should_stop());
371
372 stopper.cancel();
373
374 assert!(stop.should_stop());
375 assert_eq!(stop.check(), Err(StopReason::Cancelled));
376 }
377
378 #[test]
379 fn clone_is_cheap() {
380 let stopper = Stopper::new();
381 let stop = StopToken::new(stopper.clone());
382 let stop2 = stop.clone();
383
384 stopper.cancel();
385
386 assert!(stop.should_stop());
388 assert!(stop2.should_stop());
389 }
390
391 #[cfg(feature = "std")]
392 #[test]
393 fn clone_send_to_thread() {
394 let stopper = Stopper::new();
395 let stop = StopToken::new(stopper.clone());
396
397 let handle = std::thread::spawn({
398 let stop = stop.clone();
399 move || stop.should_stop()
400 });
401
402 stopper.cancel();
403 let _ = handle.join().unwrap();
405 }
406
407 #[test]
408 fn is_send_sync() {
409 fn assert_send_sync<T: Send + Sync>() {}
410 assert_send_sync::<StopToken>();
411 }
412
413 #[test]
414 fn debug_format() {
415 let stop = StopToken::new(Unstoppable);
416 let debug = alloc::format!("{:?}", stop);
417 assert!(debug.contains("StopToken"));
418 }
419
420 #[test]
421 fn may_stop_delegates() {
422 assert!(!StopToken::new(Unstoppable).may_stop());
423 assert!(StopToken::new(Stopper::new()).may_stop());
424 }
425
426 #[test]
427 fn unstoppable_is_none_internally() {
428 let stop = StopToken::new(Unstoppable);
429 assert!(!stop.may_stop());
430 assert!(stop.check().is_ok());
431 }
432
433 #[test]
434 fn collapses_nested_dyn_stop() {
435 let inner = StopToken::new(Unstoppable);
436 let outer = StopToken::new(inner);
437 assert!(!outer.may_stop());
438 }
439
440 #[test]
441 fn collapses_nested_stopper() {
442 let stopper = Stopper::new();
443 let inner = StopToken::new(stopper.clone());
444 let outer = StopToken::new(inner.clone());
445
446 stopper.cancel();
448 assert!(outer.should_stop());
449 assert!(inner.should_stop());
450 }
451
452 #[test]
453 fn from_arc() {
454 let stopper = Arc::new(Stopper::new());
455 let cancel_handle = stopper.clone();
456 let stop = StopToken::from_arc(stopper);
457
458 assert!(!stop.should_stop());
459 cancel_handle.cancel();
460 assert!(stop.should_stop());
461 }
462
463 #[test]
464 fn from_non_clone_fn_stop() {
465 let flag = Arc::new(core::sync::atomic::AtomicBool::new(false));
467 let flag2 = flag.clone();
468 let stop = StopToken::new(FnStop::new(move || {
469 flag2.load(core::sync::atomic::Ordering::Relaxed)
470 }));
471
472 assert!(!stop.should_stop());
473
474 let stop2 = stop.clone();
476 flag.store(true, core::sync::atomic::Ordering::Relaxed);
477
478 assert!(stop.should_stop());
479 assert!(stop2.should_stop());
480 }
481
482 #[test]
483 fn avoids_monomorphization() {
484 fn process(stop: StopToken) -> bool {
485 stop.should_stop()
486 }
487
488 assert!(!process(StopToken::new(Unstoppable)));
489 assert!(!process(StopToken::new(StopSource::new())));
490 assert!(!process(StopToken::new(Stopper::new())));
491 }
492
493 #[test]
494 fn hot_loop_pattern() {
495 let stop = StopToken::new(Unstoppable);
496 for _ in 0..1000 {
497 assert!(stop.check().is_ok()); }
499 }
500
501 #[test]
502 fn from_stopper_zero_cost() {
503 let stopper = Stopper::new();
504 let cancel = stopper.clone();
505 let stop: StopToken = stopper.into(); assert!(!stop.should_stop());
508 cancel.cancel();
509 assert!(stop.should_stop()); }
511
512 #[test]
513 fn from_sync_stopper_reuses_its_arc_behind_the_vtable() {
514 let stopper = crate::SyncStopper::new();
515 let cancel = stopper.clone();
516 let stop: StopToken = stopper.into();
517 assert_eq!(direct(&stop), "dyn");
518 let StopTokenInner::Dyn(inner) = &stop.inner else {
519 unreachable!()
520 };
521 assert_eq!(
522 Arc::as_ptr(inner).cast::<()>(),
523 Arc::as_ptr(&cancel.inner).cast::<()>()
524 );
525
526 assert!(!stop.should_stop());
527 cancel.cancel();
528 assert!(stop.should_stop());
529 assert_eq!(stop.check(), Err(StopReason::Cancelled));
530 }
531
532 #[test]
533 fn new_stopper_flattens() {
534 let stopper = Stopper::new();
537 let cancel = stopper.clone();
538 let stop = StopToken::new(stopper);
539
540 cancel.cancel();
541 assert!(stop.should_stop());
542 }
543
544 #[test]
545 fn new_sync_stopper_flattens() {
546 let stopper = crate::SyncStopper::new();
547 let cancel = stopper.clone();
548 let stop = StopToken::new(stopper);
549
550 cancel.cancel();
551 assert!(stop.should_stop());
552 }
553
554 #[test]
555 fn from_arc_collapses_dynstop() {
556 let inner = StopToken::new(Stopper::new());
558 let arc = alloc::sync::Arc::new(inner);
559 let stop = StopToken::from_arc(arc);
560 assert!(!stop.should_stop());
561 }
562
563 #[test]
564 fn stopper_inner_debug() {
565 let stop = Stopper::new();
566 let debug = alloc::format!("{:?}", stop);
567 assert!(debug.contains("cancelled"));
568 }
569
570 #[test]
571 fn sync_stopper_inner_debug() {
572 let stop = crate::SyncStopper::new();
573 let debug = alloc::format!("{:?}", stop);
574 assert!(debug.contains("cancelled"));
575 }
576
577 #[test]
578 fn from_stopper_clone_shares_state() {
579 let stopper = Stopper::new();
580 let stop: StopToken = stopper.clone().into();
581 let stop2 = stop.clone(); stopper.cancel();
584 assert!(stop.should_stop());
586 assert!(stop2.should_stop());
587 }
588}