low_latency_data_structures/
mem.rs1use std::mem::MaybeUninit;
4
5use crate::shim::alloc;
6use crate::shim::cell::UnsafeCell;
7
8pub trait Allocator {
14 type Options;
17 type Allocation<T>: Allocation<T>;
19
20 fn allocate<T>(items: usize, options: Self::Options) -> Self::Allocation<T>;
26}
27
28pub trait Allocation<T> {
33 fn ptr(&self) -> *mut UnsafeCell<MaybeUninit<T>>;
36}
37
38#[cfg(test)]
39pub(crate) mod test_util {
40 use std::marker::PhantomData;
41
42 use super::*;
43
44 pub(crate) struct NeverAllocation<T> {
47 _t: PhantomData<T>,
48 }
49
50 pub(crate) struct NeverAllocator;
51
52 impl Allocator for NeverAllocator {
53 type Options = ();
54 type Allocation<T> = NeverAllocation<T>;
55
56 fn allocate<T>(_items: usize, _options: Self::Options) -> Self::Allocation<T> {
57 unreachable!("NeverAllocator is a stub for trait checks only");
58 }
59 }
60
61 impl<T> Allocation<T> for NeverAllocation<T> {
62 fn ptr(&self) -> *mut UnsafeCell<MaybeUninit<T>> {
63 unreachable!("NeverAllocation is a stub for trait checks only")
64 }
65 }
66}
67
68pub mod hugepages {
70 use super::*;
71
72 #[derive(Clone, Copy)]
74 pub enum HugepageSize {
75 H64KB,
77 H512KB,
79 H1MB,
81 H2MB,
83 H8MB,
85 H16MB,
87 H32MB,
89 H256MB,
91 H512MB,
93 }
94
95 impl HugepageSize {
96 fn as_usize_bytes(&self) -> usize {
97 match self {
98 HugepageSize::H64KB => 64 * 1024,
99 HugepageSize::H512KB => 512 * 1024,
100 HugepageSize::H1MB => 1024 * 1024,
101 HugepageSize::H2MB => 2 * 1024 * 1024,
102 HugepageSize::H8MB => 8 * 1024 * 1024,
103 HugepageSize::H16MB => 16 * 1024 * 1024,
104 HugepageSize::H32MB => 32 * 1024 * 1024,
105 HugepageSize::H256MB => 256 * 1024 * 1024,
106 HugepageSize::H512MB => 512 * 1024 * 1024,
107 }
108 }
109
110 fn as_mmap_size_flag(&self) -> libc::c_int {
111 match self {
112 HugepageSize::H64KB => libc::MAP_HUGE_64KB,
113 HugepageSize::H512KB => libc::MAP_HUGE_512KB,
114 HugepageSize::H1MB => libc::MAP_HUGE_1MB,
115 HugepageSize::H2MB => libc::MAP_HUGE_2MB,
116 HugepageSize::H8MB => libc::MAP_HUGE_8MB,
117 HugepageSize::H16MB => libc::MAP_HUGE_16MB,
118 HugepageSize::H32MB => libc::MAP_HUGE_32MB,
119 HugepageSize::H256MB => libc::MAP_HUGE_256MB,
120 HugepageSize::H512MB => libc::MAP_HUGE_512MB,
121 }
122 }
123 }
124
125 pub struct HugepageAllocator;
148
149 #[derive(bon::Builder)]
151 pub struct HugepageAllocatorOptions {
152 mlock: bool,
154 hugepage_size: HugepageSize,
156 }
157
158 impl Allocator for HugepageAllocator {
159 type Options = HugepageAllocatorOptions;
160 type Allocation<T> = HugepageAllocation<T>;
161
162 #[inline]
163 fn allocate<T>(items: usize, options: Self::Options) -> Self::Allocation<T> {
164 let layout =
165 alloc::Layout::array::<UnsafeCell<MaybeUninit<T>>>(items).unwrap_or_else(|_| {
166 panic!(
167 "Layout::array overflow: {items} x {} bytes exceeds isize::MAX",
168 std::mem::size_of::<UnsafeCell<MaybeUninit<T>>>()
169 )
170 });
171 let page_size = options.hugepage_size.as_usize_bytes();
172 let alloc_size = (layout.size() + page_size - 1) & !(page_size - 1);
174 let ptr = unsafe {
175 libc::mmap(
176 std::ptr::null_mut(),
177 alloc_size,
178 libc::PROT_READ | libc::PROT_WRITE,
179 libc::MAP_POPULATE
180 | libc::MAP_PRIVATE
181 | libc::MAP_ANONYMOUS
182 | libc::MAP_HUGETLB
183 | options.hugepage_size.as_mmap_size_flag(),
184 -1,
185 0,
186 )
187 };
188 assert_ne!(
189 ptr,
190 libc::MAP_FAILED,
191 "hugepage mmap failed: {}",
192 std::io::Error::last_os_error()
193 );
194 if options.mlock {
195 let rc = unsafe { libc::mlock(ptr.cast(), alloc_size) };
196 assert_ne!(
197 rc,
198 -1,
199 "mlock hugepage allocation failed: {}",
200 std::io::Error::last_os_error()
201 );
202 }
203 HugepageAllocation {
204 ptr: ptr as *mut UnsafeCell<MaybeUninit<T>>,
205 length: alloc_size,
206 }
207 }
208 }
209
210 pub struct HugepageAllocation<T> {
213 ptr: *mut UnsafeCell<MaybeUninit<T>>,
214 length: usize,
215 }
216
217 unsafe impl<T> Send for HugepageAllocation<T> where T: Send {}
218
219 impl<T> Drop for HugepageAllocation<T> {
220 fn drop(&mut self) {
221 let rc = unsafe { libc::munmap(self.ptr.cast(), self.length) };
222 assert_ne!(
223 rc,
224 -1,
225 "failed to munmap hugepage allocation: {}",
226 std::io::Error::last_os_error()
227 );
228 }
229 }
230
231 impl<T> Allocation<T> for HugepageAllocation<T> {
232 #[inline]
233 fn ptr(&self) -> *mut UnsafeCell<MaybeUninit<T>> {
234 self.ptr
235 }
236 }
237}
238
239pub mod global {
241 use super::*;
242
243 pub struct GlobalAllocator;
246
247 #[derive(bon::Builder)]
249 pub struct GlobalAllocatorOptions {
250 mlock: bool,
252 }
253
254 impl Allocator for GlobalAllocator {
255 type Options = GlobalAllocatorOptions;
256 type Allocation<T> = GlobalAllocation<T>;
257
258 #[inline]
259 fn allocate<T>(items: usize, options: Self::Options) -> Self::Allocation<T> {
260 let layout =
261 alloc::Layout::array::<UnsafeCell<MaybeUninit<T>>>(items).unwrap_or_else(|_| {
262 panic!(
263 "Layout::array overflow: {items} x {} bytes exceeds isize::MAX",
264 std::mem::size_of::<UnsafeCell<MaybeUninit<T>>>()
265 )
266 });
267 let ptr = unsafe { alloc::alloc(layout) };
268 if ptr.is_null() {
269 alloc::handle_alloc_error(layout);
270 }
271 if options.mlock {
272 let rc = unsafe { libc::mlock(ptr.cast(), layout.size()) };
273 assert_ne!(
274 rc,
275 -1,
276 "mlock global allocation failed: {}",
277 std::io::Error::last_os_error()
278 );
279 }
280 GlobalAllocation {
281 ptr: ptr.cast(),
282 layout,
283 }
284 }
285 }
286
287 pub struct GlobalAllocation<T> {
290 ptr: *mut UnsafeCell<MaybeUninit<T>>,
291 layout: alloc::Layout,
292 }
293
294 unsafe impl<T> Send for GlobalAllocation<T> where T: Send {}
295
296 impl<T> Drop for GlobalAllocation<T> {
297 fn drop(&mut self) {
298 unsafe { alloc::dealloc(self.ptr.cast(), self.layout) };
299 }
300 }
301
302 impl<T> Allocation<T> for GlobalAllocation<T> {
303 #[inline]
304 fn ptr(&self) -> *mut UnsafeCell<MaybeUninit<T>> {
305 self.ptr
306 }
307 }
308}
309
310#[cfg(test)]
311#[cfg(feature = "tests_loom")]
312pub(crate) mod loom {
313 use super::*;
314
315 pub(crate) struct LoomVecAllocator;
316
317 pub(crate) struct LoomVecAllocatorOptions;
318
319 impl Allocator for LoomVecAllocator {
320 type Options = LoomVecAllocatorOptions;
321 type Allocation<T> = LoomVecAllocation<T>;
322
323 #[inline]
324 fn allocate<T>(items: usize, _options: Self::Options) -> Self::Allocation<T> {
325 type RealT<T> = UnsafeCell<MaybeUninit<T>>;
326 let mut buffer: Vec<RealT<T>> = Vec::with_capacity(items);
327 for _ in 0..items {
328 buffer.push(UnsafeCell::new(MaybeUninit::uninit()));
329 }
330 let ptr = buffer.as_mut_ptr();
331 std::mem::forget(buffer);
332 LoomVecAllocation {
333 ptr,
334 capacity: items,
335 }
336 }
337 }
338
339 pub struct LoomVecAllocation<T> {
340 ptr: *mut UnsafeCell<MaybeUninit<T>>,
341 capacity: usize,
342 }
343
344 unsafe impl<T> Send for LoomVecAllocation<T> where T: Send {}
345
346 impl<T> Drop for LoomVecAllocation<T> {
347 fn drop(&mut self) {
348 unsafe { drop(Vec::from_raw_parts(self.ptr, self.capacity, self.capacity)) };
349 }
350 }
351
352 impl<T> Allocation<T> for LoomVecAllocation<T> {
353 #[inline]
354 fn ptr(&self) -> *mut UnsafeCell<MaybeUninit<T>> {
355 self.ptr
356 }
357 }
358}
359
360pub enum BackingAllocation {
362 Global,
364 Hugepages(hugepages::HugepageSize),
366}