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)))]
44const MAX_SPINLOOP: usize = 1024;
45
46pub(crate) struct Backoff {
47    #[cfg(all(not(shuttle), not(loom)))]
48    state: usize,
49}
50
51impl Backoff {
52    pub(crate) fn new() -> Self {
53        #[cfg(all(not(shuttle), not(loom)))]
54        return Self { state: 1 };
55        #[cfg(any(shuttle, loom))]
56        return Self {};
57    }
58
59    pub(crate) fn backoff(&mut self) {
60        #[cfg(all(not(shuttle), not(loom)))]
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))]
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    #[allow(unnameable_types)]
87    pub trait Sealed {}
88}
89
90/// # Safety:
91/// width here is the bit-width taken up by the lower value
92/// we assume that lower is already properly truncated
93macro_rules! pack {
94    (($upper:expr, $lower:expr): $width:expr) => {
95        (($upper) << $width) | ($lower)
96    };
97}
98
99/// # Safety:
100/// width is the bit-width taken up by the lower value
101/// the value as produced by pack!() with the correct parameters
102macro_rules! unpack {
103    (($packed:expr): $width:expr) => {{
104        // make sure to evaluate passed exprs only once
105        let width = $width;
106        let packed_value = $packed;
107        let upper = packed_value >> width;
108        let lower = packed_value & ((1 << width) - 1);
109        (upper, lower)
110    }};
111}
112
113#[cfg(test)]
114pub(crate) fn sign_erase(ptr: u64) -> u64 {
115    ptr & ((1u64 << 48) - 1)
116}
117
118pub(crate) fn sign_extend(ptr: u64) -> u64 {
119    if ptr & (1u64 << 47) != 0 {
120        ptr | (!((1u64 << 48) - 1))
121    } else {
122        ptr
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    mod tagged_ptr {
131        use core::ptr::null;
132
133        use super::{sign_erase, sign_extend};
134
135        #[test]
136        fn into_tagged() {
137            let ptr = u64::MAX as *const u8;
138            let count = 0xDEAD;
139            let res = pack!((count, sign_erase(ptr as u64)): 48);
140            assert_eq!(res, 0xDEAD_FFFF_FFFF_FFFF);
141
142            let ptr2 = 0xDEAD_BEEF as *const u8;
143            let res = pack!((count, sign_erase(ptr2 as u64)): 48);
144            assert_eq!(res, 0xDEAD_0000_DEAD_BEEF);
145
146            let ptr: *const u8 = null();
147            assert_eq!(pack!((0, ptr as u64): 48), 0);
148        }
149
150        #[test]
151        fn from_tagged() {
152            let ptr = u64::MAX as *const u8;
153            let count = 0xDEAD;
154            let res = 0xDEAD_FFFF_FFFF_FFFF;
155
156            let (v1, v2) = unpack!((res): 48);
157
158            assert_eq!((v1, sign_extend(v2)), (count, ptr as u64));
159
160            let ptr2 = 0xDEAD_BEEF as *const u8;
161            let res = 0xDEAD_0000_DEAD_BEEF;
162
163            let (v1, v2) = unpack!((res): 48);
164
165            assert_eq!((v1, sign_extend(v2)), (count, ptr2 as u64));
166
167            let ptr: *const u8 = null();
168            assert_eq!(unpack!((0_u64): 48), (0, ptr as u64));
169        }
170
171        #[test]
172        fn tagged() {
173            let ptr = u64::MAX as *const u8;
174            let ptr2 = 0xDEAD_BEEF as *const u8;
175            let count = 0xDEAD;
176
177            let (v1, v2) = unpack!((pack!((count, sign_erase(ptr as u64)): 48)): 48);
178
179            assert_eq!((v1, sign_extend(v2)), (count, ptr as u64));
180
181            let (v1, v2) = unpack!((pack!((count, sign_erase(ptr2 as u64)): 48)): 48);
182
183            assert_eq!((v1, sign_extend(v2)), (count, ptr2 as u64));
184
185            let data = &4242;
186            let count = 42;
187            let ptr = pack!((count, data as *const i32 as u64): 48);
188            let (count_, data_): (_, u64) = unpack!((ptr): 48);
189            assert_eq!(count, count_);
190            // SAFETY:
191            // ptr to data or data was not modified, if components_as_tagged + from_tagged work as intended
192            assert_eq!(*data, unsafe { *(sign_extend(data_) as *const i32) });
193        }
194    }
195
196    mod dword {
197        use core::ptr::null;
198
199        #[test]
200        fn into_dword() {
201            let ptr = u64::MAX as *const u8;
202            let count = 0xDEAD;
203            let res = pack!((count, ptr as u128): 64);
204            assert_eq!(res, 0xDEAD_u128 << 64 | u64::MAX as u128);
205
206            let ptr2 = 0xDEAD_BEEF as *const u8;
207            let res = pack!((count, ptr2 as u128): 64);
208            assert_eq!(res, 0xDEAD_u128 << 64 | 0xDEAD_BEEF_u128);
209
210            let ptr: *const u8 = null();
211            assert_eq!(pack!((0, ptr as u128): 64), 0);
212        }
213
214        #[test]
215        fn from_dword() {
216            let ptr = u64::MAX as *const u8;
217            let count = 0xDEAD;
218            let res = 0xDEAD_u128 << 64 | u64::MAX as u128;
219
220            assert_eq!(unpack!((res): 64), (count, ptr as u128));
221
222            let ptr2 = 0xDEAD_BEEF as *const u8;
223            let res = 0xDEAD_u128 << 64 | 0xDEAD_BEEF_u128;
224
225            assert_eq!(unpack!((res): 64), (count, ptr2 as u128));
226
227            let ptr: *const u8 = null();
228            assert_eq!(unpack!((0_u128): 64), (0, ptr as u128));
229        }
230
231        #[test]
232        fn dword() {
233            let ptr = u64::MAX as *const u8;
234            let ptr2 = 0xDEAD_BEEF as *const u8;
235            let count = 0xDEAD;
236
237            assert_eq!(
238                unpack!((pack!((count, ptr as u128): 64)): 64),
239                (count, ptr as u128)
240            );
241            assert_eq!(
242                unpack!((pack!((count, ptr2 as u128): 64)): 64),
243                (count, ptr2 as u128)
244            );
245
246            let data = &4242;
247            let count = 42;
248            let val = pack!((count, (data as *const i32).cast::<u8>() as u128): 64);
249            let (count_, data_): (_, u128) = unpack!((val): 64);
250            assert_eq!(count, count_);
251            // Safety:
252            // ptr to data or data was not modified, if components_as_tagged + from_tagged work as intended
253            assert_eq!(unsafe { *(data_ as *const i32) }, *data);
254        }
255    }
256
257    #[test]
258    fn prev_() {
259        assert_eq!(prev(9, 10), 8);
260        assert_eq!(prev(0, 5), 4);
261    }
262
263    #[test]
264    fn comp_() {
265        // cells are part of the same round,
266        // cell i is before j, if i < j
267        assert!(comp(0, 0, 1, 0, u16::MAX as u64 + 1));
268        assert!(!comp(1, 1, 0, 1, u16::MAX as u64 + 1));
269
270        // cells are part of different rounds,
271        // cell i is before cell j, if its count is "1 less" than js
272        assert!(comp(0, 1, 1, 2, u16::MAX as u64 + 1));
273        assert!(!comp(0, 1, 1, 0, u16::MAX as u64 + 1));
274        assert!(comp(0, u16::MAX as u64, 1, 0, u16::MAX as u64 + 1));
275    }
276}