Skip to content

Commit d89e736

Browse files
committed
emit thread safety warning in defaultdict.__missing__
1 parent 127ba72 commit d89e736

2 files changed

Lines changed: 26 additions & 0 deletions

File tree

Include/internal/pycore_critical_section.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,26 @@ _PyCriticalSection_AssertHeldObj(PyObject *op)
252252
#endif
253253
}
254254

255+
static inline void
256+
_PyCriticalSection_WarnIfNotHeld(PyObject *op, const char *message)
257+
{
258+
#ifdef Py_GIL_DISABLED
259+
PyMutex *mutex = &_PyObject_CAST(op)->ob_mutex;
260+
PyThreadState *tstate = _PyThreadState_GET();
261+
uintptr_t prev = tstate->critical_section;
262+
if (prev & _Py_CRITICAL_SECTION_TWO_MUTEXES) {
263+
PyCriticalSection2 *cs = (PyCriticalSection2 *)(prev & ~_Py_CRITICAL_SECTION_MASK);
264+
if (cs == NULL || (cs->_cs_base._cs_mutex != mutex && cs->_cs_mutex2 != mutex))
265+
PyErr_WarnEx(NULL, message, 2);
266+
}
267+
else {
268+
PyCriticalSection *cs = (PyCriticalSection *)(prev & ~_Py_CRITICAL_SECTION_MASK);
269+
if (cs == NULL || cs->_cs_mutex != mutex)
270+
PyErr_WarnEx(NULL, message, 2);
271+
}
272+
#endif
273+
}
274+
255275
#undef Py_BEGIN_CRITICAL_SECTION
256276
# define Py_BEGIN_CRITICAL_SECTION(op) \
257277
{ \

Modules/_collectionsmodule.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2262,6 +2262,12 @@ PyDoc_STRVAR(defdict_missing_doc,
22622262
static PyObject *
22632263
defdict_missing(PyObject *op, PyObject *key)
22642264
{
2265+
_PyCriticalSection_WarnIfNotHeld(
2266+
op,
2267+
"the defaultdict.__missing__ method should not be called directly; "
2268+
"use dd.pop(key, None) to safely trigger a reset to a default value "
2269+
"the next time key is accessed"
2270+
);
22652271
defdictobject *dd = defdictobject_CAST(op);
22662272
PyObject *factory = dd->default_factory;
22672273
PyObject *value;

0 commit comments

Comments
 (0)