mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
bpo-40521: Make MemoryError free list per interpreter (GH-21086)
Each interpreter now has its own MemoryError free list: it is not longer shared by all interpreters. Add _Py_exc_state structure and PyInterpreterState.exc_state member. Move also errnomap into _Py_exc_state.
This commit is contained in:
parent
2c6e4e91c5
commit
281cce1106
5 changed files with 58 additions and 39 deletions
|
@ -152,6 +152,13 @@ struct _Py_context_state {
|
||||||
int numfree;
|
int numfree;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct _Py_exc_state {
|
||||||
|
// The dict mapping from errno codes to OSError subclasses
|
||||||
|
PyObject *errnomap;
|
||||||
|
PyBaseExceptionObject *memerrors_freelist;
|
||||||
|
int memerrors_numfree;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* interpreter state */
|
/* interpreter state */
|
||||||
|
|
||||||
|
@ -251,6 +258,7 @@ struct _is {
|
||||||
struct _Py_frame_state frame;
|
struct _Py_frame_state frame;
|
||||||
struct _Py_async_gen_state async_gen;
|
struct _Py_async_gen_state async_gen;
|
||||||
struct _Py_context_state context;
|
struct _Py_context_state context;
|
||||||
|
struct _Py_exc_state exc_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Used by _PyImport_Cleanup() */
|
/* Used by _PyImport_Cleanup() */
|
||||||
|
|
|
@ -43,7 +43,7 @@ extern PyStatus _PySys_Create(
|
||||||
extern PyStatus _PySys_ReadPreinitWarnOptions(PyWideStringList *options);
|
extern PyStatus _PySys_ReadPreinitWarnOptions(PyWideStringList *options);
|
||||||
extern PyStatus _PySys_ReadPreinitXOptions(PyConfig *config);
|
extern PyStatus _PySys_ReadPreinitXOptions(PyConfig *config);
|
||||||
extern int _PySys_InitMain(PyThreadState *tstate);
|
extern int _PySys_InitMain(PyThreadState *tstate);
|
||||||
extern PyStatus _PyExc_Init(void);
|
extern PyStatus _PyExc_Init(PyThreadState *tstate);
|
||||||
extern PyStatus _PyErr_Init(void);
|
extern PyStatus _PyErr_Init(void);
|
||||||
extern PyStatus _PyBuiltins_AddExceptions(PyObject * bltinmod);
|
extern PyStatus _PyBuiltins_AddExceptions(PyObject * bltinmod);
|
||||||
extern PyStatus _PyImportHooks_Init(PyThreadState *tstate);
|
extern PyStatus _PyImportHooks_Init(PyThreadState *tstate);
|
||||||
|
@ -69,7 +69,7 @@ extern void _PyAsyncGen_Fini(PyThreadState *tstate);
|
||||||
|
|
||||||
extern void PyOS_FiniInterrupts(void);
|
extern void PyOS_FiniInterrupts(void);
|
||||||
|
|
||||||
extern void _PyExc_Fini(void);
|
extern void _PyExc_Fini(PyThreadState *tstate);
|
||||||
extern void _PyImport_Fini(void);
|
extern void _PyImport_Fini(void);
|
||||||
extern void _PyImport_Fini2(void);
|
extern void _PyImport_Fini2(void);
|
||||||
extern void _PyGC_Fini(PyThreadState *tstate);
|
extern void _PyGC_Fini(PyThreadState *tstate);
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
Each interpreter now its has own free lists, singletons and caches:
|
Each interpreter now its has own free lists, singletons and caches:
|
||||||
|
|
||||||
* Free lists: float, tuple, list, dict, frame, context,
|
* Free lists: float, tuple, list, dict, frame, context,
|
||||||
asynchronous generator.
|
asynchronous generator, MemoryError.
|
||||||
* Singletons: empty tuple, empty bytes string,
|
* Singletons: empty tuple, empty bytes string,
|
||||||
single byte character.
|
single byte character.
|
||||||
* Slice cache.
|
* Slice cache.
|
||||||
|
|
||||||
They are no longer shared by all interpreters.
|
They are no longer shared by all interpreters.
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,13 @@ PyObject *PyExc_IOError = NULL;
|
||||||
PyObject *PyExc_WindowsError = NULL;
|
PyObject *PyExc_WindowsError = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* The dict map from errno codes to OSError subclasses */
|
|
||||||
static PyObject *errnomap = NULL;
|
static struct _Py_exc_state*
|
||||||
|
get_exc_state(void)
|
||||||
|
{
|
||||||
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
|
return &interp->exc_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* NOTE: If the exception class hierarchy changes, don't forget to update
|
/* NOTE: If the exception class hierarchy changes, don't forget to update
|
||||||
|
@ -985,10 +990,11 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
))
|
))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
struct _Py_exc_state *state = get_exc_state();
|
||||||
if (myerrno && PyLong_Check(myerrno) &&
|
if (myerrno && PyLong_Check(myerrno) &&
|
||||||
errnomap && (PyObject *) type == PyExc_OSError) {
|
state->errnomap && (PyObject *) type == PyExc_OSError) {
|
||||||
PyObject *newtype;
|
PyObject *newtype;
|
||||||
newtype = PyDict_GetItemWithError(errnomap, myerrno);
|
newtype = PyDict_GetItemWithError(state->errnomap, myerrno);
|
||||||
if (newtype) {
|
if (newtype) {
|
||||||
assert(PyType_Check(newtype));
|
assert(PyType_Check(newtype));
|
||||||
type = (PyTypeObject *) newtype;
|
type = (PyTypeObject *) newtype;
|
||||||
|
@ -2274,8 +2280,6 @@ SimpleExtendsException(PyExc_Exception, ReferenceError,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define MEMERRORS_SAVE 16
|
#define MEMERRORS_SAVE 16
|
||||||
static PyBaseExceptionObject *memerrors_freelist = NULL;
|
|
||||||
static int memerrors_numfree = 0;
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
|
@ -2284,16 +2288,22 @@ MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
|
|
||||||
if (type != (PyTypeObject *) PyExc_MemoryError)
|
if (type != (PyTypeObject *) PyExc_MemoryError)
|
||||||
return BaseException_new(type, args, kwds);
|
return BaseException_new(type, args, kwds);
|
||||||
if (memerrors_freelist == NULL)
|
|
||||||
|
struct _Py_exc_state *state = get_exc_state();
|
||||||
|
if (state->memerrors_freelist == NULL) {
|
||||||
return BaseException_new(type, args, kwds);
|
return BaseException_new(type, args, kwds);
|
||||||
|
}
|
||||||
|
|
||||||
/* Fetch object from freelist and revive it */
|
/* Fetch object from freelist and revive it */
|
||||||
self = memerrors_freelist;
|
self = state->memerrors_freelist;
|
||||||
self->args = PyTuple_New(0);
|
self->args = PyTuple_New(0);
|
||||||
/* This shouldn't happen since the empty tuple is persistent */
|
/* This shouldn't happen since the empty tuple is persistent */
|
||||||
if (self->args == NULL)
|
if (self->args == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
memerrors_freelist = (PyBaseExceptionObject *) self->dict;
|
}
|
||||||
memerrors_numfree--;
|
|
||||||
|
state->memerrors_freelist = (PyBaseExceptionObject *) self->dict;
|
||||||
|
state->memerrors_numfree--;
|
||||||
self->dict = NULL;
|
self->dict = NULL;
|
||||||
_Py_NewReference((PyObject *)self);
|
_Py_NewReference((PyObject *)self);
|
||||||
_PyObject_GC_TRACK(self);
|
_PyObject_GC_TRACK(self);
|
||||||
|
@ -2305,12 +2315,15 @@ MemoryError_dealloc(PyBaseExceptionObject *self)
|
||||||
{
|
{
|
||||||
_PyObject_GC_UNTRACK(self);
|
_PyObject_GC_UNTRACK(self);
|
||||||
BaseException_clear(self);
|
BaseException_clear(self);
|
||||||
if (memerrors_numfree >= MEMERRORS_SAVE)
|
|
||||||
|
struct _Py_exc_state *state = get_exc_state();
|
||||||
|
if (state->memerrors_numfree >= MEMERRORS_SAVE) {
|
||||||
Py_TYPE(self)->tp_free((PyObject *)self);
|
Py_TYPE(self)->tp_free((PyObject *)self);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
self->dict = (PyObject *) memerrors_freelist;
|
self->dict = (PyObject *) state->memerrors_freelist;
|
||||||
memerrors_freelist = self;
|
state->memerrors_freelist = self;
|
||||||
memerrors_numfree++;
|
state->memerrors_numfree++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2335,11 +2348,11 @@ preallocate_memerrors(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
free_preallocated_memerrors(void)
|
free_preallocated_memerrors(struct _Py_exc_state *state)
|
||||||
{
|
{
|
||||||
while (memerrors_freelist != NULL) {
|
while (state->memerrors_freelist != NULL) {
|
||||||
PyObject *self = (PyObject *) memerrors_freelist;
|
PyObject *self = (PyObject *) state->memerrors_freelist;
|
||||||
memerrors_freelist = (PyBaseExceptionObject *) memerrors_freelist->dict;
|
state->memerrors_freelist = (PyBaseExceptionObject *)state->memerrors_freelist->dict;
|
||||||
Py_TYPE(self)->tp_free((PyObject *)self);
|
Py_TYPE(self)->tp_free((PyObject *)self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2507,8 +2520,10 @@ SimpleExtendsException(PyExc_Warning, ResourceWarning,
|
||||||
#endif /* MS_WINDOWS */
|
#endif /* MS_WINDOWS */
|
||||||
|
|
||||||
PyStatus
|
PyStatus
|
||||||
_PyExc_Init(void)
|
_PyExc_Init(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
|
struct _Py_exc_state *state = &tstate->interp->exc_state;
|
||||||
|
|
||||||
#define PRE_INIT(TYPE) \
|
#define PRE_INIT(TYPE) \
|
||||||
if (!(_PyExc_ ## TYPE.tp_flags & Py_TPFLAGS_READY)) { \
|
if (!(_PyExc_ ## TYPE.tp_flags & Py_TPFLAGS_READY)) { \
|
||||||
if (PyType_Ready(&_PyExc_ ## TYPE) < 0) { \
|
if (PyType_Ready(&_PyExc_ ## TYPE) < 0) { \
|
||||||
|
@ -2521,7 +2536,7 @@ _PyExc_Init(void)
|
||||||
do { \
|
do { \
|
||||||
PyObject *_code = PyLong_FromLong(CODE); \
|
PyObject *_code = PyLong_FromLong(CODE); \
|
||||||
assert(_PyObject_RealIsSubclass(PyExc_ ## TYPE, PyExc_OSError)); \
|
assert(_PyObject_RealIsSubclass(PyExc_ ## TYPE, PyExc_OSError)); \
|
||||||
if (!_code || PyDict_SetItem(errnomap, _code, PyExc_ ## TYPE)) \
|
if (!_code || PyDict_SetItem(state->errnomap, _code, PyExc_ ## TYPE)) \
|
||||||
return _PyStatus_ERR("errmap insertion problem."); \
|
return _PyStatus_ERR("errmap insertion problem."); \
|
||||||
Py_DECREF(_code); \
|
Py_DECREF(_code); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
@ -2595,15 +2610,14 @@ _PyExc_Init(void)
|
||||||
PRE_INIT(TimeoutError);
|
PRE_INIT(TimeoutError);
|
||||||
|
|
||||||
if (preallocate_memerrors() < 0) {
|
if (preallocate_memerrors() < 0) {
|
||||||
return _PyStatus_ERR("Could not preallocate MemoryError object");
|
return _PyStatus_NO_MEMORY();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add exceptions to errnomap */
|
/* Add exceptions to errnomap */
|
||||||
if (!errnomap) {
|
assert(state->errnomap == NULL);
|
||||||
errnomap = PyDict_New();
|
state->errnomap = PyDict_New();
|
||||||
if (!errnomap) {
|
if (!state->errnomap) {
|
||||||
return _PyStatus_ERR("Cannot allocate map from errnos to OSError subclasses");
|
return _PyStatus_NO_MEMORY();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ADD_ERRNO(BlockingIOError, EAGAIN);
|
ADD_ERRNO(BlockingIOError, EAGAIN);
|
||||||
|
@ -2741,10 +2755,11 @@ _PyBuiltins_AddExceptions(PyObject *bltinmod)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
_PyExc_Fini(void)
|
_PyExc_Fini(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
free_preallocated_memerrors();
|
struct _Py_exc_state *state = &tstate->interp->exc_state;
|
||||||
Py_CLEAR(errnomap);
|
free_preallocated_memerrors(state);
|
||||||
|
Py_CLEAR(state->errnomap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper to do the equivalent of "raise X from Y" in C, but always using
|
/* Helper to do the equivalent of "raise X from Y" in C, but always using
|
||||||
|
|
|
@ -602,7 +602,7 @@ pycore_init_types(PyThreadState *tstate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status = _PyExc_Init();
|
status = _PyExc_Init(tstate);
|
||||||
if (_PyStatus_EXCEPTION(status)) {
|
if (_PyStatus_EXCEPTION(status)) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -1249,6 +1249,7 @@ flush_std_files(void)
|
||||||
static void
|
static void
|
||||||
finalize_interp_types(PyThreadState *tstate, int is_main_interp)
|
finalize_interp_types(PyThreadState *tstate, int is_main_interp)
|
||||||
{
|
{
|
||||||
|
_PyExc_Fini(tstate);
|
||||||
_PyFrame_Fini(tstate);
|
_PyFrame_Fini(tstate);
|
||||||
_PyAsyncGen_Fini(tstate);
|
_PyAsyncGen_Fini(tstate);
|
||||||
_PyContext_Fini(tstate);
|
_PyContext_Fini(tstate);
|
||||||
|
@ -1289,10 +1290,6 @@ finalize_interp_clear(PyThreadState *tstate)
|
||||||
|
|
||||||
_PyWarnings_Fini(tstate->interp);
|
_PyWarnings_Fini(tstate->interp);
|
||||||
|
|
||||||
if (is_main_interp) {
|
|
||||||
_PyExc_Fini();
|
|
||||||
}
|
|
||||||
|
|
||||||
finalize_interp_types(tstate, is_main_interp);
|
finalize_interp_types(tstate, is_main_interp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue