From 40aa833fa3f31c0d9e657263f22d870566801e9a Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Wed, 19 Nov 2014 10:35:47 -0500 Subject: [PATCH] Document almost all of atomics. I don't know enough about the free functions to give them better docs right now. --- src/libcore/atomic.rs | 394 +++++++++++++++++++++++++++++++----------- 1 file changed, 296 insertions(+), 98 deletions(-) diff --git a/src/libcore/atomic.rs b/src/libcore/atomic.rs index d2bca1e6ec7..e930f353b52 100644 --- a/src/libcore/atomic.rs +++ b/src/libcore/atomic.rs @@ -18,28 +18,28 @@ use std::kinds::marker; use cell::UnsafeCell; -/// An atomic boolean type. +/// A boolean type which can be safely shared between threads. #[stable] pub struct AtomicBool { v: UnsafeCell, nocopy: marker::NoCopy } -/// A signed atomic integer type, supporting basic atomic arithmetic operations +/// A signed integer type which can be safely shared between threads. #[stable] pub struct AtomicInt { v: UnsafeCell, nocopy: marker::NoCopy } -/// An unsigned atomic integer type, supporting basic atomic arithmetic operations +/// An unsigned integer type which can be safely shared between threads. #[stable] pub struct AtomicUint { v: UnsafeCell, nocopy: marker::NoCopy } -/// An unsafe atomic pointer. Only supports basic atomic operations +/// A raw pointer type which can be safely shared between threads. #[stable] pub struct AtomicPtr { p: UnsafeCell, @@ -54,43 +54,42 @@ pub struct AtomicPtr { /// to be moved either before or after the atomic operation; on the other end /// "relaxed" atomics allow all reorderings. /// -/// Rust's memory orderings are the same as in C++[1]. -/// -/// 1: http://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync +/// Rust's memory orderings are [the same as +/// C++'s](http://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync). #[stable] pub enum Ordering { - /// No ordering constraints, only atomic operations + /// No ordering constraints, only atomic operations. #[stable] Relaxed, /// When coupled with a store, all previous writes become visible /// to another thread that performs a load with `Acquire` ordering - /// on the same value + /// on the same value. #[stable] Release, /// When coupled with a load, all subsequent loads will see data /// written before a store with `Release` ordering on the same value - /// in another thread + /// in another thread. #[stable] Acquire, /// When coupled with a load, uses `Acquire` ordering, and with a store - /// `Release` ordering + /// `Release` ordering. #[stable] AcqRel, /// Like `AcqRel` with the additional guarantee that all threads see all /// sequentially consistent operations in the same order. #[stable] - SeqCst + SeqCst, } -/// An `AtomicBool` initialized to `false` +/// An `AtomicBool` initialized to `false`. #[unstable = "may be renamed, pending conventions for static initalizers"] pub const INIT_ATOMIC_BOOL: AtomicBool = AtomicBool { v: UnsafeCell { value: 0 }, nocopy: marker::NoCopy }; -/// An `AtomicInt` initialized to `0` +/// An `AtomicInt` initialized to `0`. #[unstable = "may be renamed, pending conventions for static initalizers"] pub const INIT_ATOMIC_INT: AtomicInt = AtomicInt { v: UnsafeCell { value: 0 }, nocopy: marker::NoCopy }; -/// An `AtomicUint` initialized to `0` +/// An `AtomicUint` initialized to `0`. #[unstable = "may be renamed, pending conventions for static initalizers"] pub const INIT_ATOMIC_UINT: AtomicUint = AtomicUint { v: UnsafeCell { value: 0, }, nocopy: marker::NoCopy }; @@ -99,7 +98,16 @@ pub enum Ordering { const UINT_TRUE: uint = -1; impl AtomicBool { - /// Create a new `AtomicBool` + /// Creates a new `AtomicBool`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::AtomicBool; + /// + /// let atomic_true = AtomicBool::new(true); + /// let atomic_false = AtomicBool::new(false); + /// ``` #[inline] #[stable] pub fn new(v: bool) -> AtomicBool { @@ -107,18 +115,42 @@ pub fn new(v: bool) -> AtomicBool { AtomicBool { v: UnsafeCell::new(val), nocopy: marker::NoCopy } } - /// Load the value + /// Loads a value from the bool. + /// + /// `load` takes an `Ordering` argument which describes the memory ordering of this operation. /// /// # Panics /// /// Panics if `order` is `Release` or `AcqRel`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let some_bool = AtomicBool::new(true); + /// + /// let value = some_bool.load(Ordering::Relaxed); + /// ``` #[inline] #[stable] pub fn load(&self, order: Ordering) -> bool { unsafe { atomic_load(self.v.get() as *const uint, order) > 0 } } - /// Store the value + /// Stores a value into the bool. + /// + /// `store` takes an `Ordering` argument which describes the memory ordering of this operation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let some_bool = AtomicBool::new(true); + /// + /// some_bool.store(false, Ordering::Relaxed); + /// ``` /// /// # Panics /// @@ -131,7 +163,19 @@ pub fn store(&self, val: bool, order: Ordering) { unsafe { atomic_store(self.v.get(), val, order); } } - /// Store a value, returning the old value + /// Stores a value into the bool, returning the old value. + /// + /// `swap` takes an `Ordering` argument which describes the memory ordering of this operation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let some_bool = AtomicBool::new(true); + /// + /// let value = some_bool.swap(false, Ordering::Relaxed); + /// ``` #[inline] #[stable] pub fn swap(&self, val: bool, order: Ordering) -> bool { @@ -140,48 +184,21 @@ pub fn swap(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_swap(self.v.get(), val, order) > 0 } } - /// If the current value is the same as expected, store a new value + /// Stores a value into the bool if the current value is the same as the expected value. /// - /// Compare the current value with `old`; if they are the same then - /// replace the current value with `new`. Return the previous value. /// If the return value is equal to `old` then the value was updated. /// + /// `swap` also takes an `Ordering` argument which describes the memory ordering of this + /// operation. + /// /// # Examples /// - /// ```rust - /// use std::sync::Arc; - /// use std::sync::atomic::{AtomicBool, SeqCst}; - /// use std::task::deschedule; + /// ``` + /// use std::sync::atomic::{AtomicBool, Ordering}; /// - /// fn main() { - /// let spinlock = Arc::new(AtomicBool::new(false)); - /// let spinlock_clone = spinlock.clone(); + /// let some_bool = AtomicBool::new(true); /// - /// spawn(proc() { - /// with_lock(&spinlock, || println!("task 1 in lock")); - /// }); - /// - /// spawn(proc() { - /// with_lock(&spinlock_clone, || println!("task 2 in lock")); - /// }); - /// } - /// - /// fn with_lock(spinlock: &Arc, f: || -> ()) { - /// // CAS loop until we are able to replace `false` with `true` - /// while spinlock.compare_and_swap(false, true, SeqCst) != false { - /// // Since tasks may not be preemptive (if they are green threads) - /// // yield to the scheduler to let the other task run. Low level - /// // concurrent code needs to take into account Rust's two threading - /// // models. - /// deschedule(); - /// } - /// - /// // Now we have the spinlock - /// f(); - /// - /// // Release the lock - /// spinlock.store(false, SeqCst); - /// } + /// let value = some_bool.store(false, Ordering::Relaxed); /// ``` #[inline] #[stable] @@ -192,10 +209,11 @@ pub fn compare_and_swap(&self, old: bool, new: bool, order: Ordering) -> bool { unsafe { atomic_compare_and_swap(self.v.get(), old, new, order) > 0 } } - /// A logical "and" operation + /// Logical "and" with a boolean value. + /// + /// Performs a logical "and" operation on the current value and the argument `val`, and sets + /// the new value to the result. /// - /// Performs a logical "and" operation on the current value and the - /// argument `val`, and sets the new value to the result. /// Returns the previous value. /// /// # Examples @@ -223,10 +241,11 @@ pub fn fetch_and(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_and(self.v.get(), val, order) > 0 } } - /// A logical "nand" operation + /// Logical "nand" with a boolean value. + /// + /// Performs a logical "nand" operation on the current value and the argument `val`, and sets + /// the new value to the result. /// - /// Performs a logical "nand" operation on the current value and the - /// argument `val`, and sets the new value to the result. /// Returns the previous value. /// /// # Examples @@ -255,10 +274,11 @@ pub fn fetch_nand(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_nand(self.v.get(), val, order) > 0 } } - /// A logical "or" operation + /// Logical "or" with a boolean value. + /// + /// Performs a logical "or" operation on the current value and the argument `val`, and sets the + /// new value to the result. /// - /// Performs a logical "or" operation on the current value and the - /// argument `val`, and sets the new value to the result. /// Returns the previous value. /// /// # Examples @@ -286,10 +306,11 @@ pub fn fetch_or(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_or(self.v.get(), val, order) > 0 } } - /// A logical "xor" operation + /// Logical "xor" with a boolean value. + /// + /// Performs a logical "xor" operation on the current value and the argument `val`, and sets + /// the new value to the result. /// - /// Performs a logical "xor" operation on the current value and the - /// argument `val`, and sets the new value to the result. /// Returns the previous value. /// /// # Examples @@ -319,25 +340,57 @@ pub fn fetch_xor(&self, val: bool, order: Ordering) -> bool { } impl AtomicInt { - /// Create a new `AtomicInt` + /// Creates a new `AtomicInt`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::AtomicInt; + /// + /// let atomic_forty_two = AtomicInt::new(42); + /// ``` #[inline] #[stable] pub fn new(v: int) -> AtomicInt { AtomicInt {v: UnsafeCell::new(v), nocopy: marker::NoCopy} } - /// Load the value + /// Loads a value from the int. + /// + /// `load` takes an `Ordering` argument which describes the memory ordering of this operation. /// /// # Panics /// /// Panics if `order` is `Release` or `AcqRel`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicInt, Ordering}; + /// + /// let some_int = AtomicInt::new(5); + /// + /// let value = some_int.load(Ordering::Relaxed); + /// ``` #[inline] #[stable] pub fn load(&self, order: Ordering) -> int { unsafe { atomic_load(self.v.get() as *const int, order) } } - /// Store the value + /// Stores a value into the int. + /// + /// `store` takes an `Ordering` argument which describes the memory ordering of this operation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicInt, Ordering}; + /// + /// let some_int = AtomicInt::new(5); + /// + /// some_int.store(10, Ordering::Relaxed); + /// ``` /// /// # Panics /// @@ -348,25 +401,48 @@ pub fn store(&self, val: int, order: Ordering) { unsafe { atomic_store(self.v.get(), val, order); } } - /// Store a value, returning the old value + /// Stores a value into the int, returning the old value. + /// + /// `swap` takes an `Ordering` argument which describes the memory ordering of this operation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicInt, Ordering}; + /// + /// let some_int = AtomicInt::new(5); + /// + /// let value = some_int.swap(10, Ordering::Relaxed); + /// ``` #[inline] #[stable] pub fn swap(&self, val: int, order: Ordering) -> int { unsafe { atomic_swap(self.v.get(), val, order) } } - /// If the current value is the same as expected, store a new value + /// Stores a value into the int if the current value is the same as the expected value. /// - /// Compare the current value with `old`; if they are the same then - /// replace the current value with `new`. Return the previous value. /// If the return value is equal to `old` then the value was updated. + /// + /// `compare_and_swap` also takes an `Ordering` argument which describes the memory ordering of + /// this operation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicInt, Ordering}; + /// + /// let some_int = AtomicInt::new(5); + /// + /// let value = some_int.compare_and_swap(5, 10, Ordering::Relaxed); + /// ``` #[inline] #[stable] pub fn compare_and_swap(&self, old: int, new: int, order: Ordering) -> int { unsafe { atomic_compare_and_swap(self.v.get(), old, new, order) } } - /// Add to the current value, returning the previous + /// Add an int to the current value, returning the previous value. /// /// # Examples /// @@ -383,7 +459,7 @@ pub fn fetch_add(&self, val: int, order: Ordering) -> int { unsafe { atomic_add(self.v.get(), val, order) } } - /// Subtract from the current value, returning the previous + /// Subtract an int from the current value, returning the previous value. /// /// # Examples /// @@ -400,7 +476,7 @@ pub fn fetch_sub(&self, val: int, order: Ordering) -> int { unsafe { atomic_sub(self.v.get(), val, order) } } - /// Bitwise and with the current value, returning the previous + /// Bitwise and with the current int, returning the previous value. /// /// # Examples /// @@ -416,7 +492,7 @@ pub fn fetch_and(&self, val: int, order: Ordering) -> int { unsafe { atomic_and(self.v.get(), val, order) } } - /// Bitwise or with the current value, returning the previous + /// Bitwise or with the current int, returning the previous value. /// /// # Examples /// @@ -432,7 +508,7 @@ pub fn fetch_or(&self, val: int, order: Ordering) -> int { unsafe { atomic_or(self.v.get(), val, order) } } - /// Bitwise xor with the current value, returning the previous + /// Bitwise xor with the current int, returning the previous value. /// /// # Examples /// @@ -450,25 +526,57 @@ pub fn fetch_xor(&self, val: int, order: Ordering) -> int { } impl AtomicUint { - /// Create a new `AtomicUint` + /// Creates a new `AtomicUint`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::AtomicUint; + /// + /// let atomic_forty_two = AtomicUint::new(42u); + /// ``` #[inline] #[stable] pub fn new(v: uint) -> AtomicUint { AtomicUint { v: UnsafeCell::new(v), nocopy: marker::NoCopy } } - /// Load the value + /// Loads a value from the uint. + /// + /// `load` takes an `Ordering` argument which describes the memory ordering of this operation. /// /// # Panics /// /// Panics if `order` is `Release` or `AcqRel`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicUint, Ordering}; + /// + /// let some_uint = AtomicUint::new(5); + /// + /// let value = some_uint.load(Ordering::Relaxed); + /// ``` #[inline] #[stable] pub fn load(&self, order: Ordering) -> uint { unsafe { atomic_load(self.v.get() as *const uint, order) } } - /// Store the value + /// Stores a value into the uint. + /// + /// `store` takes an `Ordering` argument which describes the memory ordering of this operation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicUint, Ordering}; + /// + /// let some_uint = AtomicUint::new(5); + /// + /// some_uint.store(10, Ordering::Relaxed); + /// ``` /// /// # Panics /// @@ -479,25 +587,48 @@ pub fn store(&self, val: uint, order: Ordering) { unsafe { atomic_store(self.v.get(), val, order); } } - /// Store a value, returning the old value + /// Stores a value into the uint, returning the old value. + /// + /// `swap` takes an `Ordering` argument which describes the memory ordering of this operation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicUint, Ordering}; + /// + /// let some_uint = AtomicUint::new(5); + /// + /// let value = some_uint.swap(10, Ordering::Relaxed); + /// ``` #[inline] #[stable] pub fn swap(&self, val: uint, order: Ordering) -> uint { unsafe { atomic_swap(self.v.get(), val, order) } } - /// If the current value is the same as expected, store a new value + /// Stores a value into the uint if the current value is the same as the expected value. /// - /// Compare the current value with `old`; if they are the same then - /// replace the current value with `new`. Return the previous value. /// If the return value is equal to `old` then the value was updated. + /// + /// `compare_and_swap` also takes an `Ordering` argument which describes the memory ordering of + /// this operation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicUint, Ordering}; + /// + /// let some_uint = AtomicUint::new(5); + /// + /// let value = some_uint.compare_and_swap(5, 10, Ordering::Relaxed); + /// ``` #[inline] #[stable] pub fn compare_and_swap(&self, old: uint, new: uint, order: Ordering) -> uint { unsafe { atomic_compare_and_swap(self.v.get(), old, new, order) } } - /// Add to the current value, returning the previous + /// Add to the current uint, returning the previous value. /// /// # Examples /// @@ -514,7 +645,7 @@ pub fn fetch_add(&self, val: uint, order: Ordering) -> uint { unsafe { atomic_add(self.v.get(), val, order) } } - /// Subtract from the current value, returning the previous + /// Subtract from the current uint, returning the previous value. /// /// # Examples /// @@ -531,7 +662,7 @@ pub fn fetch_sub(&self, val: uint, order: Ordering) -> uint { unsafe { atomic_sub(self.v.get(), val, order) } } - /// Bitwise and with the current value, returning the previous + /// Bitwise and with the current uint, returning the previous value. /// /// # Examples /// @@ -547,7 +678,7 @@ pub fn fetch_and(&self, val: uint, order: Ordering) -> uint { unsafe { atomic_and(self.v.get(), val, order) } } - /// Bitwise or with the current value, returning the previous + /// Bitwise or with the current uint, returning the previous value. /// /// # Examples /// @@ -563,7 +694,7 @@ pub fn fetch_or(&self, val: uint, order: Ordering) -> uint { unsafe { atomic_or(self.v.get(), val, order) } } - /// Bitwise xor with the current value, returning the previous + /// Bitwise xor with the current uint, returning the previous value. /// /// # Examples /// @@ -581,18 +712,40 @@ pub fn fetch_xor(&self, val: uint, order: Ordering) -> uint { } impl AtomicPtr { - /// Create a new `AtomicPtr` + /// Creates a new `AtomicPtr`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::AtomicPtr; + /// + /// let ptr = &mut 5i; + /// let atomic_ptr = AtomicPtr::new(ptr); + /// ``` #[inline] #[stable] pub fn new(p: *mut T) -> AtomicPtr { AtomicPtr { p: UnsafeCell::new(p as uint), nocopy: marker::NoCopy } } - /// Load the value + /// Loads a value from the pointer. + /// + /// `load` takes an `Ordering` argument which describes the memory ordering of this operation. /// /// # Panics /// /// Panics if `order` is `Release` or `AcqRel`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr = &mut 5i; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let value = some_ptr.load(Ordering::Relaxed); + /// ``` #[inline] #[stable] pub fn load(&self, order: Ordering) -> *mut T { @@ -601,7 +754,22 @@ pub fn load(&self, order: Ordering) -> *mut T { } } - /// Store the value + /// Stores a value into the pointer. + /// + /// `store` takes an `Ordering` argument which describes the memory ordering of this operation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr = &mut 5i; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let other_ptr = &mut 10i; + /// + /// some_ptr.store(other_ptr, Ordering::Relaxed); + /// ``` /// /// # Panics /// @@ -612,18 +780,48 @@ pub fn store(&self, ptr: *mut T, order: Ordering) { unsafe { atomic_store(self.p.get(), ptr as uint, order); } } - /// Store a value, returning the old value + /// Stores a value into the pointer, returning the old value. + /// + /// `swap` takes an `Ordering` argument which describes the memory ordering of this operation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr = &mut 5i; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let other_ptr = &mut 10i; + /// + /// let value = some_ptr.swap(other_ptr, Ordering::Relaxed); + /// ``` #[inline] #[stable] pub fn swap(&self, ptr: *mut T, order: Ordering) -> *mut T { unsafe { atomic_swap(self.p.get(), ptr as uint, order) as *mut T } } - /// If the current value is the same as expected, store a new value + /// Stores a value into the pointer if the current value is the same as the expected value. /// - /// Compare the current value with `old`; if they are the same then - /// replace the current value with `new`. Return the previous value. /// If the return value is equal to `old` then the value was updated. + /// + /// `compare_and_swap` also takes an `Ordering` argument which describes the memory ordering of + /// this operation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr = &mut 5i; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let other_ptr = &mut 10i; + /// let another_ptr = &mut 10i; + /// + /// let value = some_ptr.compare_and_swap(other_ptr, another_ptr, Ordering::Relaxed); + /// ``` #[inline] #[stable] pub fn compare_and_swap(&self, old: *mut T, new: *mut T, order: Ordering) -> *mut T { @@ -777,7 +975,7 @@ unsafe fn atomic_xor(dst: *mut T, val: T, order: Ordering) -> T { /// /// # Panics /// -/// Panics if `order` is `Relaxed` +/// Panics if `order` is `Relaxed`. #[inline] #[stable] pub fn fence(order: Ordering) {