Skip to content

Commit 2f7a64c

Browse files
committed
Add helper function.
Add _PyRecursiveMutex_at_fork_reinit() helper function.
1 parent 63ad59b commit 2f7a64c

3 files changed

Lines changed: 23 additions & 11 deletions

File tree

‎Include/internal/pycore_lock.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,9 @@ PyAPI_FUNC(void) _PyRecursiveMutex_Lock(_PyRecursiveMutex *m);
173173
extern PyLockStatus _PyRecursiveMutex_LockTimed(_PyRecursiveMutex *m, PyTime_t timeout, _PyLockFlags flags);
174174
PyAPI_FUNC(void) _PyRecursiveMutex_Unlock(_PyRecursiveMutex *m);
175175
extern int _PyRecursiveMutex_TryUnlock(_PyRecursiveMutex *m);
176+
#ifdef HAVE_FORK
177+
extern void _PyRecursiveMutex_at_fork_reinit(_PyRecursiveMutex *m, int owned);
178+
#endif
176179

177180
// A readers-writer (RW) lock. The lock supports multiple concurrent readers or
178181
// a single writer. The lock is write-preferring: if a writer is waiting while

‎Python/_warnings.c‎

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -282,24 +282,14 @@ void
282282
_PyWarnings_BeforeFork(PyInterpreterState *interp)
283283
{
284284
WarningsState *st = warnings_get_state(interp);
285-
// Record ownership before fork(): the thread ID can change in the child.
286285
st->lock_held_at_fork = _PyRecursiveMutex_IsLockedByCurrentThread(&st->lock);
287286
}
288287

289288
void
290289
_PyWarnings_AfterFork(PyInterpreterState *interp)
291290
{
292291
WarningsState *st = warnings_get_state(interp);
293-
if (st->lock_held_at_fork) {
294-
// The surviving thread will still release the lock as its stack
295-
// unwinds. Preserve the recursion depth, but discard dead waiters.
296-
st->lock.mutex = (PyMutex){._bits = _Py_LOCKED};
297-
st->lock.thread = PyThread_get_thread_ident_ex();
298-
}
299-
else {
300-
// The owner (if any) no longer exists in the child.
301-
st->lock = (_PyRecursiveMutex){0};
302-
}
292+
_PyRecursiveMutex_at_fork_reinit(&st->lock, st->lock_held_at_fork);
303293
st->lock_held_at_fork = false;
304294
}
305295
#endif

‎Python/lock.c‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,25 @@ _PyRecursiveMutex_TryUnlock(_PyRecursiveMutex *m)
453453
return 0;
454454
}
455455

456+
#ifdef HAVE_FORK
457+
// Reinitialize the mutex in the child process after fork(). The caller must
458+
// record before fork() whether the forking thread owns the mutex, since the
459+
// thread ident can change in the child (gh-126688). If it does, the child
460+
// keeps the mutex with the same recursion level; otherwise the owner and any
461+
// waiters no longer exist in the child and the mutex is reset.
462+
void
463+
_PyRecursiveMutex_at_fork_reinit(_PyRecursiveMutex *m, int owned)
464+
{
465+
if (owned) {
466+
m->mutex = (PyMutex){._bits = _Py_LOCKED};
467+
_Py_atomic_store_ullong_relaxed(&m->thread, PyThread_get_thread_ident_ex());
468+
}
469+
else {
470+
memset(m, 0, sizeof(*m));
471+
}
472+
}
473+
#endif
474+
456475
#define _Py_WRITE_LOCKED 1
457476
#define _PyRWMutex_READER_SHIFT 2
458477
#define _Py_RWMUTEX_MAX_READERS (UINTPTR_MAX >> _PyRWMutex_READER_SHIFT)

0 commit comments

Comments
 (0)