Author: Maciej Fijalkowski <[email protected]>
Branch:
Changeset: r59453:787d5dc0fed6
Date: 2012-12-16 11:37 +0200
http://bitbucket.org/pypy/pypy/changeset/787d5dc0fed6/
Log: merge
diff --git a/pypy/module/fcntl/interp_fcntl.py
b/pypy/module/fcntl/interp_fcntl.py
--- a/pypy/module/fcntl/interp_fcntl.py
+++ b/pypy/module/fcntl/interp_fcntl.py
@@ -74,21 +74,6 @@
return wrap_oserror(space, OSError(errno, funcname),
exception_name = 'w_IOError')
-def _check_flock_op(space, op):
-
- if op == LOCK_UN:
- l_type = F_UNLCK
- elif op & LOCK_SH:
- l_type = F_RDLCK
- elif op & LOCK_EX:
- l_type = F_WRLCK
- else:
- raise OperationError(space.w_ValueError,
- space.wrap("unrecognized flock argument"))
- l = lltype.malloc(_flock.TO, flavor='raw')
- l.c_l_type = rffi.cast(rffi.SHORT, l_type)
- return l
-
@unwrap_spec(op=int, w_arg=WrappedDefault(0))
def fcntl(space, w_fd, op, w_arg):
"""fcntl(fd, op, [arg])
@@ -143,21 +128,14 @@
manual flock(3) for details. (On some systems, this function is
emulated using fcntl().)"""
- fd = space.c_filedescriptor_w(w_fd)
-
if has_flock:
+ fd = space.c_filedescriptor_w(w_fd)
+ op = rffi.cast(rffi.INT, op) # C long => C int
rv = c_flock(fd, op)
if rv < 0:
raise _get_error(space, "flock")
else:
- l = _check_flock_op(space, op)
- rffi.setintfield(l, 'c_l_whence', 0)
- rffi.setintfield(l, 'c_l_start', 0)
- rffi.setintfield(l, 'c_l_len', 0)
- op = [F_SETLKW, F_SETLK][int(bool(op & LOCK_NB))]
- op = rffi.cast(rffi.INT, op) # C long => C int
- fcntl_flock(fd, op, l)
- lltype.free(l, flavor='raw')
+ lockf(space, w_fd, op)
@unwrap_spec(op=int, length=int, start=int, whence=int)
def lockf(space, w_fd, op, length=0, start=0, whence=0):
@@ -187,22 +165,28 @@
fd = space.c_filedescriptor_w(w_fd)
- l = _check_flock_op(space, op)
- if start:
+ if op == LOCK_UN:
+ l_type = F_UNLCK
+ elif op & LOCK_SH:
+ l_type = F_RDLCK
+ elif op & LOCK_EX:
+ l_type = F_WRLCK
+ else:
+ raise OperationError(space.w_ValueError,
+ space.wrap("unrecognized lock operation"))
+
+ op = [F_SETLKW, F_SETLK][int(bool(op & LOCK_NB))]
+ op = rffi.cast(rffi.INT, op) # C long => C int
+
+ l = lltype.malloc(_flock.TO, flavor='raw')
+ try:
+ rffi.setintfield(l, 'c_l_type', l_type)
rffi.setintfield(l, 'c_l_start', int(start))
- else:
- rffi.setintfield(l, 'c_l_start', 0)
- if len:
rffi.setintfield(l, 'c_l_len', int(length))
- else:
- rffi.setintfield(l, 'c_l_len', 0)
-
- l.c_l_whence = rffi.cast(rffi.SHORT, whence)
-
- try:
- op = [F_SETLKW, F_SETLK][int(bool(op & LOCK_NB))]
- op = rffi.cast(rffi.INT, op) # C long => C int
- fcntl_flock(fd, op, l)
+ rffi.setintfield(l, 'c_l_whence', int(whence))
+ rv = fcntl_flock(fd, op, l)
+ if rv < 0:
+ raise _get_error(space, "fcntl")
finally:
lltype.free(l, flavor='raw')
diff --git a/pypy/module/fcntl/test/test_fcntl.py
b/pypy/module/fcntl/test/test_fcntl.py
--- a/pypy/module/fcntl/test/test_fcntl.py
+++ b/pypy/module/fcntl/test/test_fcntl.py
@@ -100,22 +100,43 @@
def test_flock(self):
import fcntl
- import sys
+ import os
+ import errno
f = open(self.tmp + "c", "w+")
raises(TypeError, fcntl.flock, "foo")
raises(TypeError, fcntl.flock, f, "foo")
- fcntl.flock(f, fcntl.LOCK_SH)
- # this is an error EWOULDBLOCK, man: The file is locked and the
- # LOCK_NB flag was selected.
- raises(IOError, fcntl.flock, f, fcntl.LOCK_NB)
+
+ fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
+
+ pid = os.fork()
+ if pid == 0:
+ rval = 2
+ try:
+ fcntl.flock(open(f.name, f.mode), fcntl.LOCK_EX |
fcntl.LOCK_NB)
+ except IOError, e:
+ if e.errno not in (errno.EACCES, errno.EAGAIN):
+ raise
+ rval = 0
+ else:
+ rval = 1
+ finally:
+ os._exit(rval)
+
+ assert pid > 0
+ (pid, status) = os.waitpid(pid, 0)
+ assert os.WIFEXITED(status) == True
+ assert os.WEXITSTATUS(status) == 0
+
fcntl.flock(f, fcntl.LOCK_UN)
f.close()
def test_lockf(self):
import fcntl
+ import os
+ import errno
f = open(self.tmp + "d", "w+")
@@ -124,7 +145,27 @@
raises(ValueError, fcntl.lockf, f, -256)
raises(ValueError, fcntl.lockf, f, 256)
- fcntl.lockf(f, fcntl.LOCK_SH)
+ fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
+
+ pid = os.fork()
+ if pid == 0:
+ rval = 2
+ try:
+ fcntl.lockf(open(f.name, f.mode), fcntl.LOCK_EX |
fcntl.LOCK_NB)
+ except IOError, e:
+ if e.errno not in (errno.EACCES, errno.EAGAIN):
+ raise
+ rval = 0
+ else:
+ rval = 1
+ finally:
+ os._exit(rval)
+
+ assert pid > 0
+ (pid, status) = os.waitpid(pid, 0)
+ assert os.WIFEXITED(status) == True
+ assert os.WEXITSTATUS(status) == 0
+
fcntl.lockf(f, fcntl.LOCK_UN)
f.close()
@@ -132,11 +173,10 @@
def test_ioctl(self):
import fcntl
import array
- import sys, os
+ import os
try:
from termios import TIOCGPGRP
- import pty
except ImportError:
skip("don't know how to test ioctl() on this platform")
@@ -145,10 +185,10 @@
#raises(TypeError, fcntl.ioctl, 0, TIOCGPGRP, float(0))
raises(TypeError, fcntl.ioctl, 0, TIOCGPGRP, 1, "foo")
- child_pid, mfd = pty.fork()
- if child_pid == 0:
- # We're the child
- return
+ try:
+ mfd = open("/dev/tty", 'r')
+ except IOError:
+ skip("couldn't open /dev/tty")
try:
buf = array.array('i', [0])
res = fcntl.ioctl(mfd, TIOCGPGRP, buf, True)
@@ -169,7 +209,7 @@
res = fcntl.ioctl(mfd, TIOCGPGRP, "\x00\x00\x00\x00")
assert res == expected
finally:
- os.close(mfd)
+ mfd.close()
def test_ioctl_int(self):
import os
@@ -177,21 +217,17 @@
try:
from termios import TCFLSH, TCIOFLUSH
- import pty
except ImportError:
skip("don't know how to test ioctl() on this platform")
- mfd, sfd = pty.openpty()
+ try:
+ mfd = open("/dev/tty", 'r')
+ except IOError:
+ skip("couldn't open /dev/tty")
try:
assert fcntl.ioctl(mfd, TCFLSH, TCIOFLUSH) == 0
finally:
- os.close(mfd)
- os.close(sfd)
-
- def test_lockf_with_ex(self):
- import fcntl
- f = open(self.tmp, "w")
- fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ mfd.close()
def test_large_flag(self):
import sys
diff --git a/pypy/module/select/test/test_select.py
b/pypy/module/select/test/test_select.py
--- a/pypy/module/select/test/test_select.py
+++ b/pypy/module/select/test/test_select.py
@@ -281,6 +281,10 @@
try:
space.call_method(cls.w_sock, "bind", cls.w_sockaddress)
break
+ except OperationError, e: # should get a "Permission denied"
+ if not e.match(space, space.getattr(w_socketmod,
space.wrap("error"))):
+ raise
+ print e
except cls.w_sock_err, e: # should get a "Permission denied"
print e
else:
diff --git a/pypy/translator/backendopt/graphanalyze.py
b/pypy/translator/backendopt/graphanalyze.py
--- a/pypy/translator/backendopt/graphanalyze.py
+++ b/pypy/translator/backendopt/graphanalyze.py
@@ -7,9 +7,7 @@
def __init__(self, translator):
self.translator = translator
- self.analyzed_calls = {}
- self.analyzed_indirect_calls = {}
- self.recursion_hit = False
+ self._analyzed_calls = {}
# method overridden by subclasses
@@ -106,18 +104,10 @@
return x
def analyze_direct_call(self, graph, seen=None):
- if graph in self.analyzed_calls:
- return self.analyzed_calls[graph]
if seen is None:
- seen = set([graph])
- self.recursion_hit = False
- started_here = True
- elif graph in seen:
- self.recursion_hit = True
- return self.bottom_result()
- else:
- started_here = False
- seen.add(graph)
+ seen = DependencyTracker(self)
+ if not seen.enter(graph):
+ return seen.get_cached_result(graph)
result = self.bottom_result()
graphinfo = self.compute_graph_info(graph)
for block in graph.iterblocks():
@@ -134,23 +124,15 @@
result = self.join_two_results(
result, self.analyze_link(exit, seen))
if self.is_top_result(result):
- self.analyzed_calls[graph] = result
- return result
- if not self.recursion_hit or started_here:
- self.analyzed_calls[graph] = result
+ break
+ seen.leave_with(result)
return result
def analyze_indirect_call(self, graphs, seen=None):
- graphs_t = tuple(graphs)
- try:
- return self.analyzed_indirect_calls[graphs_t]
- except KeyError:
- results = []
- for graph in graphs:
- results.append(self.analyze_direct_call(graph, seen))
- res = self.join_results(results)
- self.analyzed_indirect_calls[graphs_t] = res
- return res
+ results = []
+ for graph in graphs:
+ results.append(self.analyze_direct_call(graph, seen))
+ return self.join_results(results)
def analyze_oosend(self, TYPE, name, seen=None):
graphs = TYPE._lookup_graphs(name)
@@ -163,6 +145,78 @@
for block, op in graph.iterblockops():
self.analyze(op)
+
+class DependencyTracker(object):
+ """This tracks the analysis of cyclic call graphs."""
+
+ # The point is that one analyzer works fine if the question we ask
+ # it is about a single graph, but in the case of recursion, it will
+ # fail if we ask it about multiple graphs. The purpose of this
+ # class is to fix the cache in GraphAnalyzer._analyzed_calls after
+ # each round, whenever a new set of graphs have been added to it.
+ # It works by assuming that we can simply use 'join_two_results'
+ # in order to do so.
+
+ def __init__(self, analyzer):
+ self.analyzer = analyzer
+ # mapping {graph: result} (shared with GraphAnalyzer._analyzed_calls)
+ self.graph_results = analyzer._analyzed_calls
+ # mapping {graph: set_of_graphs_that_depend_on_it}
+ self.backward_dependencies = {}
+ # the current stack of graphs being analyzed
+ self.current_stack = []
+ # the set of graphs at which recursion occurs
+ self.recursion_points = set()
+
+ def enter(self, graph):
+ if self.current_stack:
+ caller_graph = self.current_stack[-1]
+ # record a dependency between the old graph and the new one,
+ # i.e. going backward: FROM the new graph...
+ deps = self.backward_dependencies.setdefault(graph, set())
+ deps.add(caller_graph) # ... TO the caller one.
+ #
+ if graph not in self.graph_results:
+ self.current_stack.append(graph)
+ self.graph_results[graph] = Ellipsis
+ return True
+ else:
+ self.recursion_points.add(graph)
+ return False
+
+ def leave_with(self, result):
+ graph = self.current_stack.pop()
+ assert self.graph_results[graph] is Ellipsis
+ self.graph_results[graph] = result
+ #
+ if not self.current_stack:
+ self._propagate_backward_recursion()
+
+ def get_cached_result(self, graph):
+ result = self.graph_results[graph]
+ if result is Ellipsis:
+ return self.analyzer.bottom_result()
+ return result
+
+ def _propagate_backward_recursion(self):
+ # called at the end of the analysis. We need to back-propagate
+ # the results to all graphs, starting from the graphs in
+ # 'recursion_points', if any.
+ recpts = self.recursion_points
+ bwdeps = self.backward_dependencies
+ grpres = self.graph_results
+ join_two_res = self.analyzer.join_two_results
+ while recpts:
+ callee_graph = recpts.pop()
+ result = grpres[callee_graph]
+ for caller_graph in bwdeps.get(callee_graph, ()):
+ oldvalue1 = grpres[caller_graph]
+ result1 = join_two_res(result, oldvalue1)
+ if result1 != oldvalue1:
+ grpres[caller_graph] = result1
+ recpts.add(caller_graph)
+
+
class BoolGraphAnalyzer(GraphAnalyzer):
"""generic way to analyze graphs: recursively follow it until the first
operation is found on which self.analyze_simple_operation returns True"""
diff --git a/pypy/translator/backendopt/test/test_canraise.py
b/pypy/translator/backendopt/test/test_canraise.py
--- a/pypy/translator/backendopt/test/test_canraise.py
+++ b/pypy/translator/backendopt/test/test_canraise.py
@@ -65,6 +65,27 @@
result =
ra.can_raise(fgraph.startblock.exits[0].target.operations[-1]) # the call to g
assert result
+ def test_recursive_cannot_raise(self):
+ # intentionally don't insert stack checks. The goal is to verify
+ # the graph analyzer, which should return "no" on such a recursion.
+ def g(x):
+ return f(x)
+
+ def f(x):
+ if x:
+ if x % 2:
+ return x
+ return 42
+ return g(x - 1)
+
+ t, ra = self.translate(f, [int])
+ ggraph = graphof(t, g)
+ fgraph = graphof(t, f)
+ result = ra.can_raise(ggraph.startblock.operations[-1]) # the call to f
+ assert not result
+ result =
ra.can_raise(fgraph.startblock.exits[0].target.operations[-1]) # the call to g
+ assert not result
+
def test_can_raise_exception(self):
def g():
raise ValueError
diff --git a/pypy/translator/platform/linux.py
b/pypy/translator/platform/linux.py
--- a/pypy/translator/platform/linux.py
+++ b/pypy/translator/platform/linux.py
@@ -13,8 +13,7 @@
+ os.environ.get('LDFLAGS', '').split())
extra_libs = ('-lrt',)
cflags = tuple(
- ['-Os', # more compact and actually a bit faster
- '-pthread', '-fomit-frame-pointer',
+ ['-O3', '-pthread', '-fomit-frame-pointer',
'-Wall', '-Wno-unused']
+ os.environ.get('CFLAGS', '').split())
standalone_only = ()
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit