Author: Matti Picus <[email protected]>
Branch: win32-cleanup2
Changeset: r54965:7bdaeda41b24
Date: 2012-05-08 19:46 +0300
http://bitbucket.org/pypy/pypy/changeset/7bdaeda41b24/

Log:    merge default into branch

diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst
--- a/pypy/doc/cpython_differences.rst
+++ b/pypy/doc/cpython_differences.rst
@@ -138,12 +138,8 @@
 useful: they will appear to stay alive for a bit longer in PyPy, and
 suddenly they will really be dead, raising a ``ReferenceError`` on the
 next access.  Any code that uses weak proxies must carefully catch such
-``ReferenceError`` at any place that uses them.
-
-As a side effect, the ``finally`` clause inside a generator will be executed
-only when the generator object is garbage collected (see `issue 736`__).
-
-.. __: http://bugs.pypy.org/issue736
+``ReferenceError`` at any place that uses them.  (Or, better yet, don't use
+``weakref.proxy()`` at all; use ``weakref.ref()``.)
 
 There are a few extra implications for the difference in the GC.  Most
 notably, if an object has a ``__del__``, the ``__del__`` is never called more
@@ -162,7 +158,10 @@
 example, a generator left pending in the middle is --- again ---
 garbage-collected later in PyPy than in CPython.  You can see the
 difference if the ``yield`` keyword it is suspended at is itself
-enclosed in a ``try:`` or a ``with:`` block.
+enclosed in a ``try:`` or a ``with:`` block.  This shows up for example
+as `issue 736`__.
+
+.. __: http://bugs.pypy.org/issue736
 
 Using the default GC called ``minimark``, the built-in function ``id()``
 works like it does in CPython.  With other GCs it returns numbers that
@@ -186,7 +185,8 @@
 Even more obscure: the same is true, for old-style classes, if you attach
 the ``__del__`` to an instance (even in CPython this does not work with
 new-style classes).  You get a RuntimeWarning in PyPy.  To fix these cases
-just make sure there is a ``__del__`` method in the class to start with.
+just make sure there is a ``__del__`` method in the class to start with
+(even containing only ``pass``; replacing or overriding it later works fine).
 
 
 Subclasses of built-in types
diff --git a/pypy/jit/metainterp/optimizeopt/earlyforce.py 
b/pypy/jit/metainterp/optimizeopt/earlyforce.py
--- a/pypy/jit/metainterp/optimizeopt/earlyforce.py
+++ b/pypy/jit/metainterp/optimizeopt/earlyforce.py
@@ -7,7 +7,9 @@
         opnum = op.getopnum()
         if (opnum != rop.SETFIELD_GC and 
             opnum != rop.SETARRAYITEM_GC and
-            opnum != rop.QUASIIMMUT_FIELD):
+            opnum != rop.QUASIIMMUT_FIELD and
+            opnum != rop.SAME_AS and
+            opnum != rop.MARK_OPAQUE_PTR):
                
             for arg in op.getarglist():
                 if arg in self.optimizer.values:
diff --git a/pypy/jit/metainterp/optimizeopt/optimizer.py 
b/pypy/jit/metainterp/optimizeopt/optimizer.py
--- a/pypy/jit/metainterp/optimizeopt/optimizer.py
+++ b/pypy/jit/metainterp/optimizeopt/optimizer.py
@@ -658,6 +658,9 @@
     def optimize_SAME_AS(self, op):
         self.make_equal_to(op.result, self.getvalue(op.getarg(0)))
 
+    def optimize_MARK_OPAQUE_PTR(self, op):
+        value = self.getvalue(op.getarg(0))
+        self.optimizer.opaque_pointers[value] = True
 
 dispatch_opt = make_dispatcher_method(Optimizer, 'optimize_',
         default=Optimizer.optimize_default)
diff --git a/pypy/jit/metainterp/optimizeopt/rewrite.py 
b/pypy/jit/metainterp/optimizeopt/rewrite.py
--- a/pypy/jit/metainterp/optimizeopt/rewrite.py
+++ b/pypy/jit/metainterp/optimizeopt/rewrite.py
@@ -481,10 +481,6 @@
                                         args = [op.getarg(0), 
ConstInt(highest_bit(val))])
         self.emit_operation(op)
 
-    def optimize_MARK_OPAQUE_PTR(self, op):
-        value = self.getvalue(op.getarg(0))
-        self.optimizer.opaque_pointers[value] = True
-
     def optimize_CAST_PTR_TO_INT(self, op):
         self.pure(rop.CAST_INT_TO_PTR, [op.result], op.getarg(0))
         self.emit_operation(op)
