auto merge of #14169 : alexcrichton/rust/atomics, r=sanxiyn
This module is a foundation on which many other algorithms are built. When hardware support is missing, stubs are provided in libcompiler-rt.a, so this should be available on all platforms.
This commit is contained in:
commit
db5ca23118
791
src/libcore/atomics.rs
Normal file
791
src/libcore/atomics.rs
Normal file
@ -0,0 +1,791 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Core atomic primitives
|
||||
|
||||
use intrinsics;
|
||||
use std::kinds::marker;
|
||||
use ty::Unsafe;
|
||||
|
||||
/// An atomic boolean type.
|
||||
pub struct AtomicBool {
|
||||
v: Unsafe<uint>,
|
||||
nocopy: marker::NoCopy
|
||||
}
|
||||
|
||||
/// A signed atomic integer type, supporting basic atomic arithmetic operations
|
||||
pub struct AtomicInt {
|
||||
v: Unsafe<int>,
|
||||
nocopy: marker::NoCopy
|
||||
}
|
||||
|
||||
/// An unsigned atomic integer type, supporting basic atomic arithmetic operations
|
||||
pub struct AtomicUint {
|
||||
v: Unsafe<uint>,
|
||||
nocopy: marker::NoCopy
|
||||
}
|
||||
|
||||
/// An unsafe atomic pointer. Only supports basic atomic operations
|
||||
pub struct AtomicPtr<T> {
|
||||
p: Unsafe<uint>,
|
||||
nocopy: marker::NoCopy
|
||||
}
|
||||
|
||||
/// Atomic memory orderings
|
||||
///
|
||||
/// Memory orderings limit the ways that both the compiler and CPU may reorder
|
||||
/// instructions around atomic operations. At its most restrictive,
|
||||
/// "sequentially consistent" atomics allow neither reads nor writes
|
||||
/// 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
|
||||
pub enum Ordering {
|
||||
/// No ordering constraints, only atomic operations
|
||||
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
|
||||
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
|
||||
Acquire,
|
||||
/// When coupled with a load, uses `Acquire` ordering, and with a store
|
||||
/// `Release` ordering
|
||||
AcqRel,
|
||||
/// Like `AcqRel` with the additional guarantee that all threads see all
|
||||
/// sequentially consistent operations in the same order.
|
||||
SeqCst
|
||||
}
|
||||
|
||||
/// An `AtomicBool` initialized to `false`
|
||||
pub static INIT_ATOMIC_BOOL : AtomicBool = AtomicBool { v: Unsafe{value: 0,
|
||||
marker1: marker::InvariantType},
|
||||
nocopy: marker::NoCopy };
|
||||
/// An `AtomicInt` initialized to `0`
|
||||
pub static INIT_ATOMIC_INT : AtomicInt = AtomicInt { v: Unsafe{value: 0,
|
||||
marker1: marker::InvariantType},
|
||||
nocopy: marker::NoCopy };
|
||||
/// An `AtomicUint` initialized to `0`
|
||||
pub static INIT_ATOMIC_UINT : AtomicUint = AtomicUint { v: Unsafe{value: 0,
|
||||
marker1: marker::InvariantType},
|
||||
nocopy: marker::NoCopy };
|
||||
|
||||
// NB: Needs to be -1 (0b11111111...) to make fetch_nand work correctly
|
||||
static UINT_TRUE: uint = -1;
|
||||
|
||||
impl AtomicBool {
|
||||
/// Create a new `AtomicBool`
|
||||
pub fn new(v: bool) -> AtomicBool {
|
||||
let val = if v { UINT_TRUE } else { 0 };
|
||||
AtomicBool { v: Unsafe::new(val), nocopy: marker::NoCopy }
|
||||
}
|
||||
|
||||
/// Load the value
|
||||
#[inline]
|
||||
pub fn load(&self, order: Ordering) -> bool {
|
||||
unsafe { atomic_load(self.v.get() as *uint, order) > 0 }
|
||||
}
|
||||
|
||||
/// Store the value
|
||||
#[inline]
|
||||
pub fn store(&self, val: bool, order: Ordering) {
|
||||
let val = if val { UINT_TRUE } else { 0 };
|
||||
|
||||
unsafe { atomic_store(self.v.get(), val, order); }
|
||||
}
|
||||
|
||||
/// Store a value, returning the old value
|
||||
#[inline]
|
||||
pub fn swap(&self, val: bool, order: Ordering) -> bool {
|
||||
let val = if val { UINT_TRUE } else { 0 };
|
||||
|
||||
unsafe { atomic_swap(self.v.get(), val, order) > 0 }
|
||||
}
|
||||
|
||||
/// If the current value is the same as expected, store a new 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.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// # // FIXME: Needs PR #12430
|
||||
/// extern crate sync;
|
||||
///
|
||||
/// use sync::Arc;
|
||||
/// use std::sync::atomics::{AtomicBool, SeqCst};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let spinlock = Arc::new(AtomicBool::new(false));
|
||||
/// let spinlock_clone = spin_lock.clone();
|
||||
///
|
||||
/// 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<AtomicBool>, 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);
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn compare_and_swap(&self, old: bool, new: bool, order: Ordering) -> bool {
|
||||
let old = if old { UINT_TRUE } else { 0 };
|
||||
let new = if new { UINT_TRUE } else { 0 };
|
||||
|
||||
unsafe { atomic_compare_and_swap(self.v.get(), old, new, order) > 0 }
|
||||
}
|
||||
|
||||
/// A logical "and" operation
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicBool, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_and(false, SeqCst));
|
||||
/// assert_eq!(false, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_and(true, SeqCst));
|
||||
/// assert_eq!(true, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(false);
|
||||
/// assert_eq!(false, foo.fetch_and(false, SeqCst));
|
||||
/// assert_eq!(false, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn fetch_and(&self, val: bool, order: Ordering) -> bool {
|
||||
let val = if val { UINT_TRUE } else { 0 };
|
||||
|
||||
unsafe { atomic_and(self.v.get(), val, order) > 0 }
|
||||
}
|
||||
|
||||
/// A logical "nand" operation
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicBool, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_nand(false, SeqCst));
|
||||
/// assert_eq!(true, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_nand(true, SeqCst));
|
||||
/// assert_eq!(0, foo.load(SeqCst) as int);
|
||||
/// assert_eq!(false, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(false);
|
||||
/// assert_eq!(false, foo.fetch_nand(false, SeqCst));
|
||||
/// assert_eq!(true, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn fetch_nand(&self, val: bool, order: Ordering) -> bool {
|
||||
let val = if val { UINT_TRUE } else { 0 };
|
||||
|
||||
unsafe { atomic_nand(self.v.get(), val, order) > 0 }
|
||||
}
|
||||
|
||||
/// A logical "or" operation
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicBool, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_or(false, SeqCst));
|
||||
/// assert_eq!(true, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_or(true, SeqCst));
|
||||
/// assert_eq!(true, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(false);
|
||||
/// assert_eq!(false, foo.fetch_or(false, SeqCst));
|
||||
/// assert_eq!(false, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn fetch_or(&self, val: bool, order: Ordering) -> bool {
|
||||
let val = if val { UINT_TRUE } else { 0 };
|
||||
|
||||
unsafe { atomic_or(self.v.get(), val, order) > 0 }
|
||||
}
|
||||
|
||||
/// A logical "xor" operation
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicBool, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_xor(false, SeqCst));
|
||||
/// assert_eq!(true, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_xor(true, SeqCst));
|
||||
/// assert_eq!(false, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(false);
|
||||
/// assert_eq!(false, foo.fetch_xor(false, SeqCst));
|
||||
/// assert_eq!(false, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn fetch_xor(&self, val: bool, order: Ordering) -> bool {
|
||||
let val = if val { UINT_TRUE } else { 0 };
|
||||
|
||||
unsafe { atomic_xor(self.v.get(), val, order) > 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomicInt {
|
||||
/// Create a new `AtomicInt`
|
||||
pub fn new(v: int) -> AtomicInt {
|
||||
AtomicInt {v: Unsafe::new(v), nocopy: marker::NoCopy}
|
||||
}
|
||||
|
||||
/// Load the value
|
||||
#[inline]
|
||||
pub fn load(&self, order: Ordering) -> int {
|
||||
unsafe { atomic_load(self.v.get() as *int, order) }
|
||||
}
|
||||
|
||||
/// Store the value
|
||||
#[inline]
|
||||
pub fn store(&self, val: int, order: Ordering) {
|
||||
unsafe { atomic_store(self.v.get(), val, order); }
|
||||
}
|
||||
|
||||
/// Store a value, returning the old value
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// 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.
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicInt, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicInt::new(0);
|
||||
/// assert_eq!(0, foo.fetch_add(10, SeqCst));
|
||||
/// assert_eq!(10, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicInt, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicInt::new(0);
|
||||
/// assert_eq!(0, foo.fetch_sub(10, SeqCst));
|
||||
/// assert_eq!(-10, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(0b101101);
|
||||
/// assert_eq!(0b101101, foo.fetch_and(0b110011, SeqCst));
|
||||
/// assert_eq!(0b100001, foo.load(SeqCst));
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(0b101101);
|
||||
/// assert_eq!(0b101101, foo.fetch_or(0b110011, SeqCst));
|
||||
/// assert_eq!(0b111111, foo.load(SeqCst));
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(0b101101);
|
||||
/// assert_eq!(0b101101, foo.fetch_xor(0b110011, SeqCst));
|
||||
/// assert_eq!(0b011110, foo.load(SeqCst));
|
||||
#[inline]
|
||||
pub fn fetch_xor(&self, val: int, order: Ordering) -> int {
|
||||
unsafe { atomic_xor(self.v.get(), val, order) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomicUint {
|
||||
/// Create a new `AtomicUint`
|
||||
pub fn new(v: uint) -> AtomicUint {
|
||||
AtomicUint { v: Unsafe::new(v), nocopy: marker::NoCopy }
|
||||
}
|
||||
|
||||
/// Load the value
|
||||
#[inline]
|
||||
pub fn load(&self, order: Ordering) -> uint {
|
||||
unsafe { atomic_load(self.v.get() as *uint, order) }
|
||||
}
|
||||
|
||||
/// Store the value
|
||||
#[inline]
|
||||
pub fn store(&self, val: uint, order: Ordering) {
|
||||
unsafe { atomic_store(self.v.get(), val, order); }
|
||||
}
|
||||
|
||||
/// Store a value, returning the old value
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// 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.
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(0);
|
||||
/// assert_eq!(0, foo.fetch_add(10, SeqCst));
|
||||
/// assert_eq!(10, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(10);
|
||||
/// assert_eq!(10, foo.fetch_sub(10, SeqCst));
|
||||
/// assert_eq!(0, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(0b101101);
|
||||
/// assert_eq!(0b101101, foo.fetch_and(0b110011, SeqCst));
|
||||
/// assert_eq!(0b100001, foo.load(SeqCst));
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(0b101101);
|
||||
/// assert_eq!(0b101101, foo.fetch_or(0b110011, SeqCst));
|
||||
/// assert_eq!(0b111111, foo.load(SeqCst));
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(0b101101);
|
||||
/// assert_eq!(0b101101, foo.fetch_xor(0b110011, SeqCst));
|
||||
/// assert_eq!(0b011110, foo.load(SeqCst));
|
||||
#[inline]
|
||||
pub fn fetch_xor(&self, val: uint, order: Ordering) -> uint {
|
||||
unsafe { atomic_xor(self.v.get(), val, order) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AtomicPtr<T> {
|
||||
/// Create a new `AtomicPtr`
|
||||
pub fn new(p: *mut T) -> AtomicPtr<T> {
|
||||
AtomicPtr { p: Unsafe::new(p as uint), nocopy: marker::NoCopy }
|
||||
}
|
||||
|
||||
/// Load the value
|
||||
#[inline]
|
||||
pub fn load(&self, order: Ordering) -> *mut T {
|
||||
unsafe {
|
||||
atomic_load(self.p.get() as **mut T, order) as *mut T
|
||||
}
|
||||
}
|
||||
|
||||
/// Store the value
|
||||
#[inline]
|
||||
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
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// 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.
|
||||
#[inline]
|
||||
pub fn compare_and_swap(&self, old: *mut T, new: *mut T, order: Ordering) -> *mut T {
|
||||
unsafe {
|
||||
atomic_compare_and_swap(self.p.get(), old as uint,
|
||||
new as uint, order) as *mut T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_store<T>(dst: *mut T, val: T, order:Ordering) {
|
||||
match order {
|
||||
Release => intrinsics::atomic_store_rel(dst, val),
|
||||
Relaxed => intrinsics::atomic_store_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_store(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_load<T>(dst: *T, order:Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_load_acq(dst),
|
||||
Relaxed => intrinsics::atomic_load_relaxed(dst),
|
||||
_ => intrinsics::atomic_load(dst)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_swap<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_xchg_acq(dst, val),
|
||||
Release => intrinsics::atomic_xchg_rel(dst, val),
|
||||
AcqRel => intrinsics::atomic_xchg_acqrel(dst, val),
|
||||
Relaxed => intrinsics::atomic_xchg_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_xchg(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the old value (like __sync_fetch_and_add).
|
||||
#[inline]
|
||||
unsafe fn atomic_add<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_xadd_acq(dst, val),
|
||||
Release => intrinsics::atomic_xadd_rel(dst, val),
|
||||
AcqRel => intrinsics::atomic_xadd_acqrel(dst, val),
|
||||
Relaxed => intrinsics::atomic_xadd_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_xadd(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the old value (like __sync_fetch_and_sub).
|
||||
#[inline]
|
||||
unsafe fn atomic_sub<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_xsub_acq(dst, val),
|
||||
Release => intrinsics::atomic_xsub_rel(dst, val),
|
||||
AcqRel => intrinsics::atomic_xsub_acqrel(dst, val),
|
||||
Relaxed => intrinsics::atomic_xsub_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_xsub(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_compare_and_swap<T>(dst: *mut T, old:T, new:T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_cxchg_acq(dst, old, new),
|
||||
Release => intrinsics::atomic_cxchg_rel(dst, old, new),
|
||||
AcqRel => intrinsics::atomic_cxchg_acqrel(dst, old, new),
|
||||
Relaxed => intrinsics::atomic_cxchg_relaxed(dst, old, new),
|
||||
_ => intrinsics::atomic_cxchg(dst, old, new),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_and<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_and_acq(dst, val),
|
||||
Release => intrinsics::atomic_and_rel(dst, val),
|
||||
AcqRel => intrinsics::atomic_and_acqrel(dst, val),
|
||||
Relaxed => intrinsics::atomic_and_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_and(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_nand<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_nand_acq(dst, val),
|
||||
Release => intrinsics::atomic_nand_rel(dst, val),
|
||||
AcqRel => intrinsics::atomic_nand_acqrel(dst, val),
|
||||
Relaxed => intrinsics::atomic_nand_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_nand(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_or<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_or_acq(dst, val),
|
||||
Release => intrinsics::atomic_or_rel(dst, val),
|
||||
AcqRel => intrinsics::atomic_or_acqrel(dst, val),
|
||||
Relaxed => intrinsics::atomic_or_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_or(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_xor<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_xor_acq(dst, val),
|
||||
Release => intrinsics::atomic_xor_rel(dst, val),
|
||||
AcqRel => intrinsics::atomic_xor_acqrel(dst, val),
|
||||
Relaxed => intrinsics::atomic_xor_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_xor(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// An atomic fence.
|
||||
///
|
||||
/// A fence 'A' which has `Release` ordering semantics, synchronizes with a
|
||||
/// fence 'B' with (at least) `Acquire` semantics, if and only if there exists
|
||||
/// atomic operations X and Y, both operating on some atomic object 'M' such
|
||||
/// that A is sequenced before X, Y is synchronized before B and Y observers
|
||||
/// the change to M. This provides a happens-before dependence between A and B.
|
||||
///
|
||||
/// Atomic operations with `Release` or `Acquire` semantics can also synchronize
|
||||
/// with a fence.
|
||||
///
|
||||
/// A fence with has `SeqCst` ordering, in addition to having both `Acquire` and
|
||||
/// `Release` semantics, participates in the global program order of the other
|
||||
/// `SeqCst` operations and/or fences.
|
||||
///
|
||||
/// Accepts `Acquire`, `Release`, `AcqRel` and `SeqCst` orderings.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Fails if `order` is `Relaxed`
|
||||
#[inline]
|
||||
pub fn fence(order: Ordering) {
|
||||
unsafe {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_fence_acq(),
|
||||
Release => intrinsics::atomic_fence_rel(),
|
||||
AcqRel => intrinsics::atomic_fence_acqrel(),
|
||||
SeqCst => intrinsics::atomic_fence(),
|
||||
Relaxed => fail!("there is no such thing as a relaxed fence")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn bool_() {
|
||||
let a = AtomicBool::new(false);
|
||||
assert_eq!(a.compare_and_swap(false, true, SeqCst), false);
|
||||
assert_eq!(a.compare_and_swap(false, true, SeqCst), true);
|
||||
|
||||
a.store(false, SeqCst);
|
||||
assert_eq!(a.compare_and_swap(false, true, SeqCst), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bool_and() {
|
||||
let a = AtomicBool::new(true);
|
||||
assert_eq!(a.fetch_and(false, SeqCst),true);
|
||||
assert_eq!(a.load(SeqCst),false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uint_and() {
|
||||
let x = AtomicUint::new(0xf731);
|
||||
assert_eq!(x.fetch_and(0x137f, SeqCst), 0xf731);
|
||||
assert_eq!(x.load(SeqCst), 0xf731 & 0x137f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uint_or() {
|
||||
let x = AtomicUint::new(0xf731);
|
||||
assert_eq!(x.fetch_or(0x137f, SeqCst), 0xf731);
|
||||
assert_eq!(x.load(SeqCst), 0xf731 | 0x137f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uint_xor() {
|
||||
let x = AtomicUint::new(0xf731);
|
||||
assert_eq!(x.fetch_xor(0x137f, SeqCst), 0xf731);
|
||||
assert_eq!(x.load(SeqCst), 0xf731 ^ 0x137f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_and() {
|
||||
let x = AtomicInt::new(0xf731);
|
||||
assert_eq!(x.fetch_and(0x137f, SeqCst), 0xf731);
|
||||
assert_eq!(x.load(SeqCst), 0xf731 & 0x137f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_or() {
|
||||
let x = AtomicInt::new(0xf731);
|
||||
assert_eq!(x.fetch_or(0x137f, SeqCst), 0xf731);
|
||||
assert_eq!(x.load(SeqCst), 0xf731 | 0x137f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_xor() {
|
||||
let x = AtomicInt::new(0xf731);
|
||||
assert_eq!(x.fetch_xor(0x137f, SeqCst), 0xf731);
|
||||
assert_eq!(x.load(SeqCst), 0xf731 ^ 0x137f);
|
||||
}
|
||||
|
||||
static mut S_BOOL : AtomicBool = INIT_ATOMIC_BOOL;
|
||||
static mut S_INT : AtomicInt = INIT_ATOMIC_INT;
|
||||
static mut S_UINT : AtomicUint = INIT_ATOMIC_UINT;
|
||||
|
||||
#[test]
|
||||
fn static_init() {
|
||||
unsafe {
|
||||
assert!(!S_BOOL.load(SeqCst));
|
||||
assert!(S_INT.load(SeqCst) == 0);
|
||||
assert!(S_UINT.load(SeqCst) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn different_sizes() {
|
||||
unsafe {
|
||||
let mut slot = 0u16;
|
||||
assert_eq!(super::atomic_swap(&mut slot, 1, SeqCst), 0);
|
||||
|
||||
let mut slot = 0u8;
|
||||
assert_eq!(super::atomic_compare_and_swap(&mut slot, 1, 2, SeqCst), 0);
|
||||
|
||||
let slot = 0u32;
|
||||
assert_eq!(super::atomic_load(&slot, SeqCst), 0);
|
||||
|
||||
let mut slot = 0u64;
|
||||
super::atomic_store(&mut slot, 2, SeqCst);
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
|
||||
//! Failure support for libcore
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(dead_code, missing_doc)]
|
||||
|
||||
#[cfg(not(test))]
|
||||
use str::raw::c_str_to_static_slice;
|
||||
|
@ -9,6 +9,27 @@
|
||||
// except according to those terms.
|
||||
|
||||
//! The Rust core library
|
||||
//!
|
||||
//! This library is meant to represent the core functionality of rust that is
|
||||
//! maximally portable to other platforms. To that exent, this library has no
|
||||
//! knowledge of things like allocation, threads, I/O, etc. This library is
|
||||
//! built on the assumption of a few existing symbols:
|
||||
//!
|
||||
//! * `memcpy`, `memcmp`, `memset` - These are core memory routines which are
|
||||
//! often generated by LLVM. Additionally, this library can make explicit
|
||||
//! calls to these funcitons. Their signatures are the same as found in C.
|
||||
//!
|
||||
//! * `rust_begin_unwind` - This function takes three arguments, a
|
||||
//! `&fmt::Arguments`, a `&str`, and a `uint. These three arguments dictate
|
||||
//! the failure message, the file at which failure was invoked, and the line.
|
||||
//! It is up to consumers of this core library to define this failure
|
||||
//! function; it is only required to never return.
|
||||
//!
|
||||
//! Currently, it is *not* recommended to use the core library. The stable
|
||||
//! functionality of libcore is exported directly into the
|
||||
//! [standard library](../std/index.html). The composition of this library is
|
||||
//! subject to change over time, only the interface exposed through libstd is
|
||||
//! intended to be stable.
|
||||
|
||||
#![crate_id = "core#0.11.0-pre"]
|
||||
#![license = "MIT/ASL2"]
|
||||
@ -81,9 +102,11 @@ pub mod container;
|
||||
mod unicode;
|
||||
mod unit;
|
||||
pub mod any;
|
||||
pub mod atomics;
|
||||
pub mod bool;
|
||||
pub mod cell;
|
||||
pub mod char;
|
||||
pub mod failure;
|
||||
pub mod finally;
|
||||
pub mod iter;
|
||||
pub mod option;
|
||||
@ -96,13 +119,15 @@ pub mod tuple;
|
||||
#[cfg(stage0, not(test))]
|
||||
pub mod owned;
|
||||
|
||||
mod failure;
|
||||
|
||||
// FIXME: this module should not exist. Once owned allocations are no longer a
|
||||
// language type, this module can move outside to the owned allocation
|
||||
// crate.
|
||||
mod should_not_exist;
|
||||
|
||||
mod core {
|
||||
pub use failure;
|
||||
}
|
||||
|
||||
mod std {
|
||||
pub use clone;
|
||||
pub use cmp;
|
||||
|
@ -17,7 +17,7 @@ macro_rules! fail(
|
||||
fail!("explicit failure")
|
||||
);
|
||||
($msg:expr) => (
|
||||
::failure::begin_unwind($msg, file!(), line!())
|
||||
::core::failure::begin_unwind($msg, file!(), line!())
|
||||
);
|
||||
)
|
||||
|
||||
|
@ -8,6 +8,22 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// As noted by this file name, this file should not exist. This file should not
|
||||
// exist because it performs allocations which libcore is not allowed to do. The
|
||||
// reason for this file's existence is that the `~[T]` and `~str` types are
|
||||
// language-defined types. Traits are defined in libcore, such as `Clone`, which
|
||||
// these types need to implement, but the implementation can only be found in
|
||||
// libcore.
|
||||
//
|
||||
// Plan of attack for solving this problem:
|
||||
//
|
||||
// 1. Implement DST
|
||||
// 2. Make `Box<T>` not a language feature
|
||||
// 3. Move `Box<T>` to a separate crate, liballoc.
|
||||
// 4. Implement relevant trais in liballoc, not libcore
|
||||
//
|
||||
// Currently, no progress has been made on this list.
|
||||
|
||||
use char::Char;
|
||||
use clone::Clone;
|
||||
use container::Container;
|
||||
|
@ -105,39 +105,15 @@
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
#![allow(missing_doc)]
|
||||
|
||||
use intrinsics;
|
||||
use mem;
|
||||
use ops::Drop;
|
||||
use option::{Option,Some,None};
|
||||
use owned::Box;
|
||||
use std::kinds::marker;
|
||||
use ty::Unsafe;
|
||||
|
||||
/// An atomic boolean type.
|
||||
pub struct AtomicBool {
|
||||
v: Unsafe<uint>,
|
||||
nocopy: marker::NoCopy
|
||||
}
|
||||
|
||||
/// A signed atomic integer type, supporting basic atomic arithmetic operations
|
||||
pub struct AtomicInt {
|
||||
v: Unsafe<int>,
|
||||
nocopy: marker::NoCopy
|
||||
}
|
||||
|
||||
/// An unsigned atomic integer type, supporting basic atomic arithmetic operations
|
||||
pub struct AtomicUint {
|
||||
v: Unsafe<uint>,
|
||||
nocopy: marker::NoCopy
|
||||
}
|
||||
|
||||
/// An unsafe atomic pointer. Only supports basic atomic operations
|
||||
pub struct AtomicPtr<T> {
|
||||
p: Unsafe<uint>,
|
||||
nocopy: marker::NoCopy
|
||||
}
|
||||
pub use core::atomics::{AtomicBool, AtomicInt, AtomicUint, AtomicPtr};
|
||||
pub use core::atomics::{Ordering, Relaxed, Release, Acquire, AcqRel, SeqCst};
|
||||
pub use core::atomics::{INIT_ATOMIC_BOOL, INIT_ATOMIC_INT, INIT_ATOMIC_UINT};
|
||||
pub use core::atomics::fence;
|
||||
|
||||
/// An atomic, nullable unique pointer
|
||||
///
|
||||
@ -145,544 +121,26 @@ pub struct AtomicPtr<T> {
|
||||
/// owned heap objects across tasks.
|
||||
#[unsafe_no_drop_flag]
|
||||
pub struct AtomicOption<T> {
|
||||
p: Unsafe<uint>,
|
||||
}
|
||||
|
||||
/// Atomic memory orderings
|
||||
///
|
||||
/// Memory orderings limit the ways that both the compiler and CPU may reorder
|
||||
/// instructions around atomic operations. At its most restrictive,
|
||||
/// "sequentially consistent" atomics allow neither reads nor writes
|
||||
/// 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
|
||||
pub enum Ordering {
|
||||
/// No ordering constraints, only atomic operations
|
||||
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
|
||||
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
|
||||
Acquire,
|
||||
/// When coupled with a load, uses `Acquire` ordering, and with a store
|
||||
/// `Release` ordering
|
||||
AcqRel,
|
||||
/// Like `AcqRel` with the additional guarantee that all threads see all
|
||||
/// sequentially consistent operations in the same order.
|
||||
SeqCst
|
||||
}
|
||||
|
||||
/// An `AtomicBool` initialized to `false`
|
||||
pub static INIT_ATOMIC_BOOL : AtomicBool = AtomicBool { v: Unsafe{value: 0,
|
||||
marker1: marker::InvariantType},
|
||||
nocopy: marker::NoCopy };
|
||||
/// An `AtomicInt` initialized to `0`
|
||||
pub static INIT_ATOMIC_INT : AtomicInt = AtomicInt { v: Unsafe{value: 0,
|
||||
marker1: marker::InvariantType},
|
||||
nocopy: marker::NoCopy };
|
||||
/// An `AtomicUint` initialized to `0`
|
||||
pub static INIT_ATOMIC_UINT : AtomicUint = AtomicUint { v: Unsafe{value: 0,
|
||||
marker1: marker::InvariantType},
|
||||
nocopy: marker::NoCopy };
|
||||
|
||||
// NB: Needs to be -1 (0b11111111...) to make fetch_nand work correctly
|
||||
static UINT_TRUE: uint = -1;
|
||||
|
||||
impl AtomicBool {
|
||||
/// Create a new `AtomicBool`
|
||||
pub fn new(v: bool) -> AtomicBool {
|
||||
let val = if v { UINT_TRUE } else { 0 };
|
||||
AtomicBool { v: Unsafe::new(val), nocopy: marker::NoCopy }
|
||||
}
|
||||
|
||||
/// Load the value
|
||||
#[inline]
|
||||
pub fn load(&self, order: Ordering) -> bool {
|
||||
unsafe { atomic_load(self.v.get() as *uint, order) > 0 }
|
||||
}
|
||||
|
||||
/// Store the value
|
||||
#[inline]
|
||||
pub fn store(&self, val: bool, order: Ordering) {
|
||||
let val = if val { UINT_TRUE } else { 0 };
|
||||
|
||||
unsafe { atomic_store(self.v.get(), val, order); }
|
||||
}
|
||||
|
||||
/// Store a value, returning the old value
|
||||
#[inline]
|
||||
pub fn swap(&self, val: bool, order: Ordering) -> bool {
|
||||
let val = if val { UINT_TRUE } else { 0 };
|
||||
|
||||
unsafe { atomic_swap(self.v.get(), val, order) > 0 }
|
||||
}
|
||||
|
||||
/// If the current value is the same as expected, store a new 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.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// # // FIXME: Needs PR #12430
|
||||
/// extern crate sync;
|
||||
///
|
||||
/// use sync::Arc;
|
||||
/// use std::sync::atomics::{AtomicBool, SeqCst};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let spinlock = Arc::new(AtomicBool::new(false));
|
||||
/// let spinlock_clone = spin_lock.clone();
|
||||
///
|
||||
/// 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<AtomicBool>, 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);
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn compare_and_swap(&self, old: bool, new: bool, order: Ordering) -> bool {
|
||||
let old = if old { UINT_TRUE } else { 0 };
|
||||
let new = if new { UINT_TRUE } else { 0 };
|
||||
|
||||
unsafe { atomic_compare_and_swap(self.v.get(), old, new, order) > 0 }
|
||||
}
|
||||
|
||||
/// A logical "and" operation
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicBool, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_and(false, SeqCst));
|
||||
/// assert_eq!(false, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_and(true, SeqCst));
|
||||
/// assert_eq!(true, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(false);
|
||||
/// assert_eq!(false, foo.fetch_and(false, SeqCst));
|
||||
/// assert_eq!(false, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn fetch_and(&self, val: bool, order: Ordering) -> bool {
|
||||
let val = if val { UINT_TRUE } else { 0 };
|
||||
|
||||
unsafe { atomic_and(self.v.get(), val, order) > 0 }
|
||||
}
|
||||
|
||||
/// A logical "nand" operation
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicBool, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_nand(false, SeqCst));
|
||||
/// assert_eq!(true, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_nand(true, SeqCst));
|
||||
/// assert_eq!(0, foo.load(SeqCst) as int);
|
||||
/// assert_eq!(false, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(false);
|
||||
/// assert_eq!(false, foo.fetch_nand(false, SeqCst));
|
||||
/// assert_eq!(true, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn fetch_nand(&self, val: bool, order: Ordering) -> bool {
|
||||
let val = if val { UINT_TRUE } else { 0 };
|
||||
|
||||
unsafe { atomic_nand(self.v.get(), val, order) > 0 }
|
||||
}
|
||||
|
||||
/// A logical "or" operation
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicBool, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_or(false, SeqCst));
|
||||
/// assert_eq!(true, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_or(true, SeqCst));
|
||||
/// assert_eq!(true, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(false);
|
||||
/// assert_eq!(false, foo.fetch_or(false, SeqCst));
|
||||
/// assert_eq!(false, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn fetch_or(&self, val: bool, order: Ordering) -> bool {
|
||||
let val = if val { UINT_TRUE } else { 0 };
|
||||
|
||||
unsafe { atomic_or(self.v.get(), val, order) > 0 }
|
||||
}
|
||||
|
||||
/// A logical "xor" operation
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicBool, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_xor(false, SeqCst));
|
||||
/// assert_eq!(true, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(true);
|
||||
/// assert_eq!(true, foo.fetch_xor(true, SeqCst));
|
||||
/// assert_eq!(false, foo.load(SeqCst));
|
||||
///
|
||||
/// let foo = AtomicBool::new(false);
|
||||
/// assert_eq!(false, foo.fetch_xor(false, SeqCst));
|
||||
/// assert_eq!(false, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn fetch_xor(&self, val: bool, order: Ordering) -> bool {
|
||||
let val = if val { UINT_TRUE } else { 0 };
|
||||
|
||||
unsafe { atomic_xor(self.v.get(), val, order) > 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomicInt {
|
||||
/// Create a new `AtomicInt`
|
||||
pub fn new(v: int) -> AtomicInt {
|
||||
AtomicInt {v: Unsafe::new(v), nocopy: marker::NoCopy}
|
||||
}
|
||||
|
||||
/// Load the value
|
||||
#[inline]
|
||||
pub fn load(&self, order: Ordering) -> int {
|
||||
unsafe { atomic_load(self.v.get() as *int, order) }
|
||||
}
|
||||
|
||||
/// Store the value
|
||||
#[inline]
|
||||
pub fn store(&self, val: int, order: Ordering) {
|
||||
unsafe { atomic_store(self.v.get(), val, order); }
|
||||
}
|
||||
|
||||
/// Store a value, returning the old value
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// 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.
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicInt, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicInt::new(0);
|
||||
/// assert_eq!(0, foo.fetch_add(10, SeqCst));
|
||||
/// assert_eq!(10, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicInt, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicInt::new(0);
|
||||
/// assert_eq!(0, foo.fetch_sub(10, SeqCst));
|
||||
/// assert_eq!(-10, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(0b101101);
|
||||
/// assert_eq!(0b101101, foo.fetch_and(0b110011, SeqCst));
|
||||
/// assert_eq!(0b100001, foo.load(SeqCst));
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(0b101101);
|
||||
/// assert_eq!(0b101101, foo.fetch_or(0b110011, SeqCst));
|
||||
/// assert_eq!(0b111111, foo.load(SeqCst));
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(0b101101);
|
||||
/// assert_eq!(0b101101, foo.fetch_xor(0b110011, SeqCst));
|
||||
/// assert_eq!(0b011110, foo.load(SeqCst));
|
||||
#[inline]
|
||||
pub fn fetch_xor(&self, val: int, order: Ordering) -> int {
|
||||
unsafe { atomic_xor(self.v.get(), val, order) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomicUint {
|
||||
/// Create a new `AtomicUint`
|
||||
pub fn new(v: uint) -> AtomicUint {
|
||||
AtomicUint { v: Unsafe::new(v), nocopy: marker::NoCopy }
|
||||
}
|
||||
|
||||
/// Load the value
|
||||
#[inline]
|
||||
pub fn load(&self, order: Ordering) -> uint {
|
||||
unsafe { atomic_load(self.v.get() as *uint, order) }
|
||||
}
|
||||
|
||||
/// Store the value
|
||||
#[inline]
|
||||
pub fn store(&self, val: uint, order: Ordering) {
|
||||
unsafe { atomic_store(self.v.get(), val, order); }
|
||||
}
|
||||
|
||||
/// Store a value, returning the old value
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// 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.
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(0);
|
||||
/// assert_eq!(0, foo.fetch_add(10, SeqCst));
|
||||
/// assert_eq!(10, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(10);
|
||||
/// assert_eq!(10, foo.fetch_sub(10, SeqCst));
|
||||
/// assert_eq!(0, foo.load(SeqCst));
|
||||
/// ```
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(0b101101);
|
||||
/// assert_eq!(0b101101, foo.fetch_and(0b110011, SeqCst));
|
||||
/// assert_eq!(0b100001, foo.load(SeqCst));
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(0b101101);
|
||||
/// assert_eq!(0b101101, foo.fetch_or(0b110011, SeqCst));
|
||||
/// assert_eq!(0b111111, foo.load(SeqCst));
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
///
|
||||
/// let foo = AtomicUint::new(0b101101);
|
||||
/// assert_eq!(0b101101, foo.fetch_xor(0b110011, SeqCst));
|
||||
/// assert_eq!(0b011110, foo.load(SeqCst));
|
||||
#[inline]
|
||||
pub fn fetch_xor(&self, val: uint, order: Ordering) -> uint {
|
||||
unsafe { atomic_xor(self.v.get(), val, order) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AtomicPtr<T> {
|
||||
/// Create a new `AtomicPtr`
|
||||
pub fn new(p: *mut T) -> AtomicPtr<T> {
|
||||
AtomicPtr { p: Unsafe::new(p as uint), nocopy: marker::NoCopy }
|
||||
}
|
||||
|
||||
/// Load the value
|
||||
#[inline]
|
||||
pub fn load(&self, order: Ordering) -> *mut T {
|
||||
unsafe {
|
||||
atomic_load(self.p.get() as **mut T, order) as *mut T
|
||||
}
|
||||
}
|
||||
|
||||
/// Store the value
|
||||
#[inline]
|
||||
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
|
||||
#[inline]
|
||||
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
|
||||
///
|
||||
/// 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.
|
||||
#[inline]
|
||||
pub fn compare_and_swap(&self, old: *mut T, new: *mut T, order: Ordering) -> *mut T {
|
||||
unsafe {
|
||||
atomic_compare_and_swap(self.p.get(), old as uint,
|
||||
new as uint, order) as *mut T
|
||||
}
|
||||
}
|
||||
p: AtomicUint,
|
||||
}
|
||||
|
||||
impl<T> AtomicOption<T> {
|
||||
/// Create a new `AtomicOption`
|
||||
pub fn new(p: Box<T>) -> AtomicOption<T> {
|
||||
unsafe { AtomicOption { p: Unsafe::new(mem::transmute(p)) } }
|
||||
unsafe { AtomicOption { p: AtomicUint::new(mem::transmute(p)) } }
|
||||
}
|
||||
|
||||
/// Create a new `AtomicOption` that doesn't contain a value
|
||||
pub fn empty() -> AtomicOption<T> { AtomicOption { p: Unsafe::new(0) } }
|
||||
pub fn empty() -> AtomicOption<T> { AtomicOption { p: AtomicUint::new(0) } }
|
||||
|
||||
/// Store a value, returning the old value
|
||||
#[inline]
|
||||
pub fn swap(&self, val: Box<T>, order: Ordering) -> Option<Box<T>> {
|
||||
unsafe {
|
||||
let val = mem::transmute(val);
|
||||
let val = unsafe { mem::transmute(val) };
|
||||
|
||||
let p = atomic_swap(self.p.get(), val, order);
|
||||
if p as uint == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(mem::transmute(p))
|
||||
}
|
||||
match self.p.swap(val, order) {
|
||||
0 => None,
|
||||
n => Some(unsafe { mem::transmute(n) }),
|
||||
}
|
||||
}
|
||||
|
||||
@ -702,7 +160,7 @@ impl<T> AtomicOption<T> {
|
||||
unsafe {
|
||||
let val = mem::transmute(val);
|
||||
let expected = mem::transmute(0);
|
||||
let oldval = atomic_compare_and_swap(self.p.get(), expected, val, order);
|
||||
let oldval = self.p.compare_and_swap(expected, val, order);
|
||||
if oldval == expected {
|
||||
None
|
||||
} else {
|
||||
@ -717,7 +175,7 @@ impl<T> AtomicOption<T> {
|
||||
/// result does not get invalidated by another task after this returns.
|
||||
#[inline]
|
||||
pub fn is_empty(&self, order: Ordering) -> bool {
|
||||
unsafe { atomic_load(self.p.get() as *uint, order) as uint == 0 }
|
||||
self.p.load(order) as uint == 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -728,165 +186,11 @@ impl<T> Drop for AtomicOption<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_store<T>(dst: *mut T, val: T, order:Ordering) {
|
||||
match order {
|
||||
Release => intrinsics::atomic_store_rel(dst, val),
|
||||
Relaxed => intrinsics::atomic_store_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_store(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_load<T>(dst: *T, order:Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_load_acq(dst),
|
||||
Relaxed => intrinsics::atomic_load_relaxed(dst),
|
||||
_ => intrinsics::atomic_load(dst)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_swap<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_xchg_acq(dst, val),
|
||||
Release => intrinsics::atomic_xchg_rel(dst, val),
|
||||
AcqRel => intrinsics::atomic_xchg_acqrel(dst, val),
|
||||
Relaxed => intrinsics::atomic_xchg_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_xchg(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the old value (like __sync_fetch_and_add).
|
||||
#[inline]
|
||||
unsafe fn atomic_add<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_xadd_acq(dst, val),
|
||||
Release => intrinsics::atomic_xadd_rel(dst, val),
|
||||
AcqRel => intrinsics::atomic_xadd_acqrel(dst, val),
|
||||
Relaxed => intrinsics::atomic_xadd_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_xadd(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the old value (like __sync_fetch_and_sub).
|
||||
#[inline]
|
||||
unsafe fn atomic_sub<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_xsub_acq(dst, val),
|
||||
Release => intrinsics::atomic_xsub_rel(dst, val),
|
||||
AcqRel => intrinsics::atomic_xsub_acqrel(dst, val),
|
||||
Relaxed => intrinsics::atomic_xsub_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_xsub(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_compare_and_swap<T>(dst: *mut T, old:T, new:T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_cxchg_acq(dst, old, new),
|
||||
Release => intrinsics::atomic_cxchg_rel(dst, old, new),
|
||||
AcqRel => intrinsics::atomic_cxchg_acqrel(dst, old, new),
|
||||
Relaxed => intrinsics::atomic_cxchg_relaxed(dst, old, new),
|
||||
_ => intrinsics::atomic_cxchg(dst, old, new),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_and<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_and_acq(dst, val),
|
||||
Release => intrinsics::atomic_and_rel(dst, val),
|
||||
AcqRel => intrinsics::atomic_and_acqrel(dst, val),
|
||||
Relaxed => intrinsics::atomic_and_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_and(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_nand<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_nand_acq(dst, val),
|
||||
Release => intrinsics::atomic_nand_rel(dst, val),
|
||||
AcqRel => intrinsics::atomic_nand_acqrel(dst, val),
|
||||
Relaxed => intrinsics::atomic_nand_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_nand(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_or<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_or_acq(dst, val),
|
||||
Release => intrinsics::atomic_or_rel(dst, val),
|
||||
AcqRel => intrinsics::atomic_or_acqrel(dst, val),
|
||||
Relaxed => intrinsics::atomic_or_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_or(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
unsafe fn atomic_xor<T>(dst: *mut T, val: T, order: Ordering) -> T {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_xor_acq(dst, val),
|
||||
Release => intrinsics::atomic_xor_rel(dst, val),
|
||||
AcqRel => intrinsics::atomic_xor_acqrel(dst, val),
|
||||
Relaxed => intrinsics::atomic_xor_relaxed(dst, val),
|
||||
_ => intrinsics::atomic_xor(dst, val)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// An atomic fence.
|
||||
///
|
||||
/// A fence 'A' which has `Release` ordering semantics, synchronizes with a
|
||||
/// fence 'B' with (at least) `Acquire` semantics, if and only if there exists
|
||||
/// atomic operations X and Y, both operating on some atomic object 'M' such
|
||||
/// that A is sequenced before X, Y is synchronized before B and Y observers
|
||||
/// the change to M. This provides a happens-before dependence between A and B.
|
||||
///
|
||||
/// Atomic operations with `Release` or `Acquire` semantics can also synchronize
|
||||
/// with a fence.
|
||||
///
|
||||
/// A fence with has `SeqCst` ordering, in addition to having both `Acquire` and
|
||||
/// `Release` semantics, participates in the global program order of the other
|
||||
/// `SeqCst` operations and/or fences.
|
||||
///
|
||||
/// Accepts `Acquire`, `Release`, `AcqRel` and `SeqCst` orderings.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Fails if `order` is `Relaxed`
|
||||
#[inline]
|
||||
pub fn fence(order: Ordering) {
|
||||
unsafe {
|
||||
match order {
|
||||
Acquire => intrinsics::atomic_fence_acq(),
|
||||
Release => intrinsics::atomic_fence_rel(),
|
||||
AcqRel => intrinsics::atomic_fence_acqrel(),
|
||||
SeqCst => intrinsics::atomic_fence(),
|
||||
Relaxed => fail!("there is no such thing as a relaxed fence")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use option::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn bool_() {
|
||||
let a = AtomicBool::new(false);
|
||||
assert_eq!(a.compare_and_swap(false, true, SeqCst), false);
|
||||
assert_eq!(a.compare_and_swap(false, true, SeqCst), true);
|
||||
|
||||
a.store(false, SeqCst);
|
||||
assert_eq!(a.compare_and_swap(false, true, SeqCst), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_empty() {
|
||||
let option: AtomicOption<()> = AtomicOption::empty();
|
||||
@ -900,109 +204,31 @@ mod test {
|
||||
|
||||
let b = p.swap(a, SeqCst);
|
||||
|
||||
assert_eq!(b, Some(box 1));
|
||||
assert_eq!(p.take(SeqCst), Some(box 2));
|
||||
assert!(b == Some(box 1));
|
||||
assert!(p.take(SeqCst) == Some(box 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_take() {
|
||||
let p = AtomicOption::new(box 1);
|
||||
|
||||
assert_eq!(p.take(SeqCst), Some(box 1));
|
||||
assert_eq!(p.take(SeqCst), None);
|
||||
assert!(p.take(SeqCst) == Some(box 1));
|
||||
assert!(p.take(SeqCst) == None);
|
||||
|
||||
let p2 = box 2;
|
||||
p.swap(p2, SeqCst);
|
||||
|
||||
assert_eq!(p.take(SeqCst), Some(box 2));
|
||||
assert!(p.take(SeqCst) == Some(box 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_fill() {
|
||||
let p = AtomicOption::new(box 1);
|
||||
assert!(p.fill(box 2, SeqCst).is_some()); // should fail; shouldn't leak!
|
||||
assert_eq!(p.take(SeqCst), Some(box 1));
|
||||
assert!(p.take(SeqCst) == Some(box 1));
|
||||
|
||||
assert!(p.fill(box 2, SeqCst).is_none()); // shouldn't fail
|
||||
assert_eq!(p.take(SeqCst), Some(box 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bool_and() {
|
||||
let a = AtomicBool::new(true);
|
||||
assert_eq!(a.fetch_and(false, SeqCst),true);
|
||||
assert_eq!(a.load(SeqCst),false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uint_and() {
|
||||
let x = AtomicUint::new(0xf731);
|
||||
assert_eq!(x.fetch_and(0x137f, SeqCst), 0xf731);
|
||||
assert_eq!(x.load(SeqCst), 0xf731 & 0x137f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uint_or() {
|
||||
let x = AtomicUint::new(0xf731);
|
||||
assert_eq!(x.fetch_or(0x137f, SeqCst), 0xf731);
|
||||
assert_eq!(x.load(SeqCst), 0xf731 | 0x137f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uint_xor() {
|
||||
let x = AtomicUint::new(0xf731);
|
||||
assert_eq!(x.fetch_xor(0x137f, SeqCst), 0xf731);
|
||||
assert_eq!(x.load(SeqCst), 0xf731 ^ 0x137f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_and() {
|
||||
let x = AtomicInt::new(0xf731);
|
||||
assert_eq!(x.fetch_and(0x137f, SeqCst), 0xf731);
|
||||
assert_eq!(x.load(SeqCst), 0xf731 & 0x137f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_or() {
|
||||
let x = AtomicInt::new(0xf731);
|
||||
assert_eq!(x.fetch_or(0x137f, SeqCst), 0xf731);
|
||||
assert_eq!(x.load(SeqCst), 0xf731 | 0x137f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_xor() {
|
||||
let x = AtomicInt::new(0xf731);
|
||||
assert_eq!(x.fetch_xor(0x137f, SeqCst), 0xf731);
|
||||
assert_eq!(x.load(SeqCst), 0xf731 ^ 0x137f);
|
||||
}
|
||||
|
||||
static mut S_BOOL : AtomicBool = INIT_ATOMIC_BOOL;
|
||||
static mut S_INT : AtomicInt = INIT_ATOMIC_INT;
|
||||
static mut S_UINT : AtomicUint = INIT_ATOMIC_UINT;
|
||||
|
||||
#[test]
|
||||
fn static_init() {
|
||||
unsafe {
|
||||
assert!(!S_BOOL.load(SeqCst));
|
||||
assert!(S_INT.load(SeqCst) == 0);
|
||||
assert!(S_UINT.load(SeqCst) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn different_sizes() {
|
||||
unsafe {
|
||||
let mut slot = 0u16;
|
||||
assert_eq!(super::atomic_swap(&mut slot, 1, SeqCst), 0);
|
||||
|
||||
let mut slot = 0u8;
|
||||
assert_eq!(super::atomic_compare_and_swap(&mut slot, 1, 2, SeqCst), 0);
|
||||
|
||||
let slot = 0u32;
|
||||
assert_eq!(super::atomic_load(&slot, SeqCst), 0);
|
||||
|
||||
let mut slot = 0u64;
|
||||
super::atomic_store(&mut slot, 2, SeqCst);
|
||||
}
|
||||
assert!(p.take(SeqCst) == Some(box 2));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user