Commit 4f4841aa authored by Stefan Behnel's avatar Stefan Behnel

Make raise-statements inside of nogil blocks automatically acquire the GIL,...

Make raise-statements inside of nogil blocks automatically acquire the GIL, instead of requiring an explicit ``with gil`` block around them.
parent 08efed85
......@@ -8,6 +8,9 @@ Cython Changelog
Features added
--------------
* Raising exceptions from nogil code will automatically acquire the GIL, instead
of requiring an explicit ``with gil`` block.
* In CPython 3.6 and later, looking up globals in the module dict is almost
as fast as looking up C globals.
......@@ -16,12 +19,12 @@ Features added
* Some internal and 1-argument method calls are faster.
* The coverage plugin considers more C file extensions such as ``.cc`` and ``.cxx``.
(Github issue #2266)
* Modules that cimport many external extension types from other Cython modules
execute less import requests during module initialisation.
* The coverage plugin considers more C file extensions such as ``.cc`` and ``.cxx``.
(Github issue #2266)
Bugs fixed
----------
......
......@@ -2776,6 +2776,53 @@ class CreateClosureClasses(CythonTransform):
return node
class InjectGilHandling(VisitorTransform, SkipDeclarations):
"""
Allow certain Python operations inside of GIL blocks by implicitly acquiring the GIL.
"""
def __call__(self, root):
self.nogil = False
return super(InjectGilHandling, self).__call__(root)
# special node handling
def visit_RaiseStatNode(self, node):
"""Allow raising exceptions in nogil sections by wrapping them in a 'with gil' block."""
if self.nogil:
node = Nodes.GILStatNode(node.pos, state='gil', body=node)
return node
# nogil tracking
def visit_GILStatNode(self, node):
was_nogil = self.nogil
self.nogil = (node.state == 'nogil')
self.visitchildren(node)
self.nogil = was_nogil
return node
def visit_CFuncDefNode(self, node):
was_nogil = self.nogil
if isinstance(node.declarator, Nodes.CFuncDeclaratorNode):
self.nogil = node.declarator.nogil and not node.declarator.with_gil
self.visitchildren(node)
self.nogil = was_nogil
return node
def visit_ParallelRangeNode(self, node):
was_nogil = self.nogil
self.nogil = node.nogil
self.visitchildren(node)
self.nogil = was_nogil
return node
def visit_ExprNode(self, node):
# No special GIL handling inside of expressions for now.
return node
visit_Node = VisitorTransform.recurse_to_children
class GilCheck(VisitorTransform):
"""
Call `node.gil_check(env)` on each node to make sure we hold the
......
......@@ -141,7 +141,7 @@ def create_pipeline(context, mode, exclude_classes=()):
assert mode in ('pyx', 'py', 'pxd')
from .Visitor import PrintTree
from .ParseTreeTransforms import WithTransform, NormalizeTree, PostParse, PxdPostParse
from .ParseTreeTransforms import ForwardDeclareTypes, AnalyseDeclarationsTransform
from .ParseTreeTransforms import ForwardDeclareTypes, InjectGilHandling, AnalyseDeclarationsTransform
from .ParseTreeTransforms import AnalyseExpressionsTransform, FindInvalidUseOfFusedTypes
from .ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform
from .ParseTreeTransforms import TrackNumpyAttributes, InterpretCompilerDirectives, TransformBuiltinMethods
......@@ -194,6 +194,7 @@ def create_pipeline(context, mode, exclude_classes=()):
FlattenInListTransform(),
DecoratorTransform(context),
ForwardDeclareTypes(context),
InjectGilHandling(),
AnalyseDeclarationsTransform(context),
AutoTestDictTransform(context),
EmbedSignature(context),
......
......@@ -55,7 +55,7 @@ cdef object m():
print obj
del fred
return obj
raise obj
raise obj # allowed!
if obj:
pass
while obj:
......@@ -155,7 +155,6 @@ _ERRORS = u"""
55:8: Python print statement not allowed without gil
56:8: Deleting Python object not allowed without gil
57:8: Returning Python object not allowed without gil
58:8: Raising exception not allowed without gil
59:11: Truth-testing Python object not allowed without gil
61:14: Truth-testing Python object not allowed without gil
63:8: For-loop using object bounds or target not allowed without gil
......
# mode: run
# tag: nogil, withgil, exceptions
cdef void foo_nogil(int i) nogil except *:
if i != 0: raise ValueError("huhu !")
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_nogil():
"""
>>> test_foo_nogil()
"""
#
foo_nogil(0)
foo_nogil(0)
with nogil:
foo_nogil(0)
foo_nogil(0)
#
try:
with nogil:
foo_nogil(0)
finally:
pass
#
try:
with nogil:
foo_nogil(0)
with nogil:
foo_nogil(0)
finally:
pass
#
try:
with nogil:
foo_nogil(0)
with nogil:
foo_nogil(1)
except:
with nogil:
foo_nogil(0)
finally:
with nogil:
foo_nogil(0)
pass
#
try:
with nogil:
foo_nogil(0)
foo_nogil(0)
finally:
pass
#
try:
with nogil:
foo_nogil(0)
foo_nogil(1)
except:
with nogil:
foo_nogil(0)
finally:
with nogil:
foo_nogil(0)
pass
#
try:
with nogil:
foo_nogil(0)
try:
with nogil:
foo_nogil(1)
except:
with nogil:
foo_nogil(1)
finally:
with nogil:
foo_nogil(0)
pass
except:
with nogil:
foo_nogil(0)
finally:
with nogil:
foo_nogil(0)
pass
#
try:
with nogil:
foo_nogil(0)
try:
with nogil:
foo_nogil(1)
except:
with nogil:
foo_nogil(1)
finally:
with nogil:
foo_nogil(1)
pass
except:
with nogil:
foo_nogil(0)
finally:
with nogil:
foo_nogil(0)
pass
#
def test_foo():
"""
>>> test_foo()
......@@ -109,6 +220,7 @@ def test_foo():
pass
#
def test_bar():
"""
>>> test_bar()
......
......@@ -106,7 +106,7 @@ def test_boundscheck(x):
## return y
def test_with_nogil(nogil):
def test_with_nogil(nogil, should_raise=False):
"""
>>> raised = []
>>> class nogil(object):
......@@ -121,14 +121,25 @@ def test_with_nogil(nogil):
True
>>> raised
[None]
>>> test_with_nogil(nogil(), should_raise=True)
Traceback (most recent call last):
ValueError: RAISED!
>>> raised[1] is None
False
"""
result = False
should_raise_bool = True if should_raise else False # help the type inference ...
with nogil:
print("WORKS")
with cython.nogil:
result = True
if should_raise_bool:
raise ValueError("RAISED!")
return result
MyUnion = cython.union(n=cython.int, x=cython.double)
MyStruct = cython.struct(is_integral=cython.bint, data=MyUnion)
MyStruct2 = cython.typedef(MyStruct[2])
......
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