Author: Armin Rigo <[email protected]>
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)
[email protected]_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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit