[pypy-commit] pypy default: merge heads

mattip noreply at buildbot.pypy.org
Wed May 9 22:55:51 CEST 2012


Author: Matti Picus <matti.picus at gmail.com>
Branch: 
Changeset: r54998:d8e00a3ec08d
Date: 2012-05-09 23:55 +0300
http://bitbucket.org/pypy/pypy/changeset/d8e00a3ec08d/

Log:	merge heads

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',   # only if 'rweakref'
+        '_local':                 'os_local.Local',
         'error':                  'space.fromcache(error.Cache).w_error',
     }
 
@@ -34,8 +34,3 @@
         from pypy.module.posix.interp_posix import add_fork_hook
         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/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,16 +1,21 @@
-from pypy.rlib.rweakref import RWeakKeyDictionary
+import weakref
+from pypy.rlib import jit
 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)
 
 
+ExecutionContext._thread_local_objs = None
+
+
 class Local(Wrappable):
     """Thread-local data"""
 
+    @jit.dont_look_inside
     def __init__(self, space, initargs):
         self.initargs = initargs
-        self.dicts = RWeakKeyDictionary(ExecutionContext, W_Root)
+        self.dicts = {}   # mapping ExecutionContexts to the wraped dict
         # 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
@@ -18,26 +23,42 @@
         # to call __init__() a second time.
         ec = space.getexecutioncontext()
         w_dict = space.newdict(instance=True)
-        self.dicts.set(ec, w_dict)
+        self.dicts[ec] = w_dict
+        self._register_in_ec(ec)
+
+    def _register_in_ec(self, ec):
+        if not ec.space.config.translation.rweakref:
+            return    # without weakrefs, works but 'dicts' is never cleared
+        if ec._thread_local_objs is None:
+            ec._thread_local_objs = []
+        ec._thread_local_objs.append(weakref.ref(self))
+
+    @jit.dont_look_inside
+    def create_new_dict(self, ec):
+        # create a new dict for this thread
+        space = ec.space
+        w_dict = space.newdict(instance=True)
+        self.dicts[ec] = w_dict
+        # call __init__
+        try:
+            w_self = space.wrap(self)
+            w_type = space.type(w_self)
+            w_init = space.getattr(w_type, space.wrap("__init__"))
+            space.call_obj_args(w_init, w_self, self.initargs)
+        except:
+            # failed, forget w_dict and propagate the exception
+            del self.dicts[ec]
+            raise
+        # ready
+        self._register_in_ec(ec)
+        return w_dict
 
     def getdict(self, space):
         ec = space.getexecutioncontext()
-        w_dict = self.dicts.get(ec)
-        if w_dict is None:
-            # create a new dict for this thread
-            w_dict = space.newdict(instance=True)
-            self.dicts.set(ec, w_dict)
-            # call __init__
-            try:
-                w_self = space.wrap(self)
-                w_type = space.type(w_self)
-                w_init = space.getattr(w_type, space.wrap("__init__"))
-                space.call_obj_args(w_init, w_self, self.initargs)
-            except:
-                # failed, forget w_dict and propagate the exception
-                self.dicts.set(ec, None)
-                raise
-            # ready
+        try:
+            w_dict = self.dicts[ec]
+        except KeyError:
+            w_dict = self.create_new_dict(ec)
         return w_dict
 
     def descr_local__new__(space, w_subtype, __args__):
@@ -55,3 +76,13 @@
                         __init__ = interp2app(Local.descr_local__init__),
                         __dict__ = GetSetProperty(descr_get_dict, cls=Local),
                         )
+
+def thread_is_stopping(ec):
+    tlobjs = ec._thread_local_objs
+    if tlobjs is None:
+        return
+    ec._thread_local_objs = None
+    for wref in tlobjs:
+        local = wref()
+        if local is not None:
+            del local.dicts[ec]
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
@@ -54,4 +54,8 @@
 
     def leave_thread(self, space):
         "Notification that the current thread is about to stop."
-        self.setvalue(None)
+        from pypy.module.thread.os_local import thread_is_stopping
+        try:
+            thread_is_stopping(self.getvalue())
+        finally:
+            self.setvalue(None)
diff --git a/pypy/rlib/test/test_rweakkeydict.py b/pypy/rlib/test/test_rweakkeydict.py
--- a/pypy/rlib/test/test_rweakkeydict.py
+++ b/pypy/rlib/test/test_rweakkeydict.py
@@ -135,3 +135,32 @@
             d = RWeakKeyDictionary(KX, VY)
         d.set(KX(), VX())
     py.test.raises(Exception, interpret, g, [1])
+
+
+def test_rpython_free_values():
+    import py; py.test.skip("XXX not implemented, messy")
+    class VXDel:
+        def __del__(self):
+            state.freed.append(1)
+    class State:
+        pass
+    state = State()
+    state.freed = []
+    #
+    def add_me():
+        k = KX()
+        v = VXDel()
+        d = RWeakKeyDictionary(KX, VXDel)
+        d.set(k, v)
+        return d
+    def f():
+        del state.freed[:]
+        d = add_me()
+        rgc.collect()
+        # we want the dictionary to be really empty here.  It's hard to
+        # ensure in the current implementation after just one collect(),
+        # but at least two collects should be enough.
+        rgc.collect()
+        return len(state.freed)
+    assert f() == 1
+    assert interpret(f, []) == 1


More information about the pypy-commit mailing list