Commit f3e94744 authored by Lisandro Dalcin's avatar Lisandro Dalcin

fix Python exception checking within nogil blocks

parent 17111fa7
......@@ -2692,6 +2692,7 @@ class SimpleCallNode(CallNode):
# coerced_self ExprNode or None used internally
# wrapper_call bool used internally
# has_optional_args bool used internally
# nogil bool used internally
subexprs = ['self', 'coerced_self', 'function', 'args', 'arg_tuple']
......@@ -2865,8 +2866,13 @@ class SimpleCallNode(CallNode):
elif func_type.exception_value is not None \
or func_type.exception_check:
self.is_temp = 1
# C++ exception handler
# Called in 'nogil' context?
self.nogil = env.nogil
if (self.nogil and
func_type.exception_check and
func_type.exception_check != '+'):
env.use_utility_code(pyerr_occurred_withgil_utility_code)
# C++ exception handler
if func_type.exception_check == '+':
if func_type.exception_value is None:
env.use_utility_code(cpp_exception_utility_code)
......@@ -2940,7 +2946,10 @@ class SimpleCallNode(CallNode):
if exc_val is not None:
exc_checks.append("%s == %s" % (self.result(), exc_val))
if exc_check:
exc_checks.append("PyErr_Occurred()")
if self.nogil:
exc_checks.append("__Pyx_ErrOccurredWithGIL()")
else:
exc_checks.append("PyErr_Occurred()")
if self.is_temp or exc_checks:
rhs = self.c_call_code()
if self.result():
......@@ -7144,6 +7153,25 @@ static void __Pyx_CppExn2PyErr() {
impl = ""
)
pyerr_occurred_withgil_utility_code= UtilityCode(
proto = """
static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void); /* proto */
""",
impl = """
static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void) {
int err;
#ifdef WITH_THREAD
PyGILState_STATE _save = PyGILState_Ensure();
#endif
err = !!PyErr_Occurred();
#ifdef WITH_THREAD
PyGILState_Release(_save);
#endif
return err;
}
"""
)
#------------------------------------------------------------------------------------
raise_noneattr_error_utility_code = UtilityCode(
......
......@@ -5084,12 +5084,15 @@ class GILStatNode(TryFinallyStatNode):
def generate_execution_code(self, code):
code.mark_pos(self.pos)
code.putln("{")
if self.state == 'gil':
code.putln("#ifdef WITH_THREAD")
code.putln("{ PyGILState_STATE _save = PyGILState_Ensure();")
code.putln("PyGILState_STATE _save = PyGILState_Ensure();")
code.putln("#endif")
else:
code.putln("{ PyThreadState *_save;")
code.putln("#ifdef WITH_THREAD")
code.putln("PyThreadState *_save;")
code.putln("#endif")
code.putln("Py_UNBLOCK_THREADS")
TryFinallyStatNode.generate_execution_code(self, code)
code.putln("}")
......@@ -5108,7 +5111,7 @@ class GILExitNode(StatNode):
def generate_execution_code(self, code):
if self.state == 'gil':
code.putln("#ifdef WITH_THREAD")
code.putln("PyGILState_Release(_save); }")
code.putln("PyGILState_Release(_save);")
code.putln("#endif")
else:
code.putln("Py_BLOCK_THREADS")
......
cdef void foo(int i) except * with gil:
if i != 0: raise ValueError
cdef int bar(int i) except? -1 with gil:
if i != 0: raise ValueError
return 0
cdef int spam(int i) except? -1 with gil:
if i != 0: raise TypeError
return -1
def test_foo():
"""
>>> test_foo()
"""
#
foo(0)
foo(0)
with nogil:
foo(0)
foo(0)
#
try:
with nogil:
foo(0)
finally:
pass
#
try:
with nogil:
foo(0)
with nogil:
foo(0)
finally:
pass
#
try:
with nogil:
foo(0)
with nogil:
foo(1)
except:
with nogil:
foo(0)
finally:
with nogil:
foo(0)
pass
#
try:
with nogil:
foo(0)
foo(0)
finally:
pass
#
try:
with nogil:
foo(0)
foo(1)
except:
with nogil:
foo(0)
finally:
with nogil:
foo(0)
pass
#
try:
with nogil:
foo(0)
try:
with nogil:
foo(1)
except:
with nogil:
foo(1)
finally:
with nogil:
foo(0)
pass
except:
with nogil:
foo(0)
finally:
with nogil:
foo(0)
pass
#
try:
with nogil:
foo(0)
try:
with nogil:
foo(1)
except:
with nogil:
foo(1)
finally:
with nogil:
foo(1)
pass
except:
with nogil:
foo(0)
finally:
with nogil:
foo(0)
pass
#
def test_bar():
"""
>>> test_bar()
"""
#
bar(0)
bar(0)
with nogil:
bar(0)
bar(0)
#
try:
with nogil:
bar(0)
finally:
pass
#
try:
with nogil:
bar(0)
with nogil:
bar(0)
finally:
pass
#
try:
with nogil:
bar(0)
with nogil:
bar(1)
except ValueError:
with nogil:
bar(0)
finally:
with nogil:
bar(0)
pass
#
try:
with nogil:
bar(0)
bar(0)
finally:
pass
#
try:
with nogil:
bar(0)
bar(1)
except ValueError:
with nogil:
bar(0)
finally:
with nogil:
bar(0)
pass
#
try:
with nogil:
bar(0)
try:
with nogil:
bar(1)
except ValueError:
with nogil:
bar(1)
finally:
with nogil:
bar(0)
pass
except ValueError:
with nogil:
bar(0)
finally:
with nogil:
bar(0)
pass
#
try:
with nogil:
bar(0)
try:
with nogil:
bar(1)
except ValueError:
with nogil:
bar(1)
finally:
with nogil:
bar(1)
pass
except ValueError:
with nogil:
bar(0)
finally:
with nogil:
bar(0)
pass
#
def test_spam():
"""
>>> test_spam()
"""
#
spam(0)
spam(0)
with nogil:
spam(0)
spam(0)
#
try:
with nogil:
spam(0)
finally:
pass
#
try:
with nogil:
spam(0)
with nogil:
spam(0)
finally:
pass
#
try:
with nogil:
spam(0)
with nogil:
spam(1)
except TypeError:
with nogil:
spam(0)
finally:
with nogil:
spam(0)
pass
#
try:
with nogil:
spam(0)
spam(0)
finally:
pass
#
try:
with nogil:
spam(0)
spam(1)
except TypeError:
with nogil:
spam(0)
finally:
with nogil:
spam(0)
pass
#
try:
with nogil:
spam(0)
try:
with nogil:
spam(1)
except TypeError:
with nogil:
spam(1)
finally:
with nogil:
spam(0)
pass
except TypeError:
with nogil:
spam(0)
finally:
with nogil:
spam(0)
pass
#
try:
with nogil:
spam(0)
try:
with nogil:
spam(1)
except TypeError:
with nogil:
spam(1)
finally:
with nogil:
spam(1)
pass
except TypeError:
with nogil:
spam(0)
finally:
with nogil:
spam(0)
pass
#
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