maitake_sync/util/
cache_pad.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use core::{
    fmt,
    ops::{Deref, DerefMut},
};

pub use self::inner::CachePadded;

/// When configured not to pad to cache alignment, just provide a no-op wrapper struct
/// This feature is useful for platforms with no data cache, such as many Cortex-M
/// targets.
#[cfg(feature = "no-cache-pad")]
mod inner {
    /// Aligns the wrapped value to the size of a cache line.
    ///
    /// This is used to avoid [false sharing] for values that may be
    /// accessed concurrently.
    ///
    /// # Size/Alignment
    ///
    /// The size and alignment of this type depends on the target architecture,
    /// and on whether or not the `no-cache-pad` feature flag is enabled.
    ///
    /// When the `no-cache-pad` crate feature flag is enabled, this is simply a
    /// no-op wrapper struct. This is intended for use on useful for platforms
    /// with no data cache, such as many Cortex-M targets.
    ///
    /// In other cases, this type is always aligned to the size of a cache line,
    /// based on the target architecture. On `x86_64`/`aarch64`, a cache line is
    /// 128 bytes. On all other targets, a cache line is assumed to 64 bytes
    /// long. This type's size will always be a multiple of the cache line size;
    /// if the wrapped type is longer than the alignment of a cache line, then
    /// this type will be padded to multiple cache lines.
    ///
    /// [false sharing]: https://en.wikipedia.org/wiki/False_sharing
    #[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
    pub struct CachePadded<T>(pub(super) T);
}

/// When not inhibited, determine cache alignment based on target architecture.
/// Align to 128 bytes on 64-bit x86/ARM targets, otherwise align to 64 bytes.
#[cfg(not(feature = "no-cache-pad"))]
mod inner {
    /// Aligns the wrapped value to the size of a cache line.
    ///
    /// This is used to avoid [false sharing] for values that may be
    /// accessed concurrently.
    ///
    /// # Size/Alignment
    ///
    /// The size and alignment of this type depends on the target architecture,
    /// and on whether or not the `no-cache-pad` feature flag is enabled.
    ///
    /// When the `no-cache-pad` crate feature flag is enabled, this is simply a
    /// no-op wrapper struct. This is intended for use on useful for platforms
    /// with no data cache, such as many Cortex-M targets.
    ///
    /// In other cases, this type is always aligned to the size of a cache line,
    /// based on the target architecture. On `x86_64`/`aarch64`, a cache line is
    /// 128 bytes. On all other targets, a cache line is assumed to 64 bytes
    /// long. This type's size will always be a multiple of the cache line size;
    /// if the wrapped type is longer than the alignment of a cache line, then
    /// this type will be padded to multiple cache lines.
    ///
    /// [false sharing]: https://en.wikipedia.org/wiki/False_sharing
    #[cfg_attr(any(target_arch = "x86_64", target_arch = "aarch64"), repr(align(128)))]
    #[cfg_attr(
        not(any(target_arch = "x86_64", target_arch = "aarch64")),
        repr(align(64))
    )]
    #[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
    pub struct CachePadded<T>(pub(super) T);
}

// === impl CachePadded ===

impl<T> CachePadded<T> {
    /// Pads `value` to the length of a cache line.
    pub const fn new(value: T) -> Self {
        Self(value)
    }

    /// Unwraps the inner value and returns it.
    pub fn into_inner(self) -> T {
        self.0
    }
}

impl<T> Deref for CachePadded<T> {
    type Target = T;

    #[inline]
    fn deref(&self) -> &T {
        &self.0
    }
}

impl<T> DerefMut for CachePadded<T> {
    #[inline]
    fn deref_mut(&mut self) -> &mut T {
        &mut self.0
    }
}

impl<T: fmt::Debug> fmt::Debug for CachePadded<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}