Make PyErr_Occurred return NULL if there is no current thread. Previously it

would Py_FatalError, which called PyErr_Occurred, resulting in a semi-infinite
recursion.

Fixes issue 3605.
This commit is contained in:
Jeffrey Yasskin 2010-05-13 18:31:05 +00:00
parent f55621115c
commit 8e0bdfd1d4
3 changed files with 34 additions and 3 deletions

View File

@ -2,9 +2,10 @@
# these are all functions _testcapi exports whose name begins with 'test_'.
from __future__ import with_statement
import random
import subprocess
import sys
import time
import random
import unittest
from test import support
try:
@ -35,6 +36,19 @@ class CAPITest(unittest.TestCase):
self.assertEqual(testfunction.attribute, "test")
self.assertRaises(AttributeError, setattr, inst.testfunction, "attribute", "test")
def test_no_FatalError_infinite_loop(self):
p = subprocess.Popen([sys.executable, "-c",
'import _testcapi;'
'_testcapi.crash_no_current_thread()'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
(out, err) = p.communicate()
self.assertEqual(out, b'')
# This used to cause an infinite loop.
self.assertEqual(err,
b'Fatal Python error:'
b' PyThreadState_Get: no current thread\n')
@unittest.skipUnless(threading, 'Threading required for this test.')
class TestPendingCalls(unittest.TestCase):

View File

@ -2005,6 +2005,17 @@ make_exception_with_doc(PyObject *self, PyObject *args, PyObject *kwargs)
return PyErr_NewExceptionWithDoc(name, doc, base, dict);
}
/* Test that the fatal error from not having a current thread doesn't
cause an infinite loop. Run via Lib/test/test_capi.py */
static PyObject *
crash_no_current_thread(PyObject *self)
{
Py_BEGIN_ALLOW_THREADS
PyErr_SetString(PyExc_SystemError, "bork bork bork");
Py_END_ALLOW_THREADS
return NULL;
}
static PyMethodDef TestMethods[] = {
{"raise_exception", raise_exception, METH_VARARGS},
{"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS},
@ -2069,6 +2080,7 @@ static PyMethodDef TestMethods[] = {
{"code_newempty", code_newempty, METH_VARARGS},
{"make_exception_with_doc", (PyCFunction)make_exception_with_doc,
METH_VARARGS | METH_KEYWORDS},
{"crash_no_current_thread", (PyCFunction)crash_no_current_thread, METH_NOARGS},
{NULL, NULL} /* sentinel */
};

View File

@ -130,9 +130,14 @@ PyErr_SetString(PyObject *exception, const char *string)
PyObject *
PyErr_Occurred(void)
{
PyThreadState *tstate = PyThreadState_GET();
/* If there is no thread state, PyThreadState_GET calls
Py_FatalError, which calls PyErr_Occurred. To avoid the
resulting infinite loop, we inline PyThreadState_GET here and
treat no thread as no error. */
PyThreadState *tstate =
((PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current));
return tstate->curexc_type;
return tstate == NULL ? NULL : tstate->curexc_type;
}