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
