Author: Matti Picus <matti.pi...@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 _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit