Skip to content

Add sync::ReentrantLock #193

Closed
Closed

Description

Proposal

Problem statement

Provide a lightweight, correct reentrant mutex in std.

Motivation, use-cases

A number of crates need a reentrant mutex for implementing deadlock-free synchronization. Because such a lock is not currently available, crate authors turn to parking_lot1 (bringing in quite a lot of extra weight for such a simple purpose), manually wrap pthread_mutex 2 (aaaaah!) or implement it themselves 3, sometimes in very hacky fashion4.

Solution sketches

std already has the private ReentrantMutex, which it uses to implement Stdout locking. I propose to make this type public with the following API (copying some elements from Mutex):

// in std::sync

pub struct ReentrantLock<T: ?Sized> { .. }

impl<T> ReentrantLock<T> {
    pub const fn new(t: T) -> ReentrantLock<T>;
    pub const fn into_inner(self) -> T;
}

impl<T: ?Sized> ReentrantLock<T> {
    pub fn lock(&self) -> ReentrantLockGuard<'_, T>;
    pub fn get_mut(&mut self) -> &mut T;
}

impl<T: ?Sized + Send> Send for ReentrantLock<T> {}
impl<T: ?Sized + Send> Sync for ReentrantLock<T> {}

impl<T: ?Sized + Debug> Debug for ReentrantLock<T> { .. }
impl<T: Default> Default for ReentrantLock<T> { .. }
impl<T> From<T> for ReentrantLock<T> { .. }

pub struct ReentrantLockGuard<'a, T> { .. }

impl<'a, T: ?Sized> !Send for ReentrantLockGuard<'a, T> {}
impl<'a, T: ?Sized + Sync> Sync for ReentrantLockGuard<'a, T> {}

impl<'a, T: ?Sized> Deref for ReentrantLockGuard<'a, T> { .. }
impl<'a, T: ?Sized + Debug> Debug for ReentrantLockGuard<'a, T> { .. }
impl<'a, T: ?Sized + Display> Display for ReentrantLockGuard<'a, T> { .. }

Unresolved questions

Poisoning

I would argue that ReentrantLock should not implement lock poisoning. Since it only provides shared access, breaking the invariants of the inner type is something that the lock has no control over, as it requires interior mutability. It would make sense to require that T: RefUnwindSafe, but I think that is too inconvenient for now. If RefUnwindSafe were a lint trait, it should definitely be used.

Fallible locking

Is there a need for fallible locking, even if it cannot fail because of reentrancy? How would this look like, considering that TryLockResult also has a poisoning case, which would never be used here?

Naming

New synchronization primitives like OnceLock have used the Lock suffix. IMHO the name ReentrantLock rhymes well with the other types and is more consistent, but ReentrantMutex better highlights the similarity to Mutex.

Links and related work

What happens now?

This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.

Footnotes

  1. https://github.com/imgui-rs/imgui-rs/blob/ba02f2287fb5781bb921b7dc4a7ec95cd88b74d0/imgui/src/context.rs#L1

  2. https://github.com/douchuan/jvm/blob/5678df31a13ec56faa0daddb561d34ae2def8f1d/crates/vm/src/runtime/thread/mutex.rs#L10

  3. https://github.com/servo/servo/blob/be6e25a1b223325f47db85050c47982c641c8f0f/components/remutex/lib.rs#L156

  4. https://github.com/rust-lang/backtrace-rs/blob/a3406f93f3e6d91e05525c671fa694a0aea22c82/src/lib.rs#L154

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions