Skip to content

Commit 06a8d59

Browse files
committed
Add a copy-on-write container.
1 parent 5ff7b28 commit 06a8d59

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

src/libextra/arc.rs

+108
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,50 @@ impl<'a, T:Freeze + Send> RWReadMode<'a, T> {
549549
}
550550
}
551551

552+
/****************************************************************************
553+
* Copy-on-write Arc
554+
****************************************************************************/
555+
556+
pub struct CowArc<T> { priv x: UnsafeArc<T> }
557+
558+
/// A Copy-on-write Arc functions the same way as an `arc` except it allows
559+
/// mutation of the contents if there is only a single reference to
560+
/// the data. If there are multiple references the data is automatically
561+
/// cloned and the task modifies the cloned data in place of the shared data.
562+
impl<T:Clone+Send> CowArc<T> {
563+
/// Create a copy-on-write atomically reference counted wrapper
564+
#[inline]
565+
pub fn new(data: T) -> CowArc<T> {
566+
CowArc { x: UnsafeArc::new(data) }
567+
}
568+
569+
#[inline]
570+
pub fn get<'a>(&'a self) -> &'a T {
571+
unsafe { &*self.x.get_immut() }
572+
}
573+
574+
/// get a mutable reference to the contents. If there are more then one
575+
/// reference to the contents of the `CowArc` will be cloned
576+
/// and this reference updated to point to the cloned data.
577+
#[inline]
578+
pub fn get_mut<'a>(&'a mut self) -> &'a mut T {
579+
if !self.x.is_owned() {
580+
*self = CowArc::new(self.get().clone())
581+
}
582+
unsafe { &mut *self.x.get() }
583+
}
584+
}
585+
586+
impl<T:Clone+Send> Clone for CowArc<T> {
587+
/// Duplicate a Copy-on-write Arc. See arc::clone for more details.
588+
#[inline]
589+
fn clone(&self) -> CowArc<T> {
590+
CowArc { x: self.x.clone() }
591+
}
592+
}
593+
594+
595+
552596
/****************************************************************************
553597
* Tests
554598
****************************************************************************/
@@ -958,4 +1002,68 @@ mod tests {
9581002
// and I wasn't sure why :( . This is a mediocre "next best" option.
9591003
8.times(|| test_rw_write_cond_downgrade_read_race_helper());
9601004
}
1005+
1006+
#[test]
1007+
fn test_cowarc_clone()
1008+
{
1009+
let cow0 = CowArc::new(75u);
1010+
let cow1 = cow0.clone();
1011+
let cow2 = cow1.clone();
1012+
1013+
assert!(75 == *cow0.get());
1014+
assert!(75 == *cow1.get());
1015+
assert!(75 == *cow2.get());
1016+
1017+
assert!(cow0.get() == cow1.get());
1018+
assert!(cow0.get() == cow2.get());
1019+
}
1020+
1021+
#[test]
1022+
fn test_cowarc_clone_get_mut()
1023+
{
1024+
let mut cow0 = CowArc::new(75u);
1025+
let mut cow1 = cow0.clone();
1026+
let mut cow2 = cow1.clone();
1027+
1028+
assert!(75 == *cow0.get_mut());
1029+
assert!(75 == *cow1.get_mut());
1030+
assert!(75 == *cow2.get_mut());
1031+
1032+
*cow0.get_mut() += 1;
1033+
*cow1.get_mut() += 2;
1034+
*cow2.get_mut() += 3;
1035+
1036+
assert!(76 == *cow0.get());
1037+
assert!(77 == *cow1.get());
1038+
assert!(78 == *cow2.get());
1039+
1040+
// none should point to the same backing memory
1041+
assert!(cow0.get() != cow1.get());
1042+
assert!(cow0.get() != cow2.get());
1043+
assert!(cow1.get() != cow2.get());
1044+
}
1045+
1046+
#[test]
1047+
fn test_cowarc_clone_get_mut2()
1048+
{
1049+
let mut cow0 = CowArc::new(75u);
1050+
let cow1 = cow0.clone();
1051+
let cow2 = cow1.clone();
1052+
1053+
assert!(75 == *cow0.get());
1054+
assert!(75 == *cow1.get());
1055+
assert!(75 == *cow2.get());
1056+
1057+
*cow0.get_mut() += 1;
1058+
1059+
assert!(76 == *cow0.get());
1060+
assert!(75 == *cow1.get());
1061+
assert!(75 == *cow2.get());
1062+
1063+
// cow1 and cow2 should share the same contents
1064+
// cow0 should have a unique reference
1065+
assert!(cow0.get() != cow1.get());
1066+
assert!(cow0.get() != cow2.get());
1067+
assert!(cow1.get() == cow2.get());
1068+
}
9611069
}

src/libstd/sync/arc.rs

+8
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ impl<T: Send> UnsafeArc<T> {
9494
return &(*self.data).data as *T;
9595
}
9696
}
97+
98+
/// checks if this is the only reference to the arc protected data
99+
#[inline]
100+
pub fn is_owned(&self) -> bool {
101+
unsafe {
102+
(*self.data).count.load(Relaxed) == 1
103+
}
104+
}
97105
}
98106

99107
impl<T: Send> Clone for UnsafeArc<T> {

0 commit comments

Comments
 (0)