Skip to content

Commit f8d71ed

Browse files
committed
Add documentation comments
1 parent cddf80e commit f8d71ed

File tree

1 file changed

+264
-5
lines changed

1 file changed

+264
-5
lines changed

src/lib.rs

Lines changed: 264 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,128 @@
1+
//! Atomic References
2+
//!
3+
//! These types act similarially to the Atomic types from std::sync::atomic,
4+
//! Except that instead of containing an integer type or a pointer, they contain
5+
//! an `Option<&'a T>` value.
6+
//!
7+
//! Like other option values, these types present operations which, when used
8+
//! correctly, synchronize updates between threads. This type is a form of
9+
//! interior mutability, like `Cell<T>`, `RefCell<T>`, or `Mutex<T>`.
10+
//!
11+
//! To store an atomic reference in a static variable, a the macro
12+
//! `static_atomic_ref!` must be used. A static initializer like
13+
//! `ATOMIC_REF_INIT` is not possible due to the need to be generic over any
14+
//! reference target type.
15+
//!
16+
//! This type in static position is often used for lazy global initialization.
17+
//!
18+
//! `AtomicRef` may only contain `Sized` types, as unsized types have wide
19+
//! pointers which cannot be atomically written to or read from.
20+
//!
21+
//!
22+
//! # Examples
23+
//!
24+
//! Static logger state
25+
//!
26+
//! ```
27+
//! #[macro_use]
28+
//! extern crate atomic_ref;
29+
//! use atomic_ref::AtomicRef;
30+
//! use std::sync::atomic::Ordering;
31+
//! use std::io::{stdout, Write};
32+
//!
33+
//! // Define the idea of a logger
34+
//! trait Logger {
35+
//! fn log(&self, msg: &str) {}
36+
//! }
37+
//! struct LoggerInfo {
38+
//! logger: &'static (Logger + Sync)
39+
//! }
40+
//!
41+
//! // The methods for working with our currently defined static logger
42+
//! static_atomic_ref! {
43+
//! static LOGGER: AtomicRef<LoggerInfo>;
44+
//! }
45+
//! fn log(msg: &str) -> bool {
46+
//! if let Some(info) = LOGGER.load(Ordering::SeqCst) {
47+
//! info.logger.log(msg);
48+
//! true
49+
//! } else {
50+
//! false
51+
//! }
52+
//! }
53+
//! fn set_logger(logger: Option<&'static LoggerInfo>) {
54+
//! LOGGER.store(logger, Ordering::SeqCst);
55+
//! }
56+
//!
57+
//! // Defining the standard out example logger
58+
//! struct StdoutLogger;
59+
//! impl Logger for StdoutLogger {
60+
//! fn log(&self, msg: &str) {
61+
//! stdout().write(msg.as_bytes());
62+
//! }
63+
//! }
64+
//! static STDOUT_LOGGER: LoggerInfo = LoggerInfo { logger: &StdoutLogger };
65+
//!
66+
//! fn main() {
67+
//! let res = log("This will fail");
68+
//! assert!(!res);
69+
//! set_logger(Some(&STDOUT_LOGGER));
70+
//! let res = log("This will succeed");
71+
//! assert!(res);
72+
//! }
73+
//! ```
74+
175
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
276
use std::marker::PhantomData;
377
use std::fmt;
478
use std::default::Default;
579

80+
/// A mutable Option<&'a, T> type which can be safely shared between threads.
681
#[repr(C)]
782
pub struct AtomicRef<'a, T: 'a> {
883
data: AtomicUsize,
984
_marker: PhantomData<&'a T>,
1085
}
1186

12-
/// We cannot have an ATOMIC_REF_INIT method, as we can't make a const which is
13-
/// templated over a type
87+
/// You will probably never need to use this type. It exists mostly for internal
88+
/// use in the `static_atomic_ref!` macro.
89+
///
90+
/// Unlike `AtomicUsize` and its ilk, we cannot have an `ATOMIC_REF_INIT` const
91+
/// which is initialized to `None`, as constants cannot be generic over a type
92+
/// parameter. This is the same reason why `AtomicPtr` does not have an
93+
/// `ATOMIC_PTR_INIT` const.
1494
///
15-
/// Instead, we define it for a specific type (in this case &'static u8), and
16-
/// define a macro static_atomic_ref! which uses unsafe code to allow the
17-
/// creation of other types based on this layout.
95+
/// Instead, we have a single const for `&'static u8`, and take advantage of the
96+
/// fact that all AtomicRef types have identical layout to implement the
97+
/// `static_atomic_ref!` macro.
98+
///
99+
/// Please use `static_atomic_ref!` instead of this constant if you need to
100+
/// implement a static atomic reference variable.
18101
pub const ATOMIC_U8_REF_INIT: AtomicRef<'static, u8> = AtomicRef {
19102
data: ATOMIC_USIZE_INIT,
20103
_marker: PhantomData,
21104
};
22105

