Change:
* blocked on the mutex) will also fail immediately. */ #[inline] <del> pub unsafe fn unsafe_access<U>(&self, blk: |x: &mut T| -> U) -> U { </del> <ins> pub fn access<U>(&self, blk: |x: &mut T| -> U) -> U { </ins> let state = self.x.get(); <del> // Borrowck would complain about this if the function were // not already unsafe. See borrow_rwlock, far below. (&(*state).lock).lock(|| { check_poison(true, (*state).failed); let _z = PoisonOnFail::new(&mut (*state).failed); blk(&mut (*state).data) }) </del> <ins> unsafe { // Borrowck would complain about this if the code were // not already unsafe. See borrow_rwlock, far below. (&(*state).lock).lock(|| { check_poison(true, (*state).failed); let _z = PoisonOnFail::new(&mut (*state).failed); blk(&mut (*state).data) }) } </ins> } <del> /// As unsafe_access(), but with a condvar, as sync::mutex.lock_cond(). </del> <ins> /// As access(), but with a condvar, as sync::mutex.lock_cond(). </ins> #[inline] <del> pub unsafe fn unsafe_access_cond<U>(&self, blk: |x: &mut T, c: &Condvar| -> U) -> U { </del> <ins> pub fn access_cond<U>(&self, blk: |x: &mut T, c: &Condvar| -> U) -> U { </ins> let state = self.x.get(); <del> (&(*state).lock).lock_cond(|cond| { check_poison(true, (*state).failed); let _z = PoisonOnFail::new(&mut (*state).failed); blk(&mut (*state).data, &Condvar {is_mutex: true, failed: &(*state).failed, cond: cond }) }) } } impl<T:Freeze + Send> MutexArc<T> { /** * As unsafe_access. * * The difference between access and unsafe_access is that the former * forbids mutexes to be nested. While unsafe_access can be used on * MutexArcs without freezable interiors, this safe version of access * requires the Freeze bound, which prohibits access on MutexArcs which * might contain nested MutexArcs inside. * * The purpose of this is to offer a safe implementation of MutexArc to be * used instead of RWArc in cases where no readers are needed and slightly * better performance is required. * * Both methods have the same failure behaviour as unsafe_access and * unsafe_access_cond. */ #[inline] pub fn access<U>(&self, blk: |x: &mut T| -> U) -> U { unsafe { self.unsafe_access(blk) } } /// As unsafe_access_cond but safe and Freeze. #[inline] pub fn access_cond<U>(&self, blk: |x: &mut T, c: &Condvar| -> U) -> U { unsafe { self.unsafe_access_cond(blk) } </del> <ins> unsafe { (&(*state).lock).lock_cond(|cond| { check_poison(true, (*state).failed); let _z = PoisonOnFail::new(&mut (*state).failed); blk(&mut (*state).data, &Condvar {is_mutex: true, failed: &(*state).failed, cond: cond }) }) } </ins> } }

Discussion:
contains two sets of methods to access the Arc's wrapped object. and are tagged unsafe as "it is possible to construct a circular reference among multiple Arcs by mutating the underlying data. This creates potential for deadlock, but worse, this will guarantee a memory leak of all involved Arcs." There are also and methods defined when the inner type is freezable as that guarantees the type can't contain a . However, it's still trivially easy to deadlock using the safe methods: It seems that the consensus in was that possibility of deadlock is inherently unsafe, so should and be removed? cc
Perhaps access should fail when it is called by the same task twice? It would be nice if people didn't have to use unsafe blocks just to get normal stuff done.
Nested locks in the same task aren't the only way you can run into deadlock:
I am aware of that, but it seems to me this particular error would be easy and cheap to catch, so if we aren't doing that yet, we probably should. Personally I don't think the possibility of a deadlock should require an unsafe block.
Consensus in the past has been that deadlock is not unsafe (). You can deadlock with pipes too, with just two lines of code. The non- accessors for are unsafe for a totally different reason, which is documented in . Regarding "cheap and easy to catch" - I don't believe we should try to fail in the single-task deadlock case unless we can reliably fail in all deadlock cases (which is not out of the question but requires scheduler support, see ). A user who is used to seeing a failure message from their previous attempts which had deadlocked would be all the more confused when they find their cross-task deadlocking code does not fail.
Is the possibility of memory leakage really considered unsafe? doesn't seem to share that assumption, but I may be missing something there.
Neither or provides a way to leak memory via the safe constructors.
We currently don't officially consider leaks to be , but I think leaking past the lifetime of tasks with references is a very questionable thing to allow.
The box annihilator is not a good solution to rc reference cycles, but it is at least a solution. Cycles of MutexArcs will produce valgrind errors.
now provides a way to leak with the safe constructors.