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_signalsmakes 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 CNo 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
impl RawRwLock for RawDetachingRwLock
Source§type GuardMarker = <RawCellRwLock as RawRwLock>::GuardMarker
type GuardMarker = <RawCellRwLock as RawRwLock>::GuardMarker
Send. Use
one of the GuardSend or GuardNoSend helper types here.Source§fn lock_exclusive(&self)
fn lock_exclusive(&self)
Source§fn try_lock_exclusive(&self) -> bool
fn try_lock_exclusive(&self) -> bool
Source§unsafe fn unlock_exclusive(&self)
unsafe fn unlock_exclusive(&self)
Source§fn is_locked_exclusive(&self) -> bool
fn is_locked_exclusive(&self) -> bool
RwLock is currently exclusively locked.Source§impl RawRwLockUpgrade for RawDetachingRwLock
impl RawRwLockUpgrade for RawDetachingRwLock
Source§fn lock_upgradable(&self)
fn lock_upgradable(&self)
Source§fn try_lock_upgradable(&self) -> bool
fn try_lock_upgradable(&self) -> bool
Source§unsafe fn unlock_upgradable(&self)
unsafe fn unlock_upgradable(&self)
Source§unsafe fn try_upgrade(&self) -> bool
unsafe fn try_upgrade(&self) -> bool
Source§impl RawRwLockUpgradeDowngrade for RawDetachingRwLock
impl RawRwLockUpgradeDowngrade for RawDetachingRwLock
Source§unsafe fn downgrade_upgradable(&self)
unsafe fn downgrade_upgradable(&self)
Source§unsafe fn downgrade_to_upgradable(&self)
unsafe fn downgrade_to_upgradable(&self)
Auto Trait Implementations§
impl !Freeze for RawDetachingRwLock
impl !RefUnwindSafe for RawDetachingRwLock
impl !Sync for RawDetachingRwLock
impl Send for RawDetachingRwLock
impl Unpin for RawDetachingRwLock
impl UnsafeUnpin for RawDetachingRwLock
impl UnwindSafe for RawDetachingRwLock
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T, U> ImaginaryInto<U> for Twhere
U: ImaginaryFrom<T>,
impl<T, U> ImaginaryInto<U> for Twhere
U: ImaginaryFrom<T>,
fn imaginary_into(self) -> U
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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