Author: Maciej Fijalkowski <fij...@gmail.com> 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 pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit