maniac_runtime/utils/
padded.rs

1use core::fmt;
2use core::ops::{Deref, DerefMut};
3
4use std::future::IntoFuture;
5
6/// Pads and aligns a value to the length of a cache line.
7///
8/// In concurrent programming, sometimes it is desirable to make sure commonly accessed pieces of
9/// data are not placed into the same cache line. Updating an atomic value invalidates the whole
10/// cache line it belongs to, which makes the next access to the same cache line slower for other
11/// CPU cores. Use `CachePadded` to ensure updating one piece of data doesn't invalidate other
12/// cached data.
13///
14/// # Size and alignment
15///
16/// Cache lines are assumed to be N bytes long, depending on the architecture:
17///
18/// * On x86-64, aarch64, and powerpc64, N = 128.
19/// * On arm, mips, mips64, sparc, and hexagon, N = 32.
20/// * On m68k, N = 16.
21/// * On s390x, N = 256.
22/// * On all others, N = 64.
23///
24/// Note that N is just a reasonable guess and is not guaranteed to match the actual cache line
25/// length of the machine the program is running on. On modern Intel architectures, spatial
26/// prefetcher is pulling pairs of 64-byte cache lines at a time, so we pessimistically assume that
27/// cache lines are 128 bytes long.
28///
29/// The size of `CachePadded<T>` is the smallest multiple of N bytes large enough to accommodate
30/// a value of type `T`.
31///
32/// The alignment of `CachePadded<T>` is the maximum of N bytes and the alignment of `T`.
33#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
34#[cfg_attr(any(target_arch = "x86_64",), repr(align(64)))]
35#[cfg_attr(
36    any(
37        target_arch = "aarch64",
38        target_arch = "arm64ec",
39        target_arch = "powerpc64",
40    ),
41    repr(align(128))
42)]
43#[cfg_attr(
44    any(
45        target_arch = "arm",
46        target_arch = "mips",
47        target_arch = "mips32r6",
48        target_arch = "mips64",
49        target_arch = "mips64r6",
50        target_arch = "sparc",
51        target_arch = "hexagon",
52    ),
53    repr(align(32))
54)]
55#[cfg_attr(target_arch = "m68k", repr(align(16)))]
56#[cfg_attr(target_arch = "s390x", repr(align(256)))]
57#[cfg_attr(
58    not(any(
59        target_arch = "x86_64",
60        target_arch = "aarch64",
61        target_arch = "arm64ec",
62        target_arch = "powerpc64",
63        target_arch = "arm",
64        target_arch = "mips",
65        target_arch = "mips32r6",
66        target_arch = "mips64",
67        target_arch = "mips64r6",
68        target_arch = "sparc",
69        target_arch = "hexagon",
70        target_arch = "m68k",
71        target_arch = "s390x",
72    )),
73    repr(align(64))
74)]
75pub struct CachePadded<T> {
76    value: T,
77}
78
79unsafe impl<T: Send> Send for CachePadded<T> {}
80unsafe impl<T: Sync> Sync for CachePadded<T> {}
81
82impl<T> CachePadded<T> {
83    /// Pads and aligns a value to the length of a cache line.
84    pub const fn new(t: T) -> CachePadded<T> {
85        CachePadded::<T> { value: t }
86    }
87
88    /// Returns the inner value.
89    pub fn into_inner(self) -> T {
90        self.value
91    }
92}
93
94impl<T> Deref for CachePadded<T> {
95    type Target = T;
96
97    fn deref(&self) -> &T {
98        &self.value
99    }
100}
101
102impl<T> DerefMut for CachePadded<T> {
103    fn deref_mut(&mut self) -> &mut T {
104        &mut self.value
105    }
106}
107
108impl<T: fmt::Debug> fmt::Debug for CachePadded<T> {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        f.debug_struct("CachePadded")
111            .field("value", &self.value)
112            .finish()
113    }
114}
115
116impl<T> From<T> for CachePadded<T> {
117    fn from(t: T) -> Self {
118        CachePadded::new(t)
119    }
120}
121
122impl<T: fmt::Display> fmt::Display for CachePadded<T> {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        fmt::Display::fmt(&self.value, f)
125    }
126}