Skip to content

Commit 91922f0

Browse files
committed
auto merge of #8966 : FlaPer87/rust/issue/7473, r=bblum
Current access methods are nestable and unsafe. This patch renames current methods implementation - prepends unsafe_ - and implements 2 new methods that are both safe and un-nestable. Fixes #7473
2 parents 67555d9 + d0ad251 commit 91922f0

File tree

2 files changed

+131
-56
lines changed

2 files changed

+131
-56
lines changed

src/libextra/arc.rs

+105-56
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,9 @@ impl<T:Freeze + Send> Clone for Arc<T> {
159159

160160
#[doc(hidden)]
161161
struct MutexArcInner<T> { priv lock: Mutex, priv failed: bool, priv data: T }
162+
162163
/// An Arc with mutable data protected by a blocking mutex.
164+
#[no_freeze]
163165
struct MutexArc<T> { priv x: UnsafeArc<MutexArcInner<T>> }
164166

165167

@@ -199,10 +201,10 @@ impl<T:Send> MutexArc<T> {
199201
* The reason this function is 'unsafe' is because it is possible to
200202
* construct a circular reference among multiple Arcs by mutating the
201203
* underlying data. This creates potential for deadlock, but worse, this
202-
* will guarantee a memory leak of all involved Arcs. Using mutex Arcs
204+
* will guarantee a memory leak of all involved Arcs. Using MutexArcs
203205
* inside of other Arcs is safe in absence of circular references.
204206
*
205-
* If you wish to nest mutex_arcs, one strategy for ensuring safety at
207+
* If you wish to nest MutexArcs, one strategy for ensuring safety at
206208
* runtime is to add a "nesting level counter" inside the stored data, and
207209
* when traversing the arcs, assert that they monotonically decrease.
208210
*
@@ -214,7 +216,7 @@ impl<T:Send> MutexArc<T> {
214216
* blocked on the mutex) will also fail immediately.
215217
*/
216218
#[inline]
217-
pub unsafe fn access<U>(&self, blk: &fn(x: &mut T) -> U) -> U {
219+
pub unsafe fn unsafe_access<U>(&self, blk: &fn(x: &mut T) -> U) -> U {
218220
let state = self.x.get();
219221
// Borrowck would complain about this if the function were
220222
// not already unsafe. See borrow_rwlock, far below.
@@ -225,9 +227,9 @@ impl<T:Send> MutexArc<T> {
225227
}
226228
}
227229

228-
/// As access(), but with a condvar, as sync::mutex.lock_cond().
230+
/// As unsafe_access(), but with a condvar, as sync::mutex.lock_cond().
229231
#[inline]
230-
pub unsafe fn access_cond<'x, 'c, U>(&self,
232+
pub unsafe fn unsafe_access_cond<'x, 'c, U>(&self,
231233
blk: &fn(x: &'x mut T,
232234
c: &'c Condvar) -> U)
233235
-> U {
@@ -259,6 +261,39 @@ impl<T:Send> MutexArc<T> {
259261
}
260262
}
261263
264+
impl<T:Freeze + Send> MutexArc<T> {
265+
266+
/**
267+
* As unsafe_access.
268+
*
269+
* The difference between access and unsafe_access is that the former
270+
* forbids mutexes to be nested. While unsafe_access can be used on
271+
* MutexArcs without freezable interiors, this safe version of access
272+
* requires the Freeze bound, which prohibits access on MutexArcs which
273+
* might contain nested MutexArcs inside.
274+
*
275+
* The purpose of this is to offer a safe implementation of MutexArc to be
276+
* used instead of RWArc in cases where no readers are needed and sightly
277+
* better performance is required.
278+
*
279+
* Both methods have the same failure behaviour as unsafe_access and
280+
* unsafe_access_cond.
281+
*/
282+
#[inline]
283+
pub fn access<U>(&self, blk: &fn(x: &mut T) -> U) -> U {
284+
unsafe { self.unsafe_access(blk) }
285+
}
286+
287+
/// As unsafe_access_cond but safe and Freeze.
288+
#[inline]
289+
pub fn access_cond<'x, 'c, U>(&self,
290+
blk: &fn(x: &'x mut T,
291+
c: &'c Condvar) -> U)
292+
-> U {
293+
unsafe { self.unsafe_access_cond(blk) }
294+
}
295+
}
296+
262297
// Common code for {mutex.access,rwlock.write}{,_cond}.
263298
#[inline]
264299
#[doc(hidden)]
@@ -589,85 +624,98 @@ mod tests {
589624
590625
#[test]
591626
fn test_mutex_arc_condvar() {
592-
unsafe {
593-
let arc = MutexArc::new(false);
594-
let arc2 = arc.clone();
595-
let (p, c) = comm::oneshot();
596-
let (c, p) = (Cell::new(c), Cell::new(p));
597-
do task::spawn {
598-
// wait until parent gets in
599-
p.take().recv();
600-
do arc2.access_cond |state, cond| {
601-
*state = true;
602-
cond.signal();
603-
}
627+
let arc = ~MutexArc::new(false);
628+
let arc2 = ~arc.clone();
629+
let (p,c) = comm::oneshot();
630+
let (c,p) = (Cell::new(c), Cell::new(p));
631+
do task::spawn || {
632+
// wait until parent gets in
633+
p.take().recv();
634+
do arc2.access_cond |state, cond| {
635+
*state = true;
636+
cond.signal();
604637
}
605-
do arc.access_cond |state, cond| {
606-
c.take().send(());
607-
assert!(!*state);
608-
while !*state {
609-
cond.wait();
610-
}
638+
}
639+
640+
do arc.access_cond |state, cond| {
641+
c.take().send(());
642+
assert!(!*state);
643+
while !*state {
644+
cond.wait();
611645
}
612646
}
613647
}
614648
615649
#[test] #[should_fail]
616650
fn test_arc_condvar_poison() {
617-
unsafe {
618-
let arc = MutexArc::new(1);
619-
let arc2 = arc.clone();
620-
let (p, c) = comm::stream();
621-
622-
do task::spawn_unlinked {
623-
let _ = p.recv();
624-
do arc2.access_cond |one, cond| {
625-
cond.signal();
626-
// Parent should fail when it wakes up.
627-
assert_eq!(*one, 0);
628-
}
651+
let arc = ~MutexArc::new(1);
652+
let arc2 = ~arc.clone();
653+
let (p, c) = comm::stream();
654+
655+
do task::spawn_unlinked || {
656+
let _ = p.recv();
657+
do arc2.access_cond |one, cond| {
658+
cond.signal();
659+
// Parent should fail when it wakes up.
660+
assert_eq!(*one, 0);
629661
}
662+
}
630663
631-
do arc.access_cond |one, cond| {
632-
c.send(());
633-
while *one == 1 {
634-
cond.wait();
635-
}
664+
do arc.access_cond |one, cond| {
665+
c.send(());
666+
while *one == 1 {
667+
cond.wait();
636668
}
637669
}
638670
}
671+
639672
#[test] #[should_fail]
640673
fn test_mutex_arc_poison() {
641-
unsafe {
642-
let arc = MutexArc::new(1);
643-
let arc2 = arc.clone();
644-
do task::try {
645-
do arc2.access |one| {
646-
assert_eq!(*one, 2);
647-
}
648-
};
649-
do arc.access |one| {
650-
assert_eq!(*one, 1);
674+
let arc = ~MutexArc::new(1);
675+
let arc2 = ~arc.clone();
676+
do task::try || {
677+
do arc2.access |one| {
678+
assert_eq!(*one, 2);
651679
}
680+
};
681+
do arc.access |one| {
682+
assert_eq!(*one, 1);
652683
}
653684
}
685+
654686
#[test] #[should_fail]
655687
pub fn test_mutex_arc_unwrap_poison() {
656688
let arc = MutexArc::new(1);
657-
let arc2 = arc.clone();
689+
let arc2 = ~(&arc).clone();
658690
let (p, c) = comm::stream();
659691
do task::spawn {
660-
unsafe {
661-
do arc2.access |one| {
662-
c.send(());
663-
assert!(*one == 2);
664-
}
692+
do arc2.access |one| {
693+
c.send(());
694+
assert!(*one == 2);
665695
}
666696
}
667697
let _ = p.recv();
668698
let one = arc.unwrap();
669699
assert!(one == 1);
670700
}
701+
702+
#[test]
703+
fn test_unsafe_mutex_arc_nested() {
704+
unsafe {
705+
// Tests nested mutexes and access
706+
// to underlaying data.
707+
let arc = ~MutexArc::new(1);
708+
let arc2 = ~MutexArc::new(*arc);
709+
do task::spawn || {
710+
do (*arc2).unsafe_access |mutex| {
711+
do (*mutex).access |one| {
712+
assert!(*one == 1);
713+
}
714+
}
715+
};
716+
}
717+
}
718+
671719
#[test] #[should_fail]
672720
fn test_rw_arc_poison_wr() {
673721
let arc = RWArc::new(1);
@@ -681,6 +729,7 @@ mod tests {
681729
assert_eq!(*one, 1);
682730
}
683731
}
732+
684733
#[test] #[should_fail]
685734
fn test_rw_arc_poison_ww() {
686735
let arc = RWArc::new(1);
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
extern mod extra;
12+
13+
use std::task;
14+
use extra::arc::{MutexArc};
15+
16+
fn test_mutex_arc_nested() {
17+
let arc = ~MutexArc::new(1);
18+
let arc2 = ~MutexArc::new(*arc);
19+
20+
do task::spawn || {
21+
do (*arc2).access |mutex| { //~ ERROR instantiating a type parameter with an incompatible type
22+
}
23+
};
24+
}
25+
26+
fn main() {}

0 commit comments

Comments
 (0)