@@ -57,3 +57,42 @@ pub type PyRwLockWriteGuard<'a, T> = RwLockWriteGuard<'a, RawRwLock, T>;
5757pub type PyMappedRwLockWriteGuard < ' a , T > = MappedRwLockWriteGuard < ' a , RawRwLock , T > ;
5858
5959// can add fn const_{mutex,rw_lock}() if necessary, but we probably won't need to
60+
61+ /// Reset a `PyMutex` to its initial (unlocked) state after `fork()`.
62+ ///
63+ /// After `fork()`, locks held by dead parent threads would deadlock in the
64+ /// child. This writes `RawMutex::INIT` via the `Mutex::raw()` accessor,
65+ /// bypassing the normal unlock path which may interact with parking_lot's
66+ /// internal waiter queues.
67+ ///
68+ /// # Safety
69+ ///
70+ /// Must only be called from the single-threaded child process immediately
71+ /// after `fork()`, before any other thread is created.
72+ #[ cfg( unix) ]
73+ pub unsafe fn reinit_mutex_after_fork < T : ?Sized > ( mutex : & PyMutex < T > ) {
74+ // Use Mutex::raw() to access the underlying lock without layout assumptions.
75+ // parking_lot::RawMutex (AtomicU8) and RawCellMutex (Cell<bool>) both
76+ // represent the unlocked state as all-zero bytes.
77+ unsafe {
78+ let raw = mutex. raw ( ) as * const RawMutex as * mut u8 ;
79+ core:: ptr:: write_bytes ( raw, 0 , core:: mem:: size_of :: < RawMutex > ( ) ) ;
80+ }
81+ }
82+
83+ /// Reset a `PyRwLock` to its initial (unlocked) state after `fork()`.
84+ ///
85+ /// Same rationale as [`reinit_mutex_after_fork`] — dead threads' read or
86+ /// write locks would cause permanent deadlock in the child.
87+ ///
88+ /// # Safety
89+ ///
90+ /// Must only be called from the single-threaded child process immediately
91+ /// after `fork()`, before any other thread is created.
92+ #[ cfg( unix) ]
93+ pub unsafe fn reinit_rwlock_after_fork < T : ?Sized > ( rwlock : & PyRwLock < T > ) {
94+ unsafe {
95+ let raw = rwlock. raw ( ) as * const RawRwLock as * mut u8 ;
96+ core:: ptr:: write_bytes ( raw, 0 , core:: mem:: size_of :: < RawRwLock > ( ) ) ;
97+ }
98+ }
0 commit comments