rust-lang / rust-lang/libs-team
Simpler and ergonomic propagation of poisoned Mutex and RwLock errors
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 178
- Forks
- 28
- Avg merge
- 15m
- Merged PRs (30d)
- 1
Description
Proposal
Problem statement
When using Mutex or RwLock, a poisoned lock returns a PoisonError containing the guard.
For example:
RwLock::read()
-> Result<RwLockReadGuard<'_, T>, PoisonError<RwLockReadGuard<'_, T>>>
This is useful if the caller wants to recover the guard with into_inner().
But in many cases I don't want to recover from the poisoned lock. I just want to propagate it as a normal error with ?.
For example, I would like something as simple as:
let guard = lock.read()?;
inside a function returning something like anyhow::Result.
Because the PoisonError contains the guard and its lifetime, this is not always possible directly.
Motivating examples or use cases
I ran into this while working on a Rust application using RwLock.
My local solution was an extension trait:
#[derive(Debug)]
pub enum RwLockExtError {
ReadPoisoned(String),
WritePoisoned(String),
}
impl std::fmt::Display for RwLockExtError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ReadPoisoned(e) => write!(f, "Failed to acquire read lock: {e}"),
Self::WritePoisoned(e) => write!(f, "Failed to acquire write lock: {e}"),
}
}
}
impl std::error::Error for RwLockExtError {}
pub trait RwLockToError<T: ?Sized> {
fn read_or_error(
&self,
) -> Result<RwLockReadGuard<'_, T>, RwLockExtError>;
fn write_or_error(
&self,
) -> Result<RwLockWriteGuard<'_, T>, RwLockExtError>;
}
Then I can simply write:
let guard = lock.read_or_error()?;
This is only a proof of concept, not necessarily the API I am proposing.
Solution sketch
One possibility could be adding methods for callers that want poisoning to remain an error but don't need to recover the guard.
For example:
impl<T: ?Sized> RwLock<T> {
pub fn read_or_error(
&self,
) -> Result<RwLockReadGuard<'_, T>, PoisonError<()>>;
pub fn write_or_error(
&self,
) -> Result<RwLockWriteGuard<'_, T>, PoisonError<()>>;
}
And similarly for Mutex:
impl<T: ?Sized> Mutex<T> {
pub fn lock_or_error(
&self,
) -> Result<MutexGuard<'_, T>, PoisonError<()>>;
}
The existing read(), write() and lock() methods would remain unchanged for callers that want recovery through into_inner().
The names above are only examples.
Alternatives
I am open to any alternative model or API that would make poisoned lock errors easier to propagate into other error types while keeping the existing recovery behavior available.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the existing Mutex, RwLock, and PoisonError APIs described in the proposal, then compare the recovery behavior with the requested propagation use case. The work is done when a concrete API model and naming approach is agreed that simplifies propagation while preserving access to the poisoned guard for recovery.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100