Fix C++ exception handling for nogil functions (with Stephane Drouard)
authorLisandro Dalcin <dalcinl@gmail.com>
Mon, 27 Sep 2010 15:58:49 +0000 (12:58 -0300)
committerLisandro Dalcin <dalcinl@gmail.com>
Mon, 27 Sep 2010 15:58:49 +0000 (12:58 -0300)
Cython/Compiler/ExprNodes.py
tests/run/cpp_exceptions_nogil.pyx [new file with mode: 0644]
tests/run/cpp_exceptions_nogil_helper.h [new file with mode: 0644]

index 8946ec0c725b72eb10135fa9e620815c251c19a8..a21254a8aa50c168667150aec8cb4e55c1795b10 100755 (executable)
@@ -2863,6 +2863,7 @@ class SimpleCallNode(CallNode):
                  or func_type.exception_check:
             self.is_temp = 1
         # C++ exception handler
+        self.nogil = env.nogil
         if func_type.exception_check == '+':
             if func_type.exception_value is None:
                 env.use_utility_code(cpp_exception_utility_code)
@@ -2957,6 +2958,8 @@ class SimpleCallNode(CallNode):
                             func_type.exception_value.entry.cname)
                     else:
                         raise_py_exception = '%s(); if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError , "Error converting c++ exception.")' % func_type.exception_value.entry.cname
+                    if self.nogil:
+                        raise_py_exception = 'Py_BLOCK_THREADS; %s; Py_UNBLOCK_THREADS' % raise_py_exception
                     code.putln(
                     "try {%s%s;} catch(...) {%s; %s}" % (
                         lhs,
diff --git a/tests/run/cpp_exceptions_nogil.pyx b/tests/run/cpp_exceptions_nogil.pyx
new file mode 100644 (file)
index 0000000..3fe9822
--- /dev/null
@@ -0,0 +1,307 @@
+cdef int raise_TypeError() except *:
+    raise TypeError("custom")
+
+cdef extern from "cpp_exceptions_nogil_helper.h" nogil:
+    cdef void foo "foo"(int i) except +
+    cdef void bar "foo"(int i) except +ValueError
+    cdef void spam"foo"(int i) except +raise_TypeError
+
+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
+    #
diff --git a/tests/run/cpp_exceptions_nogil_helper.h b/tests/run/cpp_exceptions_nogil_helper.h
new file mode 100644 (file)
index 0000000..63f159f
--- /dev/null
@@ -0,0 +1,6 @@
+void foo(int i) {
+  if (i==0)
+    return;
+  else
+    throw i;
+}