Skip to content
This repository was archived by the owner on Nov 5, 2018. It is now read-only.

Commit 3ad8298

Browse files
author
Stjepan Glavina
authored
Revamp CachePadded (#1)
* Revamp CachePadded * Use untagged unions on nightly * Get rid of the union and add more docs
1 parent 438532a commit 3ad8298

File tree

3 files changed

+185
-92
lines changed

3 files changed

+185
-92
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@ homepage = "https://github.com/crossbeam-rs/crossbeam-utils"
99
documentation = "https://docs.rs/crossbeam-utils"
1010
description = "Utilities for concurrent programming"
1111

12+
[features]
13+
nightly = []
14+
1215
[dependencies]
16+
cfg-if = "0.1"

src/cache_padded.rs

Lines changed: 177 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,121 @@
1-
use std::marker;
2-
use std::cell::UnsafeCell;
31
use std::fmt;
42
use std::mem;
5-
use std::ptr;
63
use std::ops::{Deref, DerefMut};
4+
use std::ptr;
75

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+
}
3113

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;
3716

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+
}
4021

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;
4629

47-
#[cfg(feature = "nightly")]
48-
unsafe impl ZerosValid for .. {}
30+
#[derive(Clone)]
31+
struct Inner<T> {
32+
bytes: [u8; 64],
4933

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+
}
5338

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;
5641

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+
}
5946

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+
}
6751
}
68-
}
6952

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+
}
7660
}
7761
}
7862
}
7963

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>,
8582
}
8683

84+
unsafe impl<T: Send> Send for CachePadded<T> {}
85+
unsafe impl<T: Sync> Sync for CachePadded<T> {}
86+
8787
impl<T> CachePadded<T> {
88-
/// Wrap `t` with cacheline padding.
88+
/// Pads a value to the length of a cache line.
8989
///
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.
9293
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+
9897
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;
100102
ptr::write(p, t);
103+
padded
101104
}
102-
ret
103105
}
104106
}
105107

106108
impl<T> Deref for CachePadded<T> {
107109
type Target = T;
110+
108111
fn deref(&self) -> &T {
109-
assert_valid::<T>();
110-
unsafe { mem::transmute(&self.data) }
112+
self.inner.deref()
111113
}
112114
}
113115

114116
impl<T> DerefMut for CachePadded<T> {
115117
fn deref_mut(&mut self) -> &mut T {
116-
assert_valid::<T>();
117-
unsafe { mem::transmute(&mut self.data) }
118+
self.inner.deref_mut()
118119
}
119120
}
120121

@@ -124,31 +125,117 @@ impl<T: Default> Default for CachePadded<T> {
124125
}
125126
}
126127

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)
134146
}
135147
}
136-
*/
137148

138149
#[cfg(test)]
139150
mod test {
140151
use super::*;
152+
use std::cell::Cell;
141153

142154
#[test]
143-
fn cache_padded_store_u64() {
155+
fn store_u64() {
144156
let x: CachePadded<u64> = CachePadded::new(17);
145157
assert_eq!(*x, 17);
146158
}
147159

148160
#[test]
149-
fn cache_padded_store_pair() {
161+
fn store_pair() {
150162
let x: CachePadded<(u64, u64)> = CachePadded::new((17, 37));
151163
assert_eq!(x.0, 17);
152164
assert_eq!(x.1, 37);
153165
}
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+
}
154241
}

src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
#![cfg_attr(feature = "nightly", feature(const_fn))]
1+
#![cfg_attr(feature = "nightly", feature(attr_literals, repr_align))]
22

3-
pub mod atomic_option;
43
#[macro_use]
4+
extern crate cfg_if;
5+
6+
pub mod atomic_option;
57
pub mod cache_padded;
68
pub mod scoped;

0 commit comments

Comments
 (0)