[pypy-commit] pypy no-release-gil: Port some changesets from stmgc-c4: @rgc.no_release_gil
arigo
noreply at buildbot.pypy.org
Fri Aug 30 11:52:32 CEST 2013
Author: Armin Rigo <arigo at tunes.org>
Branch: no-release-gil
Changeset: r66447:f55551a076c2
Date: 2013-08-29 09:54 +0100
http://bitbucket.org/pypy/pypy/changeset/f55551a076c2/
Log: Port some changesets from stmgc-c4: @rgc.no_release_gil
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -262,7 +262,11 @@
keepalive_until_here(newp)
return newp
-
+def no_release_gil(func):
+ func._dont_inline_ = True
+ func._no_release_gil_ = True
+ return func
+
def no_collect(func):
func._dont_inline_ = True
func._gc_no_collect_ = True
diff --git a/rpython/translator/backendopt/all.py b/rpython/translator/backendopt/all.py
--- a/rpython/translator/backendopt/all.py
+++ b/rpython/translator/backendopt/all.py
@@ -10,6 +10,7 @@
from rpython.translator.backendopt.removeassert import remove_asserts
from rpython.translator.backendopt.support import log
from rpython.translator.backendopt.storesink import storesink_graph
+from rpython.translator.backendopt import gilanalysis
from rpython.flowspace.model import checkgraph
INLINE_THRESHOLD_FOR_TEST = 33
@@ -138,6 +139,9 @@
for graph in graphs:
checkgraph(graph)
+ gilanalysis.analyze(graphs, translator)
+
+
def constfold(config, graphs):
if config.constfold:
for graph in graphs:
diff --git a/rpython/translator/backendopt/gilanalysis.py b/rpython/translator/backendopt/gilanalysis.py
new file mode 100644
--- /dev/null
+++ b/rpython/translator/backendopt/gilanalysis.py
@@ -0,0 +1,60 @@
+from rpython.translator.backendopt import graphanalyze
+
+# This is not an optimization. It checks for possible releases of the
+# GIL in all graphs starting from rgc.no_release_gil.
+
+
+class GilAnalyzer(graphanalyze.BoolGraphAnalyzer):
+
+ def analyze_direct_call(self, graph, seen=None):
+ try:
+ func = graph.func
+ except AttributeError:
+ pass
+ else:
+ if getattr(func, '_gctransformer_hint_close_stack_', False):
+ return True
+ if getattr(func, '_transaction_break_', False):
+ return True
+
+ return graphanalyze.BoolGraphAnalyzer.analyze_direct_call(
+ self, graph, seen)
+
+ def analyze_external_call(self, op, seen=None):
+ funcobj = op.args[0].value._obj
+ if getattr(funcobj, 'transactionsafe', False):
+ return False
+ else:
+ return False
+
+ def analyze_instantiate_call(self, seen=None):
+ return False
+
+ def analyze_simple_operation(self, op, graphinfo):
+ return False
+
+def analyze(graphs, translator):
+ gilanalyzer = GilAnalyzer(translator)
+ for graph in graphs:
+ func = getattr(graph, 'func', None)
+ if func and getattr(func, '_no_release_gil_', False):
+ if gilanalyzer.analyze_direct_call(graph):
+ # 'no_release_gil' function can release the gil
+ import cStringIO
+ err = cStringIO.StringIO()
+ import sys
+ prev = sys.stdout
+ try:
+ sys.stdout = err
+ ca = GilAnalyzer(translator)
+ ca.verbose = True
+ ca.analyze_direct_call(graph) # print the "traceback" here
+ sys.stdout = prev
+ except:
+ sys.stdout = prev
+ # ^^^ for the dump of which operation in which graph actually
+ # causes it to return True
+ raise Exception("'no_release_gil' function can release the GIL:"
+ " %s\n%s" % (func, err.getvalue()))
+
+
diff --git a/rpython/translator/backendopt/graphanalyze.py b/rpython/translator/backendopt/graphanalyze.py
--- a/rpython/translator/backendopt/graphanalyze.py
+++ b/rpython/translator/backendopt/graphanalyze.py
@@ -1,6 +1,7 @@
from rpython.rtyper.lltypesystem.lltype import DelayedPointer
from rpython.translator.simplify import get_graph
from rpython.tool.algo.unionfind import UnionFind
+from rpython.rtyper.lltypesystem import rclass
class GraphAnalyzer(object):
@@ -67,6 +68,9 @@
result, self.analyze_direct_call(graph, seen))
return result
+ def analyze_instantiate_call(self, seen=None):
+ return self.top_result()
+
def analyze_link(self, graph, link):
return self.bottom_result()
@@ -75,7 +79,7 @@
def compute_graph_info(self, graph):
return None
- def analyze(self, op, seen=None, graphinfo=None):
+ def analyze(self, op, seen=None, graphinfo=None, block=None):
if op.opname == "direct_call":
graph = get_graph(op.args[0], self.translator)
if graph is None:
@@ -90,6 +94,18 @@
elif op.opname == "indirect_call":
graphs = op.args[-1].value
if graphs is None:
+ if block is not None:
+ v_func = op.args[0]
+ for op1 in block.operations:
+ if (v_func is op1.result and
+ op1.opname == 'getfield' and
+ op1.args[0].concretetype == rclass.CLASSTYPE and
+ op1.args[1].value == 'instantiate'):
+ x = self.analyze_instantiate_call(seen)
+ if self.verbose and x:
+ self.dump_info('analyze_instantiate(%s): %r' % (
+ graphs, x))
+ return x
if self.verbose:
self.dump_info('%s to unknown' % (op,))
return self.top_result()
@@ -127,7 +143,7 @@
for op in block.operations:
result = self.add_to_result(
result,
- self.analyze(op, seen, graphinfo)
+ self.analyze(op, seen, graphinfo, block=block)
)
if self.is_top_result(result):
break
@@ -161,7 +177,7 @@
graphs = self.translator.graphs
for graph in graphs:
for block, op in graph.iterblockops():
- self.analyze(op)
+ self.analyze(op, block=block)
class Dependency(object):
diff --git a/rpython/translator/backendopt/test/test_gilanalysis.py b/rpython/translator/backendopt/test/test_gilanalysis.py
new file mode 100644
--- /dev/null
+++ b/rpython/translator/backendopt/test/test_gilanalysis.py
@@ -0,0 +1,80 @@
+import py
+
+from rpython.annotator.listdef import s_list_of_strings
+from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.translator.backendopt import gilanalysis
+from rpython.memory.gctransform.test.test_transform import rtype
+from rpython.translator.translator import graphof
+
+def test_canrelease_external():
+ for ths in ['auto', True, False]:
+ for sbxs in [True, False]:
+ fext = rffi.llexternal('fext2', [], lltype.Void,
+ threadsafe=ths, sandboxsafe=sbxs)
+ def g():
+ fext()
+ t = rtype(g, [])
+ gg = graphof(t, g)
+
+ releases = (ths == 'auto' and not sbxs) or ths is True
+ assert releases == gilanalysis.GilAnalyzer(t).analyze_direct_call(gg)
+
+def test_canrelease_instantiate():
+ class O:
+ pass
+ class A(O):
+ pass
+ class B(O):
+ pass
+
+ classes = [A, B]
+ def g(i):
+ classes[i]()
+
+ t = rtype(g, [int])
+ gg = graphof(t, g)
+ assert not gilanalysis.GilAnalyzer(t).analyze_direct_call(gg)
+
+
+
+def test_no_release_gil():
+ from rpython.rlib import rgc
+
+ @rgc.no_release_gil
+ def g():
+ return 1
+
+ assert g._dont_inline_
+ assert g._no_release_gil_
+
+ def entrypoint(argv):
+ return g() + 2
+
+ t = rtype(entrypoint, [s_list_of_strings])
+ gilanalysis.analyze(t.graphs, t)
+
+
+
+def test_no_release_gil_detect(gc="minimark"):
+ from rpython.rlib import rgc
+
+ fext1 = rffi.llexternal('fext1', [], lltype.Void, threadsafe=True)
+ @rgc.no_release_gil
+ def g():
+ fext1()
+ return 1
+
+ assert g._dont_inline_
+ assert g._no_release_gil_
+
+ def entrypoint(argv):
+ return g() + 2
+
+ t = rtype(entrypoint, [s_list_of_strings])
+ f = py.test.raises(Exception, gilanalysis.analyze, t.graphs, t)
+ expected = "'no_release_gil' function can release the GIL: <function g at "
+ assert str(f.value).startswith(expected)
+
+
+
+
More information about the pypy-commit
mailing list