diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index c22bea75d27..435a72a5220 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -152,6 +152,13 @@ struct _Py_context_state { 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 */ @@ -251,6 +258,7 @@ struct _is { struct _Py_frame_state frame; struct _Py_async_gen_state async_gen; struct _Py_context_state context; + struct _Py_exc_state exc_state; }; /* Used by _PyImport_Cleanup() */ diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 30ba48423f9..cd470441817 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -43,7 +43,7 @@ extern PyStatus _PySys_Create( extern PyStatus _PySys_ReadPreinitWarnOptions(PyWideStringList *options); extern PyStatus _PySys_ReadPreinitXOptions(PyConfig *config); extern int _PySys_InitMain(PyThreadState *tstate); -extern PyStatus _PyExc_Init(void); +extern PyStatus _PyExc_Init(PyThreadState *tstate); extern PyStatus _PyErr_Init(void); extern PyStatus _PyBuiltins_AddExceptions(PyObject * bltinmod); extern PyStatus _PyImportHooks_Init(PyThreadState *tstate); @@ -69,7 +69,7 @@ extern void _PyAsyncGen_Fini(PyThreadState *tstate); extern void PyOS_FiniInterrupts(void); -extern void _PyExc_Fini(void); +extern void _PyExc_Fini(PyThreadState *tstate); extern void _PyImport_Fini(void); extern void _PyImport_Fini2(void); extern void _PyGC_Fini(PyThreadState *tstate); diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst index a62383d2093..9b94bcc0169 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst @@ -1,10 +1,9 @@ Each interpreter now its has own free lists, singletons and caches: * Free lists: float, tuple, list, dict, frame, context, - asynchronous generator. + asynchronous generator, MemoryError. * Singletons: empty tuple, empty bytes string, single byte character. * Slice cache. They are no longer shared by all interpreters. - diff --git a/Objects/exceptions.c b/Objects/exceptions.c index db5e3da12b0..1195ba17922 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -19,8 +19,13 @@ PyObject *PyExc_IOError = NULL; PyObject *PyExc_WindowsError = NULL; #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 @@ -985,10 +990,11 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) )) goto error; + struct _Py_exc_state *state = get_exc_state(); if (myerrno && PyLong_Check(myerrno) && - errnomap && (PyObject *) type == PyExc_OSError) { + state->errnomap && (PyObject *) type == PyExc_OSError) { PyObject *newtype; - newtype = PyDict_GetItemWithError(errnomap, myerrno); + newtype = PyDict_GetItemWithError(state->errnomap, myerrno); if (newtype) { assert(PyType_Check(newtype)); type = (PyTypeObject *) newtype; @@ -2274,8 +2280,6 @@ SimpleExtendsException(PyExc_Exception, ReferenceError, */ #define MEMERRORS_SAVE 16 -static PyBaseExceptionObject *memerrors_freelist = NULL; -static int memerrors_numfree = 0; static PyObject * 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) 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); + } + /* Fetch object from freelist and revive it */ - self = memerrors_freelist; + self = state->memerrors_freelist; self->args = PyTuple_New(0); /* This shouldn't happen since the empty tuple is persistent */ - if (self->args == NULL) + if (self->args == NULL) { return NULL; - memerrors_freelist = (PyBaseExceptionObject *) self->dict; - memerrors_numfree--; + } + + state->memerrors_freelist = (PyBaseExceptionObject *) self->dict; + state->memerrors_numfree--; self->dict = NULL; _Py_NewReference((PyObject *)self); _PyObject_GC_TRACK(self); @@ -2305,12 +2315,15 @@ MemoryError_dealloc(PyBaseExceptionObject *self) { _PyObject_GC_UNTRACK(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); + } else { - self->dict = (PyObject *) memerrors_freelist; - memerrors_freelist = self; - memerrors_numfree++; + self->dict = (PyObject *) state->memerrors_freelist; + state->memerrors_freelist = self; + state->memerrors_numfree++; } } @@ -2335,11 +2348,11 @@ preallocate_memerrors(void) } static void -free_preallocated_memerrors(void) +free_preallocated_memerrors(struct _Py_exc_state *state) { - while (memerrors_freelist != NULL) { - PyObject *self = (PyObject *) memerrors_freelist; - memerrors_freelist = (PyBaseExceptionObject *) memerrors_freelist->dict; + while (state->memerrors_freelist != NULL) { + PyObject *self = (PyObject *) state->memerrors_freelist; + state->memerrors_freelist = (PyBaseExceptionObject *)state->memerrors_freelist->dict; Py_TYPE(self)->tp_free((PyObject *)self); } } @@ -2507,8 +2520,10 @@ SimpleExtendsException(PyExc_Warning, ResourceWarning, #endif /* MS_WINDOWS */ PyStatus -_PyExc_Init(void) +_PyExc_Init(PyThreadState *tstate) { + struct _Py_exc_state *state = &tstate->interp->exc_state; + #define PRE_INIT(TYPE) \ if (!(_PyExc_ ## TYPE.tp_flags & Py_TPFLAGS_READY)) { \ if (PyType_Ready(&_PyExc_ ## TYPE) < 0) { \ @@ -2521,7 +2536,7 @@ _PyExc_Init(void) do { \ PyObject *_code = PyLong_FromLong(CODE); \ 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."); \ Py_DECREF(_code); \ } while (0) @@ -2595,15 +2610,14 @@ _PyExc_Init(void) PRE_INIT(TimeoutError); if (preallocate_memerrors() < 0) { - return _PyStatus_ERR("Could not preallocate MemoryError object"); + return _PyStatus_NO_MEMORY(); } /* Add exceptions to errnomap */ - if (!errnomap) { - errnomap = PyDict_New(); - if (!errnomap) { - return _PyStatus_ERR("Cannot allocate map from errnos to OSError subclasses"); - } + assert(state->errnomap == NULL); + state->errnomap = PyDict_New(); + if (!state->errnomap) { + return _PyStatus_NO_MEMORY(); } ADD_ERRNO(BlockingIOError, EAGAIN); @@ -2741,10 +2755,11 @@ _PyBuiltins_AddExceptions(PyObject *bltinmod) } void -_PyExc_Fini(void) +_PyExc_Fini(PyThreadState *tstate) { - free_preallocated_memerrors(); - Py_CLEAR(errnomap); + struct _Py_exc_state *state = &tstate->interp->exc_state; + free_preallocated_memerrors(state); + Py_CLEAR(state->errnomap); } /* Helper to do the equivalent of "raise X from Y" in C, but always using diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 09945a8f7a6..f0b40b3aa68 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -602,7 +602,7 @@ pycore_init_types(PyThreadState *tstate) } } - status = _PyExc_Init(); + status = _PyExc_Init(tstate); if (_PyStatus_EXCEPTION(status)) { return status; } @@ -1249,6 +1249,7 @@ flush_std_files(void) static void finalize_interp_types(PyThreadState *tstate, int is_main_interp) { + _PyExc_Fini(tstate); _PyFrame_Fini(tstate); _PyAsyncGen_Fini(tstate); _PyContext_Fini(tstate); @@ -1289,10 +1290,6 @@ finalize_interp_clear(PyThreadState *tstate) _PyWarnings_Fini(tstate->interp); - if (is_main_interp) { - _PyExc_Fini(); - } - finalize_interp_types(tstate, is_main_interp); }