1use std::sync::atomic::{AtomicU8, Ordering};
2use std::sync::Arc;
3
4const ACTIVE: u8 = 0;
5const CANCELLED: u8 = 1;
6const COMPLETED: u8 = 2;
7
8#[derive(Clone, Debug)]
11pub(crate) struct CancellationToken {
12 state: Arc<AtomicU8>,
13}
14
15impl CancellationToken {
16 pub(crate) fn new() -> Self {
17 Self {
18 state: Arc::new(AtomicU8::new(ACTIVE)),
19 }
20 }
21
22 pub(crate) fn cancel(&self) -> bool {
25 self.state
26 .compare_exchange(ACTIVE, CANCELLED, Ordering::AcqRel, Ordering::Acquire)
27 .is_ok()
28 }
29
30 pub(crate) fn is_cancelled(&self) -> bool {
31 self.state.load(Ordering::Acquire) == CANCELLED
32 }
33
34 pub(crate) fn try_complete(&self) -> bool {
36 self.state
37 .compare_exchange(ACTIVE, COMPLETED, Ordering::AcqRel, Ordering::Acquire)
38 .is_ok()
39 }
40}
41
42impl Default for CancellationToken {
43 fn default() -> Self {
44 Self::new()
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn completion_and_cancellation_have_one_linearization_winner() {
54 let completed = CancellationToken::new();
55 assert!(completed.try_complete());
56 completed.cancel();
57 assert!(!completed.is_cancelled());
58 assert!(!completed.try_complete());
59
60 let canceled = CancellationToken::new();
61 canceled.cancel();
62 assert!(canceled.is_cancelled());
63 assert!(!canceled.try_complete());
64 }
65}