Commit dc41690a authored by Stefan Behnel's avatar Stefan Behnel

Instead of simply asserting that the error exit case and the success exit case...

Instead of simply asserting that the error exit case and the success exit case of a nogil function are aligned, make sure they are before joining them back together.
This is necessary because the error code path is more likely to require the GIL for cleanup operations than the success path – which is a good thing, actually, so we try to streamline the success case at the expense of more work on the error path.
parent 911cc28e
...@@ -1980,7 +1980,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1980,7 +1980,7 @@ class FuncDefNode(StatNode, BlockNode):
'error': code.funcstate.gil_owned, 'error': code.funcstate.gil_owned,
'gil_state_declared': gilstate_decl is None, 'gil_state_declared': gilstate_decl is None,
} }
def assure_gil(code_path): def assure_gil(code_path, code=code):
if not gil_owned[code_path]: if not gil_owned[code_path]:
if not gil_owned['gil_state_declared']: if not gil_owned['gil_state_declared']:
gilstate_decl.declare_gilstate() gilstate_decl.declare_gilstate()
...@@ -2011,6 +2011,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -2011,6 +2011,7 @@ class FuncDefNode(StatNode, BlockNode):
code.put_goto(code.return_label) code.put_goto(code.return_label)
code.put_label(code.error_label) code.put_label(code.error_label)
for cname, type in code.funcstate.all_managed_temps(): for cname, type in code.funcstate.all_managed_temps():
assure_gil('error')
code.put_xdecref(cname, type, have_gil=not lenv.nogil) code.put_xdecref(cname, type, have_gil=not lenv.nogil)
# Clean up buffers -- this calls a Python function # Clean up buffers -- this calls a Python function
...@@ -2063,6 +2064,16 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -2063,6 +2064,16 @@ class FuncDefNode(StatNode, BlockNode):
assure_gil('error') assure_gil('error')
self.getbuffer_error_cleanup(code) self.getbuffer_error_cleanup(code)
def align_error_path_gil_to_success_path(code=code.insertion_point()):
# align error and success GIL state when both join
if gil_owned['success']:
assure_gil('error', code=code)
elif gil_owned['error']:
code.put_release_ensured_gil()
gil_owned['error'] = False
assert gil_owned['error'] == gil_owned['success'], "%s: error path %s != success path %s" % (
self.pos, gil_owned['error'], gil_owned['success'])
# If we are using the non-error cleanup section we should # If we are using the non-error cleanup section we should
# jump past it if we have an error. The if-test below determine # jump past it if we have an error. The if-test below determine
# whether this section is used. # whether this section is used.
...@@ -2071,14 +2082,11 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -2071,14 +2082,11 @@ class FuncDefNode(StatNode, BlockNode):
assert gil_owned['error'] or return_type.is_memoryviewslice assert gil_owned['error'] or return_type.is_memoryviewslice
code.put_goto(code.return_from_error_cleanup_label) code.put_goto(code.return_from_error_cleanup_label)
else: else:
# align error and success GIL state # Adapt the GIL state to the success path right now.
if gil_owned['success']: align_error_path_gil_to_success_path()
assure_gil('error') else:
elif gil_owned['error']: # No error path, no need to adapt the GIL state.
code.put_release_ensured_gil() def align_error_path_gil_to_success_path(): pass
gil_owned['error'] = False
assert gil_owned['error'] == gil_owned['success'], "%s: error path %s != success path %s" % (
self.pos, gil_owned['error'], gil_owned['success'])
# ----- Non-error return cleanup # ----- Non-error return cleanup
code.put_label(code.return_label) code.put_label(code.return_label)
...@@ -2108,9 +2116,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -2108,9 +2116,7 @@ class FuncDefNode(StatNode, BlockNode):
# ----- Return cleanup for both error and no-error return # ----- Return cleanup for both error and no-error return
if code.label_used(code.return_from_error_cleanup_label): if code.label_used(code.return_from_error_cleanup_label):
# If we came through the success path, then we took the same GIL decisions as for jumping here. align_error_path_gil_to_success_path()
# If we came through the error path and did not jump, then we aligned both paths above.
# In the end, all paths are aligned from this point on.
assert gil_owned['error'] == gil_owned['success'], "%s: error path %s != success path %s" % ( assert gil_owned['error'] == gil_owned['success'], "%s: error path %s != success path %s" % (
self.pos, gil_owned['error'], gil_owned['success']) self.pos, gil_owned['error'], gil_owned['success'])
code.put_label(code.return_from_error_cleanup_label) code.put_label(code.return_from_error_cleanup_label)
......
...@@ -13,7 +13,18 @@ cdef double[:] foo(int i): ...@@ -13,7 +13,18 @@ cdef double[:] foo(int i):
raise TypeError('dummy') raise TypeError('dummy')
def propagate(i): cdef double[:] foo_nogil(int i) nogil:
if i == 1:
raise AttributeError('dummy')
if i == 2:
raise RuntimeError('dummy')
if i == 3:
raise ValueError('dummy')
if i == 4:
raise TypeError('dummy')
def propagate(i, bint nogil=False):
""" """
>>> propagate(0) >>> propagate(0)
TypeError('Memoryview return value is not initialized') TypeError('Memoryview return value is not initialized')
...@@ -25,9 +36,20 @@ def propagate(i): ...@@ -25,9 +36,20 @@ def propagate(i):
ValueError('dummy') ValueError('dummy')
>>> propagate(4) >>> propagate(4)
TypeError('dummy') TypeError('dummy')
>>> propagate(0, nogil=True)
TypeError('Memoryview return value is not initialized')
>>> propagate(1, nogil=True)
AttributeError('dummy')
>>> propagate(2, nogil=True)
RuntimeError('dummy')
>>> propagate(3, nogil=True)
ValueError('dummy')
>>> propagate(4, nogil=True)
TypeError('dummy')
""" """
try: try:
foo(i) foo_nogil(i) if nogil else foo(i)
except Exception as e: except Exception as e:
print '%s(%r)' % (e.__class__.__name__, e.args[0]) print '%s(%r)' % (e.__class__.__name__, e.args[0])
else: else:
......
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