Skip to main content

RawDetachingRwLock

Struct RawDetachingRwLock 

Source
pub struct RawDetachingRwLock(/* private fields */);
Expand description

A reader-writer lock whose blocking acquires detach first, and which is the raw lock it wraps in every other respect.

Use through PyDetachingRwLock.

§Why this exists

Stopping the world means waiting until every other thread sits at SUSPENDED, and there are two ways a thread gets there:

  • A DETACHED thread is not running interpreter code, so the requester moves it to SUSPENDED itself. The thread never finds out.
  • An ATTACHED thread can only suspend itself, at a safepoint — the check check_signals makes between bytecodes.

A thread blocked acquiring a lock runs no bytecode, so it reaches no safepoint. While ATTACHED it is a thread the world cannot stop for as long as it waits, and the requester waits without a bound.

On its own that is a pause. It becomes a deadlock as soon as the lock being waited for is held by a thread the same stop has already parked:

A  holds the lock, blocks inside allow_threads  -> DETACHED
B  requests a stop, and parks A                 -> A is SUSPENDED, holding the lock
C  wants the same lock, and waits for it        -> ATTACHED, blocked

  B waits for C to suspend           C reaches no safepoint
  C waits for A to release           A is parked
  A waits for B to start the world   B is still waiting for C

No thread in that cycle can break it, because none of them is running. It is not hypothetical: an SSLSocket.read against a peer that completed a handshake and then went quiet froze whole processes this way, the main thread included, so not even a Python-level timeout could fire.

The holder cannot be the one to give way. A lock is held across a blocking call precisely because that is what the call needs. So the waiter gives way instead: it leaves its interpreter for the duration of the wait, which is what a blocking call does anyway, and a waiter that has left is a waiter the requester can park. C detaches before it blocks, the stop completes, B finishes, A resumes and releases, and C takes the lock and attaches again.

§Only for locks a stop-the-world section never takes

The wait acquires the lock while detached, so the thread comes back holding it, and re-attaching is a point at which a stop-the-world in flight will park the thread. It is therefore parked holding the lock. Everything that stops the world must be able to finish without that lock: if a collection were to take it, the collection would block on a thread only the collection can release, and neither would move again.

So this is opt-in per lock, and the rule for opting in is that nothing reachable from a stop-the-world section takes the same lock. An object whose payload holds no references — nothing for the collector to traverse into — satisfies that; most do not.

Not implementing the vm’s Traverse for this lock enforces part of that: a payload holding one cannot derive Traverse, so it cannot become something a collection walks into. Only that part. A collection is not the only thing that stops the world — dumping tracebacks, enumerating thread frames and forking all do — and nothing checks what those reach. For them the rule is still a convention.

Trait Implementations§

Source§

impl RawRwLock for RawDetachingRwLock

Source§

const INIT: Self

Initial value for an unlocked RwLock.
Source§

type GuardMarker = <RawCellRwLock as RawRwLock>::GuardMarker

Marker type which determines whether a lock guard should be Send. Use one of the GuardSend or GuardNoSend helper types here.
Source§

fn lock_shared(&self)

Acquires a shared lock, blocking the current thread until it is able to do so.
Source§

fn try_lock_shared(&self) -> bool

Attempts to acquire a shared lock without blocking.
Source§

unsafe fn unlock_shared(&self)

Releases a shared lock. Read more
Source§

fn lock_exclusive(&self)

Acquires an exclusive lock, blocking the current thread until it is able to do so.
Source§

fn try_lock_exclusive(&self) -> bool

Attempts to acquire an exclusive lock without blocking.
Source§

unsafe fn unlock_exclusive(&self)

Releases an exclusive lock. Read more
Source§

fn is_locked(&self) -> bool

Checks if this RwLock is currently locked in any way.
Source§

fn is_locked_exclusive(&self) -> bool

Check if this RwLock is currently exclusively locked.
Source§

impl RawRwLockDowngrade for RawDetachingRwLock

Source§

unsafe fn downgrade(&self)

Atomically downgrades an exclusive lock into a shared lock without allowing any thread to take an exclusive lock in the meantime. Read more
Source§

impl RawRwLockUpgrade for RawDetachingRwLock

Source§

fn lock_upgradable(&self)

Acquires an upgradable lock, blocking the current thread until it is able to do so.
Source§

fn try_lock_upgradable(&self) -> bool

Attempts to acquire an upgradable lock without blocking.
Source§

unsafe fn unlock_upgradable(&self)

Releases an upgradable lock. Read more
Source§

unsafe fn upgrade(&self)

Upgrades an upgradable lock to an exclusive lock. Read more
Source§

unsafe fn try_upgrade(&self) -> bool

Attempts to upgrade an upgradable lock to an exclusive lock without blocking. Read more
Source§

impl RawRwLockUpgradeDowngrade for RawDetachingRwLock

Source§

unsafe fn downgrade_upgradable(&self)

Downgrades an upgradable lock to a shared lock. Read more
Source§

unsafe fn downgrade_to_upgradable(&self)

Downgrades an exclusive lock to an upgradable lock. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T, U> ExactFrom<T> for U
where U: TryFrom<T>,

Source§

fn exact_from(value: T) -> U

Source§

impl<T, U> ExactInto<U> for T
where U: ExactFrom<T>,

Source§

fn exact_into(self) -> U

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> ImaginaryInto<U> for T
where U: ImaginaryFrom<T>,

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T, U> OverflowingInto<U> for T
where U: OverflowingFrom<T>,

Source§

impl<T> PrimitiveIntRandomBounds for T

Source§

impl<T> PrimitiveSignedRandomBounds for T

Source§

impl<T, U> RoundingInto<U> for T
where U: RoundingFrom<T>,

Source§

impl<T, U> SaturatingInto<U> for T
where U: SaturatingFrom<T>,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T, U> WrappingInto<U> for T
where U: WrappingFrom<T>,

Source§

fn wrapping_into(self) -> U