From de6bf7bcd38472f9e671571e6456f6fd271a2636 Mon Sep 17 00:00:00 2001 From: Colin Sherratt Date: Mon, 30 Dec 2013 19:17:35 -0500 Subject: [PATCH 1/3] Add a copy-on-write container. --- src/libextra/arc.rs | 108 +++++++++++++++++++++++++++++++++++++++++ src/libstd/sync/arc.rs | 8 +++ 2 files changed, 116 insertions(+) diff --git a/src/libextra/arc.rs b/src/libextra/arc.rs index 9615bdc2ad731..003987cbe2075 100644 --- a/src/libextra/arc.rs +++ b/src/libextra/arc.rs @@ -554,6 +554,50 @@ impl<'a, T:Freeze + Send> RWReadMode<'a, T> { } } +/**************************************************************************** + * Copy-on-write Arc + ****************************************************************************/ + +pub struct CowArc { priv x: UnsafeArc } + +/// A Copy-on-write Arc functions the same way as an `arc` except it allows +/// mutation of the contents if there is only a single reference to +/// the data. If there are multiple references the data is automatically +/// cloned and the task modifies the cloned data in place of the shared data. +impl CowArc { + /// Create a copy-on-write atomically reference counted wrapper + #[inline] + pub fn new(data: T) -> CowArc { + CowArc { x: UnsafeArc::new(data) } + } + + #[inline] + pub fn get<'a>(&'a self) -> &'a T { + unsafe { &*self.x.get_immut() } + } + + /// get a mutable reference to the contents. If there are more then one + /// reference to the contents of the `CowArc` will be cloned + /// and this reference updated to point to the cloned data. + #[inline] + pub fn get_mut<'a>(&'a mut self) -> &'a mut T { + if !self.x.is_owned() { + *self = CowArc::new(self.get().clone()) + } + unsafe { &mut *self.x.get() } + } +} + +impl Clone for CowArc { + /// Duplicate a Copy-on-write Arc. See arc::clone for more details. + #[inline] + fn clone(&self) -> CowArc { + CowArc { x: self.x.clone() } + } +} + + + /**************************************************************************** * Tests ****************************************************************************/ @@ -963,4 +1007,68 @@ mod tests { // and I wasn't sure why :( . This is a mediocre "next best" option. for _ in range(0, 8) { test_rw_write_cond_downgrade_read_race_helper(); } } + + #[test] + fn test_cowarc_clone() + { + let cow0 = CowArc::new(75u); + let cow1 = cow0.clone(); + let cow2 = cow1.clone(); + + assert!(75 == *cow0.get()); + assert!(75 == *cow1.get()); + assert!(75 == *cow2.get()); + + assert!(cow0.get() == cow1.get()); + assert!(cow0.get() == cow2.get()); + } + + #[test] + fn test_cowarc_clone_get_mut() + { + let mut cow0 = CowArc::new(75u); + let mut cow1 = cow0.clone(); + let mut cow2 = cow1.clone(); + + assert!(75 == *cow0.get_mut()); + assert!(75 == *cow1.get_mut()); + assert!(75 == *cow2.get_mut()); + + *cow0.get_mut() += 1; + *cow1.get_mut() += 2; + *cow2.get_mut() += 3; + + assert!(76 == *cow0.get()); + assert!(77 == *cow1.get()); + assert!(78 == *cow2.get()); + + // none should point to the same backing memory + assert!(cow0.get() != cow1.get()); + assert!(cow0.get() != cow2.get()); + assert!(cow1.get() != cow2.get()); + } + + #[test] + fn test_cowarc_clone_get_mut2() + { + let mut cow0 = CowArc::new(75u); + let cow1 = cow0.clone(); + let cow2 = cow1.clone(); + + assert!(75 == *cow0.get()); + assert!(75 == *cow1.get()); + assert!(75 == *cow2.get()); + + *cow0.get_mut() += 1; + + assert!(76 == *cow0.get()); + assert!(75 == *cow1.get()); + assert!(75 == *cow2.get()); + + // cow1 and cow2 should share the same contents + // cow0 should have a unique reference + assert!(cow0.get() != cow1.get()); + assert!(cow0.get() != cow2.get()); + assert!(cow1.get() == cow2.get()); + } } diff --git a/src/libstd/sync/arc.rs b/src/libstd/sync/arc.rs index 7b94a3acc2b7d..5c452018b9b7e 100644 --- a/src/libstd/sync/arc.rs +++ b/src/libstd/sync/arc.rs @@ -94,6 +94,14 @@ impl UnsafeArc { return &(*self.data).data as *T; } } + + /// checks if this is the only reference to the arc protected data + #[inline] + pub fn is_owned(&self) -> bool { + unsafe { + (*self.data).count.load(Relaxed) == 1 + } + } } impl Clone for UnsafeArc { From 279e7e6e442326f9a160f2e8b3b051b6757a5280 Mon Sep 17 00:00:00 2001 From: Colin Sherratt Date: Tue, 31 Dec 2013 16:14:58 -0500 Subject: [PATCH 2/3] Added Freeze trait to CowArc --- src/libextra/arc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libextra/arc.rs b/src/libextra/arc.rs index 003987cbe2075..7f8d64053d51c 100644 --- a/src/libextra/arc.rs +++ b/src/libextra/arc.rs @@ -564,7 +564,7 @@ pub struct CowArc { priv x: UnsafeArc } /// mutation of the contents if there is only a single reference to /// the data. If there are multiple references the data is automatically /// cloned and the task modifies the cloned data in place of the shared data. -impl CowArc { +impl CowArc { /// Create a copy-on-write atomically reference counted wrapper #[inline] pub fn new(data: T) -> CowArc { @@ -588,7 +588,7 @@ impl CowArc { } } -impl Clone for CowArc { +impl Clone for CowArc { /// Duplicate a Copy-on-write Arc. See arc::clone for more details. #[inline] fn clone(&self) -> CowArc { From acebd23dc89797ef9390ccfa322b7296bd65b359 Mon Sep 17 00:00:00 2001 From: Daniel Fagnan Date: Thu, 6 Feb 2014 16:37:42 -0700 Subject: [PATCH 3/3] added volatile module. Signed-off-by: Daniel Fagnan --- src/libstd/unstable/mod.rs | 1 + src/libstd/unstable/volatile.rs | 225 ++++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 src/libstd/unstable/volatile.rs diff --git a/src/libstd/unstable/mod.rs b/src/libstd/unstable/mod.rs index 87870ef033142..731ea7ff0d609 100644 --- a/src/libstd/unstable/mod.rs +++ b/src/libstd/unstable/mod.rs @@ -24,6 +24,7 @@ pub mod sync; pub mod mutex; pub mod raw; pub mod stack; +pub mod volatile; /** diff --git a/src/libstd/unstable/volatile.rs b/src/libstd/unstable/volatile.rs new file mode 100644 index 0000000000000..b31e77f7c5142 --- /dev/null +++ b/src/libstd/unstable/volatile.rs @@ -0,0 +1,225 @@ +// Copyright 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + * Volatile Module. + * + * The order of variables, and their execution isn't guaranteed. The compiler may optimize + * statements to make things more efficient. However, when you're working with memory that + * other devices/programs may also be using, you need to be specific about how and when + * that memory is read and written. + * + * That's where Volatile memory comes into play. They tell the compiler not to move the + * order in which the memory is read or written. + * + * + * http://en.wikipedia.org/wiki/Volatile_variable + * http://llvm.org/docs/LangRef.html#volatile-memory-accesses + * + * + * This module provides a number of different types and implementations to work with + * volatile variables. + * + * + * * `VolatileInt`: Provides semantics to work with valued integers. + * * `VolatileBool`: Provides semantics to work with valued booleans. + * * `VolatilePtr`: Provides semantics to work with pointers. + * + * + * Implementing more specific volatile types such as `VolatileUint` and `VolatileU64` is + * fairly easy. + * + * + * This module uses [LLVM's volatile intrinsics][llvm]. + * [llvm]: http://llvm.org/docs/LangRef.html#volatile-memory-accesses + */ + +use unstable::intrinsics; +use cast; +use option::{Option,Some, None}; +use ptr; +use prelude::drop; + +pub struct VolatileBool { + priv v: uint +} + +pub struct VolatileInt { + priv v: int +} + +/// Static initializers for the specific volatile types. +/// +/// ```rust +/// static x: VolatileInt = INIT_VOLATILE_INT; +/// ``` +pub static INIT_VOLATILE_BOOL: VolatileBool = VolatileBool { v: 0 }; +pub static INIT_VOLATILE_INT: VolatileInt = VolatileInt { v: 0 }; + +impl VolatileInt { + pub fn new(val: int) -> VolatileInt { + VolatileInt { v: val } + } + + #[inline] + pub fn store(&mut self, val: int) { + unsafe { + intrinsics::volatile_store(&mut self.v, val); + } + } + + #[inline] + pub fn load(&self) -> int { + unsafe { + intrinsics::volatile_load(&self.v as *int) + } + } +} + + +impl VolatileBool { + pub fn new(val: bool) -> VolatileBool { + VolatileBool { v: if val { 1 } else { 0} } + } + + #[inline] + pub fn store(&mut self, val: bool) { + unsafe { + intrinsics::volatile_store(&mut self.v, if val { 1 } else { 0 } ); + } + } + + #[inline] + pub fn load(&self) -> bool { + unsafe { + intrinsics::volatile_load(&self.v as *uint) != 0 + } + } +} + +pub struct VolatilePtr { + priv ptr: *mut T +} + +/// VolatilePtr implementation that offers a safe wrapper around volatile types. This +/// particular implementation works with pointers; thus, it conforms to the ownership +/// semantics of Rust. +/// +/// If you want to work with values, you may use the more specific implementations, such +/// as `VolatileInt` and `VolatileBool`. +impl VolatilePtr { + /// Create a safe `VolatilePtr` based on the type given. + pub fn new(ptr: ~T) -> VolatilePtr { + VolatilePtr { + ptr: unsafe { cast::transmute(ptr) } + } + } + + /// Store a new pointer as a volatile variable. + /// + /// To make sure that we don't leak memory, we need to take ownership + /// of a potential previous value. Then write the new contents, afterwhich + /// we may drop the old contents. + #[inline] + pub fn store(&mut self, p: ~T) { + unsafe { + // Load the previous data. This will zero out the pointer, but we'll + // have transfered the ownership of the previous data here. + // + // This function needs to drop the memory contents. + let data = self.take(); + + // Replace with the new data. + let ptr: *mut T = cast::transmute(p); + intrinsics::volatile_store(&mut self.ptr, ptr); + + // Drop the old data that we still have ownership of. + match data { + Some(t) => drop(t), + None => { } + } + } + } + + /// VolatilePtr conforms to the ownership semantics of Rust. Because VolatilePtr + /// is storing a raw pointer, we must make sure that the value be transfered. + /// + /// The first operation is to load the memory, check if it's valid, zero out the + /// memory contents, and return an owned pointer. + /// + /// This ensures that we transfer the ownership of the data, and that there aren't + /// multiple copies available. + #[inline] + pub fn take(&mut self) -> Option<~T> { + let ptr = unsafe { intrinsics::volatile_load(&self.ptr) }; + if ptr::is_null(ptr) { return None; } + unsafe { intrinsics::volatile_store(&mut self.ptr, 0 as *mut T); } + Some(unsafe { cast::transmute(ptr) }) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn volatile_ptr_new() { + unsafe { + let mut v = VolatilePtr::new(~5); + assert_eq!(*v.take().unwrap(), 5); + } + } + + #[test] + fn volatile_ptr_store_and_load() { + unsafe { + let mut v = VolatilePtr::new(~1); + assert_eq!(*v.take().unwrap(), 1); + + v.store(~6); + + assert_eq!(*v.take().unwrap(), 6); + } + } + + #[test] + fn volatile_int_load() { + let vint = VolatileInt::new(16); + assert_eq!(vint.load(), 16); + } + + #[test] + fn volatile_int_store() { + let mut vint = VolatileInt::new(20); + vint.store(10); + assert_eq!(vint.load(), 10); + } + + #[test] + fn volatile_static_int() { + static t: VolatileInt = INIT_VOLATILE_INT; + static v: VolatileBool = INIT_VOLATILE_BOOL; + assert_eq!(v.load(), false); + assert_eq!(t.load(), 0); + } + + #[test] + fn volatile_bool_new_and_load() { + let b = VolatileBool::new(true); + assert_eq!(b.load(), true); + } + + #[test] + fn volatile_bool_store() { + let mut b = VolatileBool::new(true); + b.store(false); + assert_eq!(b.load(), false); + } +}