Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
nexedi
cython
Commits
7f141057
Commit
7f141057
authored
Mar 16, 2011
by
Mark Florisson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implemented 'with gil:' statement
parent
23418729
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
221 additions
and
26 deletions
+221
-26
Cython/Compiler/Code.py
Cython/Compiler/Code.py
+39
-0
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+47
-25
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/ParseTreeTransforms.py
+20
-0
Cython/Compiler/Parsing.py
Cython/Compiler/Parsing.py
+1
-1
Cython/Compiler/Symtab.py
Cython/Compiler/Symtab.py
+9
-0
tests/errors/incorrectly_nested_gil_blocks.pyx
tests/errors/incorrectly_nested_gil_blocks.pyx
+28
-0
tests/run/with_gil.pyx
tests/run/with_gil.pyx
+77
-0
No files found.
Cython/Compiler/Code.py
View file @
7f141057
...
@@ -1382,6 +1382,45 @@ class CCodeWriter(object):
...
@@ -1382,6 +1382,45 @@ class CCodeWriter(object):
doc_code
,
doc_code
,
term
))
term
))
# GIL methods
def
put_ensure_gil
(
self
):
"""
Acquire the GIL. The generated code is safe even when no PyThreadState
has been allocated for this thread (for threads not initialized by
using the Python API). Additionally, the code generated by this method
may be called recursively.
"""
from
Cython.Compiler
import
Nodes
self
.
globalstate
.
use_utility_code
(
Nodes
.
force_init_threads_utility_code
)
self
.
putln
(
"#ifdef WITH_THREAD"
)
self
.
putln
(
"PyGILState_STATE _save = PyGILState_Ensure();"
)
self
.
putln
(
"#endif"
)
def
put_release_ensured_gil
(
self
):
"""
Releases the GIL, corresponds to `put_ensure_gil`.
"""
self
.
putln
(
"#ifdef WITH_THREAD"
)
self
.
putln
(
"PyGILState_Release(_save);"
)
self
.
putln
(
"#endif"
)
def
put_acquire_gil
(
self
):
"""
Acquire the GIL. The thread's thread state must have been initialized
by a previous `put_release_gil`
"""
self
.
putln
(
"Py_BLOCK_THREADS"
)
def
put_release_gil
(
self
):
"Release the GIL, corresponds to `put_acquire_gil`."
self
.
putln
(
"#ifdef WITH_THREAD"
)
self
.
putln
(
"PyThreadState *_save = NULL;"
)
self
.
putln
(
"#endif"
)
self
.
putln
(
"Py_UNBLOCK_THREADS"
)
# error handling
# error handling
def
put_error_if_neg
(
self
,
pos
,
value
):
def
put_error_if_neg
(
self
,
pos
,
value
):
...
...
Cython/Compiler/Nodes.py
View file @
7f141057
...
@@ -1329,9 +1329,11 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1329,9 +1329,11 @@ class FuncDefNode(StatNode, BlockNode):
code
.
put
(
cenv
.
scope_class
.
type
.
declaration_code
(
Naming
.
outer_scope_cname
))
code
.
put
(
cenv
.
scope_class
.
type
.
declaration_code
(
Naming
.
outer_scope_cname
))
code
.
putln
(
";"
)
code
.
putln
(
";"
)
self
.
generate_argument_declarations
(
lenv
,
code
)
self
.
generate_argument_declarations
(
lenv
,
code
)
for
entry
in
lenv
.
var_entries
:
for
entry
in
lenv
.
var_entries
:
if
not
entry
.
in_closure
:
if
not
entry
.
in_closure
:
code
.
put_var_declaration
(
entry
)
code
.
put_var_declaration
(
entry
)
init
=
""
init
=
""
if
not
self
.
return_type
.
is_void
:
if
not
self
.
return_type
.
is_void
:
if
self
.
return_type
.
is_pyobject
:
if
self
.
return_type
.
is_pyobject
:
...
@@ -1348,16 +1350,20 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1348,16 +1350,20 @@ class FuncDefNode(StatNode, BlockNode):
code
.
put_trace_declarations
()
code
.
put_trace_declarations
()
# ----- Extern library function declarations
# ----- Extern library function declarations
lenv
.
generate_library_function_declarations
(
code
)
lenv
.
generate_library_function_declarations
(
code
)
# ----- GIL acquisition
# ----- GIL acquisition
acquire_gil
=
self
.
acquire_gil
acquire_gil
=
self
.
acquire_gil
if
acquire_gil
:
acquire_gil_for_var_decls_only
=
(
lenv
.
nogil
and
lenv
.
has_with_gil_block
)
env
.
use_utility_code
(
force_init_threads_utility_code
)
code
.
putln
(
"#ifdef WITH_THREAD"
)
use_refnanny
=
not
lenv
.
nogil
or
acquire_gil_for_var_decls_only
code
.
putln
(
"PyGILState_STATE _save = PyGILState_Ensure();"
)
code
.
putln
(
"#endif"
)
if
acquire_gil
or
acquire_gil_for_var_decls_only
:
code
.
put_ensure_gil
()
# ----- set up refnanny
# ----- set up refnanny
if
not
lenv
.
nogil
:
if
use_refnanny
:
code
.
put_setup_refcount_context
(
self
.
entry
.
name
)
code
.
put_setup_refcount_context
(
self
.
entry
.
name
)
# ----- Automatic lead-ins for certain special functions
# ----- Automatic lead-ins for certain special functions
if
is_getbuffer_slot
:
if
is_getbuffer_slot
:
self
.
getbuffer_init
(
code
)
self
.
getbuffer_init
(
code
)
...
@@ -1372,8 +1378,12 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1372,8 +1378,12 @@ class FuncDefNode(StatNode, BlockNode):
code
.
putln
(
"if (unlikely(!%s)) {"
%
Naming
.
cur_scope_cname
)
code
.
putln
(
"if (unlikely(!%s)) {"
%
Naming
.
cur_scope_cname
)
if
is_getbuffer_slot
:
if
is_getbuffer_slot
:
self
.
getbuffer_error_cleanup
(
code
)
self
.
getbuffer_error_cleanup
(
code
)
if
not
lenv
.
nogil
:
if
use_refnanny
:
code
.
put_finish_refcount_context
()
code
.
put_finish_refcount_context
()
if
acquire_gil_for_var_decls_only
:
code
.
put_release_ensured_gil
()
# FIXME: what if the error return value is a Python value?
# FIXME: what if the error return value is a Python value?
code
.
putln
(
"return %s;"
%
self
.
error_value
())
code
.
putln
(
"return %s;"
%
self
.
error_value
())
code
.
putln
(
"}"
)
code
.
putln
(
"}"
)
...
@@ -1419,6 +1429,9 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1419,6 +1429,9 @@ class FuncDefNode(StatNode, BlockNode):
if
entry
.
type
.
is_buffer
:
if
entry
.
type
.
is_buffer
:
Buffer
.
put_acquire_arg_buffer
(
entry
,
code
,
self
.
pos
)
Buffer
.
put_acquire_arg_buffer
(
entry
,
code
,
self
.
pos
)
if
acquire_gil_for_var_decls_only
:
code
.
put_release_ensured_gil
()
# -------------------------
# -------------------------
# ----- Function body -----
# ----- Function body -----
# -------------------------
# -------------------------
...
@@ -1533,13 +1546,22 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1533,13 +1546,22 @@ class FuncDefNode(StatNode, BlockNode):
code
.
put_trace_return
(
Naming
.
retval_cname
)
code
.
put_trace_return
(
Naming
.
retval_cname
)
else
:
else
:
code
.
put_trace_return
(
"Py_None"
)
code
.
put_trace_return
(
"Py_None"
)
if
not
lenv
.
nogil
:
if
not
lenv
.
nogil
:
# GIL holding funcion
code
.
put_finish_refcount_context
()
code
.
put_finish_refcount_context
()
elif
acquire_gil_for_var_decls_only
:
# 'nogil' function with 'with gil:' block, tear down refnanny
code
.
putln
(
"#if CYTHON_REFNANNY"
)
code
.
begin_block
()
code
.
put_ensure_gil
()
code
.
put_finish_refcount_context
()
code
.
put_release_ensured_gil
()
code
.
end_block
()
code
.
putln
(
"#endif"
)
if
acquire_gil
:
if
acquire_gil
:
code
.
putln
(
"#ifdef WITH_THREAD"
)
code
.
put_release_ensured_gil
()
code
.
putln
(
"PyGILState_Release(_save);"
)
code
.
putln
(
"#endif"
)
if
not
self
.
return_type
.
is_void
:
if
not
self
.
return_type
.
is_void
:
code
.
putln
(
"return %s;"
%
Naming
.
retval_cname
)
code
.
putln
(
"return %s;"
%
Naming
.
retval_cname
)
...
@@ -1773,7 +1795,7 @@ class CFuncDefNode(FuncDefNode):
...
@@ -1773,7 +1795,7 @@ class CFuncDefNode(FuncDefNode):
error
(
self
.
pos
,
error
(
self
.
pos
,
"Function with Python return type cannot be declared nogil"
)
"Function with Python return type cannot be declared nogil"
)
for
entry
in
self
.
local_scope
.
var_entries
:
for
entry
in
self
.
local_scope
.
var_entries
:
if
entry
.
type
.
is_pyobject
:
if
entry
.
type
.
is_pyobject
and
not
entry
.
in_with_gil_block
:
error
(
self
.
pos
,
"Function declared nogil has Python locals or temporaries"
)
error
(
self
.
pos
,
"Function declared nogil has Python locals or temporaries"
)
def
analyse_expressions
(
self
,
env
):
def
analyse_expressions
(
self
,
env
):
...
@@ -5541,10 +5563,16 @@ class GILStatNode(TryFinallyStatNode):
...
@@ -5541,10 +5563,16 @@ class GILStatNode(TryFinallyStatNode):
body
=
body
,
body
=
body
,
finally_clause
=
GILExitNode
(
pos
,
state
=
state
))
finally_clause
=
GILExitNode
(
pos
,
state
=
state
))
def
analyse_declarations
(
self
,
env
):
env
.
_in_with_gil_block
=
(
self
.
state
==
'gil'
)
if
self
.
state
==
'gil'
:
env
.
has_with_gil_block
=
True
return
super
(
GILStatNode
,
self
).
analyse_declarations
(
env
)
def
analyse_expressions
(
self
,
env
):
def
analyse_expressions
(
self
,
env
):
env
.
use_utility_code
(
force_init_threads_utility_code
)
env
.
use_utility_code
(
force_init_threads_utility_code
)
was_nogil
=
env
.
nogil
was_nogil
=
env
.
nogil
env
.
nogil
=
1
env
.
nogil
=
self
.
state
==
'nogil'
TryFinallyStatNode
.
analyse_expressions
(
self
,
env
)
TryFinallyStatNode
.
analyse_expressions
(
self
,
env
)
env
.
nogil
=
was_nogil
env
.
nogil
=
was_nogil
...
@@ -5552,18 +5580,14 @@ class GILStatNode(TryFinallyStatNode):
...
@@ -5552,18 +5580,14 @@ class GILStatNode(TryFinallyStatNode):
def
generate_execution_code
(
self
,
code
):
def
generate_execution_code
(
self
,
code
):
code
.
mark_pos
(
self
.
pos
)
code
.
mark_pos
(
self
.
pos
)
code
.
putln
(
"{"
)
code
.
begin_block
(
)
if
self
.
state
==
'gil'
:
if
self
.
state
==
'gil'
:
code
.
putln
(
"#ifdef WITH_THREAD"
)
code
.
put_ensure_gil
()
code
.
putln
(
"PyGILState_STATE _save = PyGILState_Ensure();"
)
code
.
putln
(
"#endif"
)
else
:
else
:
code
.
putln
(
"#ifdef WITH_THREAD"
)
code
.
put_release_gil
()
code
.
putln
(
"PyThreadState *_save = NULL;"
)
code
.
putln
(
"#endif"
)
code
.
putln
(
"Py_UNBLOCK_THREADS"
)
TryFinallyStatNode
.
generate_execution_code
(
self
,
code
)
TryFinallyStatNode
.
generate_execution_code
(
self
,
code
)
code
.
putln
(
"}"
)
code
.
end_block
(
)
class
GILExitNode
(
StatNode
):
class
GILExitNode
(
StatNode
):
...
@@ -5578,11 +5602,9 @@ class GILExitNode(StatNode):
...
@@ -5578,11 +5602,9 @@ class GILExitNode(StatNode):
def
generate_execution_code
(
self
,
code
):
def
generate_execution_code
(
self
,
code
):
if
self
.
state
==
'gil'
:
if
self
.
state
==
'gil'
:
code
.
putln
(
"#ifdef WITH_THREAD"
)
code
.
put_release_ensured_gil
()
code
.
putln
(
"PyGILState_Release(_save);"
)
code
.
putln
(
"#endif"
)
else
:
else
:
code
.
put
ln
(
"Py_BLOCK_THREADS"
)
code
.
put
_acquire_gil
(
)
class
CImportStatNode
(
StatNode
):
class
CImportStatNode
(
StatNode
):
...
...
Cython/Compiler/ParseTreeTransforms.py
View file @
7f141057
...
@@ -1931,6 +1931,10 @@ class GilCheck(VisitorTransform):
...
@@ -1931,6 +1931,10 @@ class GilCheck(VisitorTransform):
def
__call__
(
self
,
root
):
def
__call__
(
self
,
root
):
self
.
env_stack
=
[
root
.
scope
]
self
.
env_stack
=
[
root
.
scope
]
self
.
nogil
=
False
self
.
nogil
=
False
# True for 'cdef func() nogil:' functions, as the GIL may be held while
# calling this function (thus contained 'nogil' blocks may be valid).
self
.
nogil_declarator_only
=
False
return
super
(
GilCheck
,
self
).
__call__
(
root
)
return
super
(
GilCheck
,
self
).
__call__
(
root
)
def
visit_FuncDefNode
(
self
,
node
):
def
visit_FuncDefNode
(
self
,
node
):
...
@@ -1938,10 +1942,17 @@ class GilCheck(VisitorTransform):
...
@@ -1938,10 +1942,17 @@ class GilCheck(VisitorTransform):
was_nogil
=
self
.
nogil
was_nogil
=
self
.
nogil
self
.
nogil
=
node
.
local_scope
.
nogil
self
.
nogil
=
node
.
local_scope
.
nogil
if
self
.
nogil
:
self
.
nogil_declarator_only
=
True
if
self
.
nogil
and
node
.
nogil_check
:
if
self
.
nogil
and
node
.
nogil_check
:
node
.
nogil_check
(
node
.
local_scope
)
node
.
nogil_check
(
node
.
local_scope
)
self
.
visitchildren
(
node
)
self
.
visitchildren
(
node
)
# This cannot be nested, so it doesn't need backup/restore
self
.
nogil_declarator_only
=
False
self
.
env_stack
.
pop
()
self
.
env_stack
.
pop
()
self
.
nogil
=
was_nogil
self
.
nogil
=
was_nogil
return
node
return
node
...
@@ -1952,6 +1963,15 @@ class GilCheck(VisitorTransform):
...
@@ -1952,6 +1963,15 @@ class GilCheck(VisitorTransform):
was_nogil
=
self
.
nogil
was_nogil
=
self
.
nogil
self
.
nogil
=
(
node
.
state
==
'nogil'
)
self
.
nogil
=
(
node
.
state
==
'nogil'
)
if
was_nogil
==
self
.
nogil
and
not
self
.
nogil_declarator_only
:
if
not
was_nogil
:
error
(
node
.
pos
,
"Trying to acquire the GIL while it is "
"already held."
)
else
:
error
(
node
.
pos
,
"Trying to release the GIL while it was "
"previously released."
)
self
.
visitchildren
(
node
)
self
.
visitchildren
(
node
)
self
.
nogil
=
was_nogil
self
.
nogil
=
was_nogil
return
node
return
node
...
...
Cython/Compiler/Parsing.py
View file @
7f141057
...
@@ -1580,7 +1580,7 @@ def p_with_statement(s):
...
@@ -1580,7 +1580,7 @@ def p_with_statement(s):
def
p_with_items
(
s
):
def
p_with_items
(
s
):
pos
=
s
.
position
()
pos
=
s
.
position
()
if
not
s
.
in_python_file
and
s
.
sy
==
'IDENT'
and
s
.
systring
==
'nogil'
:
if
not
s
.
in_python_file
and
s
.
sy
==
'IDENT'
and
s
.
systring
in
(
'nogil'
,
'gil'
)
:
state
=
s
.
systring
state
=
s
.
systring
s
.
next
()
s
.
next
()
if
s
.
sy
==
','
:
if
s
.
sy
==
','
:
...
...
Cython/Compiler/Symtab.py
View file @
7f141057
...
@@ -176,6 +176,7 @@ class Entry(object):
...
@@ -176,6 +176,7 @@ class Entry(object):
buffer_aux = None
buffer_aux = None
prev_entry = None
prev_entry = None
might_overflow = 0
might_overflow = 0
in_with_gil_block = 0
def __init__(self, name, cname, type, pos = None, init = None):
def __init__(self, name, cname, type, pos = None, init = None):
self.name = name
self.name = name
...
@@ -1290,6 +1291,12 @@ class ModuleScope(Scope):
...
@@ -1290,6 +1291,12 @@ class ModuleScope(Scope):
class
LocalScope
(
Scope
):
class
LocalScope
(
Scope
):
# Does the function have a 'with gil:' block?
has_with_gil_block
=
False
# Transient attribute, used for symbol table variable declarations
_in_with_gil_block
=
False
def
__init__
(
self
,
name
,
outer_scope
,
parent_scope
=
None
):
def
__init__
(
self
,
name
,
outer_scope
,
parent_scope
=
None
):
if
parent_scope
is
None
:
if
parent_scope
is
None
:
parent_scope
=
outer_scope
parent_scope
=
outer_scope
...
@@ -1322,6 +1329,8 @@ class LocalScope(Scope):
...
@@ -1322,6 +1329,8 @@ class LocalScope(Scope):
entry
.
init
=
"0"
entry
.
init
=
"0"
entry
.
init_to_none
=
(
type
.
is_pyobject
or
type
.
is_unspecified
)
and
Options
.
init_local_none
entry
.
init_to_none
=
(
type
.
is_pyobject
or
type
.
is_unspecified
)
and
Options
.
init_local_none
entry
.
is_local
=
1
entry
.
is_local
=
1
entry
.
in_with_gil_block
=
self
.
_in_with_gil_block
self
.
var_entries
.
append
(
entry
)
self
.
var_entries
.
append
(
entry
)
return
entry
return
entry
...
...
tests/errors/incorrectly_nested_gil_blocks.pyx
0 → 100644
View file @
7f141057
with
gil
:
pass
with
nogil
:
with
nogil
:
pass
cdef
void
without_gil
()
nogil
:
# This is not an error, as 'func' *may* be called without the GIL, but it
# may also be held.
with
nogil
:
pass
cdef
void
with_gil
()
with
gil
:
# This is an error, as the GIL is acquired already
with
gil
:
pass
def
func
():
with
gil
:
pass
_ERRORS
=
u'''
1:5: Trying to acquire the GIL while it is already held.
5:9: Trying to release the GIL while it was previously released.
16:9: Trying to acquire the GIL while it is already held.
20:9: Trying to acquire the GIL while it is already held.
'''
tests/run/with_gil.pyx
0 → 100644
View file @
7f141057
"""
Most of these functions are 'cdef' functions, so we need to test using 'def'
test wrappers.
"""
from
libc.stdio
cimport
printf
from
cpython.ref
cimport
PyObject
,
Py_INCREF
import
sys
try
:
import
StringIO
except
ImportError
:
import
io
as
StringIO
def
simple_func
():
"""
>>> simple_func()
['spam', 'ham']
('star', 'twinkle')
"""
with
nogil
:
with
gil
:
print
[
'spam'
,
'ham'
]
cdef
_simple_func
()
cdef
void
cdef
_simple_func
()
nogil
:
with
gil
:
print
(
'star'
,
'twinkle'
)
def
with_gil
():
"""
>>> with_gil()
None
{'spam': 'ham'}
"""
print
x
with
nogil
:
with
gil
:
x
=
dict
(
spam
=
'ham'
)
print
x
cdef
void
without_gil
()
nogil
:
with
gil
:
x
=
list
((
'foo'
,
'bar'
))
raise
NameError
with
gil
:
print
"unreachable"
def
test_without_gil
():
"""
>>> test_without_gil()
Exception NameError in 'with_gil.without_gil' ignored
"""
# Doctest doesn't capture-and-match stderr
stderr
,
sys
.
stderr
=
sys
.
stderr
,
StringIO
.
StringIO
()
without_gil
()
sys
.
stdout
.
write
(
sys
.
stderr
.
getvalue
())
sys
.
stderr
=
stderr
cdef
PyObject
*
nogil_propagate_exception
()
nogil
except
NULL
:
with
nogil
:
with
gil
:
raise
Exception
(
"This exception propagates!"
)
return
<
PyObject
*>
1
def
test_nogil_propagate_exception
():
"""
>>> test_nogil_propagate_exception()
Traceback (most recent call last):
...
Exception: This exception propagates!
"""
nogil_propagate_exception
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment