From 523360415cca24ac0cbd198804d797dd05c48559 Mon Sep 17 00:00:00 2001 From: James Miller Date: Sat, 25 May 2013 17:51:26 +1200 Subject: [PATCH] Add basic atomic types --- src/libstd/unstable/atomics.rs | 343 +++++++++++++++++++++++++++++++++ src/libstd/unstable/mod.rs | 1 + 2 files changed, 344 insertions(+) create mode 100644 src/libstd/unstable/atomics.rs diff --git a/src/libstd/unstable/atomics.rs b/src/libstd/unstable/atomics.rs new file mode 100644 index 00000000000..09febfebaac --- /dev/null +++ b/src/libstd/unstable/atomics.rs @@ -0,0 +1,343 @@ +// Copyright 2012-2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + * Atomic types + */ + +use unstable::intrinsics; +use cast; +use option::{Option,Some,None}; + +pub struct AtomicFlag { + priv v:int +} + +pub struct AtomicBool { + priv v:uint +} + +pub struct AtomicInt { + priv v:int +} + +pub struct AtomicUint { + priv v:uint +} + +pub struct AtomicPtr { + priv p:~T +} + +pub enum Ordering { + Release, + Acquire, + SeqCst +} + + +impl AtomicFlag { + + fn new() -> AtomicFlag { + AtomicFlag { v: 0 } + } + + /** + * Clears the atomic flag + */ + #[inline(always)] + fn clear(&mut self, order:Ordering) { + unsafe {atomic_store(&mut self.v, 0, order)} + } + + #[inline(always)] + /** + * Sets the flag if it was previously unset, returns the previous value of the + * flag. + */ + fn test_and_set(&mut self, order:Ordering) -> bool { + unsafe {atomic_compare_and_swap(&mut self.v, 0, 1, order) > 0} + } +} + +impl AtomicBool { + fn new(v:bool) -> AtomicBool { + AtomicBool { v: if v { 1 } else { 0 } } + } + + #[inline(always)] + fn load(&self, order:Ordering) -> bool { + unsafe { atomic_load(&self.v, order) > 0 } + } + + #[inline(always)] + fn store(&mut self, val:bool, order:Ordering) { + let val = if val { 1 } else { 0 }; + + unsafe { atomic_store(&mut self.v, val, order); } + } + + #[inline(always)] + fn swap(&mut self, val:bool, order:Ordering) -> bool { + let val = if val { 1 } else { 0 }; + + unsafe { atomic_swap(&mut self.v, val, order) > 0} + } + + #[inline(always)] + fn compare_and_swap(&mut self, old: bool, new: bool, order:Ordering) -> bool { + let old = if old { 1 } else { 0 }; + let new = if new { 1 } else { 0 }; + + unsafe { atomic_compare_and_swap(&mut self.v, old, new, order) > 0 } + } +} + +impl AtomicInt { + fn new(v:int) -> AtomicInt { + AtomicInt { v:v } + } + + #[inline(always)] + fn load(&self, order:Ordering) -> int { + unsafe { atomic_load(&self.v, order) } + } + + #[inline(always)] + fn store(&mut self, val:int, order:Ordering) { + unsafe { atomic_store(&mut self.v, val, order); } + } + + #[inline(always)] + fn swap(&mut self, val:int, order:Ordering) -> int { + unsafe { atomic_swap(&mut self.v, val, order) } + } + + #[inline(always)] + fn compare_and_swap(&mut self, old: int, new: int, order:Ordering) -> int { + unsafe { atomic_compare_and_swap(&mut self.v, old, new, order) } + } + + #[inline(always)] + fn fetch_add(&mut self, val:int, order:Ordering) -> int { + unsafe { atomic_add(&mut self.v, val, order) } + } + + #[inline(always)] + fn fetch_sub(&mut self, val:int, order:Ordering) -> int { + unsafe { atomic_sub(&mut self.v, val, order) } + } +} + +impl AtomicUint { + fn new(v:uint) -> AtomicUint { + AtomicUint { v:v } + } + + #[inline(always)] + fn load(&self, order:Ordering) -> uint { + unsafe { atomic_load(&self.v, order) } + } + + #[inline(always)] + fn store(&mut self, val:uint, order:Ordering) { + unsafe { atomic_store(&mut self.v, val, order); } + } + + #[inline(always)] + fn swap(&mut self, val:uint, order:Ordering) -> uint { + unsafe { atomic_swap(&mut self.v, val, order) } + } + + #[inline(always)] + fn compare_and_swap(&mut self, old: uint, new: uint, order:Ordering) -> uint { + unsafe { atomic_compare_and_swap(&mut self.v, old, new, order) } + } + + #[inline(always)] + fn fetch_add(&mut self, val:uint, order:Ordering) -> uint { + unsafe { atomic_add(&mut self.v, val, order) } + } + + #[inline(always)] + fn fetch_sub(&mut self, val:uint, order:Ordering) -> uint { + unsafe { atomic_sub(&mut self.v, val, order) } + } +} + +impl AtomicPtr { + fn new(p:~T) -> AtomicPtr { + AtomicPtr { p:p } + } + + /** + * Atomically swaps the stored pointer with the one given. + * + * Returns None if the pointer stored has been taken + */ + #[inline(always)] + fn swap(&mut self, ptr:~T, order:Ordering) -> Option<~T> { + unsafe { + let p = atomic_swap(&mut self.p, ptr, order); + let pv : &uint = cast::transmute(&p); + + if *pv == 0 { + None + } else { + Some(p) + } + } + } + + /** + * Atomically takes the stored pointer out. + * + * Returns None if it was already taken. + */ + #[inline(always)] + fn take(&mut self, order:Ordering) -> Option<~T> { + unsafe { self.swap(cast::transmute(0), order) } + } + + /** + * Atomically stores the given pointer, this will overwrite + * and previous value stored. + */ + #[inline(always)] + fn give(&mut self, ptr:~T, order:Ordering) { + let _ = self.swap(ptr, order); + } + + /** + * Checks to see if the stored pointer has been taken. + */ + fn taken(&self, order:Ordering) -> bool { + unsafe { + let p : ~T = atomic_load(&self.p, order); + + let pv : &uint = cast::transmute(&p); + + cast::forget(p); + *pv == 0 + } + } +} + +#[inline(always)] +pub unsafe fn atomic_store(dst: &mut T, val: T, order:Ordering) { + let dst = cast::transmute(dst); + let val = cast::transmute(val); + + match order { + Release => intrinsics::atomic_store_rel(dst, val), + _ => intrinsics::atomic_store(dst, val) + } +} + +#[inline(always)] +pub unsafe fn atomic_load(dst: &T, order:Ordering) -> T { + let dst = cast::transmute(dst); + + cast::transmute(match order { + Acquire => intrinsics::atomic_load_acq(dst), + _ => intrinsics::atomic_load(dst) + }) +} + +#[inline(always)] +pub unsafe fn atomic_swap(dst: &mut T, val: T, order: Ordering) -> T { + let dst = cast::transmute(dst); + let val = cast::transmute(val); + + cast::transmute(match order { + Acquire => intrinsics::atomic_xchg_acq(dst, val), + Release => intrinsics::atomic_xchg_rel(dst, val), + _ => intrinsics::atomic_xchg(dst, val) + }) +} + +#[inline(always)] +pub unsafe fn atomic_add(dst: &mut T, val: T, order: Ordering) -> T { + let dst = cast::transmute(dst); + let val = cast::transmute(val); + + cast::transmute(match order { + Acquire => intrinsics::atomic_xadd_acq(dst, val), + Release => intrinsics::atomic_xadd_rel(dst, val), + _ => intrinsics::atomic_xadd(dst, val) + }) +} + +#[inline(always)] +pub unsafe fn atomic_sub(dst: &mut T, val: T, order: Ordering) -> T { + let dst = cast::transmute(dst); + let val = cast::transmute(val); + + cast::transmute(match order { + Acquire => intrinsics::atomic_xsub_acq(dst, val), + Release => intrinsics::atomic_xsub_rel(dst, val), + _ => intrinsics::atomic_xsub(dst, val) + }) +} + +#[inline(always)] +pub unsafe fn atomic_compare_and_swap(dst:&mut T, old:T, new:T, order: Ordering) -> T { + let dst = cast::transmute(dst); + let old = cast::transmute(old); + let new = cast::transmute(new); + + cast::transmute(match order { + Acquire => intrinsics::atomic_cxchg_acq(dst, old, new), + Release => intrinsics::atomic_cxchg_rel(dst, old, new), + _ => intrinsics::atomic_cxchg(dst, old, new), + }) +} + +#[cfg(test)] +mod test { + use option::*; + use super::*; + + #[test] + fn flag() { + let mut flg = AtomicFlag::new(); + assert!(!flg.test_and_set(SeqCst)); + assert!(flg.test_and_set(SeqCst)); + + flg.clear(SeqCst); + assert!(!flg.test_and_set(SeqCst)); + } + + #[test] + fn pointer_swap() { + let mut p = AtomicPtr::new(~1); + let a = ~2; + + let b = p.swap(a, SeqCst); + + assert_eq!(b, Some(~1)); + assert_eq!(p.take(SeqCst), Some(~2)); + } + + #[test] + fn pointer_take() { + let mut p = AtomicPtr::new(~1); + + assert_eq!(p.take(SeqCst), Some(~1)); + assert_eq!(p.take(SeqCst), None); + assert!(p.taken(SeqCst)); + + let p2 = ~2; + p.give(p2, SeqCst); + + assert_eq!(p.take(SeqCst), Some(~2)); + } + +} diff --git a/src/libstd/unstable/mod.rs b/src/libstd/unstable/mod.rs index 9681a3f36fc..afdc22a1c63 100644 --- a/src/libstd/unstable/mod.rs +++ b/src/libstd/unstable/mod.rs @@ -25,6 +25,7 @@ #[cfg(not(test))] pub mod lang; pub mod sync; +pub mod atomics; /**