Skip to content

Commit 408367b

Browse files
committed
Add a safe implementation of MutexArc::access* methods
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
1 parent b22861d commit 408367b

File tree

2 files changed

+188
-21
lines changed

2 files changed

+188
-21
lines changed

Diff for: src/libextra/arc.rs

+162-21
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

@@ -190,6 +192,35 @@ impl<T:Send> MutexArc<T> {
190192
MutexArc { x: UnsafeArc::new(data) }
191193
}
192194

195+
196+
/// Refer unsafe_access and access methods for the documentaiton.
197+
#[inline]
198+
unsafe fn lock_and_access<U>(&self, blk: &fn(x: &mut T) -> U) -> U {
199+
let state = self.x.get();
200+
// Borrowck would complain about this if the function were
201+
// not already unsafe. See borrow_rwlock, far below.
202+
do (&(*state).lock).lock {
203+
check_poison(true, (*state).failed);
204+
let _z = PoisonOnFail(&mut (*state).failed);
205+
blk(&mut (*state).data)
206+
}
207+
}
208+
209+
#[inline]
210+
unsafe fn lock_and_access_cond<'x, 'c, U>(&self,
211+
blk: &fn(x: &'x mut T,
212+
c: &'c Condvar) -> U)
213+
-> U {
214+
let state = self.x.get();
215+
do (&(*state).lock).lock_cond |cond| {
216+
check_poison(true, (*state).failed);
217+
let _z = PoisonOnFail(&mut (*state).failed);
218+
blk(&mut (*state).data,
219+
&Condvar {is_mutex: true,
220+
failed: &mut (*state).failed,
221+
cond: cond })
222+
}
223+
}
193224
/**
194225
* Access the underlying mutable data with mutual exclusion from other
195226
* tasks. The argument closure will be run with the mutex locked; all
@@ -215,31 +246,16 @@ impl<T:Send> MutexArc<T> {
215246
*/
216247
#[inline]
217248
pub unsafe fn unsafe_access<U>(&self, blk: &fn(x: &mut T) -> U) -> U {
218-
let state = self.x.get();
219-
// Borrowck would complain about this if the function were
220-
// not already unsafe. See borrow_rwlock, far below.
221-
do (&(*state).lock).lock {
222-
check_poison(true, (*state).failed);
223-
let _z = PoisonOnFail(&mut (*state).failed);
224-
blk(&mut (*state).data)
225-
}
249+
self.lock_and_access(blk)
226250
}
227251

228-
/// As access(), but with a condvar, as sync::mutex.lock_cond().
252+
/// As unsafe_access(), but with a condvar, as sync::mutex.lock_cond().
229253
#[inline]
230254
pub unsafe fn unsafe_access_cond<'x, 'c, U>(&self,
231255
blk: &fn(x: &'x mut T,
232256
c: &'c Condvar) -> U)
233257
-> U {
234-
let state = self.x.get();
235-
do (&(*state).lock).lock_cond |cond| {
236-
check_poison(true, (*state).failed);
237-
let _z = PoisonOnFail(&mut (*state).failed);
238-
blk(&mut (*state).data,
239-
&Condvar {is_mutex: true,
240-
failed: &mut (*state).failed,
241-
cond: cond })
242-
}
258+
self.lock_and_access_cond(blk)
243259
}
244260

245261
/**
@@ -259,6 +275,34 @@ impl<T:Send> MutexArc<T> {
259275
}
260276
}
261277
278+
impl<T:Freeze + Send> MutexArc<T> {
279+
280+
/**
281+
* As unsafe_access.
282+
*
283+
* The difference between access and unsafe_access is that the former
284+
* forbids mutexes to be nested. The purpose of this is to offer a safe
285+
* implementation of both methods access and access_cond to be used instead
286+
* of rwlock in cases where no readers are needed and sightly better performance
287+
* is required.
288+
*
289+
* Both methods have the same failure behaviour as unsafe_access and
290+
* unsafe_access_cond.
291+
*/
292+
#[inline]
293+
pub fn access<U>(&self, blk: &fn(x: &mut T) -> U) -> U {
294+
unsafe { self.lock_and_access(blk) }
295+
}
296+
297+
#[inline]
298+
pub fn access_cond<'x, 'c, U>(&self,
299+
blk: &fn(x: &'x mut T,
300+
c: &'c Condvar) -> U)
301+
-> U {
302+
unsafe { self.lock_and_access_cond(blk) }
303+
}
304+
}
305+
262306
// Common code for {mutex.access,rwlock.write}{,_cond}.
263307
#[inline]
264308
#[doc(hidden)]
@@ -589,6 +633,100 @@ mod tests {
589633
590634
#[test]
591635
fn test_mutex_arc_condvar() {
636+
let arc = ~MutexArc::new(false);
637+
let arc2 = ~arc.clone();
638+
let (p,c) = comm::oneshot();
639+
let (c,p) = (Cell::new(c), Cell::new(p));
640+
do task::spawn || {
641+
// wait until parent gets in
642+
p.take().recv();
643+
do arc2.access_cond |state, cond| {
644+
*state = true;
645+
cond.signal();
646+
}
647+
}
648+
649+
do arc.access_cond |state, cond| {
650+
c.take().send(());
651+
assert!(!*state);
652+
while !*state {
653+
cond.wait();
654+
}
655+
}
656+
}
657+
658+
#[test] #[should_fail]
659+
fn test_arc_condvar_poison() {
660+
let arc = ~MutexArc::new(1);
661+
let arc2 = ~arc.clone();
662+
let (p, c) = comm::stream();
663+
664+
do task::spawn_unlinked || {
665+
let _ = p.recv();
666+
do arc2.access_cond |one, cond| {
667+
cond.signal();
668+
// Parent should fail when it wakes up.
669+
assert_eq!(*one, 0);
670+
}
671+
}
672+
673+
do arc.access_cond |one, cond| {
674+
c.send(());
675+
while *one == 1 {
676+
cond.wait();
677+
}
678+
}
679+
}
680+
681+
#[test] #[should_fail]
682+
fn test_mutex_arc_poison() {
683+
let arc = ~MutexArc::new(1);
684+
let arc2 = ~arc.clone();
685+
do task::try || {
686+
do arc2.access |one| {
687+
assert_eq!(*one, 2);
688+
}
689+
};
690+
do arc.access |one| {
691+
assert_eq!(*one, 1);
692+
}
693+
}
694+
695+
#[test] #[should_fail]
696+
pub fn test_mutex_arc_unwrap_poison() {
697+
let arc = MutexArc::new(1);
698+
let arc2 = ~(&arc).clone();
699+
let (p, c) = comm::stream();
700+
do task::spawn {
701+
do arc2.access |one| {
702+
c.send(());
703+
assert!(*one == 2);
704+
}
705+
}
706+
let _ = p.recv();
707+
let one = arc.unwrap();
708+
assert!(one == 1);
709+
}
710+
711+
#[test]
712+
fn test_unsafe_mutex_arc_nested() {
713+
unsafe {
714+
// Tests nested mutexes and access
715+
// to underlaying data.
716+
let arc = ~MutexArc::new(1);
717+
let arc2 = ~MutexArc::new(*arc);
718+
do task::spawn || {
719+
do (*arc2).unsafe_access |mutex| {
720+
do (*mutex).access |one| {
721+
assert!(*one == 1);
722+
}
723+
}
724+
};
725+
}
726+
}
727+
728+
#[test]
729+
fn test_unsafe_mutex_arc_condvar() {
592730
unsafe {
593731
let arc = MutexArc::new(false);
594732
let arc2 = arc.clone();
@@ -613,7 +751,7 @@ mod tests {
613751
}
614752
615753
#[test] #[should_fail]
616-
fn test_arc_condvar_poison() {
754+
fn test_unsafe_arc_condvar_poison() {
617755
unsafe {
618756
let arc = MutexArc::new(1);
619757
let arc2 = arc.clone();
@@ -637,7 +775,7 @@ mod tests {
637775
}
638776
}
639777
#[test] #[should_fail]
640-
fn test_mutex_arc_poison() {
778+
fn test_unsafe_mutex_arc_poison() {
641779
unsafe {
642780
let arc = MutexArc::new(1);
643781
let arc2 = arc.clone();
@@ -651,8 +789,9 @@ mod tests {
651789
}
652790
}
653791
}
792+
654793
#[test] #[should_fail]
655-
pub fn test_mutex_arc_unwrap_poison() {
794+
pub fn test_unsafe_mutex_arc_unwrap_poison() {
656795
let arc = MutexArc::new(1);
657796
let arc2 = arc.clone();
658797
let (p, c) = comm::stream();
@@ -668,6 +807,7 @@ mod tests {
668807
let one = arc.unwrap();
669808
assert!(one == 1);
670809
}
810+
671811
#[test] #[should_fail]
672812
fn test_rw_arc_poison_wr() {
673813
let arc = RWArc::new(1);
@@ -681,6 +821,7 @@ mod tests {
681821
assert_eq!(*one, 1);
682822
}
683823
}
824+
684825
#[test] #[should_fail]
685826
fn test_rw_arc_poison_ww() {
686827
let arc = RWArc::new(1);

Diff for: src/test/compile-fail/mutex-arc-nested.rs

+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| { // This should fail because MutexArc is not Freeze
22+
}
23+
};
24+
}
25+
26+
fn main() {}

0 commit comments

Comments
 (0)