gh-119344: Make critical section API public (#119353)

This makes the following macros public as part of the non-limited C-API for
locking a single object or two objects at once.

* `Py_BEGIN_CRITICAL_SECTION(op)` / `Py_END_CRITICAL_SECTION()`
* `Py_BEGIN_CRITICAL_SECTION2(a, b)` / `Py_END_CRITICAL_SECTION2()`

The supporting functions and structs used by the macros are also exposed for
cases where C macros are not available.
This commit is contained in:
Sam Gross 2024-06-21 15:50:18 -04:00 committed by GitHub
parent 03fa2df927
commit 8f17d69b7b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 459 additions and 225 deletions

View file

@ -62,24 +62,13 @@ class object "PyObject *" "&PyBaseObject_Type"
// be released and reacquired during a subclass update if there's contention
// on the subclass lock.
#define TYPE_LOCK &PyInterpreterState_Get()->types.mutex
#define BEGIN_TYPE_LOCK() \
{ \
_PyCriticalSection _cs; \
_PyCriticalSection_Begin(&_cs, TYPE_LOCK); \
#define BEGIN_TYPE_LOCK() Py_BEGIN_CRITICAL_SECTION_MUT(TYPE_LOCK)
#define END_TYPE_LOCK() Py_END_CRITICAL_SECTION()
#define END_TYPE_LOCK() \
_PyCriticalSection_End(&_cs); \
}
#define BEGIN_TYPE_DICT_LOCK(d) \
Py_BEGIN_CRITICAL_SECTION2_MUT(TYPE_LOCK, &_PyObject_CAST(d)->ob_mutex)
#define BEGIN_TYPE_DICT_LOCK(d) \
{ \
_PyCriticalSection2 _cs; \
_PyCriticalSection2_Begin(&_cs, TYPE_LOCK, \
&_PyObject_CAST(d)->ob_mutex); \
#define END_TYPE_DICT_LOCK() \
_PyCriticalSection2_End(&_cs); \
}
#define END_TYPE_DICT_LOCK() Py_END_CRITICAL_SECTION2()
#define ASSERT_TYPE_LOCK_HELD() \
_Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(TYPE_LOCK)
@ -442,7 +431,7 @@ _PyType_GetBases(PyTypeObject *self)
BEGIN_TYPE_LOCK();
res = lookup_tp_bases(self);
Py_INCREF(res);
END_TYPE_LOCK()
END_TYPE_LOCK();
return res;
}
@ -513,7 +502,7 @@ _PyType_GetMRO(PyTypeObject *self)
BEGIN_TYPE_LOCK();
mro = lookup_tp_mro(self);
Py_XINCREF(mro);
END_TYPE_LOCK()
END_TYPE_LOCK();
return mro;
#else
return Py_XNewRef(lookup_tp_mro(self));
@ -951,10 +940,10 @@ PyType_Watch(int watcher_id, PyObject* obj)
return -1;
}
// ensure we will get a callback on the next modification
BEGIN_TYPE_LOCK()
BEGIN_TYPE_LOCK();
assign_version_tag(interp, type);
type->tp_watched |= (1 << watcher_id);
END_TYPE_LOCK()
END_TYPE_LOCK();
return 0;
}
@ -1080,9 +1069,9 @@ PyType_Modified(PyTypeObject *type)
return;
}
BEGIN_TYPE_LOCK()
BEGIN_TYPE_LOCK();
type_modified_unlocked(type);
END_TYPE_LOCK()
END_TYPE_LOCK();
}
static int
@ -1161,9 +1150,9 @@ void
_PyType_SetVersion(PyTypeObject *tp, unsigned int version)
{
BEGIN_TYPE_LOCK()
BEGIN_TYPE_LOCK();
set_version_unlocked(tp, version);
END_TYPE_LOCK()
END_TYPE_LOCK();
}
PyTypeObject *
@ -1245,9 +1234,9 @@ int PyUnstable_Type_AssignVersionTag(PyTypeObject *type)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
int assigned;
BEGIN_TYPE_LOCK()
BEGIN_TYPE_LOCK();
assigned = assign_version_tag(interp, type);
END_TYPE_LOCK()
END_TYPE_LOCK();
return assigned;
}
@ -1532,7 +1521,7 @@ type_get_mro(PyTypeObject *type, void *context)
{
PyObject *mro;
BEGIN_TYPE_LOCK()
BEGIN_TYPE_LOCK();
mro = lookup_tp_mro(type);
if (mro == NULL) {
mro = Py_None;
@ -1540,7 +1529,7 @@ type_get_mro(PyTypeObject *type, void *context)
Py_INCREF(mro);
}
END_TYPE_LOCK()
END_TYPE_LOCK();
return mro;
}
@ -3121,9 +3110,9 @@ static PyObject *
mro_implementation(PyTypeObject *type)
{
PyObject *mro;
BEGIN_TYPE_LOCK()
BEGIN_TYPE_LOCK();
mro = mro_implementation_unlocked(type);
END_TYPE_LOCK()
END_TYPE_LOCK();
return mro;
}
@ -3310,9 +3299,9 @@ static int
mro_internal(PyTypeObject *type, PyObject **p_old_mro)
{
int res;
BEGIN_TYPE_LOCK()
BEGIN_TYPE_LOCK();
res = mro_internal_unlocked(type, 0, p_old_mro);
END_TYPE_LOCK()
END_TYPE_LOCK();
return res;
}
@ -5173,7 +5162,7 @@ get_module_by_def(PyTypeObject *type, PyModuleDef *def)
}
PyObject *res = NULL;
BEGIN_TYPE_LOCK()
BEGIN_TYPE_LOCK();
PyObject *mro = lookup_tp_mro(type);
// The type must be ready
@ -5200,7 +5189,7 @@ get_module_by_def(PyTypeObject *type, PyModuleDef *def)
break;
}
}
END_TYPE_LOCK()
END_TYPE_LOCK();
return res;
}
@ -5458,13 +5447,13 @@ _PyType_LookupRef(PyTypeObject *type, PyObject *name)
int has_version = 0;
int version = 0;
BEGIN_TYPE_LOCK()
BEGIN_TYPE_LOCK();
res = find_name_in_mro(type, name, &error);
if (MCACHE_CACHEABLE_NAME(name)) {
has_version = assign_version_tag(interp, type);
version = type->tp_version_tag;
}
END_TYPE_LOCK()
END_TYPE_LOCK();
/* Only put NULL results into cache if there was no error. */
if (error) {
@ -8454,14 +8443,14 @@ PyType_Ready(PyTypeObject *type)
}
int res;
BEGIN_TYPE_LOCK()
BEGIN_TYPE_LOCK();
if (!(type->tp_flags & Py_TPFLAGS_READY)) {
res = type_ready(type, 1);
} else {
res = 0;
assert(_PyType_CheckConsistency(type));
}
END_TYPE_LOCK()
END_TYPE_LOCK();
return res;
}
@ -8495,7 +8484,7 @@ init_static_type(PyInterpreterState *interp, PyTypeObject *self,
int res;
BEGIN_TYPE_LOCK();
res = type_ready(self, initial);
END_TYPE_LOCK()
END_TYPE_LOCK();
if (res < 0) {
_PyStaticType_ClearWeakRefs(interp, self);
managed_static_type_state_clear(interp, self, isbuiltin, initial);
@ -8967,7 +8956,7 @@ hackcheck(PyObject *self, setattrofunc func, const char *what)
int res;
BEGIN_TYPE_LOCK();
res = hackcheck_unlocked(self, func, what);
END_TYPE_LOCK()
END_TYPE_LOCK();
return res;
}
@ -10896,14 +10885,14 @@ fixup_slot_dispatchers(PyTypeObject *type)
// This lock isn't strictly necessary because the type has not been
// exposed to anyone else yet, but update_ont_slot calls find_name_in_mro
// where we'd like to assert that the type is locked.
BEGIN_TYPE_LOCK()
BEGIN_TYPE_LOCK();
assert(!PyErr_Occurred());
for (pytype_slotdef *p = slotdefs; p->name; ) {
p = update_one_slot(type, p);
}
END_TYPE_LOCK()
END_TYPE_LOCK();
}
static void
@ -11192,7 +11181,7 @@ _super_lookup_descr(PyTypeObject *su_type, PyTypeObject *su_obj_type, PyObject *
another thread can modify it after we end the critical section
below */
Py_XINCREF(mro);
END_TYPE_LOCK()
END_TYPE_LOCK();
if (mro == NULL)
return NULL;