Skip to content

Commit 281cce1

Browse files
authored
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.
1 parent 2c6e4e9 commit 281cce1

File tree

5 files changed

+58
-39
lines changed

5 files changed

+58
-39
lines changed

Include/internal/pycore_interp.h

+8
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,13 @@ struct _Py_context_state {
152152
int numfree;
153153
};
154154

155+
struct _Py_exc_state {
156+
// The dict mapping from errno codes to OSError subclasses
157+
PyObject *errnomap;
158+
PyBaseExceptionObject *memerrors_freelist;
159+
int memerrors_numfree;
160+
};
161+
155162

156163
/* interpreter state */
157164

@@ -251,6 +258,7 @@ struct _is {
251258
struct _Py_frame_state frame;
252259
struct _Py_async_gen_state async_gen;
253260
struct _Py_context_state context;
261+
struct _Py_exc_state exc_state;
254262
};
255263

256264
/* Used by _PyImport_Cleanup() */

Include/internal/pycore_pylifecycle.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ extern PyStatus _PySys_Create(
4343
extern PyStatus _PySys_ReadPreinitWarnOptions(PyWideStringList *options);
4444
extern PyStatus _PySys_ReadPreinitXOptions(PyConfig *config);
4545
extern int _PySys_InitMain(PyThreadState *tstate);
46-
extern PyStatus _PyExc_Init(void);
46+
extern PyStatus _PyExc_Init(PyThreadState *tstate);
4747
extern PyStatus _PyErr_Init(void);
4848
extern PyStatus _PyBuiltins_AddExceptions(PyObject * bltinmod);
4949
extern PyStatus _PyImportHooks_Init(PyThreadState *tstate);
@@ -69,7 +69,7 @@ extern void _PyAsyncGen_Fini(PyThreadState *tstate);
6969

7070
extern void PyOS_FiniInterrupts(void);
7171

72-
extern void _PyExc_Fini(void);
72+
extern void _PyExc_Fini(PyThreadState *tstate);
7373
extern void _PyImport_Fini(void);
7474
extern void _PyImport_Fini2(void);
7575
extern void _PyGC_Fini(PyThreadState *tstate);
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
Each interpreter now its has own free lists, singletons and caches:
22

33
* Free lists: float, tuple, list, dict, frame, context,
4-
asynchronous generator.
4+
asynchronous generator, MemoryError.
55
* Singletons: empty tuple, empty bytes string,
66
single byte character.
77
* Slice cache.
88

99
They are no longer shared by all interpreters.
10-

Objects/exceptions.c

+45-30
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,13 @@ PyObject *PyExc_IOError = NULL;
1919
PyObject *PyExc_WindowsError = NULL;
2020
#endif
2121

22-
/* The dict map from errno codes to OSError subclasses */
23-
static PyObject *errnomap = NULL;
22+
23+
static struct _Py_exc_state*
24+
get_exc_state(void)
25+
{
26+
PyInterpreterState *interp = _PyInterpreterState_GET();
27+
return &interp->exc_state;
28+
}
2429

2530

2631
/* NOTE: If the exception class hierarchy changes, don't forget to update
@@ -985,10 +990,11 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
985990
))
986991
goto error;
987992

993+
struct _Py_exc_state *state = get_exc_state();
988994
if (myerrno && PyLong_Check(myerrno) &&
989-
errnomap && (PyObject *) type == PyExc_OSError) {
995+
state->errnomap && (PyObject *) type == PyExc_OSError) {
990996
PyObject *newtype;
991-
newtype = PyDict_GetItemWithError(errnomap, myerrno);
997+
newtype = PyDict_GetItemWithError(state->errnomap, myerrno);
992998
if (newtype) {
993999
assert(PyType_Check(newtype));
9941000
type = (PyTypeObject *) newtype;
@@ -2274,8 +2280,6 @@ SimpleExtendsException(PyExc_Exception, ReferenceError,
22742280
*/
22752281

22762282
#define MEMERRORS_SAVE 16
2277-
static PyBaseExceptionObject *memerrors_freelist = NULL;
2278-
static int memerrors_numfree = 0;
22792283

22802284
static PyObject *
22812285
MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
@@ -2284,16 +2288,22 @@ MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
22842288

22852289
if (type != (PyTypeObject *) PyExc_MemoryError)
22862290
return BaseException_new(type, args, kwds);
2287-
if (memerrors_freelist == NULL)
2291+
2292+
struct _Py_exc_state *state = get_exc_state();
2293+
if (state->memerrors_freelist == NULL) {
22882294
return BaseException_new(type, args, kwds);
2295+
}
2296+
22892297
/* Fetch object from freelist and revive it */
2290-
self = memerrors_freelist;
2298+
self = state->memerrors_freelist;
22912299
self->args = PyTuple_New(0);
22922300
/* This shouldn't happen since the empty tuple is persistent */
2293-
if (self->args == NULL)
2301+
if (self->args == NULL) {
22942302
return NULL;
2295-
memerrors_freelist = (PyBaseExceptionObject *) self->dict;
2296-
memerrors_numfree--;
2303+
}
2304+
2305+
state->memerrors_freelist = (PyBaseExceptionObject *) self->dict;
2306+
state->memerrors_numfree--;
22972307
self->dict = NULL;
22982308
_Py_NewReference((PyObject *)self);
22992309
_PyObject_GC_TRACK(self);
@@ -2305,12 +2315,15 @@ MemoryError_dealloc(PyBaseExceptionObject *self)
23052315
{
23062316
_PyObject_GC_UNTRACK(self);
23072317
BaseException_clear(self);
2308-
if (memerrors_numfree >= MEMERRORS_SAVE)
2318+
2319+
struct _Py_exc_state *state = get_exc_state();
2320+
if (state->memerrors_numfree >= MEMERRORS_SAVE) {
23092321
Py_TYPE(self)->tp_free((PyObject *)self);
2322+
}
23102323
else {
2311-
self->dict = (PyObject *) memerrors_freelist;
2312-
memerrors_freelist = self;
2313-
memerrors_numfree++;
2324+
self->dict = (PyObject *) state->memerrors_freelist;
2325+
state->memerrors_freelist = self;
2326+
state->memerrors_numfree++;
23142327
}
23152328
}
23162329

@@ -2335,11 +2348,11 @@ preallocate_memerrors(void)
23352348
}
23362349