diff --git a/pypy/jit/metainterp/optimizeopt/simplify.py 
b/pypy/jit/metainterp/optimizeopt/simplify.py
--- a/pypy/jit/metainterp/optimizeopt/simplify.py
+++ b/pypy/jit/metainterp/optimizeopt/simplify.py
@@ -29,9 +29,6 @@
         #     but it's a bit hard to implement robustly if heap.py is also run
         pass
 
-    def optimize_MARK_OPAQUE_PTR(self, op):
-        pass
-
     def optimize_RECORD_KNOWN_CLASS(self, op):
         pass
 
diff --git a/pypy/jit/metainterp/test/test_loop_unroll.py 
b/pypy/jit/metainterp/test/test_loop_unroll.py
--- a/pypy/jit/metainterp/test/test_loop_unroll.py
+++ b/pypy/jit/metainterp/test/test_loop_unroll.py
@@ -19,3 +19,4 @@
 
 class TestOOtype(LoopUnrollTest, OOJitMixin):
     pass
+
diff --git a/pypy/jit/metainterp/test/test_loop_unroll_disopt.py 
b/pypy/jit/metainterp/test/test_loop_unroll_disopt.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/test/test_loop_unroll_disopt.py
@@ -0,0 +1,25 @@
+import py
+from pypy.rlib.jit import JitDriver
+from pypy.jit.metainterp.test import test_loop
+from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
+from pypy.jit.metainterp.optimizeopt import ALL_OPTS_NAMES
+
+allopts = ALL_OPTS_NAMES.split(':')
+for optnum in range(len(allopts)):
+    myopts = allopts[:]
+    del myopts[optnum]
+
+    class TestLLtype(test_loop.LoopTest, LLJitMixin):
+        enable_opts = ':'.join(myopts)
+
+        def check_resops(self, *args, **kwargs):
+            pass
+        def check_trace_count(self, count):
+            pass
+
+    opt = allopts[optnum]
+    exec "TestLoopNo%sLLtype = TestLLtype" % (opt[0].upper() + opt[1:])
+
+del TestLLtype # No need to run the last set twice
+del TestLoopNoUnrollLLtype # This case is take care of by test_loop
+        
diff --git a/pypy/module/thread/__init__.py b/pypy/module/thread/__init__.py
--- a/pypy/module/thread/__init__.py
+++ b/pypy/module/thread/__init__.py
@@ -18,7 +18,7 @@
         'allocate_lock':          'os_lock.allocate_lock',
         'allocate':               'os_lock.allocate_lock',  # obsolete synonym
         'LockType':               'os_lock.Lock',
-        '_local':                 'os_local.Local',
+        #'_local':                'os_local.Local',   # only if 'rweakref'
         'error':                  'space.fromcache(error.Cache).w_error',
     }
 
@@ -35,3 +35,7 @@
         from pypy.module.thread.os_thread import reinit_threads
         add_fork_hook('child', reinit_threads)
 
+    def setup_after_space_initialization(self):
+        """NOT_RPYTHON"""
+        if self.space.config.translation.rweakref:
+            self.extra_interpdef('_local', 'os_local.Local')
diff --git a/pypy/module/thread/gil.py b/pypy/module/thread/gil.py
--- a/pypy/module/thread/gil.py
+++ b/pypy/module/thread/gil.py
@@ -30,7 +30,6 @@
             if not thread.gil_allocate():
                 raise wrap_thread_error(space, "can't allocate GIL")
             self.gil_ready = True
-            self.enter_thread(space)   # setup the main thread
             result = True
         else:
             result = False      # already set up
diff --git a/pypy/module/thread/os_local.py b/pypy/module/thread/os_local.py
--- a/pypy/module/thread/os_local.py
+++ b/pypy/module/thread/os_local.py
@@ -1,5 +1,6 @@
-from pypy.module.thread import ll_thread as thread
-from pypy.interpreter.baseobjspace import Wrappable
+from pypy.rlib.rweakref import RWeakKeyDictionary
+from pypy.interpreter.baseobjspace import Wrappable, W_Root
+from pypy.interpreter.executioncontext import ExecutionContext
 from pypy.interpreter.typedef import (TypeDef, interp2app, GetSetProperty,
     descr_get_dict)
 
@@ -9,16 +10,23 @@
 
     def __init__(self, space, initargs):
         self.initargs = initargs
-        ident = thread.get_ident()
-        self.dicts = {ident: space.newdict(instance=True)}
+        self.dicts = RWeakKeyDictionary(ExecutionContext, W_Root)
+        # The app-level __init__() will be called by the general
+        # instance-creation logic.  It causes getdict() to be
+        # immediately called.  If we don't prepare and set a w_dict
+        # for the current thread, then this would in cause getdict()
+        # to call __init__() a second time.
+        ec = space.getexecutioncontext()
+        w_dict = space.newdict(instance=True)
+        self.dicts.set(ec, w_dict)
 
     def getdict(self, space):