106+
/// A macro to define a statically allocated `AtomicRef<'static, T>` which is
107+
/// initialized to `None`.
108+
///
109+
/// # Examples
110+
///
111+
/// ```
112+
/// # #[macro_use]
113+
/// # extern crate atomic_ref;
114+
/// use std::sync::atomic::Ordering;
115+
///
116+
/// static_atomic_ref! {
117+
/// static SOME_REFERENCE: AtomicRef<i32>;
118+
/// pub static PUB_REFERENCE: AtomicRef<u64>;
119+
/// }
120+
///
121+
/// fn main() {
122+
/// let a: Option<&'static i32> = SOME_REFERENCE.load(Ordering::SeqCst);
123+
/// assert_eq!(a, None);
124+
/// }
125+
/// ```
23126
#[macro_export]
24127
macro_rules! static_atomic_ref {
25128
($(#[$attr:meta])* static $N:ident : AtomicRef<$T:ty>; $($t:tt)*) => {
@@ -60,48 +163,171 @@ macro_rules! static_atomic_ref {
60163
() => ();
61164
}
62165

166+
/// An internal helper function for converting `Option<&'a T>` values to usize
167+
/// for storing in the `AtomicUsize`.
63168
fn from_opt<'a, T>(p: Option<&'a T>) -> usize {
64169
match p {
65170
Some(p) => p as *const T as usize,
66171
None => 0,
67172
}
68173
}
69174

175+
/// An internal helper function for converting `usize` values stored in the
176+
/// `AtomicUsize` back into `Option<&'a T>` values.
70177
unsafe fn to_opt<'a, T>(p: usize) -> Option<&'a T> {
71178
(p as *const T).as_ref()
72179
}
73180

74181
impl<'a, T> AtomicRef<'a, T> {
182+
/// Creates a new `AtomicRef`.
183+
///
184+
/// # Examples
185+
///
186+
/// ```
187+
/// use atomic_ref::AtomicRef;
188+
///
189+
/// static VALUE: i32 = 10;
190+
/// let atomic_ref = AtomicRef::new(Some(&VALUE));
191+
/// ```
75192
pub fn new(p: Option<&'a T>) -> AtomicRef<'a, T> {
76193
AtomicRef {
77194
data: AtomicUsize::new(from_opt(p)),
78195
_marker: PhantomData,
79196
}
80197
}
81198

199+
/// Loads the value stored in the `AtomicRef`.
200+
///
201+
/// `load` takes an `Ordering` argument which describes the memory ordering of this operation.
202+
///
203+
/// # Panics
204+
///
205+
/// Panics if `order` is `Release` or `AcqRel`.
206+
///
207+
/// # Examples
208+
///
209+
/// ```
210+
/// use std::sync::atomic::Ordering;
211+
/// use atomic_ref::AtomicRef;
212+
///
213+
/// static VALUE: i32 = 10;
214+
///
215+
/// let some_ref = AtomicRef::new(Some(&VALUE));
216+
/// assert_eq!(some_ref.load(Ordering::Relaxed), Some(&10));
217+
/// ```
82218
pub fn load(&self, ordering: Ordering) -> Option<&'a T> {
83219
unsafe {
84220
to_opt(self.data.load(ordering))
85221
}
86222
}
87223

224+
/// Stores a value into the `AtomicRef`.
225+
///
226+
/// `store` takes an `Ordering` argument which describes the memory ordering of this operation.
227+
///
228+
/// # Panics
229+
///
230+
/// Panics if `order` is `Acquire` or `AcqRel`.
231+
///
232+
/// # Examples
233+
///
234+
/// ```
235+
/// use std::sync::atomic::Ordering;
236+
/// use atomic_ref::AtomicRef;
237+
///
238+
/// static VALUE: i32 = 10;
239+
///
240+
/// let some_ptr = AtomicRef::new(None);
241+
/// some_ptr.store(Some(&VALUE), Ordering::Relaxed);
242+
/// ```
88243
pub fn store(&self, ptr: Option<&'a T>, order: Ordering) {
89244
self.data.store(from_opt(ptr), order)
90245
}
91246

247+
/// Stores a value into the `AtomicRef`, returning the old value.
248+
///
249+
/// `swap` takes an `Ordering` argument which describes the memory ordering of this operation.
250+
///
251+
/// # Examples
252+
///
253+
/// ```
254+
/// use std::sync::atomic::Ordering;
255+
/// use atomic_ref::AtomicRef;
256+
///
257+
/// static VALUE: i32 = 10;
258+
/// static OTHER_VALUE: i32 = 20;
259+
///
260+
/// let some_ptr = AtomicRef::new(Some(&VALUE));
261+
/// let value = some_ptr.swap(Some(&OTHER_VALUE), Ordering::Relaxed);
262+
/// ```
92263
pub fn swap(&self, p: Option<&'a T>, order: Ordering) -> Option<&'a T> {
93264
unsafe {
94265
to_opt(self.data.swap(from_opt(p), order))
95266
}
96267
}
97268

269+
/// Stores a value into the `AtomicRef` if the current value is the "same" as
270+
/// the `current` value.
271+
///
272+
/// The return value is always the previous value. If it the "same" as
273+
/// `current`, then the value was updated.
274+
///
275+
/// This method considers two `Option<&'a T>`s to be the "same" if they are
276+
/// both `Some` and have the same pointer value, or if they are both `None`.
277+
/// This method does not use `Eq` or `PartialEq` for comparison.
278+
///
279+
/// `compare_and_swap` also takes an `Ordering` argument which describes the
280+
/// memory ordering of this operation.
281+
///
282+
/// # Examples
283+
///
284+
/// ```
285+
/// use std::sync::atomic::Ordering;
286+
/// use atomic_ref::AtomicRef;
287+
///
288+
/// static VALUE: i32 = 10;
289+
/// static OTHER_VALUE: i32 = 20;
290+
///
291+
/// let some_ptr = AtomicRef::new(Some(&VALUE));
292+
/// let value = some_ptr.compare_and_swap(Some(&OTHER_VALUE), None, Ordering::Relaxed);
293+
/// ```
98294
pub fn compare_and_swap(&self, current: Option<&'a T>, new: Option<&'a T>, order: Ordering)
99295
-> Option<&'a T> {
100296
unsafe {
101297
to_opt(self.data.compare_and_swap(from_opt(current), from_opt(new), order))
102298
}
103299
}
104300

301+
/// Stores a value into the `AtomicRef` if the current value is the "same" as
302+
/// the `current` value.
303+
///
304+
/// The return value is a result indicating whether the new value was
305+
/// written, and containing the previous value. On success this value is
306+
/// guaranteed to be the "same" as `new`.
307+
///
308+
/// This method considers two `Option<&'a T>`s to be the "same" if they are
309+
/// both `Some` and have the same pointer value, or if they are both `None`.
310+
/// This method does not use `Eq` or `PartialEq` for comparison.
311+
///
312+
/// `compare_exchange` takes two `Ordering` arguments to describe the memory
313+
/// ordering of this operation. The first describes the required ordering if
314+
/// the operation succeeds while the second describes the required ordering
315+
/// when the operation fails. The failure ordering can't be `Release` or
316+
/// `AcqRel` and must be equivalent or weaker than the success ordering.
317+
///
318+
/// # Examples
319+
///
320+
/// ```
321+
/// use std::sync::atomic::Ordering;
322+
/// use atomic_ref::AtomicRef;
323+
///
324+
/// static VALUE: i32 = 10;
325+
/// static OTHER_VALUE: i32 = 20;
326+
///
327+
/// let some_ptr = AtomicRef::new(Some(&VALUE));
328+
/// let value = some_ptr.compare_exchange(Some(&OTHER_VALUE), None,
329+
/// Ordering::SeqCst, Ordering::Relaxed);
330+
/// ```
105331
pub fn compare_exchange(&self, current: Option<&'a T>, new: Option<&'a T>,
106332
success: Ordering, failure: Ordering)
107333
-> Result<Option<&'a T>, Option<&'a T>> {
@@ -114,6 +340,39 @@ impl<'a, T> AtomicRef<'a, T> {
114340
}
115341
}
116342

343+
/// Stores a value into the pointer if the current value is the same as the `current` value.
344+
///
345+
/// Unlike `compare_exchange`, this function is allowed to spuriously fail even when the
346+
/// comparison succeeds, which can result in more efficient code on some platforms. The
347+
/// return value is a result indicating whether the new value was written and containing the
348+
/// previous value.
349+
///
350+
/// `compare_exchange_weak` takes two `Ordering` arguments to describe the memory
351+
/// ordering of this operation. The first describes the required ordering if the operation
352+
/// succeeds while the second describes the required ordering when the operation fails. The
353+
/// failure ordering can't be `Release` or `AcqRel` and must be equivalent or weaker than the
354+
/// success ordering.
355+
///
356+
/// # Examples
357+
///
358+
/// ```
359+
/// use std::sync::atomic::Ordering;
360+
/// use atomic_ref::AtomicRef;
361+
///
362+
/// static VALUE: i32 = 10;
363+
/// static OTHER_VALUE: i32 = 20;
364+
///
365+
/// let some_ptr = AtomicRef::new(Some(&VALUE));
366+
///
367+
/// let mut old = some_ptr.load(Ordering::Relaxed);
368+
/// loop {
369+
/// match some_ptr.compare_exchange_weak(old, Some(&VALUE),
370+
/// Ordering::SeqCst, Ordering::Relaxed) {
371+
/// Ok(_) => break,
372+
/// Err(x) => old = x,
373+
/// }
374+
/// }
375+
/// ```
117376
pub fn compare_exchange_weak(&self, current: Option<&'a T>, new: Option<&'a T>,
118377
success: Ordering, failure: Ordering)
119378
-> Result<Option<&'a T>, Option<&'a T>> {

0 commit comments

Comments
 (0)