Skip to main content

nblf_queue/
utils.rs

1/// cfg that disables Tagged64 slot based on architecture and feature flags.
2///
3/// Usage:
4/// ```rust
5/// use nblf_queue::cfg_atomic_tagged64;
6///
7/// cfg_atomic_tagged64! {
8///     use nblf_queue::core::slots::Tagged64;
9/// }
10/// ```
11#[macro_export]
12macro_rules! cfg_atomic_tagged64 {
13    ($($item:item)*) => {
14       $(
15           #[cfg(any(target_has_atomic = "64", feature = "atomic-fallback"))]
16            $item
17        )*
18    };
19}
20
21/// cfg that disables Tagged128 slot based on architecture and feature flags.
22///
23/// Usage:
24/// ```rust
25/// use nblf_queue::cfg_atomic_tagged128;
26///
27/// cfg_atomic_tagged128! {
28///     use nblf_queue::core::slots::Tagged128;
29/// }
30/// ```
31#[macro_export]
32macro_rules! cfg_atomic_tagged128 {
33    ($($item:item)*) => {
34        $(
35            #[cfg(any(target_has_atomic = "128", all(feature = "atomic-fallback", not(loom), not(shuttle))))]
36            $item
37        )*
38    };
39}
40
41pub(crate) use sealed::Sealed;
42
43#[cfg(all(not(shuttle), not(loom), not(feature = "std")))]
44const MAX_SPINLOOP: usize = 1024;
45
46pub(crate) struct Backoff {
47    #[cfg(all(not(shuttle), not(loom), not(feature = "std")))]
48    state: usize,
49}
50
51impl Backoff {
52    pub fn new() -> Self {
53        #[cfg(all(not(shuttle), not(loom), not(feature = "std")))]
54        return Self { state: 1 };
55        #[cfg(any(shuttle, loom, feature = "std"))]
56        return Self {};
57    }
58
59    pub fn backoff(&mut self) {
60        #[cfg(all(not(shuttle), not(loom), not(feature = "std")))]
61        {
62            for _ in 0..self.state {
63                crate::sync::hint::spin_loop();
64            }
65            self.state = (self.state * 2).min(MAX_SPINLOOP);
66        }
67        #[cfg(any(shuttle, loom, feature = "std"))]
68        crate::sync::thread::yield_now();
69    }
70}
71
72pub(crate) fn prev(i: usize, size: usize) -> usize {
73    (i + size - 1) % size
74}
75
76pub(crate) fn comp(i: usize, u: u64, j: usize, v: u64, w_max: u64) -> bool {
77    if u == v {
78        i < j
79    } else {
80        (v.wrapping_add(w_max).wrapping_sub(u)) % w_max < w_max / 2
81    }
82}
83
84pub(crate) mod sealed {
85    #[doc(hidden)]
86    pub trait Sealed {}
87}
88
89/// # Safety:
90/// width here is the bit-width taken up by the lower value
91/// we assume that lower is already properly truncated
92macro_rules! pack {
93    (($upper:expr, $lower:expr): $width:expr) => {
94        (($upper) << $width) | ($lower)
95    };
96}
97
98/// # Safety:
99/// width is the bit-width taken up by the lower value
100/// the value as produced by pack!() with the correct parameters
101macro_rules! unpack {
102    (($packed:expr): $width:expr) => {{
103        // make sure to evaluate passed exprs only once
104        let width = $width;
105        let packed_value = $packed;
106        let upper = packed_value >> width;
107        let lower = packed_value & ((1 << width) - 1);
108        (upper, lower)
109    }};
110}
111
112#[cfg(test)]
113pub(crate) fn sign_erase(ptr: u64) -> u64 {
114    ptr & ((1u64 << 48) - 1)
115}
116
117pub(crate) fn sign_extend(ptr: u64) -> u64 {
118    if ptr & (1u64 << 47) != 0 {
119        ptr | (!((1u64 << 48) - 1))
120    } else {
121        ptr
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    mod tagged_ptr {
130        use core::ptr::null;
131
132        use super::{sign_erase, sign_extend};
133
134        #[test]
135        fn into_tagged() {
136            let ptr = u64::MAX as *const u8;
137            let count = 0xDEAD;
138            let res = pack!((count, sign_erase(ptr as u64)): 48);
139            assert_eq!(res, 0xDEAD_FFFF_FFFF_FFFF);
140
141            let ptr2 = 0xDEAD_BEEF as *const u8;
142            let res = pack!((count, sign_erase(ptr2 as u64)): 48);
143            assert_eq!(res, 0xDEAD_0000_DEAD_BEEF);
144
145            let ptr: *const u8 = null();
146            assert_eq!(pack!((0, ptr as u64): 48), 0);
147        }
148
149        #[test]
150        fn from_tagged() {
151            let ptr = u64::MAX as *const u8;
152            let count = 0xDEAD;
153            let res = 0xDEAD_FFFF_FFFF_FFFF;
154
155            let (v1, v2) = unpack!((res): 48);
156
157            assert_eq!((v1, sign_extend(v2)), (count, ptr as u64));
158
159            let ptr2 = 0xDEAD_BEEF as *const u8;
160            let res = 0xDEAD_0000_DEAD_BEEF;
161
162            let (v1, v2) = unpack!((res): 48);
163
164            assert_eq!((v1, sign_extend(v2)), (count, ptr2 as u64));
165
166            let ptr: *const u8 = null();
167            assert_eq!(unpack!((0_u64): 48), (0, ptr as u64))
168        }
169
170        #[test]
171        fn tagged() {
172            let ptr = u64::MAX as *const u8;
173            let ptr2 = 0xDEAD_BEEF as *const u8;
174            let count = 0xDEAD;
175
176            let (v1, v2) = unpack!((pack!((count, sign_erase(ptr as u64)): 48)): 48);
177
178            assert_eq!((v1, sign_extend(v2)), (count, ptr as u64));
179
180            let (v1, v2) = unpack!((pack!((count, sign_erase(ptr2 as u64)): 48)): 48);
181
182            assert_eq!((v1, sign_extend(v2)), (count, ptr2 as u64));
183
184            let data = &4242;
185            let count = 42;
186            let ptr = pack!((count, data as *const i32 as u64): 48);
187            let (count_, data_): (_, u64) = unpack!((ptr): 48);
188            assert_eq!(count, count_);
189            // SAFETY:
190            // ptr to data or data was not modified, if components_as_tagged + from_tagged work as intended
191            assert_eq!(*data, unsafe { *(sign_extend(data_) as *const i32) });
192        }
193    }
194
195    mod dword {
196        use core::ptr::null;
197
198        #[test]
199        fn into_dword() {
200            let ptr = u64::MAX as *const u8;
201            let count = 0xDEAD;
202            let res = pack!((count, ptr as u128): 64);
203            assert_eq!(res, 0xDEAD_u128 << 64 | u64::MAX as u128);
204
205            let ptr2 = 0xDEAD_BEEF as *const u8;
206            let res = pack!((count, ptr2 as u128): 64);
207            assert_eq!(res, 0xDEAD_u128 << 64 | 0xDEAD_BEEF_u128);
208
209            let ptr: *const u8 = null();
210            assert_eq!(pack!((0, ptr as u128): 64), 0);
211        }
212
213        #[test]
214        fn from_dword() {
215            let ptr = u64::MAX as *const u8;
216            let count = 0xDEAD;
217            let res = 0xDEAD_u128 << 64 | u64::MAX as u128;
218
219            assert_eq!(unpack!((res): 64), (count, ptr as u128));
220
221            let ptr2 = 0xDEAD_BEEF as *const u8;
222            let res = 0xDEAD_u128 << 64 | 0xDEAD_BEEF_u128;
223
224            assert_eq!(unpack!((res): 64), (count, ptr2 as u128));
225
226            let ptr: *const u8 = null();
227            assert_eq!(unpack!((0_u128): 64), (0, ptr as u128));
228        }
229
230        #[test]
231        fn dword() {
232            let ptr = u64::MAX as *const u8;
233            let ptr2 = 0xDEAD_BEEF as *const u8;
234            let count = 0xDEAD;
235
236            assert_eq!(
237                unpack!((pack!((count, ptr as u128): 64)): 64),
238                (count, ptr as u128)
239            );
240            assert_eq!(
241                unpack!((pack!((count, ptr2 as u128): 64)): 64),
242                (count, ptr2 as u128)
243            );
244
245            let data = &4242;
246            let count = 42;
247            let val = pack!((count, data as *const i32 as *const u8 as u128): 64);
248            let (count_, data_): (_, u128) = unpack!((val): 64);
249            assert_eq!(count, count_);
250            // Safety:
251            // ptr to data or data was not modified, if components_as_tagged + from_tagged work as intended
252            assert_eq!(unsafe { *(data_ as *const i32) }, *data);
253        }
254    }
255
256    #[test]
257    fn prev_() {
258        assert_eq!(prev(9, 10), 8);
259        assert_eq!(prev(0, 5), 4);
260    }
261
262    #[test]
263    fn comp_() {
264        // cells are part of the same round,
265        // cell i is before j, if i < j
266        assert!(comp(0, 0, 1, 0, u16::MAX as u64 + 1));
267        assert!(!comp(1, 1, 0, 1, u16::MAX as u64 + 1));
268
269        // cells are part of different rounds,
270        // cell i is before cell j, if its count is "1 less" than js
271        assert!(comp(0, 1, 1, 2, u16::MAX as u64 + 1));
272        assert!(!comp(0, 1, 1, 0, u16::MAX as u64 + 1));
273        assert!(comp(0, u16::MAX as u64, 1, 0, u16::MAX as u64 + 1));
274    }
275}