Skip to content

Commit 98e2b45

Browse files
committed
Merged revisions 85896 via svnmerge from
svn+ssh://[email protected]/python/branches/py3k ........ r85896 | antoine.pitrou | 2010-10-29 00:56:58 +0200 (ven., 29 oct. 2010) | 4 lines Issue #5437: A preallocated MemoryError instance should not hold traceback data (including local variables caught in the stack trace) alive infinitely. ........
1 parent d7a3ab9 commit 98e2b45

File tree

5 files changed

+132
-35
lines changed

5 files changed

+132
-35
lines changed

Include/pyerrors.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@ PyAPI_DATA(PyObject *) PyExc_VMSError;
153153

154154
PyAPI_DATA(PyObject *) PyExc_BufferError;
155155

156-
PyAPI_DATA(PyObject *) PyExc_MemoryErrorInst;
157156
PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst;
158157

159158
/* Predefined warning categories */

Lib/test/test_exceptions.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,46 @@ def raiseMemError():
669669
tb2 = raiseMemError()
670670
self.assertEqual(tb1, tb2)
671671

672+
def test_memory_error_cleanup(self):
673+
# Issue #5437: preallocated MemoryError instances should not keep
674+
# traceback objects alive.
675+
from _testcapi import raise_memoryerror
676+
class C:
677+
pass
678+
wr = None
679+
def inner():
680+
nonlocal wr
681+
c = C()
682+
wr = weakref.ref(c)
683+
raise_memoryerror()
684+
# We cannot use assertRaises since it manually deletes the traceback
685+
try:
686+
inner()
687+
except MemoryError as e:
688+
self.assertNotEqual(wr(), None)
689+
else:
690+
self.fail("MemoryError not raised")
691+
self.assertEqual(wr(), None)
692+
693+
def test_recursion_error_cleanup(self):
694+
# Same test as above, but with "recursion exceeded" errors
695+
class C:
696+
pass
697+
wr = None
698+
def inner():
699+
nonlocal wr
700+
c = C()
701+
wr = weakref.ref(c)
702+
inner()
703+
# We cannot use assertRaises since it manually deletes the traceback
704+
try:
705+
inner()
706+
except RuntimeError as e:
707+
self.assertNotEqual(wr(), None)
708+
else:
709+
self.fail("RuntimeError not raised")
710+
self.assertEqual(wr(), None)
711+
672712
def test_main():
673713
run_unittest(ExceptionTests)
674714

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 3.1.3?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #5437: A preallocated MemoryError instance should not hold traceback
14+
data (including local variables caught in the stack trace) alive infinitely.
15+
1316
- Issue #10077: Fix logging of site module errors at startup.
1417

1518
- Issue #10186: Fix the SyntaxError caret when the offset is equal to the length

Objects/exceptions.c

