@@ -68,43 +68,46 @@ pub type PyMappedRwLockWriteGuard<'a, T> = MappedRwLockWriteGuard<'a, RawRwLock,
6868
6969// can add fn const_{mutex,rw_lock}() if necessary, but we probably won't need to
7070
71- /// Reset a `PyMutex` to its initial (unlocked) state after `fork()` .
71+ /// Reset a lock to its initial (unlocked) state by zeroing its bytes .
7272///
73- /// After `fork()`, locks held by dead parent threads would deadlock in the
74- /// child. This writes `RawMutex::INIT` via the `Mutex:: raw()` accessor,
75- /// bypassing the normal unlock path which may interact with parking_lot's
76- /// internal waiter queues.
73+ /// After `fork()`, any lock held by a now- dead thread would remain
74+ /// permanently locked. We zero the raw bytes (the unlocked state for all
75+ /// `parking_lot` raw lock types) instead of using the normal unlock path,
76+ /// which would interact with stale waiter queues.
7777///
7878/// # Safety
7979///
8080/// Must only be called from the single-threaded child process immediately
8181/// after `fork()`, before any other thread is created.
82+ /// The type `T` must represent the unlocked state as all-zero bytes
83+ /// (true for `parking_lot::RawMutex`, `RawRwLock`, `RawReentrantMutex`, etc.).
8284#[ cfg( unix) ]
83- pub unsafe fn reinit_mutex_after_fork < T : ?Sized > ( mutex : & PyMutex < T > ) {
84- // Use Mutex::raw() to access the underlying lock without layout assumptions.
85- // parking_lot::RawMutex (AtomicU8) and RawCellMutex (Cell<bool>) both
86- // represent the unlocked state as all-zero bytes.
85+ pub unsafe fn zero_reinit_after_fork < T > ( lock : * const T ) {
8786 unsafe {
88- let raw = mutex. raw ( ) as * const RawMutex as * mut u8 ;
89- core:: ptr:: write_bytes ( raw, 0 , core:: mem:: size_of :: < RawMutex > ( ) ) ;
87+ core:: ptr:: write_bytes ( lock as * mut u8 , 0 , core:: mem:: size_of :: < T > ( ) ) ;
9088 }
9189}
9290
93- /// Reset a `PyRwLock` to its initial (unlocked) state after `fork()`.
91+ /// Reset a `PyMutex` after `fork()`. See [`zero_reinit_after_fork`] .
9492///
95- /// Same rationale as [`reinit_mutex_after_fork`] — dead threads' read or
96- /// write locks would cause permanent deadlock in the child.
93+ /// # Safety
94+ ///
95+ /// Must only be called from the single-threaded child process immediately
96+ /// after `fork()`, before any other thread is created.
97+ #[ cfg( unix) ]
98+ pub unsafe fn reinit_mutex_after_fork < T : ?Sized > ( mutex : & PyMutex < T > ) {
99+ unsafe { zero_reinit_after_fork ( mutex. raw ( ) ) }
100+ }
101+
102+ /// Reset a `PyRwLock` after `fork()`. See [`zero_reinit_after_fork`].
97103///
98104/// # Safety
99105///
100106/// Must only be called from the single-threaded child process immediately
101107/// after `fork()`, before any other thread is created.
102108#[ cfg( unix) ]
103109pub unsafe fn reinit_rwlock_after_fork < T : ?Sized > ( rwlock : & PyRwLock < T > ) {
104- unsafe {
105- let raw = rwlock. raw ( ) as * const RawRwLock as * mut u8 ;
106- core:: ptr:: write_bytes ( raw, 0 , core:: mem:: size_of :: < RawRwLock > ( ) ) ;
107- }
110+ unsafe { zero_reinit_after_fork ( rwlock. raw ( ) ) }
108111}
109112
110113/// Reset a `PyThreadMutex` to its initial (unlocked, unowned) state after `fork()`.
0 commit comments