Skip to content

Commit 944b4aa

Browse files
committed
add protected RMW operations to Guard
1 parent 3c2221e commit 944b4aa

File tree

6 files changed

+94
-59
lines changed

6 files changed

+94
-59
lines changed

src/collector.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,10 @@ impl Collector {
7272
/// loads of concurrent objects for its lifetime. The thread will be
7373
/// marked as inactive when the guard is dropped.
7474
///
75-
/// Note that loads of objects that may be retired must be protected with the
76-
/// [`Guard::protect`]. See [the guide](crate::guide#starting-operations) for
77-
/// an introduction to using guards, or the documentation of [`LocalGuard`] for
75+
/// Note that loads of objects that may be retired must be protected with
76+
/// the [`Guard::protect`]. See [the
77+
/// guide](crate::guide#starting-operations) for an introduction to
78+
/// using guards, or the documentation of [`LocalGuard`] for
7879
/// more details.
7980
///
8081
/// Note that `enter` is reentrant, and it is legal to create multiple

src/guard.rs

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ pub trait Guard {
2222
/// This method is not marked as `unsafe`, but will affect the validity of
2323
/// pointers loaded using [`Guard::protect`], similar to dropping a guard.
2424
/// It is intended to be used safely by users of concurrent data structures,
25-
/// as references will be tied to the guard and this method takes `&mut self`.
25+
/// as references will be tied to the guard and this method takes `&mut
26+
/// self`.
2627
fn refresh(&mut self);
2728

2829
/// Flush any retired values in the local batch.
@@ -46,8 +47,8 @@ pub trait Guard {
4647
/// a cheap way to get an identifier for the current thread without TLS
4748
/// overhead. Note that thread IDs may be reused, so the value returned
4849
/// is only unique for the lifetime of this thread.
49-
///
5050
fn thread_id(&self) -> usize;
51+
5152
/// Protects the load of an atomic pointer.
5253
///
5354
/// Any valid pointer loaded through a guard using the `protect` method is
@@ -58,10 +59,63 @@ pub trait Guard {
5859
///
5960
/// Note that the lifetime of a guarded pointer is logically tied to that of
6061
/// the guard — when the guard is dropped the pointer is invalidated. Data
61-
/// structures that return shared references to values should ensure that the
62-
/// lifetime of the reference is tied to the lifetime of a guard.
63-
fn protect<T>(&self, ptr: &AtomicPtr<T>, ordering: Ordering) -> *mut T {
64-
ptr.load(raw::Collector::protect(ordering))
62+
/// structures that return shared references to values should ensure that
63+
/// the lifetime of the reference is tied to the lifetime of a guard.
64+
fn protect<T>(&self, ptr: &AtomicPtr<T>, order: Ordering) -> *mut T {
65+
ptr.load(raw::Collector::protect(order))
66+
}
67+
68+
/// Stores a value into the pointer, returning the protected previous value.
69+
///
70+
/// This method is equivalent to [`AtomicPtr::swap`], except the returned
71+
/// value is guaranteed to be protected with the same guarantees as
72+
/// [`Guard::protect`].
73+
fn swap<T>(&self, ptr: &AtomicPtr<T>, value: *mut T, order: Ordering) -> *mut T {
74+
ptr.swap(value, raw::Collector::protect(order))
75+
}
76+
77+
/// Stores a value into the pointer if the current value is the same as the
78+
/// `current` value, returning the protected previous value.
79+
///
80+
/// This method is equivalent to [`AtomicPtr::compare_exchange`], except the
81+
/// returned value is guaranteed to be protected with the same
82+
/// guarantees as [`Guard::protect`].
83+
fn compare_exchange<T>(
84+
&self,
85+
ptr: &AtomicPtr<T>,
86+
current: *mut T,
87+
new: *mut T,
88+
success: Ordering,
89+
failure: Ordering,
90+
) -> Result<*mut T, *mut T> {
91+
ptr.compare_exchange(
92+
current,
93+
new,
94+
raw::Collector::protect(success),
95+
raw::Collector::protect(failure),
96+
)
97+
}
98+
99+
/// Stores a value into the pointer if the current value is the same as the
100+
/// `current` value, returning the protected previous value.
101+
///
102+
/// This method is equivalent to [`AtomicPtr::compare_exchange_weak`],
103+
/// except the returned value is guaranteed to be protected with the
104+
/// same guarantees as [`Guard::protect`].
105+
fn compare_exchange_weak<T>(
106+
&self,
107+
ptr: &AtomicPtr<T>,
108+
current: *mut T,
109+
new: *mut T,
110+
success: Ordering,
111+
failure: Ordering,
112+
) -> Result<*mut T, *mut T> {
113+
ptr.compare_exchange_weak(
114+
current,
115+
new,
116+
raw::Collector::protect(success),
117+
raw::Collector::protect(failure),
118+
)
65119
}
66120

67121
/// Retires a value, running `reclaim` when no threads hold a reference to

src/raw/collector.rs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,24 +87,20 @@ impl Collector {
8787
//
8888
// Note that all pointer loads perform a light barrier to participate in the
8989
// total order.
90-
membarrier::light_store_barrier();
90+
membarrier::light_barrier();
9191
}
9292

93-
/// Strengthens an ordering to that necessary to protect the load of a pointer.
93+
/// Strengthens an ordering to that necessary to protect the load of a
94+
/// pointer.
9495
#[inline]
95-
pub fn protect(_ordering: Ordering) -> Ordering {
96-
// This fence shouldn't really be necessary because loads use `SeqCst`
97-
// unconditionally, but it doesn't hurt.
98-
membarrier::light_load_barrier();
99-
96+
pub fn protect(_order: Ordering) -> Ordering {
10097
// We have to respect both the user provided ordering and the ordering required
10198
// by the membarrier strategy. `SeqCst` is equivalent to `Acquire` on
10299
// most platforms, so we just use it unconditionally.
103100
//
104-
// Loads performed with this ordering, paired with the light barrier above, will
105-
// participate in the total order established by `enter`, and thus see the new
106-
// values of any pointers that were retired when the thread was
107-
// inactive.
101+
// Loads performed with this ordering, paired with the light barrier in `enter`,
102+
// will participate in the total order established by `enter`, and thus see the
103+
// new values of any pointers that were retired when the thread was inactive.
108104
Ordering::SeqCst
109105
}
110106

src/raw/membarrier.rs

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
//!
55
//! There is a total order over all memory barriers provided by this module:
66
//! - Light store barriers, created by a pair of [`light_store`] and
7-
//! [`light_store_barrier`].
8-
//! - Light load barriers, created by a pair of [`light_load_barrier`] and
7+
//! [`light_barrier`].
8+
//! - Light load barriers, created by a pair of [`light_barrier`] and
99
//! [`light_load`].
1010
//! - Sequentially consistent barriers, or cumulative light barriers.
1111
//! - Heavy barriers, created by [`heavy`].
@@ -50,15 +50,10 @@ mod default {
5050
Ordering::SeqCst
5151
}
5252

53-
/// Issues a light memory barrier for a preceding store operation.
53+
/// Issues a light memory barrier for a preceding store or subsequent load
54+
/// operation.
5455
#[inline]
55-
pub fn light_store_barrier() {
56-
// This is a no-op due to strong loads and stores.
57-
}
58-
59-
/// Issues a light memory barrier for a subsequent load operation.
60-
#[inline]
61-
pub fn light_load_barrier() {
56+
pub fn light_barrier() {
6257
// This is a no-op due to strong loads and stores.
6358
}
6459

@@ -95,17 +90,10 @@ mod linux {
9590
}
9691
}
9792

98-
/// Issues a light memory barrier for a preceding store operation.
99-
#[inline]
100-
pub fn light_store_barrier() {
101-
atomic::compiler_fence(atomic::Ordering::SeqCst)
102-
}
103-
104-
/// Issues a light memory barrier for a subsequent load operation.
93+
/// Issues a light memory barrier for a preceding store or subsequent load
94+
/// operation.
10595
#[inline]
106-
pub fn light_load_barrier() {
107-
// This fence shouldn't really be necessary because loads use `SeqCst`
108-
// unconditionally, but it doesn't hurt.
96+
pub fn light_barrier() {
10997
atomic::compiler_fence(atomic::Ordering::SeqCst)
11098
}
11199

@@ -167,9 +155,9 @@ mod linux {
167155
///
168156
/// # Caveat
169157
///
170-
/// We're defining it here because, unfortunately, the `libc` crate currently doesn't
171-
/// expose `membarrier_cmd` for us. You can find the numbers in the
172-
/// [Linux source code](https://github.com/torvalds/linux/blob/master/include/uapi/linux/membarrier.h).
158+
/// We're defining it here because, unfortunately, the `libc` crate
159+
/// currently doesn't expose `membarrier_cmd` for us. You can
160+
/// find the numbers in the [Linux source code](https://github.com/torvalds/linux/blob/master/include/uapi/linux/membarrier.h).
173161
///
174162
/// This enum should really be `#[repr(libc::c_int)]`, but Rust
175163
/// currently doesn't allow it.
@@ -349,16 +337,11 @@ mod windows {
349337
Ordering::Relaxed
350338
}
351339

352-
/// Issues a light memory barrier for a preceding store operation.
353-
#[inline]
354-
pub fn light_store_barrier() {
355-
atomic::compiler_fence(Ordering::SeqCst);
356-
}
357-
358-
/// Issues a light memory barrier for a subsequent load operation.
340+
/// Issues a light memory barrier for a preceding store or subsequent load
341+
/// operation.
359342
#[inline]
360-
pub fn light_load_barrier() {
361-
atomic::compiler_fence(Ordering::SeqCst);
343+
pub fn light_barrier() {
344+
atomic::compiler_fence(atomic::Ordering::SeqCst)
362345
}
363346

364347
/// The ordering for a load operation that synchronizes with heavy barriers.

src/raw/tls/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ unsafe impl<T: Send> Send for ThreadLocal<T> {}
3939

4040
/// Safety:
4141
///
42-
/// - Values can be inserted through a shared reference and thus dropped
43-
/// on another thread than they were created on, hence `T: Send`.
44-
/// - However, there is no way to access a `T` inserted by another thread
45-
/// except through iteration, which is unsafe, so `T: Sync` is not required.
42+
/// - Values can be inserted through a shared reference and thus dropped on
43+
/// another thread than they were created on, hence `T: Send`.
44+
/// - However, there is no way to access a `T` inserted by another thread except
45+
/// through iteration, which is unsafe, so `T: Sync` is not required.
4646
unsafe impl<T: Send> Sync for ThreadLocal<T> {}
4747

4848
impl<T> ThreadLocal<T> {

src/reclaim.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
//! Common memory reclaimers.
22
//!
3-
//! The functions in this module can be passed to [`retire`](crate::Collector::retire)
4-
//! to free allocated memory or run drop glue. See [the guide](crate#custom-reclaimers)
5-
//! for details about memory reclamation, and writing custom reclaimers.
3+
//! The functions in this module can be passed to
4+
//! [`retire`](crate::Collector::retire) to free allocated memory or run drop
5+
//! glue. See [the guide](crate#custom-reclaimers) for details about memory
6+
//! reclamation, and writing custom reclaimers.
67
78
use std::ptr;
89

0 commit comments

Comments
 (0)