fix Python exception checking within nogil blocks
authorLisandro Dalcin <dalcinl@gmail.com>
Wed, 6 Oct 2010 14:42:05 +0000 (11:42 -0300)
committerLisandro Dalcin <dalcinl@gmail.com>
Wed, 6 Oct 2010 14:42:05 +0000 (11:42 -0300)
Cython/Compiler/ExprNodes.py
Cython/Compiler/Nodes.py
tests/run/exceptions_nogil.pyx [new file with mode: 0644]

index 950c0df32691fab739a13498aa921752a561cad9..d12d6cfc775624e35a8dc6f7ad37a41dedbcdbc1 100755 (executable)
@@ -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(
index 2288185c2742b30014d38943a7e7a5b17d3fb90b..a7c9592683a0a5329f51a20557d94ca5c0b32ebe 100644 (file)
@@ -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 (file)
index 0000000..c8032cf
--- /dev/null
@@ -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
+    #