Unpoisoning a Mutex

While it is possible to ignore the poisoning of a Mutex with lock.lock().unwrap_or_else(|e| e.into_inner()), as far as I can tell there isn't any way to mark a mutex as un-poisoned. This means that even if you see that the lock is poisoned, and then ignore the existing value, and replace it with a new value, the mutex continues to be poisoned forever.

I have a situation where in one usage of the lock, if it is poisoned I want to recover by replacing the contents of the mutex, but in another usage, if it is poisoned, I want to panic.

I propose adding an API to mark a poisoned Mutex as unpoisoned, probably as an API on PoisonError.

1 Like

I've recently wondered the same and I think adding some API for this sounds quite reasonable.

1 Like

A method on MutexGuard seems more reasonable since that gives you the ability to inspect the data before deciding whether to unpoison it. You could probably just go ahead and PR this. It's pretty trivial to implement:

// in sync/poison.rs
impl Flag {
    pub fn unpoison(&self, _guard: &Guard) {
        self.failed.store(false, Ordering::Relaxed);
    }
}

// in sync/mutex.rs
impl<'a, T> MutexGuard<'a, T> {
    fn unpoison(guard: &mut MutexGuard<'a, T>) {
        self.lock.poison.unpoison(&self.poison);
    }
}
3 Likes

That should probably be a release ordering, though perhaps the atomic synchronization of the mutex itself is enough? Just a knee-jerk "that looks wrong" on the ordering, feel free to ignore.

I’m curious why atomics are used in the first place and the poisoning information doesn’t just live in the UnsafeCell together with the payload.

Edit: AFAIK modification of the poisoning state only happens when panicking while holding mutable access, and AFAICT, reading the poisoning information … actually, now that I look at the API again, I think the Mutex::is_poisoned method might be only reason why atomics are used. Otherwise, it only becomes relevant for the result of lock calls, when the Mutex is already locked. The same holds for RwLock, where the lock will already be acquired (mutably or immutably) before poisoning is checked.

Edit2: On that note, Relaxed ordering for unpoisoning should be completely fine.

Edit3: And a similar method on RwLockWriteGuard is probably a good idea. In both cases, one could also debate additional methods on the (write-) guards for

  • checking whether or not it’s currently poisoned (even though that information is also available from the result of locking); and
  • manually adding poison without needing to use a panic.
1 Like

Is unpoisoning safe? Should this be an unsafe method?

Yes, it's safe. Mutex poisoning is similar to (and related to) "unwind safety" which is - despite its name - a concept thats not related to "safe vs. unsafe" code, but instead often said to being similar to just a lint.

Edit: The only contrived example I can come up with where Mutex unpoisoning might be problematic is: Unsafe code that uses Mutexes and somehow relies on a poisoned Mutex never becoming unpoisoned, yet it also hands out a MutexGuard (or a shared reference to the Mutex itself) to external/untrusted (safe) code which would, with this API change, then gain the ability unpoison a Mutex that’s never supposed to be unpoisoned.

3 Likes

I agree, that is a contrived example! :stuck_out_tongue_closed_eyes:

But thank you for answering my concerns. Unpoisoning feels like something that could be dangerous if used without care, but as long as it doesn't violate rust's sense of safety, it's fine.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.