Author: Armin Rigo <ar...@tunes.org> Branch: py3.5 Changeset: r88745:2c609fde7223 Date: 2016-11-29 16:31 +0100 http://bitbucket.org/pypy/pypy/changeset/2c609fde7223/
Log: hg merge default diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -192,6 +192,14 @@ assert self._finalize_.im_func is not W_Root._finalize_.im_func space.finalizer_queue.register_finalizer(self) + def may_unregister_rpython_finalizer(self, space): + """Optimization hint only: if there is no user-defined __del__() + method, pass the hint ``don't call any finalizer'' to rgc. + """ + if not self.getclass(space).hasuserdel: + from rpython.rlib import rgc + rgc.may_ignore_finalizer(self) + # hooks that the mapdict implementations needs: def _get_mapdict_map(self): return None diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py --- a/pypy/interpreter/generator.py +++ b/pypy/interpreter/generator.py @@ -3,7 +3,7 @@ from pypy.interpreter.pyopcode import LoopBlock, SApplicationException, Yield from pypy.interpreter.pycode import CO_YIELD_INSIDE_TRY from pypy.interpreter.astcompiler import consts -from rpython.rlib import jit +from rpython.rlib import jit, rgc from rpython.rlib.objectmodel import specialize from rpython.rlib.rarithmetic import r_uint @@ -20,7 +20,7 @@ self.running = False self._name = name # may be null, use get_name() self._qualname = qualname # may be null, use get_qualname() - if (isinstance(self, Coroutine) # XXX would be cool not to need this + if (isinstance(self, Coroutine) or self.pycode.co_flags & CO_YIELD_INSIDE_TRY): self.register_finalizer(self.space) self.saved_operr = None @@ -89,7 +89,7 @@ # if the frame is now marked as finished, it was RETURNed from if frame.frame_finished_execution: - self.frame = None + self.frame_is_finished() if space.is_w(w_result, space.w_None): raise OperationError(space.w_StopIteration, space.w_None) else: @@ -116,7 +116,7 @@ if e.match(space, space.w_StopIteration): self._leak_stopiteration(e) finally: - self.frame = None + self.frame_is_finished() raise finally: frame.f_backref = jit.vref_None @@ -323,6 +323,10 @@ break block = block.previous + def frame_is_finished(self): + self.frame = None + rgc.may_ignore_finalizer(self) + class GeneratorIterator(GeneratorOrCoroutine): "An iterator created by a generator." @@ -364,7 +368,7 @@ break # if the frame is now marked as finished, it was RETURNed from if frame.frame_finished_execution: - self.frame = None + self.frame_is_finished() break results.append(w_result) # YIELDed return unpack_into diff --git a/pypy/module/_cffi_backend/cdataobj.py b/pypy/module/_cffi_backend/cdataobj.py --- a/pypy/module/_cffi_backend/cdataobj.py +++ b/pypy/module/_cffi_backend/cdataobj.py @@ -397,7 +397,7 @@ space = self.space if space.is_none(w_destructor): if isinstance(self, W_CDataGCP): - self.w_destructor = None + self.detach_destructor() return space.w_None raise oefmt(space.w_TypeError, "Can remove destructor only on a object " @@ -604,6 +604,10 @@ self.w_destructor = None self.space.call_function(w_destructor, self.w_original_cdata) + def detach_destructor(self): + self.w_destructor = None + self.may_unregister_rpython_finalizer(self.space) + W_CData.typedef = TypeDef( '_cffi_backend.CData', diff --git a/pypy/module/_cffi_backend/cdlopen.py b/pypy/module/_cffi_backend/cdlopen.py --- a/pypy/module/_cffi_backend/cdlopen.py +++ b/pypy/module/_cffi_backend/cdlopen.py @@ -55,6 +55,7 @@ if not libhandle: raise oefmt(self.ffi.w_FFIError, "library '%s' is already closed", self.libname) + self.may_unregister_rpython_finalizer(self.ffi.space) # Clear the dict to force further accesses to do cdlopen_fetch() # again, and fail because the library was closed. Note that the diff --git a/pypy/module/_socket/interp_socket.py b/pypy/module/_socket/interp_socket.py --- a/pypy/module/_socket/interp_socket.py +++ b/pypy/module/_socket/interp_socket.py @@ -266,6 +266,7 @@ except SocketError: # cpython doesn't return any errors on close pass + self.may_unregister_rpython_finalizer(space) def connect_w(self, space, w_addr): """connect(address) diff --git a/pypy/module/_weakref/interp__weakref.py b/pypy/module/_weakref/interp__weakref.py --- a/pypy/module/_weakref/interp__weakref.py +++ b/pypy/module/_weakref/interp__weakref.py @@ -218,7 +218,7 @@ return self.space.w_None return w_obj - def descr__eq__(self, space, w_ref2): + def compare(self, space, w_ref2, invert): if not isinstance(w_ref2, W_Weakref): return space.w_NotImplemented ref1 = self @@ -226,11 +226,18 @@ w_obj1 = ref1.dereference() w_obj2 = ref2.dereference() if w_obj1 is None or w_obj2 is None: - return space.is_(ref1, ref2) - return space.eq(w_obj1, w_obj2) + w_res = space.is_(ref1, ref2) + else: + w_res = space.eq(w_obj1, w_obj2) + if invert: + w_res = space.not_(w_res) + return w_res + + def descr__eq__(self, space, w_ref2): + return self.compare(space, w_ref2, invert=False) def descr__ne__(self, space, w_ref2): - return space.not_(space.eq(self, w_ref2)) + return self.compare(space, w_ref2, invert=True) def descr_callback(self, space): return self.w_callable diff --git a/pypy/module/_weakref/test/test_weakref.py b/pypy/module/_weakref/test/test_weakref.py --- a/pypy/module/_weakref/test/test_weakref.py +++ b/pypy/module/_weakref/test/test_weakref.py @@ -153,6 +153,14 @@ assert not (ref1 == []) assert ref1 != [] + def test_ne(self): + import _weakref + class X(object): + pass + ref1 = _weakref.ref(X()) + assert ref1.__eq__(X()) is NotImplemented + assert ref1.__ne__(X()) is NotImplemented + def test_getweakrefs(self): import _weakref, gc class A(object): diff --git a/pypy/module/select/interp_epoll.py b/pypy/module/select/interp_epoll.py --- a/pypy/module/select/interp_epoll.py +++ b/pypy/module/select/interp_epoll.py @@ -81,6 +81,7 @@ class W_Epoll(W_Root): def __init__(self, space, epfd): + self.space = space self.epfd = epfd self.register_finalizer(space) @@ -113,6 +114,7 @@ if not self.get_closed(): socketclose(self.epfd) self.epfd = -1 + self.may_unregister_rpython_finalizer(self.space) def epoll_ctl(self, space, ctl, w_fd, eventmask, ignore_ebadf=False): fd = space.c_filedescriptor_w(w_fd) diff --git a/pypy/module/select/interp_kqueue.py b/pypy/module/select/interp_kqueue.py --- a/pypy/module/select/interp_kqueue.py +++ b/pypy/module/select/interp_kqueue.py @@ -109,6 +109,7 @@ class W_Kqueue(W_Root): def __init__(self, space, kqfd): + self.space = space self.kqfd = kqfd self.register_finalizer(space) @@ -137,6 +138,7 @@ kqfd = self.kqfd self.kqfd = -1 socketclose_no_errno(kqfd) + self.may_unregister_rpython_finalizer(self.space) def check_closed(self, space): if self.get_closed(): diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -183,6 +183,7 @@ if mode == rzlib.Z_FINISH: # release the data structures now rzlib.deflateEnd(self.stream) self.stream = rzlib.null_stream + self.may_unregister_rpython_finalizer(space) finally: self.unlock() except rzlib.RZlibError as e: diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py --- a/pypy/objspace/fake/objspace.py +++ b/pypy/objspace/fake/objspace.py @@ -66,15 +66,16 @@ class W_MyType(W_MyObject): name = "foobar" flag_map_or_seq = '?' + hasuserdel = False def __init__(self): self.mro_w = [w_some_obj(), w_some_obj()] self.dict_w = {'__str__': w_some_obj()} + self.hasuserdel = True def get_module(self): return w_some_obj() - def getname(self, space): return self.name.decode('utf-8') diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py --- a/rpython/memory/gc/incminimark.py +++ b/rpython/memory/gc/incminimark.py @@ -155,7 +155,10 @@ # 'old_objects_pointing_to_pinned' and doesn't have to be added again. GCFLAG_PINNED_OBJECT_PARENT_KNOWN = GCFLAG_PINNED -_GCFLAG_FIRST_UNUSED = first_gcflag << 10 # the first unused bit +# record that ignore_finalizer() has been called +GCFLAG_IGNORE_FINALIZER = first_gcflag << 10 + +_GCFLAG_FIRST_UNUSED = first_gcflag << 11 # the first unused bit # States for the incremental GC @@ -1672,7 +1675,7 @@ self.rrc_minor_collection_trace() # # visit the "probably young" objects with finalizers. They - # always all survive. + # all survive, except if IGNORE_FINALIZER is set. if self.probably_young_objects_with_finalizers.non_empty(): self.deal_with_young_objects_with_finalizers() # @@ -2675,6 +2678,8 @@ while self.probably_young_objects_with_finalizers.non_empty(): obj = self.probably_young_objects_with_finalizers.popleft() fq_nr = self.probably_young_objects_with_finalizers.popleft() + if self.header(obj).tid & GCFLAG_IGNORE_FINALIZER: + continue self.singleaddr.address[0] = obj self._trace_drag_out1(self.singleaddr) obj = self.singleaddr.address[0] @@ -2697,6 +2702,8 @@ fq_nr = self.old_objects_with_finalizers.popleft() ll_assert(self._finalization_state(x) != 1, "bad finalization state 1") + if self.header(x).tid & GCFLAG_IGNORE_FINALIZER: + continue if self.header(x).tid & GCFLAG_VISITED: new_with_finalizer.append(x) new_with_finalizer.append(fq_nr) @@ -2787,6 +2794,9 @@ self.objects_to_trace.append(obj) self.visit_all_objects() + def ignore_finalizer(self, obj): + self.header(obj).tid |= GCFLAG_IGNORE_FINALIZER + # ---------- # Weakrefs diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py --- a/rpython/memory/gctransform/framework.py +++ b/rpython/memory/gctransform/framework.py @@ -545,6 +545,12 @@ s_gcref], annmodel.s_None) + self.ignore_finalizer_ptr = None + if hasattr(GCClass, 'ignore_finalizer'): + self.ignore_finalizer_ptr = getfn(GCClass.ignore_finalizer, + [s_gc, SomeAddress()], + annmodel.s_None) + def create_custom_trace_funcs(self, gc, rtyper): custom_trace_funcs = tuple(rtyper.custom_trace_funcs) rtyper.custom_trace_funcs = custom_trace_funcs @@ -1572,6 +1578,13 @@ hop.genop("cast_adr_to_ptr", [v_adr], resultvar = hop.spaceop.result) + def gct_gc_ignore_finalizer(self, hop): + if self.ignore_finalizer_ptr is not None: + v_adr = hop.genop("cast_ptr_to_adr", [hop.spaceop.args[0]], + resulttype=llmemory.Address) + hop.genop("direct_call", [self.ignore_finalizer_ptr, + self.c_const_gc, v_adr]) + class TransformerLayoutBuilder(gctypelayout.TypeLayoutBuilder): diff --git a/rpython/rlib/rfile.py b/rpython/rlib/rfile.py --- a/rpython/rlib/rfile.py +++ b/rpython/rlib/rfile.py @@ -4,7 +4,7 @@ """ import os, stat, errno, sys -from rpython.rlib import rposix +from rpython.rlib import rposix, rgc from rpython.rlib.objectmodel import enforceargs from rpython.rlib.rarithmetic import intmask from rpython.rlib.rstring import StringBuilder @@ -294,6 +294,7 @@ if ll_file: # double close is allowed self._ll_file = lltype.nullptr(FILEP.TO) + rgc.may_ignore_finalizer(self) do_close = self._close2[0] try: if do_close: diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py --- a/rpython/rlib/rgc.py +++ b/rpython/rlib/rgc.py @@ -527,6 +527,13 @@ hop.exception_cannot_occur() return hop.inputconst(lltype.Signed, hop.s_result.const) +@jit.dont_look_inside +def may_ignore_finalizer(obj): + """Optimization hint: says that it is valid for any finalizer + for 'obj' to be ignored, depending on the GC.""" + from rpython.rtyper.lltypesystem.lloperation import llop + llop.gc_ignore_finalizer(lltype.Void, obj) + # ____________________________________________________________ diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py --- a/rpython/rtyper/lltypesystem/lloperation.py +++ b/rpython/rtyper/lltypesystem/lloperation.py @@ -486,6 +486,7 @@ 'gc_add_memory_pressure': LLOp(), 'gc_fq_next_dead' : LLOp(), 'gc_fq_register' : LLOp(), + 'gc_ignore_finalizer' : LLOp(canrun=True), 'gc_rawrefcount_init': LLOp(), 'gc_rawrefcount_create_link_pypy': LLOp(), diff --git a/rpython/rtyper/lltypesystem/opimpl.py b/rpython/rtyper/lltypesystem/opimpl.py --- a/rpython/rtyper/lltypesystem/opimpl.py +++ b/rpython/rtyper/lltypesystem/opimpl.py @@ -736,6 +736,9 @@ assert isinstance(x, bool) return x +def op_gc_ignore_finalizer(obj): + pass + # ____________________________________________________________ def get_op_impl(opname): diff --git a/rpython/translator/c/src/mem.h b/rpython/translator/c/src/mem.h --- a/rpython/translator/c/src/mem.h +++ b/rpython/translator/c/src/mem.h @@ -152,6 +152,7 @@ #define OP_GC_IS_RPY_INSTANCE(x, r) r = 0 #define OP_GC_DUMP_RPY_HEAP(fd, r) r = 0 #define OP_GC_SET_EXTRA_THRESHOLD(x, r) /* nothing */ +#define OP_GC_IGNORE_FINALIZER(x, r) /* nothing */ /****************************/ /* The "asmgcc" root finder */ diff --git a/rpython/translator/c/test/test_boehm.py b/rpython/translator/c/test/test_boehm.py --- a/rpython/translator/c/test/test_boehm.py +++ b/rpython/translator/c/test/test_boehm.py @@ -409,7 +409,9 @@ # def fn(): for i in range(1000): - fq.register_finalizer(A(i)) + x = A(i) + fq.register_finalizer(x) + rgc.may_ignore_finalizer(x) # this is ignored with Boehm rgc.collect() rgc.collect() if glob.triggered == 0: diff --git a/rpython/translator/c/test/test_newgc.py b/rpython/translator/c/test/test_newgc.py --- a/rpython/translator/c/test/test_newgc.py +++ b/rpython/translator/c/test/test_newgc.py @@ -1705,6 +1705,38 @@ res = self.run("limited_memory_linux", -1, runner=myrunner) assert res == 42 + def define_ignore_finalizer(cls): + class X(object): + pass + class FQ(rgc.FinalizerQueue): + Class = X + def finalizer_trigger(self): + pass + queue = FQ() + def g(): + x1 = X() + x2 = X() + queue.register_finalizer(x1) + queue.register_finalizer(x2) + rgc.may_ignore_finalizer(x1) + g._dont_inline_ = True + def f(): + g() + rgc.collect() + seen = 0 + while True: + obj = queue.next_dead() + if obj is None: + break + seen += 1 + return seen + assert f() == 2 # untranslated: may_ignore_finalizer() is ignored + return f + + def test_ignore_finalizer(self): + res = self.run("ignore_finalizer") + assert res == 1 # translated: x1 is removed from the list + # ____________________________________________________________________ _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit