Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Include/cpython/critical_section.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m);
PyAPI_FUNC(void)
PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2);

// Attempts to enter a critical section locking both `a` and `b` without
// blocking. Returns non-zero (1) if successful, in which case the critical
// section is active and must be terminated with `PyCriticalSection2_End(c)`.
// Returns 0 if acquiring the locks would block, in which case no locks are
// acquired and the critical section is not entered. On GIL-enabled builds,
// this always succeeds and returns 1.
PyAPI_FUNC(int)
PyCriticalSection2_TryBegin(PyCriticalSection2 *c, PyObject *a, PyObject *b);

#ifndef Py_GIL_DISABLED
#undef Py_BEGIN_CRITICAL_SECTION
#undef Py_END_CRITICAL_SECTION
Expand Down
54 changes: 54 additions & 0 deletions Include/internal/pycore_critical_section.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,60 @@ _PyCriticalSection2_Begin(PyThreadState *tstate, PyCriticalSection2 *c, PyObject
_PyCriticalSection2_BeginMutex(tstate, c, &a->ob_mutex, &b->ob_mutex);
}

static inline int
_PyCriticalSection_TryBeginMutex(PyThreadState *tstate, PyCriticalSection *c, PyMutex *m)
{
if (PyMutex_LockFast(m)) {
c->_cs_mutex = m;
c->_cs_prev = tstate->critical_section;
tstate->critical_section = (uintptr_t)c;
return 1;
}
return 0;
}

static inline int
_PyCriticalSection2_TryBeginMutex(PyThreadState *tstate, PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2)
{
if (m1 == m2) {
c->_cs_mutex2 = NULL;
return _PyCriticalSection_TryBeginMutex(tstate, &c->_cs_base, m1);
}

if ((uintptr_t)m2 < (uintptr_t)m1) {
PyMutex *tmp = m1;
m1 = m2;
m2 = tmp;
}

if (PyMutex_LockFast(m1)) {
if (PyMutex_LockFast(m2)) {
c->_cs_base._cs_mutex = m1;
c->_cs_mutex2 = m2;
c->_cs_base._cs_prev = tstate->critical_section;

uintptr_t p = (uintptr_t)c | _Py_CRITICAL_SECTION_TWO_MUTEXES;
tstate->critical_section = p;
return 1;
}
else {
PyMutex_Unlock(m1);
return 0;
}
}
return 0;
}

// Attempts to enter a two-object critical section without blocking.
// Returns 1 if both mutexes were locked immediately, in which case the
// critical section is entered and must be exited with `_PyCriticalSection2_End`.
// Returns 0 if locking would block, leaving no locks held.
static inline int
_PyCriticalSection2_TryBegin(PyThreadState *tstate, PyCriticalSection2 *c, PyObject *a, PyObject *b)
{
return _PyCriticalSection2_TryBeginMutex(tstate, c, &a->ob_mutex, &b->ob_mutex);
}

static inline void
_PyCriticalSection2_End(PyThreadState *tstate, PyCriticalSection2 *c)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Reduced lock contention during LOAD_GLOBAL bytecode specialization under
free threading.
11 changes: 11 additions & 0 deletions Python/critical_section.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,14 @@ PyCriticalSection2_End(PyCriticalSection2 *c)
_PyCriticalSection2_End(_PyThreadState_GET(), c);
#endif
}

#undef PyCriticalSection2_TryBegin
int
PyCriticalSection2_TryBegin(PyCriticalSection2 *c, PyObject *a, PyObject *b)
{
#ifdef Py_GIL_DISABLED
return _PyCriticalSection2_TryBegin(_PyThreadState_GET(), c, a, b);
#else
return 1;
#endif
}
13 changes: 11 additions & 2 deletions Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -1457,9 +1457,18 @@ _Py_Specialize_LoadGlobal(
PyObject *globals, PyObject *builtins,
_Py_CODEUNIT *instr, PyObject *name)
{
Py_BEGIN_CRITICAL_SECTION2(globals, builtins);
#ifdef Py_GIL_DISABLED
PyCriticalSection2 cs;
if (PyCriticalSection2_TryBegin(&cs, globals, builtins)) {
specialize_load_global_lock_held(globals, builtins, instr, name);
PyCriticalSection2_End(&cs);
}
else {
unspecialize(instr);
}
#else
specialize_load_global_lock_held(globals, builtins, instr, name);
Py_END_CRITICAL_SECTION2();
#endif
}

static int
Expand Down
Loading