turbo_tasks/
no_move_vec.rs1use std::{
2 ptr::null_mut,
3 slice::from_raw_parts_mut,
4 sync::{
5 Mutex,
6 atomic::{AtomicPtr, Ordering},
7 },
8};
9
10const BUCKETS: usize = (usize::BITS + 1) as usize;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15#[repr(u8)]
16enum COption<T> {
17 None = 0,
24 Some(T),
25}
26
27impl<T> Default for COption<T> {
28 fn default() -> Self {
29 Self::None
30 }
31}
32
33impl<T> COption<T> {
34 fn new_none_slice(size: usize) -> Box<[Self]> {
36 let slice = Box::<[COption<T>]>::new_zeroed_slice(size);
37 unsafe { slice.assume_init() }
40 }
41
42 fn as_option_ref(&self) -> Option<&T> {
44 match self {
45 COption::None => None,
46 COption::Some(t) => Some(t),
47 }
48 }
49}
50
51pub struct NoMoveVec<T, const INITIAL_CAPACITY_BITS: u32 = 6> {
52 buckets: [(AtomicPtr<COption<T>>, Mutex<()>); BUCKETS],
53}
54
55fn get_bucket_index<const INITIAL_CAPACITY_BITS: u32>(idx: usize) -> u32 {
56 (usize::BITS - idx.leading_zeros()).saturating_sub(INITIAL_CAPACITY_BITS)
57}
58
59fn get_bucket_size<const INITIAL_CAPACITY_BITS: u32>(bucket_index: u32) -> usize {
60 if bucket_index != 0 {
61 1 << (bucket_index + INITIAL_CAPACITY_BITS - 1)
62 } else {
63 1 << INITIAL_CAPACITY_BITS
64 }
65}
66
67fn get_index_in_bucket<const INITIAL_CAPACITY_BITS: u32>(idx: usize, bucket_index: u32) -> usize {
68 if bucket_index != 0 {
69 idx ^ (1 << (bucket_index + INITIAL_CAPACITY_BITS - 1))
70 } else {
71 idx
72 }
73}
74
75fn allocate_bucket<const INITIAL_CAPACITY_BITS: u32, T>(bucket_index: u32) -> *mut COption<T> {
77 let size = get_bucket_size::<INITIAL_CAPACITY_BITS>(bucket_index);
78 let slice = COption::<T>::new_none_slice(size);
79 Box::into_raw(slice) as *mut COption<T>
80}
81
82impl<T, const INITIAL_CAPACITY_BITS: u32> Default for NoMoveVec<T, INITIAL_CAPACITY_BITS> {
83 fn default() -> Self {
84 Self::new()
85 }
86}
87
88impl<T, const INITIAL_CAPACITY_BITS: u32> NoMoveVec<T, INITIAL_CAPACITY_BITS> {
89 pub fn new() -> Self {
90 let mut buckets = [null_mut(); BUCKETS];
91 buckets[0] = allocate_bucket::<INITIAL_CAPACITY_BITS, T>(0);
92 let buckets = buckets.map(|p| (AtomicPtr::new(p), Mutex::new(())));
93 NoMoveVec { buckets }
94 }
95
96 pub fn get(&self, idx: usize) -> Option<&T> {
97 let bucket_idx = get_bucket_index::<INITIAL_CAPACITY_BITS>(idx);
98 let bucket_ptr = unsafe { self.buckets.get_unchecked(bucket_idx as usize) }
99 .0
100 .load(Ordering::Acquire);
101 if bucket_ptr.is_null() {
102 return None;
103 }
104 let index = get_index_in_bucket::<INITIAL_CAPACITY_BITS>(idx, bucket_idx);
105 unsafe { &*bucket_ptr.add(index) }.as_option_ref()
106 }
107
108 pub unsafe fn insert(&self, idx: usize, value: T) -> &T {
111 let bucket_idx = get_bucket_index::<INITIAL_CAPACITY_BITS>(idx);
112 let bucket = unsafe { self.buckets.get_unchecked(bucket_idx as usize) };
113 let mut bucket_ptr = bucket.0.load(Ordering::Relaxed);
116 if bucket_ptr.is_null() {
117 bucket_ptr = bucket.0.load(Ordering::Acquire);
118 if bucket_ptr.is_null() {
119 let lock = bucket.1.lock();
120 let guarded_bucket_ptr = bucket.0.load(Ordering::Acquire);
121 if guarded_bucket_ptr.is_null() {
122 let new_bucket = allocate_bucket::<INITIAL_CAPACITY_BITS, T>(bucket_idx);
123 bucket_ptr = match bucket.0.compare_exchange(
124 null_mut(),
125 new_bucket,
126 Ordering::AcqRel,
127 Ordering::Relaxed,
128 ) {
129 Ok(_) => new_bucket,
130 Err(current_bucket) => {
131 drop(unsafe { Box::from_raw(new_bucket) });
132 current_bucket
133 }
134 };
135 drop(lock);
136 } else {
137 bucket_ptr = guarded_bucket_ptr;
138 }
139 }
140 }
141 let index = get_index_in_bucket::<INITIAL_CAPACITY_BITS>(idx, bucket_idx);
142 let item = unsafe { &mut *bucket_ptr.add(index) };
143 *item = COption::Some(value);
144 bucket.0.store(bucket_ptr, Ordering::Release);
146 item.as_option_ref().unwrap()
147 }
148}
149
150impl<T, const INITIAL_CAPACITY_BITS: u32> Drop for NoMoveVec<T, INITIAL_CAPACITY_BITS> {
151 fn drop(&mut self) {
152 for (bucket_index, (bucket, _)) in self.buckets.iter_mut().enumerate() {
153 if bucket_index < (usize::BITS + 1 - INITIAL_CAPACITY_BITS) as usize {
154 let bucket_size = get_bucket_size::<INITIAL_CAPACITY_BITS>(bucket_index as u32);
155 let bucket_ptr = *bucket.get_mut();
156
157 if !bucket_ptr.is_null() {
158 drop(unsafe { Box::from_raw(from_raw_parts_mut(bucket_ptr, bucket_size)) });
159 }
160 }
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::NoMoveVec;
168
169 #[test]
170 fn basic_operations() {
171 let v = NoMoveVec::<(usize, usize)>::new();
172 assert_eq!(v.get(0), None);
173 assert_eq!(v.get(1), None);
174 assert_eq!(v.get(8), None);
175 assert_eq!(v.get(9), None);
176 assert_eq!(v.get(15), None);
177 assert_eq!(v.get(16), None);
178 assert_eq!(v.get(100), None);
179 assert_eq!(v.get(1000), None);
180
181 for i in 0..1000 {
182 unsafe {
183 v.insert(i, (i, i));
184 }
185 assert_eq!(v.get(i), Some(&(i, i)));
186 }
187 for i in 0..1000 {
188 assert_eq!(v.get(i), Some(&(i, i)));
189 }
190 assert_eq!(v.get(1001), None);
191
192 unsafe {
193 v.insert(1000000, (0, 0));
194 }
195 assert_eq!(v.get(1000000), Some(&(0, 0)));
196 assert_eq!(v.get(10000), None);
197 }
198}