Lines changed: 88 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1763,7 +1763,91 @@ SimpleExtendsException(PyExc_Exception, ReferenceError,
17631763
/*
17641764
* MemoryError extends Exception
17651765
*/
1766-
SimpleExtendsException(PyExc_Exception, MemoryError, "Out of memory.");
1766+
1767+
#define MEMERRORS_SAVE 16
1768+
static PyBaseExceptionObject *memerrors_freelist = NULL;
1769+
static int memerrors_numfree = 0;
1770+
1771+
static PyObject *
1772+
MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1773+
{
1774+
PyBaseExceptionObject *self;
1775+
1776+
if (type != (PyTypeObject *) PyExc_MemoryError)
1777+
return BaseException_new(type, args, kwds);
1778+
if (memerrors_freelist == NULL)
1779+
return BaseException_new(type, args, kwds);
1780+
/* Fetch object from freelist and revive it */
1781+
self = memerrors_freelist;
1782+
self->args = PyTuple_New(0);
1783+
/* This shouldn't happen since the empty tuple is persistent */
1784+
if (self->args == NULL)
1785+
return NULL;
1786+
memerrors_freelist = (PyBaseExceptionObject *) self->dict;
1787+
memerrors_numfree--;
1788+
self->dict = NULL;
1789+
_Py_NewReference((PyObject *)self);
1790+
_PyObject_GC_TRACK(self);
1791+
return (PyObject *)self;
1792+
}
1793+
1794+
static void
1795+
MemoryError_dealloc(PyBaseExceptionObject *self)
1796+
{
1797+
_PyObject_GC_UNTRACK(self);
1798+
BaseException_clear(self);
1799+
if (memerrors_numfree >= MEMERRORS_SAVE)
1800+
Py_TYPE(self)->tp_free((PyObject *)self);
1801+
else {
1802+
self->dict = (PyObject *) memerrors_freelist;
1803+
memerrors_freelist = self;
1804+
memerrors_numfree++;
1805+
}
1806+
}
1807+
1808+
static void
1809+
preallocate_memerrors(void)
1810+
{
1811+
/* We create enough MemoryErrors and then decref them, which will fill
1812+
up the freelist. */
1813+
int i;
1814+
PyObject *errors[MEMERRORS_SAVE];
1815+
for (i = 0; i < MEMERRORS_SAVE; i++) {
1816+
errors[i] = MemoryError_new((PyTypeObject *) PyExc_MemoryError,
1817+
NULL, NULL);
1818+
if (!errors[i])
1819+
Py_FatalError("Could not preallocate MemoryError object");
1820+
}
1821+
for (i = 0; i < MEMERRORS_SAVE; i++) {
1822+
Py_DECREF(errors[i]);
1823+
}
1824+
}
1825+
1826+
static void
1827+
free_preallocated_memerrors(void)
1828+
{
1829+
while (memerrors_freelist != NULL) {
1830+
PyObject *self = (PyObject *) memerrors_freelist;
1831+
memerrors_freelist = (PyBaseExceptionObject *) memerrors_freelist->dict;
1832+
Py_TYPE(self)->tp_free((PyObject *)self);
1833+
}
1834+
}
1835+
1836+
1837+
static PyTypeObject _PyExc_MemoryError = {
1838+
PyVarObject_HEAD_INIT(NULL, 0)
1839+
"MemoryError",
1840+
sizeof(PyBaseExceptionObject),
1841+
0, (destructor)MemoryError_dealloc, 0, 0, 0, 0, 0, 0, 0,
1842+
0, 0, 0, 0, 0, 0, 0,
1843+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
1844+
PyDoc_STR("Out of memory."), (traverseproc)BaseException_traverse,
1845+
(inquiry)BaseException_clear, 0, 0, 0, 0, 0, 0, 0, &_PyExc_Exception,
1846+
0, 0, 0, offsetof(PyBaseExceptionObject, dict),
1847+
(initproc)BaseException_init, 0, MemoryError_new
1848+
};
1849+
PyObject *PyExc_MemoryError = (PyObject *) &_PyExc_MemoryError;
1850+
17671851

17681852
/*
17691853
* BufferError extends Exception
@@ -1847,11 +1931,6 @@ SimpleExtendsException(PyExc_Warning, BytesWarning,
18471931

18481932

18491933

1850-
/* Pre-computed MemoryError instance. Best to create this as early as
1851-
* possible and not wait until a MemoryError is actually raised!
1852-
*/
1853-
PyObject *PyExc_MemoryErrorInst=NULL;
1854-
18551934
/* Pre-computed RuntimeError instance for when recursion depth is reached.
18561935
Meant to be used when normalizing the exception for exceeding the recursion
18571936
depth will cause its own infinite recursion.
@@ -1988,9 +2067,7 @@ _PyExc_Init(void)
19882067
POST_INIT(UnicodeWarning)
19892068
POST_INIT(BytesWarning)
19902069

1991-
PyExc_MemoryErrorInst = BaseException_new(&_PyExc_MemoryError, NULL, NULL);
1992-
if (!PyExc_MemoryErrorInst)
1993-
Py_FatalError("Cannot pre-allocate MemoryError instance");
2070+
preallocate_memerrors();
19942071

19952072
PyExc_RecursionErrorInst = BaseException_new(&_PyExc_RuntimeError, NULL, NULL);
19962073
if (!PyExc_RecursionErrorInst)
@@ -2021,6 +2098,6 @@ _PyExc_Init(void)
20212098
void
20222099
_PyExc_Fini(void)
20232100
{
2024-
Py_XDECREF(PyExc_MemoryErrorInst);
2025-
PyExc_MemoryErrorInst = NULL;
2101+
Py_CLEAR(PyExc_RecursionErrorInst);
2102+
free_preallocated_memerrors();
20262103
}

Python/errors.c

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -328,29 +328,7 @@ PyErr_BadArgument(void)
328328
PyObject *
329329
PyErr_NoMemory(void)
330330
{
331-
if (PyErr_ExceptionMatches(PyExc_MemoryError))
332-
/* already current */
333-
return NULL;
334-
335-
/* raise the pre-allocated instance if it still exists */
336-
if (PyExc_MemoryErrorInst)
337-
{
338-
/* Clear the previous traceback, otherwise it will be appended
339-
* to the current one.
340-
*
341-
* The following statement is not likely to raise any error;
342-
* if it does, we simply discard it.
343-
*/
344-
PyException_SetTraceback(PyExc_MemoryErrorInst, Py_None);
345-
346-
PyErr_SetObject(PyExc_MemoryError, PyExc_MemoryErrorInst);
347-
}
348-
else
349-
/* this will probably fail since there's no memory and hee,
350-
hee, we have to instantiate this class
351-
*/
352-
PyErr_SetNone(PyExc_MemoryError);
353-
331+
PyErr_SetNone(PyExc_MemoryError);
354332
return NULL;
355333
}
356334

0 commit comments

Comments
 (0)