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