23372350
static void
2338-
free_preallocated_memerrors(void)
2351+
free_preallocated_memerrors(struct _Py_exc_state *state)
23392352
{
2340-
while (memerrors_freelist != NULL) {
2341-
PyObject *self = (PyObject *) memerrors_freelist;
2342-
memerrors_freelist = (PyBaseExceptionObject *) memerrors_freelist->dict;
2353+
while (state->memerrors_freelist != NULL) {
2354+
PyObject *self = (PyObject *) state->memerrors_freelist;
2355+
state->memerrors_freelist = (PyBaseExceptionObject *)state->memerrors_freelist->dict;
23432356
Py_TYPE(self)->tp_free((PyObject *)self);
23442357
}
23452358
}
@@ -2507,8 +2520,10 @@ SimpleExtendsException(PyExc_Warning, ResourceWarning,
25072520
#endif /* MS_WINDOWS */
25082521

25092522
PyStatus
2510-
_PyExc_Init(void)
2523+
_PyExc_Init(PyThreadState *tstate)
25112524
{
2525+
struct _Py_exc_state *state = &tstate->interp->exc_state;
2526+
25122527
#define PRE_INIT(TYPE) \
25132528
if (!(_PyExc_ ## TYPE.tp_flags & Py_TPFLAGS_READY)) { \
25142529
if (PyType_Ready(&_PyExc_ ## TYPE) < 0) { \
@@ -2521,7 +2536,7 @@ _PyExc_Init(void)
25212536
do { \
25222537
PyObject *_code = PyLong_FromLong(CODE); \
25232538
assert(_PyObject_RealIsSubclass(PyExc_ ## TYPE, PyExc_OSError)); \
2524-
if (!_code || PyDict_SetItem(errnomap, _code, PyExc_ ## TYPE)) \
2539+
if (!_code || PyDict_SetItem(state->errnomap, _code, PyExc_ ## TYPE)) \
25252540
return _PyStatus_ERR("errmap insertion problem."); \
25262541
Py_DECREF(_code); \
25272542
} while (0)
@@ -2595,15 +2610,14 @@ _PyExc_Init(void)
25952610
PRE_INIT(TimeoutError);
25962611

25972612
if (preallocate_memerrors() < 0) {
2598-
return _PyStatus_ERR("Could not preallocate MemoryError object");
2613+
return _PyStatus_NO_MEMORY();
25992614
}
26002615

26012616
/* Add exceptions to errnomap */
2602-
if (!errnomap) {
2603-
errnomap = PyDict_New();
2604-
if (!errnomap) {
2605-
return _PyStatus_ERR("Cannot allocate map from errnos to OSError subclasses");
2606-
}
2617+
assert(state->errnomap == NULL);
2618+
state->errnomap = PyDict_New();
2619+
if (!state->errnomap) {
2620+
return _PyStatus_NO_MEMORY();
26072621
}
26082622

26092623
ADD_ERRNO(BlockingIOError, EAGAIN);
@@ -2741,10 +2755,11 @@ _PyBuiltins_AddExceptions(PyObject *bltinmod)
27412755
}
27422756

27432757
void
2744-
_PyExc_Fini(void)
2758+
_PyExc_Fini(PyThreadState *tstate)
27452759
{
2746-
free_preallocated_memerrors();
2747-
Py_CLEAR(errnomap);
2760+
struct _Py_exc_state *state = &tstate->interp->exc_state;
2761+
free_preallocated_memerrors(state);
2762+
Py_CLEAR(state->errnomap);
27482763
}
27492764

27502765
/* Helper to do the equivalent of "raise X from Y" in C, but always using

Python/pylifecycle.c

+2-5
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ pycore_init_types(PyThreadState *tstate)
602602
}
603603
}
604604

605-
status = _PyExc_Init();
605+
status = _PyExc_Init(tstate);
606606
if (_PyStatus_EXCEPTION(status)) {
607607
return status;
608608
}
@@ -1249,6 +1249,7 @@ flush_std_files(void)
12491249
static void
12501250
finalize_interp_types(PyThreadState *tstate, int is_main_interp)
12511251
{
1252+
_PyExc_Fini(tstate);
12521253
_PyFrame_Fini(tstate);
12531254
_PyAsyncGen_Fini(tstate);
12541255
_PyContext_Fini(tstate);
@@ -1289,10 +1290,6 @@ finalize_interp_clear(PyThreadState *tstate)
12891290

12901291
_PyWarnings_Fini(tstate->interp);
12911292

1292-
if (is_main_interp) {
1293-
_PyExc_Fini();
1294-
}
1295-
12961293
finalize_interp_types(tstate, is_main_interp);
12971294
}
12981295

0 commit comments

Comments
 (0)