From 06a8d59ded67dfaa589112ceb8a8e031bc575249 Mon Sep 17 00:00:00 2001 From: Colin Sherratt Date: Mon, 30 Dec 2013 19:17:35 -0500 Subject: [PATCH 1/2] 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 a411c4e9185b1..81b183cc0edc1 100644 --- a/src/libextra/arc.rs +++ b/src/libextra/arc.rs @@ -549,6 +549,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 ****************************************************************************/ @@ -958,4 +1002,68 @@ mod tests { // and I wasn't sure why :( . This is a mediocre "next best" option. 8.times(|| 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 4f462a05066ea4ddbe65d3033402e7c80247ddde Mon Sep 17 00:00:00 2001 From: Colin Sherratt Date: Tue, 31 Dec 2013 16:14:58 -0500 Subject: [PATCH 2/2] 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 81b183cc0edc1..9cb33d3c65a94 100644 --- a/src/libextra/arc.rs +++ b/src/libextra/arc.rs @@ -559,7 +559,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 { @@ -583,7 +583,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 {