1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
// 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. //! Atomic types //! //! Atomic types provide primitive shared-memory communication between //! threads, and are the building blocks of other concurrent //! types. //! //! This module defines atomic versions of a select number of primitive //! types, including `AtomicBool`, `AtomicInt`, `AtomicUint`, and `AtomicOption`. //! Atomic types present operations that, when used correctly, synchronize //! updates between threads. //! //! Each method takes an `Ordering` which represents the strength of //! the memory barrier for that operation. These orderings are the //! same as [C++11 atomic orderings][1]. //! //! [1]: http://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync //! //! Atomic variables are safe to share between threads (they implement `Sync`) //! but they do not themselves provide the mechanism for sharing. The most //! common way to share an atomic variable is to put it into an `Arc` (an //! atomically-reference-counted shared pointer). //! //! Most atomic types may be stored in static variables, initialized using //! the provided static initializers like `INIT_ATOMIC_BOOL`. Atomic statics //! are often used for lazy global initialization. //! //! //! # Examples //! //! A simple spinlock: //! //! ``` //! use std::sync::Arc; //! use std::sync::atomic::{AtomicUint, SeqCst}; //! use std::thread::Thread; //! //! fn main() { //! let spinlock = Arc::new(AtomicUint::new(1)); //! //! let spinlock_clone = spinlock.clone(); //! Thread::spawn(move|| { //! spinlock_clone.store(0, SeqCst); //! }).detach(); //! //! // Wait for the other task to release the lock //! while spinlock.load(SeqCst) != 0 {} //! } //! ``` //! //! Transferring a heap object with `AtomicOption`: //! //! ``` //! use std::sync::Arc; //! use std::sync::atomic::{AtomicOption, SeqCst}; //! use std::thread::Thread; //! //! fn main() { //! struct BigObject; //! //! let shared_big_object = Arc::new(AtomicOption::empty()); //! //! let shared_big_object_clone = shared_big_object.clone(); //! Thread::spawn(move|| { //! let unwrapped_big_object = shared_big_object_clone.take(SeqCst); //! if unwrapped_big_object.is_some() { //! println!("got a big object from another task"); //! } else { //! println!("other task hasn't sent big object yet"); //! } //! }).detach(); //! //! shared_big_object.swap(box BigObject, SeqCst); //! } //! ``` //! //! Keep a global count of live tasks: //! //! ``` //! use std::sync::atomic::{AtomicUint, SeqCst, INIT_ATOMIC_UINT}; //! //! static GLOBAL_TASK_COUNT: AtomicUint = INIT_ATOMIC_UINT; //! //! let old_task_count = GLOBAL_TASK_COUNT.fetch_add(1, SeqCst); //! println!("live tasks: {}", old_task_count + 1); //! ``` #![allow(deprecated)] use alloc::boxed::Box; use core::mem; use core::prelude::{Send, Drop, None, Option, Some}; pub use core::atomic::{AtomicBool, AtomicInt, AtomicUint, AtomicPtr}; pub use core::atomic::{INIT_ATOMIC_BOOL, INIT_ATOMIC_INT, INIT_ATOMIC_UINT}; pub use core::atomic::fence; pub use core::atomic::Ordering::{mod, Relaxed, Release, Acquire, AcqRel, SeqCst}; /// An atomic, nullable unique pointer /// /// This can be used as the concurrency primitive for operations that transfer /// owned heap objects across tasks. #[unsafe_no_drop_flag] #[deprecated = "no longer used; will eventually be replaced by a higher-level\ concept like MVar"] pub struct AtomicOption<T> { p: AtomicUint, } impl<T: Send> AtomicOption<T> { /// Create a new `AtomicOption` pub fn new(p: Box<T>) -> AtomicOption<T> { 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: AtomicUint::new(0) } } /// Store a value, returning the old value #[inline] pub fn swap(&self, val: Box<T>, order: Ordering) -> Option<Box<T>> { let val = unsafe { mem::transmute(val) }; match self.p.swap(val, order) { 0 => None, n => Some(unsafe { mem::transmute(n) }), } } /// Remove the value, leaving the `AtomicOption` empty. #[inline] pub fn take(&self, order: Ordering) -> Option<Box<T>> { unsafe { self.swap(mem::transmute(0u), order) } } /// Replace an empty value with a non-empty value. /// /// Succeeds if the option is `None` and returns `None` if so. If /// the option was already `Some`, returns `Some` of the rejected /// value. #[inline] pub fn fill(&self, val: Box<T>, order: Ordering) -> Option<Box<T>> { unsafe { let val = mem::transmute(val); let expected = mem::transmute(0u); let oldval = self.p.compare_and_swap(expected, val, order); if oldval == expected { None } else { Some(mem::transmute(val)) } } } /// Returns `true` if the `AtomicOption` is empty. /// /// Be careful: The caller must have some external method of ensuring the /// result does not get invalidated by another task after this returns. #[inline] pub fn is_empty(&self, order: Ordering) -> bool { self.p.load(order) as uint == 0 } } #[unsafe_destructor] impl<T: Send> Drop for AtomicOption<T> { fn drop(&mut self) { let _ = self.take(SeqCst); } } #[cfg(test)] mod test { use prelude::{Some, None}; use super::*; #[test] fn option_empty() { let option: AtomicOption<()> = AtomicOption::empty(); assert!(option.is_empty(SeqCst)); } #[test] fn option_swap() { let p = AtomicOption::new(box 1i); let a = box 2i; let b = p.swap(a, SeqCst); assert!(b == Some(box 1)); assert!(p.take(SeqCst) == Some(box 2)); } #[test] fn option_take() { let p = AtomicOption::new(box 1i); assert!(p.take(SeqCst) == Some(box 1)); assert!(p.take(SeqCst) == None); let p2 = box 2i; p.swap(p2, SeqCst); assert!(p.take(SeqCst) == Some(box 2)); } #[test] fn option_fill() { let p = AtomicOption::new(box 1i); assert!(p.fill(box 2i, SeqCst).is_some()); // should fail; shouldn't leak! assert!(p.take(SeqCst) == Some(box 1)); assert!(p.fill(box 2i, SeqCst).is_none()); // shouldn't fail assert!(p.take(SeqCst) == Some(box 2)); } }