-        ident = thread.get_ident()
-        try:
-            w_dict = self.dicts[ident]
-        except KeyError:
+        ec = space.getexecutioncontext()
+        w_dict = self.dicts.get(ec)
+        if w_dict is None:
             # create a new dict for this thread
-            w_dict = self.dicts[ident] = space.newdict(instance=True)
+            w_dict = space.newdict(instance=True)
+            self.dicts.set(ec, w_dict)
             # call __init__
             try:
                 w_self = space.wrap(self)
@@ -27,10 +35,9 @@
                 space.call_obj_args(w_init, w_self, self.initargs)
             except:
                 # failed, forget w_dict and propagate the exception
-                del self.dicts[ident]
+                self.dicts.set(ec, None)
                 raise
             # ready
-            space.threadlocals.atthreadexit(space, finish_thread, self)
         return w_dict
 
     def descr_local__new__(space, w_subtype, __args__):
@@ -48,8 +55,3 @@
                         __init__ = interp2app(Local.descr_local__init__),
                         __dict__ = GetSetProperty(descr_get_dict, cls=Local),
                         )
-
-def finish_thread(w_obj):
-    assert isinstance(w_obj, Local)
-    ident = thread.get_ident()
-    del w_obj.dicts[ident]
diff --git a/pypy/module/thread/os_thread.py b/pypy/module/thread/os_thread.py
--- a/pypy/module/thread/os_thread.py
+++ b/pypy/module/thread/os_thread.py
@@ -90,7 +90,6 @@
         bootstrapper.nbthreads += 1
         bootstrapper.release()
         # run!
-        space.threadlocals.enter_thread(space)
         try:
             bootstrapper.run(space, w_callable, args)
         finally:
diff --git a/pypy/module/thread/test/test_local.py 
b/pypy/module/thread/test/test_local.py
--- a/pypy/module/thread/test/test_local.py
+++ b/pypy/module/thread/test/test_local.py
@@ -48,7 +48,7 @@
 
     def test_local_init(self):
         import thread
-        tags = [1, 2, 3, 4, 5, 54321]
+        tags = ['???', 1, 2, 3, 4, 5, 54321]
         seen = []
 
         raises(TypeError, thread._local, a=1)
@@ -61,6 +61,7 @@
 
         x = X(42)
         assert x.tag == 54321
+        assert x.tag == 54321
         def f():
             seen.append(x.tag)
         for i in range(5):
@@ -69,7 +70,7 @@
         seen1 = seen[:]
         seen1.sort()
         assert seen1 == [1, 2, 3, 4, 5]
-        assert tags == []
+        assert tags == ['???']
 
     def test_local_setdict(self):
         import thread
@@ -87,3 +88,23 @@
             thread.start_new_thread(f, (i,))
         self.waitfor(lambda: len(done) == 5, delay=2)
         assert len(done) == 5
+
+    def test_local_is_not_immortal(self):
+        import thread, gc, time
+        class Local(thread._local):
+            def __del__(self):
+                done.append('del')
+        done = []
+        def f():
+            assert not hasattr(l, 'foo')
+            l.bar = 42
+            done.append('ok')
+            self.waitfor(lambda: len(done) == 3, delay=8)
+        l = Local()
+        l.foo = 42
+        thread.start_new_thread(f, ())
+        self.waitfor(lambda: len(done) == 1, delay=2)
+        l = None
+        gc.collect()
+        assert done == ['ok', 'del']
+        done.append('shutdown')
diff --git a/pypy/module/thread/threadlocals.py 
b/pypy/module/thread/threadlocals.py
--- a/pypy/module/thread/threadlocals.py
+++ b/pypy/module/thread/threadlocals.py
@@ -1,5 +1,6 @@
 from pypy.module.thread import ll_thread as thread
 
+
 class OSThreadLocals:
     """Thread-local storage for OS-level threads.
     For memory management, this version depends on explicit notification when
@@ -51,21 +52,6 @@
     def getallvalues(self):
         return self._valuedict
 
-    def enter_thread(self, space):
-        "Notification that the current thread is just starting."
-        ec = space.getexecutioncontext()
-        ec.thread_exit_funcs = []
-
     def leave_thread(self, space):
         "Notification that the current thread is about to stop."
-        try:
-            ec = space.getexecutioncontext()
-            while ec.thread_exit_funcs:
-                exit_func, w_obj = ec.thread_exit_funcs.pop()
-                exit_func(w_obj)
-        finally:
-            self.setvalue(None)
-
-    def atthreadexit(self, space, exit_func, w_obj):
-        ec = space.getexecutioncontext()
-        ec.thread_exit_funcs.append((exit_func, w_obj))
+        self.setvalue(None)
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to