1
- use std:: marker;
2
- use std:: cell:: UnsafeCell ;
3
1
use std:: fmt;
4
2
use std:: mem;
5
- use std:: ptr;
6
3
use std:: ops:: { Deref , DerefMut } ;
4
+ use std:: ptr;
7
5
8
- // For now, treat this as an arch-independent constant.
9
- const CACHE_LINE : usize = 32 ;
10
-
11
- #[ cfg_attr( feature = "nightly" , repr( simd) ) ]
12
- #[ derive( Debug ) ]
13
- struct Padding ( u64 , u64 , u64 , u64 ) ;
14
-
15
- /// Pad `T` to the length of a cacheline.
16
- ///
17
- /// Sometimes concurrent programming requires a piece of data to be padded out
18
- /// to the size of a cacheline to avoid "false sharing": cachelines being
19
- /// invalidated due to unrelated concurrent activity. Use the `CachePadded` type
20
- /// when you want to *avoid* cache locality.
21
- ///
22
- /// At the moment, cache lines are assumed to be 32 * sizeof(usize) on all
23
- /// architectures.
24
- ///
25
- /// **Warning**: the wrapped data is never dropped; move out using `ptr::read`
26
- /// if you need to run dtors.
27
- pub struct CachePadded < T > {
28
- data : UnsafeCell < [ usize ; CACHE_LINE ] > ,
29
- _marker : ( [ Padding ; 0 ] , marker:: PhantomData < T > ) ,
30
- }
6
+ cfg_if ! {
7
+ if #[ cfg( feature = "nightly" ) ] {
8
+ #[ derive( Clone ) ]
9
+ #[ repr( align( 64 ) ) ]
10
+ struct Inner <T > {
11
+ value: T ,
12
+ }
31
13
32
- impl < T > fmt:: Debug for CachePadded < T > {
33
- fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
34
- write ! ( f, "CachePadded {{ ... }}" )
35
- }
36
- }
14
+ impl <T > Deref for Inner <T > {
15
+ type Target = T ;
37
16
38
- unsafe impl < T : Send > Send for CachePadded < T > { }
39
- unsafe impl < T : Sync > Sync for CachePadded < T > { }
17
+ fn deref( & self ) -> & T {
18
+ & self . value
19
+ }
20
+ }
40
21
41
- /// Types for which `mem::zeroed()` is safe.
42
- ///
43
- /// If a type `T: ZerosValid`, then a sequence of zeros the size of `T` must be
44
- /// a valid member of the type `T`.
45
- pub unsafe trait ZerosValid { }
22
+ impl <T > DerefMut for Inner <T > {
23
+ fn deref_mut( & mut self ) -> & mut T {
24
+ & mut self . value
25
+ }
26
+ }
27
+ } else {
28
+ use std:: marker:: PhantomData ;
46
29
47
- #[ cfg( feature = "nightly" ) ]
48
- unsafe impl ZerosValid for .. { }
30
+ #[ derive( Clone ) ]
31
+ struct Inner <T > {
32
+ bytes: [ u8 ; 64 ] ,
49
33
50
- macro_rules! zeros_valid { ( $( $T: ty ) * ) => ( $(
51
- unsafe impl ZerosValid for $T { }
52
- ) * ) }
34
+ /// `[T; 0]` ensures correct alignment.
35
+ /// `PhantomData<T>` signals that `CachePadded<T>` contains a `T`.
36
+ _marker: ( [ T ; 0 ] , PhantomData <T >) ,
37
+ }
53
38
54
- zeros_valid ! ( u8 u16 u32 u64 usize ) ;
55
- zeros_valid ! ( i8 i16 i32 i64 isize ) ;
39
+ impl < T > Deref for Inner < T > {
40
+ type Target = T ;
56
41
57
- unsafe impl ZerosValid for :: std:: sync:: atomic:: AtomicUsize { }
58
- unsafe impl < T > ZerosValid for :: std:: sync:: atomic:: AtomicPtr < T > { }
42
+ fn deref( & self ) -> & T {
43
+ unsafe { & * ( self . bytes. as_ptr( ) as * const T ) }
44
+ }
45
+ }
59
46
60
- impl < T : ZerosValid > CachePadded < T > {
61
- /// A const fn equivalent to mem::zeroed().
62
- #[ cfg( not( feature = "nightly" ) ) ]
63
- pub fn zeroed ( ) -> CachePadded < T > {
64
- CachePadded {
65
- data : UnsafeCell :: new ( [ 0 ; CACHE_LINE ] ) ,
66
- _marker : ( [ ] , marker:: PhantomData ) ,
47
+ impl <T > DerefMut for Inner <T > {
48
+ fn deref_mut( & mut self ) -> & mut T {
49
+ unsafe { & mut * ( self . bytes. as_ptr( ) as * mut T ) }
50
+ }
67
51
}
68
- }
69
52
70
- /// A const fn equivalent to mem::zeroed().
71
- #[ cfg( feature = "nightly" ) ]
72
- pub const fn zeroed ( ) -> CachePadded < T > {
73
- CachePadded {
74
- data : UnsafeCell :: new ( [ 0 ; CACHE_LINE ] ) ,
75
- _marker : ( [ ] , marker:: PhantomData ) ,
53
+ impl <T > Drop for CachePadded <T > {
54
+ fn drop( & mut self ) {
55
+ let p: * mut T = self . deref_mut( ) ;
56
+ unsafe {
57
+ ptr:: drop_in_place( p) ;
58
+ }
59
+ }
76
60
}
77
61
}
78
62
}
79
63
80
- #[ inline]
81
- /// Assert that the size and alignment of `T` are consistent with `CachePadded<T>`.
82
- fn assert_valid < T > ( ) {
83
- assert ! ( mem:: size_of:: <T >( ) <= mem:: size_of:: <CachePadded <T >>( ) ) ;
84
- assert ! ( mem:: align_of:: <T >( ) <= mem:: align_of:: <CachePadded <T >>( ) ) ;
64
+ /// Pads `T` to the length of a cache line.
65
+ ///
66
+ /// Sometimes concurrent programming requires a piece of data to be padded out to the size of a
67
+ /// cacheline to avoid "false sharing": cache lines being invalidated due to unrelated concurrent
68
+ /// activity. Use this type when you want to *avoid* cache locality.
69
+ ///
70
+ /// At the moment, cache lines are assumed to be 64 bytes on all architectures.
71
+ ///
72
+ /// # Size and alignment
73
+ ///
74
+ /// By default, the size of `CachePadded<T>` is 64 bytes. If `T` is larger than that, then
75
+ /// `CachePadded::<T>::new` will panic. Alignment of `CachePadded<T>` is the same as that of `T`.
76
+ ///
77
+ /// However, if the `nightly` feature is enabled, arbitrarily large types `T` can be stored inside
78
+ /// a `CachePadded<T>`. The size will then be a multiple of 64 at least the size of `T`, and the
79
+ /// alignment will be the maximum of 64 and the alignment of `T`.
80
+ pub struct CachePadded < T > {
81
+ inner : Inner < T > ,
85
82
}
86
83
84
+ unsafe impl < T : Send > Send for CachePadded < T > { }
85
+ unsafe impl < T : Sync > Sync for CachePadded < T > { }
86
+
87
87
impl < T > CachePadded < T > {
88
- /// Wrap `t` with cacheline padding .
88
+ /// Pads a value to the length of a cache line .
89
89
///
90
- /// **Warning**: the wrapped data is never dropped; move out using
91
- /// `ptr:read` if you need to run dtors.
90
+ /// # Panics
91
+ ///
92
+ /// If `nightly` is not enabled and `T` is larger than 64 bytes, this function will panic.
92
93
pub fn new ( t : T ) -> CachePadded < T > {
93
- assert_valid :: < T > ( ) ;
94
- let ret = CachePadded {
95
- data : UnsafeCell :: new ( [ 0 ; CACHE_LINE ] ) ,
96
- _marker : ( [ ] , marker:: PhantomData ) ,
97
- } ;
94
+ assert ! ( mem:: size_of:: <T >( ) <= mem:: size_of:: <CachePadded <T >>( ) ) ;
95
+ assert ! ( mem:: align_of:: <T >( ) <= mem:: align_of:: <CachePadded <T >>( ) ) ;
96
+
98
97
unsafe {
99
- let p: * mut T = & ret. data as * const UnsafeCell < [ usize ; CACHE_LINE ] > as * mut T ;
98
+ let mut padded = CachePadded {
99
+ inner : mem:: uninitialized ( ) ,
100
+ } ;
101
+ let p: * mut T = & mut * padded;
100
102
ptr:: write ( p, t) ;
103
+ padded
101
104
}
102
- ret
103
105
}
104
106
}
105
107
106
108
impl < T > Deref for CachePadded < T > {
107
109
type Target = T ;
110
+
108
111
fn deref ( & self ) -> & T {
109
- assert_valid :: < T > ( ) ;
110
- unsafe { mem:: transmute ( & self . data ) }
112
+ self . inner . deref ( )
111
113
}
112
114
}
113
115
114
116
impl < T > DerefMut for CachePadded < T > {
115
117
fn deref_mut ( & mut self ) -> & mut T {
116
- assert_valid :: < T > ( ) ;
117
- unsafe { mem:: transmute ( & mut self . data ) }
118
+ self . inner . deref_mut ( )
118
119
}
119
120
}
120
121
@@ -124,31 +125,117 @@ impl<T: Default> Default for CachePadded<T> {
124
125
}
125
126
}
126
127
127
- // FIXME: support Drop by pulling out a version usable for statics
128
- /*
129
- impl<T> Drop for CachePadded<T> {
130
- fn drop(&mut self) {
131
- assert_valid::<T>();
132
- let p: *mut T = mem::transmute(&self.data);
133
- mem::drop(ptr::read(p));
128
+ impl < T : Clone > Clone for CachePadded < T > {
129
+ fn clone ( & self ) -> Self {
130
+ CachePadded {
131
+ inner : self . inner . clone ( ) ,
132
+ }
133
+ }
134
+ }
135
+
136
+ impl < T : fmt:: Debug > fmt:: Debug for CachePadded < T > {
137
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
138
+ let inner: & T = & * self ;
139
+ write ! ( f, "CachePadded {{ {:?} }}" , inner)
140
+ }
141
+ }
142
+
143
+ impl < T > From < T > for CachePadded < T > {
144
+ fn from ( t : T ) -> Self {
145
+ CachePadded :: new ( t)
134
146
}
135
147
}
136
- */
137
148
138
149
#[ cfg( test) ]
139
150
mod test {
140
151
use super :: * ;
152
+ use std:: cell:: Cell ;
141
153
142
154
#[ test]
143
- fn cache_padded_store_u64 ( ) {
155
+ fn store_u64 ( ) {
144
156
let x: CachePadded < u64 > = CachePadded :: new ( 17 ) ;
145
157
assert_eq ! ( * x, 17 ) ;
146
158
}
147
159
148
160
#[ test]
149
- fn cache_padded_store_pair ( ) {
161
+ fn store_pair ( ) {
150
162
let x: CachePadded < ( u64 , u64 ) > = CachePadded :: new ( ( 17 , 37 ) ) ;
151
163
assert_eq ! ( x. 0 , 17 ) ;
152
164
assert_eq ! ( x. 1 , 37 ) ;
153
165
}
166
+
167
+ #[ test]
168
+ fn distance ( ) {
169
+ let arr = [ CachePadded :: new ( 17u8 ) , CachePadded :: new ( 37u8 ) ] ;
170
+ let a = & * arr[ 0 ] as * const u8 ;
171
+ let b = & * arr[ 1 ] as * const u8 ;
172
+ assert_eq ! ( a. wrapping_offset( 64 ) , b) ;
173
+ }
174
+
175
+ #[ test]
176
+ fn different_sizes ( ) {
177
+ CachePadded :: new ( 17u8 ) ;
178
+ CachePadded :: new ( 17u16 ) ;
179
+ CachePadded :: new ( 17u32 ) ;
180
+ CachePadded :: new ( [ 17u64 ; 0 ] ) ;
181
+ CachePadded :: new ( [ 17u64 ; 1 ] ) ;
182
+ CachePadded :: new ( [ 17u64 ; 2 ] ) ;
183
+ CachePadded :: new ( [ 17u64 ; 3 ] ) ;
184
+ CachePadded :: new ( [ 17u64 ; 4 ] ) ;
185
+ CachePadded :: new ( [ 17u64 ; 5 ] ) ;
186
+ CachePadded :: new ( [ 17u64 ; 6 ] ) ;
187
+ CachePadded :: new ( [ 17u64 ; 7 ] ) ;
188
+ CachePadded :: new ( [ 17u64 ; 8 ] ) ;
189
+ }
190
+
191
+ cfg_if ! {
192
+ if #[ cfg( feature = "nightly" ) ] {
193
+ #[ test]
194
+ fn large( ) {
195
+ let a = [ 17u64 ; 9 ] ;
196
+ let b = CachePadded :: new( a) ;
197
+ assert!( mem:: size_of_val( & a) <= mem:: size_of_val( & b) ) ;
198
+ }
199
+ } else {
200
+ #[ test]
201
+ #[ should_panic]
202
+ fn large( ) {
203
+ CachePadded :: new( [ 17u64 ; 9 ] ) ;
204
+ }
205
+ }
206
+ }
207
+
208
+ #[ test]
209
+ fn debug ( ) {
210
+ assert_eq ! ( format!( "{:?}" , CachePadded :: new( 17u64 ) ) , "CachePadded { 17 }" ) ;
211
+ }
212
+
213
+ #[ test]
214
+ fn drops ( ) {
215
+ let count = Cell :: new ( 0 ) ;
216
+
217
+ struct Foo < ' a > ( & ' a Cell < usize > ) ;
218
+
219
+ impl < ' a > Drop for Foo < ' a > {
220
+ fn drop ( & mut self ) {
221
+ self . 0 . set ( self . 0 . get ( ) + 1 ) ;
222
+ }
223
+ }
224
+
225
+ let a = CachePadded :: new ( Foo ( & count) ) ;
226
+ let b = CachePadded :: new ( Foo ( & count) ) ;
227
+
228
+ assert_eq ! ( count. get( ) , 0 ) ;
229
+ drop ( a) ;
230
+ assert_eq ! ( count. get( ) , 1 ) ;
231
+ drop ( b) ;
232
+ assert_eq ! ( count. get( ) , 2 ) ;
233
+ }
234
+
235
+ #[ test]
236
+ fn clone ( ) {
237
+ let a = CachePadded :: new ( 17 ) ;
238
+ let b = a. clone ( ) ;
239
+ assert_eq ! ( * a, * b) ;
240
+ }
154
241
}
0 commit comments