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

Reply via email to