From f3e947442cefc6df968922ea8824e0ce9d1f9936 Mon Sep 17 00:00:00 2001 From: Lisandro Dalcin Date: Wed, 6 Oct 2010 11:42:05 -0300 Subject: [PATCH] fix Python exception checking within nogil blocks --- Cython/Compiler/ExprNodes.py | 32 +++- Cython/Compiler/Nodes.py | 9 +- tests/run/exceptions_nogil.pyx | 310 +++++++++++++++++++++++++++++++++ 3 files changed, 346 insertions(+), 5 deletions(-) create mode 100644 tests/run/exceptions_nogil.pyx diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 950c0df3..d12d6cfc 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -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( diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 2288185c..a7c95926 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -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") diff --git a/tests/run/exceptions_nogil.pyx b/tests/run/exceptions_nogil.pyx new file mode 100644 index 00000000..c8032cfe --- /dev/null +++ b/tests/run/exceptions_nogil.pyx @@ -0,0 +1,310 @@ +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 + # -- 2.26.2