mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
gh-129668: Fix thread-safety of MemoryError freelist in free threaded build (gh-129704)
The MemoryError freelist was not thread-safe in the free threaded build. Use a mutex to protect accesses to the freelist. Unlike other freelists, the MemoryError freelist is not performance sensitive.
This commit is contained in:
parent
4d56c40440
commit
51b4edb1a4
3 changed files with 42 additions and 27 deletions
|
@ -24,6 +24,9 @@ struct _Py_exc_state {
|
||||||
PyObject *errnomap;
|
PyObject *errnomap;
|
||||||
PyBaseExceptionObject *memerrors_freelist;
|
PyBaseExceptionObject *memerrors_freelist;
|
||||||
int memerrors_numfree;
|
int memerrors_numfree;
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
PyMutex memerrors_lock;
|
||||||
|
#endif
|
||||||
// The ExceptionGroup type
|
// The ExceptionGroup type
|
||||||
PyObject *PyExc_ExceptionGroup;
|
PyObject *PyExc_ExceptionGroup;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix race condition when raising :exc:`MemoryError` in the free threaded
|
||||||
|
build.
|
|
@ -3832,36 +3832,43 @@ SimpleExtendsException(PyExc_Exception, ReferenceError,
|
||||||
|
|
||||||
#define MEMERRORS_SAVE 16
|
#define MEMERRORS_SAVE 16
|
||||||
|
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
# define MEMERRORS_LOCK(state) PyMutex_LockFlags(&state->memerrors_lock, _Py_LOCK_DONT_DETACH)
|
||||||
|
# define MEMERRORS_UNLOCK(state) PyMutex_Unlock(&state->memerrors_lock)
|
||||||
|
#else
|
||||||
|
# define MEMERRORS_LOCK(state) ((void)0)
|
||||||
|
# define MEMERRORS_UNLOCK(state) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
get_memory_error(int allow_allocation, PyObject *args, PyObject *kwds)
|
get_memory_error(int allow_allocation, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
PyBaseExceptionObject *self;
|
PyBaseExceptionObject *self = NULL;
|
||||||
struct _Py_exc_state *state = get_exc_state();
|
struct _Py_exc_state *state = get_exc_state();
|
||||||
if (state->memerrors_freelist == NULL) {
|
|
||||||
if (!allow_allocation) {
|
MEMERRORS_LOCK(state);
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
if (state->memerrors_freelist != NULL) {
|
||||||
return Py_NewRef(
|
/* Fetch MemoryError from freelist and initialize it */
|
||||||
&_Py_INTERP_SINGLETON(interp, last_resort_memory_error));
|
self = state->memerrors_freelist;
|
||||||
}
|
state->memerrors_freelist = (PyBaseExceptionObject *) self->dict;
|
||||||
PyObject *result = BaseException_new((PyTypeObject *)PyExc_MemoryError, args, kwds);
|
state->memerrors_numfree--;
|
||||||
return result;
|
self->dict = NULL;
|
||||||
|
self->args = (PyObject *)&_Py_SINGLETON(tuple_empty);
|
||||||
|
_Py_NewReference((PyObject *)self);
|
||||||
|
_PyObject_GC_TRACK(self);
|
||||||
|
}
|
||||||
|
MEMERRORS_UNLOCK(state);
|
||||||
|
|
||||||
|
if (self != NULL) {
|
||||||
|
return (PyObject *)self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fetch object from freelist and revive it */
|
if (!allow_allocation) {
|
||||||
self = state->memerrors_freelist;
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
self->args = PyTuple_New(0);
|
return Py_NewRef(
|
||||||
/* This shouldn't happen since the empty tuple is persistent */
|
&_Py_INTERP_SINGLETON(interp, last_resort_memory_error));
|
||||||
|
|
||||||
if (self->args == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
return BaseException_new((PyTypeObject *)PyExc_MemoryError, args, kwds);
|
||||||
state->memerrors_freelist = (PyBaseExceptionObject *) self->dict;
|
|
||||||
state->memerrors_numfree--;
|
|
||||||
self->dict = NULL;
|
|
||||||
_Py_NewReference((PyObject *)self);
|
|
||||||
_PyObject_GC_TRACK(self);
|
|
||||||
return (PyObject *)self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -3907,14 +3914,17 @@ MemoryError_dealloc(PyObject *obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct _Py_exc_state *state = get_exc_state();
|
struct _Py_exc_state *state = get_exc_state();
|
||||||
if (state->memerrors_numfree >= MEMERRORS_SAVE) {
|
MEMERRORS_LOCK(state);
|
||||||
Py_TYPE(self)->tp_free((PyObject *)self);
|
if (state->memerrors_numfree < MEMERRORS_SAVE) {
|
||||||
}
|
|
||||||
else {
|
|
||||||
self->dict = (PyObject *) state->memerrors_freelist;
|
self->dict = (PyObject *) state->memerrors_freelist;
|
||||||
state->memerrors_freelist = self;
|
state->memerrors_freelist = self;
|
||||||
state->memerrors_numfree++;
|
state->memerrors_numfree++;
|
||||||
|
MEMERRORS_UNLOCK(state);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
MEMERRORS_UNLOCK(state);
|
||||||
|
|
||||||
|
Py_TYPE(self)->tp_free((PyObject *)self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue