Skip to main content

qubit_cas/fast/
fast_cas_policy.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Retry policy for fast compare-and-swap operations.
11
12/// Retry policy used by [`crate::FastCas`] when compare-and-swap loses a race.
13#[derive(Debug, Clone, Copy, Eq, PartialEq)]
14pub enum FastCasPolicy {
15    /// Try the operation once and return conflict on the first failed CAS.
16    Once,
17
18    /// Retry immediately until CAS succeeds, the operation aborts, or the
19    /// attempt budget is exhausted.
20    Spin {
21        /// Maximum number of CAS attempts, including the first attempt.
22        max_attempts: u32,
23    },
24
25    /// Spin for a short prefix, then yield between retries until the attempt
26    /// budget is exhausted.
27    SpinYield {
28        /// Number of attempts to run without yielding.
29        spin_attempts: u32,
30
31        /// Maximum number of CAS attempts, including the first attempt.
32        max_attempts: u32,
33    },
34}
35
36impl FastCasPolicy {
37    /// Creates a policy that performs one CAS attempt.
38    ///
39    /// # Returns
40    /// A single-attempt policy.
41    #[inline]
42    pub const fn once() -> Self {
43        Self::Once
44    }
45
46    /// Creates an immediate-spin retry policy.
47    ///
48    /// # Parameters
49    /// - `max_attempts`: Maximum number of CAS attempts. Zero is normalized to
50    ///   one attempt.
51    ///
52    /// # Returns
53    /// A spin policy with a non-zero attempt budget.
54    #[inline]
55    pub const fn spin(max_attempts: u32) -> Self {
56        Self::Spin {
57            max_attempts: normalize_attempts(max_attempts),
58        }
59    }
60
61    /// Creates a spin-then-yield retry policy.
62    ///
63    /// # Parameters
64    /// - `spin_attempts`: Number of attempts to run before yielding.
65    /// - `max_attempts`: Maximum number of CAS attempts. Zero is normalized to
66    ///   one attempt.
67    ///
68    /// # Returns
69    /// A spin-yield policy with a non-zero attempt budget.
70    #[inline]
71    pub const fn spin_yield(spin_attempts: u32, max_attempts: u32) -> Self {
72        let max_attempts = normalize_attempts(max_attempts);
73        let spin_attempts = if spin_attempts < max_attempts {
74            spin_attempts
75        } else {
76            max_attempts
77        };
78        Self::SpinYield {
79            spin_attempts,
80            max_attempts,
81        }
82    }
83
84    /// Returns the maximum number of attempts allowed by this policy.
85    ///
86    /// # Returns
87    /// A non-zero attempt count.
88    #[inline]
89    pub const fn max_attempts(self) -> u32 {
90        match self {
91            Self::Once => 1,
92            Self::Spin { max_attempts } => normalize_attempts(max_attempts),
93            Self::SpinYield { max_attempts, .. } => normalize_attempts(max_attempts),
94        }
95    }
96
97    /// Returns whether the caller should yield before the next attempt.
98    ///
99    /// # Parameters
100    /// - `next_attempt`: One-based number of the next attempt to execute.
101    ///
102    /// # Returns
103    /// `true` when this policy should yield before `next_attempt`.
104    #[inline]
105    pub const fn should_yield_before(self, next_attempt: u32) -> bool {
106        match self {
107            Self::SpinYield { spin_attempts, .. } => next_attempt > spin_attempts,
108            Self::Once | Self::Spin { .. } => false,
109        }
110    }
111}
112
113/// Normalizes an attempt budget so every policy can execute at least once.
114///
115/// # Parameters
116/// - `attempts`: Requested attempt budget.
117///
118/// # Returns
119/// `attempts` when non-zero, otherwise `1`.
120#[inline]
121const fn normalize_attempts(attempts: u32) -> u32 {
122    if attempts == 0 { 1 } else { attempts }
123}