diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2010-10-28 22:56:58 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2010-10-28 22:56:58 (GMT) |
commit | 07e20ef50b52d2359213bc5d91f809a76cfd1524 (patch) | |
tree | 74803fa7efe3959104b0d9760e4cff172e5c3919 /Objects/exceptions.c | |
parent | 1842d0c4d88fffb5bea53b410acb77796b66bc8b (diff) | |
download | cpython-07e20ef50b52d2359213bc5d91f809a76cfd1524.zip cpython-07e20ef50b52d2359213bc5d91f809a76cfd1524.tar.gz cpython-07e20ef50b52d2359213bc5d91f809a76cfd1524.tar.bz2 |
Issue #5437: A preallocated MemoryError instance should not hold traceback
data (including local variables caught in the stack trace) alive infinitely.
Diffstat (limited to 'Objects/exceptions.c')
-rw-r--r-- | Objects/exceptions.c | 97 |
1 files changed, 87 insertions, 10 deletions
diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 5715b26..0106ba3 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1777,7 +1777,91 @@ SimpleExtendsException(PyExc_Exception, ReferenceError, /* * MemoryError extends Exception */ -SimpleExtendsException(PyExc_Exception, MemoryError, "Out of memory."); + +#define MEMERRORS_SAVE 16 +static PyBaseExceptionObject *memerrors_freelist = NULL; +static int memerrors_numfree = 0; + +static PyObject * +MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyBaseExceptionObject *self; + + if (type != (PyTypeObject *) PyExc_MemoryError) + return BaseException_new(type, args, kwds); + if (memerrors_freelist == NULL) + return BaseException_new(type, args, kwds); + /* Fetch object from freelist and revive it */ + self = memerrors_freelist; + self->args = PyTuple_New(0); + /* This shouldn't happen since the empty tuple is persistent */ + if (self->args == NULL) + return NULL; + memerrors_freelist = (PyBaseExceptionObject *) self->dict; + memerrors_numfree--; + self->dict = NULL; + _Py_NewReference((PyObject *)self); + _PyObject_GC_TRACK(self); + return (PyObject *)self; +} + +static void +MemoryError_dealloc(PyBaseExceptionObject *self) +{ + _PyObject_GC_UNTRACK(self); + BaseException_clear(self); + if (memerrors_numfree >= MEMERRORS_SAVE) + Py_TYPE(self)->tp_free((PyObject *)self); + else { + self->dict = (PyObject *) memerrors_freelist; + memerrors_freelist = self; + memerrors_numfree++; + } +} + +static void +preallocate_memerrors(void) +{ + /* We create enough MemoryErrors and then decref them, which will fill + up the freelist. */ + int i; + PyObject *errors[MEMERRORS_SAVE]; + for (i = 0; i < MEMERRORS_SAVE; i++) { + errors[i] = MemoryError_new((PyTypeObject *) PyExc_MemoryError, + NULL, NULL); + if (!errors[i]) + Py_FatalError("Could not preallocate MemoryError object"); + } + for (i = 0; i < MEMERRORS_SAVE; i++) { + Py_DECREF(errors[i]); + } +} + +static void +free_preallocated_memerrors(void) +{ + while (memerrors_freelist != NULL) { + PyObject *self = (PyObject *) memerrors_freelist; + memerrors_freelist = (PyBaseExceptionObject *) memerrors_freelist->dict; + Py_TYPE(self)->tp_free((PyObject *)self); + } +} + + +static PyTypeObject _PyExc_MemoryError = { + PyVarObject_HEAD_INIT(NULL, 0) + "MemoryError", + sizeof(PyBaseExceptionObject), + 0, (destructor)MemoryError_dealloc, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + PyDoc_STR("Out of memory."), (traverseproc)BaseException_traverse, + (inquiry)BaseException_clear, 0, 0, 0, 0, 0, 0, 0, &_PyExc_Exception, + 0, 0, 0, offsetof(PyBaseExceptionObject, dict), + (initproc)BaseException_init, 0, MemoryError_new +}; +PyObject *PyExc_MemoryError = (PyObject *) &_PyExc_MemoryError; + /* * BufferError extends Exception @@ -1869,11 +1953,6 @@ SimpleExtendsException(PyExc_Warning, ResourceWarning, -/* Pre-computed MemoryError instance. Best to create this as early as - * possible and not wait until a MemoryError is actually raised! - */ -PyObject *PyExc_MemoryErrorInst=NULL; - /* Pre-computed RuntimeError instance for when recursion depth is reached. Meant to be used when normalizing the exception for exceeding the recursion depth will cause its own infinite recursion. @@ -2012,9 +2091,7 @@ _PyExc_Init(void) POST_INIT(BytesWarning) POST_INIT(ResourceWarning) - PyExc_MemoryErrorInst = BaseException_new(&_PyExc_MemoryError, NULL, NULL); - if (!PyExc_MemoryErrorInst) - Py_FatalError("Cannot pre-allocate MemoryError instance"); + preallocate_memerrors(); PyExc_RecursionErrorInst = BaseException_new(&_PyExc_RuntimeError, NULL, NULL); if (!PyExc_RecursionErrorInst) @@ -2045,6 +2122,6 @@ _PyExc_Init(void) void _PyExc_Fini(void) { - Py_CLEAR(PyExc_MemoryErrorInst); Py_CLEAR(PyExc_RecursionErrorInst); + free_preallocated_memerrors(); } |