Commit 98e2b452 authored by Antoine Pitrou's avatar Antoine Pitrou

Merged revisions 85896 via svnmerge from

svn+ssh://pythondev@svn.python.org/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.
........
parent d7a3ab96
...@@ -153,7 +153,6 @@ PyAPI_DATA(PyObject *) PyExc_VMSError; ...@@ -153,7 +153,6 @@ PyAPI_DATA(PyObject *) PyExc_VMSError;
PyAPI_DATA(PyObject *) PyExc_BufferError; PyAPI_DATA(PyObject *) PyExc_BufferError;
PyAPI_DATA(PyObject *) PyExc_MemoryErrorInst;
PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst; PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst;
/* Predefined warning categories */ /* Predefined warning categories */
......
...@@ -669,6 +669,46 @@ class ExceptionTests(unittest.TestCase): ...@@ -669,6 +669,46 @@ class ExceptionTests(unittest.TestCase):
tb2 = raiseMemError() tb2 = raiseMemError()
self.assertEqual(tb1, tb2) self.assertEqual(tb1, tb2)
def test_memory_error_cleanup(self):
# Issue #5437: preallocated MemoryError instances should not keep
# traceback objects alive.
from _testcapi import raise_memoryerror
class C:
pass
wr = None
def inner():
nonlocal wr
c = C()
wr = weakref.ref(c)
raise_memoryerror()
# We cannot use assertRaises since it manually deletes the traceback
try:
inner()
except MemoryError as e:
self.assertNotEqual(wr(), None)
else:
self.fail("MemoryError not raised")
self.assertEqual(wr(), None)
def test_recursion_error_cleanup(self):
# Same test as above, but with "recursion exceeded" errors
class C:
pass
wr = None
def inner():
nonlocal wr
c = C()
wr = weakref.ref(c)
inner()
# We cannot use assertRaises since it manually deletes the traceback
try:
inner()
except RuntimeError as e:
self.assertNotEqual(wr(), None)
else:
self.fail("RuntimeError not raised")
self.assertEqual(wr(), None)
def test_main(): def test_main():
run_unittest(ExceptionTests) run_unittest(ExceptionTests)
......
...@@ -10,6 +10,9 @@ What's New in Python 3.1.3? ...@@ -10,6 +10,9 @@ What's New in Python 3.1.3?
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #5437: A preallocated MemoryError instance should not hold traceback
data (including local variables caught in the stack trace) alive infinitely.
- Issue #10077: Fix logging of site module errors at startup. - Issue #10077: Fix logging of site module errors at startup.
- Issue #10186: Fix the SyntaxError caret when the offset is equal to the length - Issue #10186: Fix the SyntaxError caret when the offset is equal to the length
......
...@@ -1763,7 +1763,91 @@ SimpleExtendsException(PyExc_Exception, ReferenceError, ...@@ -1763,7 +1763,91 @@ SimpleExtendsException(PyExc_Exception, ReferenceError,
/* /*
* MemoryError extends Exception * 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 * BufferError extends Exception
...@@ -1847,11 +1931,6 @@ SimpleExtendsException(PyExc_Warning, BytesWarning, ...@@ -1847,11 +1931,6 @@ SimpleExtendsException(PyExc_Warning, BytesWarning,
/* 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. /* Pre-computed RuntimeError instance for when recursion depth is reached.
Meant to be used when normalizing the exception for exceeding the recursion Meant to be used when normalizing the exception for exceeding the recursion
depth will cause its own infinite recursion. depth will cause its own infinite recursion.
...@@ -1988,9 +2067,7 @@ _PyExc_Init(void) ...@@ -1988,9 +2067,7 @@ _PyExc_Init(void)
POST_INIT(UnicodeWarning) POST_INIT(UnicodeWarning)
POST_INIT(BytesWarning) POST_INIT(BytesWarning)
PyExc_MemoryErrorInst = BaseException_new(&_PyExc_MemoryError, NULL, NULL); preallocate_memerrors();
if (!PyExc_MemoryErrorInst)
Py_FatalError("Cannot pre-allocate MemoryError instance");
PyExc_RecursionErrorInst = BaseException_new(&_PyExc_RuntimeError, NULL, NULL); PyExc_RecursionErrorInst = BaseException_new(&_PyExc_RuntimeError, NULL, NULL);
if (!PyExc_RecursionErrorInst) if (!PyExc_RecursionErrorInst)
...@@ -2021,6 +2098,6 @@ _PyExc_Init(void) ...@@ -2021,6 +2098,6 @@ _PyExc_Init(void)
void void
_PyExc_Fini(void) _PyExc_Fini(void)
{ {
Py_XDECREF(PyExc_MemoryErrorInst); Py_CLEAR(PyExc_RecursionErrorInst);
PyExc_MemoryErrorInst = NULL; free_preallocated_memerrors();
} }
...@@ -328,29 +328,7 @@ PyErr_BadArgument(void) ...@@ -328,29 +328,7 @@ PyErr_BadArgument(void)
PyObject * PyObject *
PyErr_NoMemory(void) PyErr_NoMemory(void)
{ {
if (PyErr_ExceptionMatches(PyExc_MemoryError)) PyErr_SetNone(PyExc_MemoryError);
/* already current */
return NULL;
/* raise the pre-allocated instance if it still exists */
if (PyExc_MemoryErrorInst)
{
/* Clear the previous traceback, otherwise it will be appended
* to the current one.
*
* The following statement is not likely to raise any error;
* if it does, we simply discard it.
*/
PyException_SetTraceback(PyExc_MemoryErrorInst, Py_None);
PyErr_SetObject(PyExc_MemoryError, PyExc_MemoryErrorInst);
}
else
/* this will probably fail since there's no memory and hee,
hee, we have to instantiate this class
*/
PyErr_SetNone(PyExc_MemoryError);
return NULL; return NULL;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment