Author: Alex Gaynor <[email protected]>
Branch: virtual-dicts
Changeset: r48449:d876b901547e
Date: 2011-10-25 14:38 -0400
http://bitbucket.org/pypy/pypy/changeset/d876b901547e/
Log: merged default
diff --git a/lib-python/modified-2.7/urllib2.py
b/lib-python/modified-2.7/urllib2.py
--- a/lib-python/modified-2.7/urllib2.py
+++ b/lib-python/modified-2.7/urllib2.py
@@ -395,11 +395,7 @@
meth_name = protocol+"_response"
for processor in self.process_response.get(protocol, []):
meth = getattr(processor, meth_name)
- try:
- response = meth(req, response)
- except:
- response.close()
- raise
+ response = meth(req, response)
return response
diff --git a/pypy/jit/backend/llsupport/gc.py b/pypy/jit/backend/llsupport/gc.py
--- a/pypy/jit/backend/llsupport/gc.py
+++ b/pypy/jit/backend/llsupport/gc.py
@@ -649,10 +649,13 @@
def malloc_basic(size, tid):
type_id = llop.extract_ushort(llgroup.HALFWORD, tid)
has_finalizer = bool(tid & (1<<llgroup.HALFSHIFT))
+ has_light_finalizer = bool(tid & (1<<(llgroup.HALFSHIFT + 1)))
check_typeid(type_id)
res = llop1.do_malloc_fixedsize_clear(llmemory.GCREF,
type_id, size,
- has_finalizer, False)
+ has_finalizer,
+ has_light_finalizer,
+ False)
# In case the operation above failed, we are returning NULL
# from this function to assembler. There is also an RPython
# exception set, typically MemoryError; but it's easier and
@@ -723,7 +726,7 @@
# also use it to allocate varsized objects. The tid
# and possibly the length are both set afterward.
gcref = llop1.do_malloc_fixedsize_clear(llmemory.GCREF,
- 0, size, False, False)
+ 0, size, False, False, False)
return rffi.cast(lltype.Signed, gcref)
self.malloc_slowpath = malloc_slowpath
self.MALLOC_SLOWPATH = lltype.FuncType([lltype.Signed], lltype.Signed)
@@ -747,7 +750,9 @@
type_id = self.layoutbuilder.get_type_id(S)
assert not self.layoutbuilder.is_weakref_type(S)
has_finalizer = bool(self.layoutbuilder.has_finalizer(S))
- flags = int(has_finalizer) << llgroup.HALFSHIFT
+ has_light_finalizer = bool(self.layoutbuilder.has_light_finalizer(S))
+ flags = (int(has_finalizer) << llgroup.HALFSHIFT |
+ int(has_light_finalizer) << (llgroup.HALFSHIFT + 1))
descr.tid = llop.combine_ushort(lltype.Signed, type_id, flags)
def init_array_descr(self, A, descr):
diff --git a/pypy/jit/backend/llsupport/test/test_gc.py
b/pypy/jit/backend/llsupport/test/test_gc.py
--- a/pypy/jit/backend/llsupport/test/test_gc.py
+++ b/pypy/jit/backend/llsupport/test/test_gc.py
@@ -247,11 +247,12 @@
self.record = []
def do_malloc_fixedsize_clear(self, RESTYPE, type_id, size,
- has_finalizer, contains_weakptr):
+ has_finalizer, has_light_finalizer,
+ contains_weakptr):
assert not contains_weakptr
p = llmemory.raw_malloc(size)
p = llmemory.cast_adr_to_ptr(p, RESTYPE)
- flags = int(has_finalizer) << 16
+ flags = (int(has_finalizer) << 16) | (int(has_light_finalizer) << 17)
tid = llop.combine_ushort(lltype.Signed, type_id, flags)
self.record.append(("fixedsize", repr(size), tid, p))
return p
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
@@ -19,7 +19,7 @@
class W_RSocket(Wrappable, RSocket):
def __del__(self):
self.clear_all_weakrefs()
- self.close()
+ RSocket.__del__(self)
def accept_w(self, space):
"""accept() -> (socket object, address info)
diff --git a/pypy/module/array/interp_array.py
b/pypy/module/array/interp_array.py
--- a/pypy/module/array/interp_array.py
+++ b/pypy/module/array/interp_array.py
@@ -211,7 +211,9 @@
return result
def __del__(self):
- self.clear_all_weakrefs()
+ # note that we don't call clear_all_weakrefs here because
+ # an array with freed buffer is ok to see - it's just empty with 0
+ # length
self.setlen(0)
def setlen(self, size):
diff --git a/pypy/module/array/test/test_array.py
b/pypy/module/array/test/test_array.py
--- a/pypy/module/array/test/test_array.py
+++ b/pypy/module/array/test/test_array.py
@@ -824,6 +824,22 @@
r = weakref.ref(a)
assert r() is a
+ def test_subclass_del(self):
+ import array, gc, weakref
+ l = []
+
+ class A(array.array):
+ pass
+
+ a = A('d')
+ a.append(3.0)
+ r = weakref.ref(a, lambda a: l.append(a()))
+ del a
+ gc.collect()
+ assert l
+ assert l[0] is None or len(l[0]) == 0
+
+
class TestCPythonsOwnArray(BaseArrayTests):
def setup_class(cls):
@@ -844,11 +860,7 @@
cls.w_tempfile = cls.space.wrap(
str(py.test.ensuretemp('array').join('tmpfile')))
cls.w_maxint = cls.space.wrap(sys.maxint)
-
-
-
-
-
+
def test_buffer_info(self):
a = self.array('c', 'Hi!')
bi = a.buffer_info()
diff --git a/pypy/rlib/rgc.py b/pypy/rlib/rgc.py
--- a/pypy/rlib/rgc.py
+++ b/pypy/rlib/rgc.py
@@ -214,6 +214,10 @@
func._gc_no_collect_ = True
return func
+def is_light_finalizer(func):
+ func._is_light_finalizer_ = True
+ return func
+
# ____________________________________________________________
def get_rpy_roots():
diff --git a/pypy/rlib/rmmap.py b/pypy/rlib/rmmap.py
--- a/pypy/rlib/rmmap.py
+++ b/pypy/rlib/rmmap.py
@@ -292,6 +292,9 @@
elif _POSIX:
self.closed = True
if self.fd != -1:
+ # XXX this is buggy - raising in an RPython del is not a good
+ # idea, we should swallow the exception or ignore the
+ # underlaying close error code
os.close(self.fd)
self.fd = -1
if self.size > 0:
diff --git a/pypy/rlib/rsocket.py b/pypy/rlib/rsocket.py
--- a/pypy/rlib/rsocket.py
+++ b/pypy/rlib/rsocket.py
@@ -56,6 +56,7 @@
_FAMILIES = {}
+
class Address(object):
"""The base class for RPython-level objects representing addresses.
Fields: addr - a _c.sockaddr_ptr (memory owned by the Address instance)
@@ -77,9 +78,8 @@
self.addrlen = addrlen
def __del__(self):
- addr = self.addr_p
- if addr:
- lltype.free(addr, flavor='raw')
+ if self.addr_p:
+ lltype.free(self.addr_p, flavor='raw')
def setdata(self, addr, addrlen):
# initialize self.addr and self.addrlen. 'addr' can be a different
@@ -613,7 +613,10 @@
self.timeout = defaults.timeout
def __del__(self):
- self.close()
+ fd = self.fd
+ if fd != _c.INVALID_SOCKET:
+ self.fd = _c.INVALID_SOCKET
+ _c.socketclose(fd)
if hasattr(_c, 'fcntl'):
def _setblocking(self, block):
diff --git a/pypy/rpython/memory/gc/base.py b/pypy/rpython/memory/gc/base.py
--- a/pypy/rpython/memory/gc/base.py
+++ b/pypy/rpython/memory/gc/base.py
@@ -1,4 +1,5 @@
-from pypy.rpython.lltypesystem import lltype, llmemory, llarena
+from pypy.rpython.lltypesystem import lltype, llmemory, llarena, rffi
+from pypy.rpython.lltypesystem.lloperation import llop
from pypy.rlib.debug import ll_assert
from pypy.rpython.memory.gcheader import GCHeaderBuilder
from pypy.rpython.memory.support import DEFAULT_CHUNK_SIZE
@@ -62,6 +63,7 @@
def set_query_functions(self, is_varsize, has_gcptr_in_varsize,
is_gcarrayofgcptr,
getfinalizer,
+ getlightfinalizer,
offsets_to_gc_pointers,
fixed_size, varsize_item_sizes,
varsize_offset_to_variable_part,
@@ -74,6 +76,7 @@
get_custom_trace,
fast_path_tracing):
self.getfinalizer = getfinalizer
+ self.getlightfinalizer = getlightfinalizer
self.is_varsize = is_varsize
self.has_gcptr_in_varsize = has_gcptr_in_varsize
self.is_gcarrayofgcptr = is_gcarrayofgcptr
@@ -139,6 +142,7 @@
size = self.fixed_size(typeid)
needs_finalizer = bool(self.getfinalizer(typeid))
+ finalizer_is_light = bool(self.getlightfinalizer(typeid))
contains_weakptr = self.weakpointer_offset(typeid) >= 0
assert not (needs_finalizer and contains_weakptr)
if self.is_varsize(typeid):
@@ -158,6 +162,7 @@
else:
malloc_fixedsize = self.malloc_fixedsize
ref = malloc_fixedsize(typeid, size, needs_finalizer,
+ finalizer_is_light,
contains_weakptr)
# lots of cast and reverse-cast around...
return llmemory.cast_ptr_to_adr(ref)
diff --git a/pypy/rpython/memory/gc/generation.py
b/pypy/rpython/memory/gc/generation.py
--- a/pypy/rpython/memory/gc/generation.py
+++ b/pypy/rpython/memory/gc/generation.py
@@ -167,7 +167,9 @@
return self.nursery <= addr < self.nursery_top
def malloc_fixedsize_clear(self, typeid, size,
- has_finalizer=False, contains_weakptr=False):
+ has_finalizer=False,
+ is_finalizer_light=False,
+ contains_weakptr=False):
if (has_finalizer or
(raw_malloc_usage(size) > self.lb_young_fixedsize and
raw_malloc_usage(size) > self.largest_young_fixedsize)):
@@ -179,6 +181,7 @@
# "non-simple" case or object too big: don't use the nursery
return SemiSpaceGC.malloc_fixedsize_clear(self, typeid, size,
has_finalizer,
+ is_finalizer_light,
contains_weakptr)
size_gc_header = self.gcheaderbuilder.size_gc_header
totalsize = size_gc_header + size
diff --git a/pypy/rpython/memory/gc/marksweep.py
b/pypy/rpython/memory/gc/marksweep.py
--- a/pypy/rpython/memory/gc/marksweep.py
+++ b/pypy/rpython/memory/gc/marksweep.py
@@ -93,7 +93,8 @@
pass
def malloc_fixedsize(self, typeid16, size,
- has_finalizer=False, contains_weakptr=False):
+ has_finalizer=False, is_finalizer_light=False,
+ contains_weakptr=False):
self.maybe_collect()
size_gc_header = self.gcheaderbuilder.size_gc_header
try:
@@ -128,7 +129,9 @@
malloc_fixedsize._dont_inline_ = True
def malloc_fixedsize_clear(self, typeid16, size,
- has_finalizer=False, contains_weakptr=False):
+ has_finalizer=False,
+ is_finalizer_light=False,
+ contains_weakptr=False):
self.maybe_collect()
size_gc_header = self.gcheaderbuilder.size_gc_header
try:
diff --git a/pypy/rpython/memory/gc/minimark.py
b/pypy/rpython/memory/gc/minimark.py
--- a/pypy/rpython/memory/gc/minimark.py
+++ b/pypy/rpython/memory/gc/minimark.py
@@ -290,6 +290,8 @@
#
# A list of all objects with finalizers (these are never young).
self.objects_with_finalizers = self.AddressDeque()
+ self.young_objects_with_light_finalizers = self.AddressStack()
+ self.old_objects_with_light_finalizers = self.AddressStack()
#
# Two lists of the objects with weakrefs. No weakref can be an
# old object weakly pointing to a young object: indeed, weakrefs
@@ -457,14 +459,16 @@
def malloc_fixedsize_clear(self, typeid, size,
- needs_finalizer=False, contains_weakptr=False):
+ needs_finalizer=False,
+ is_finalizer_light=False,
+ contains_weakptr=False):
size_gc_header = self.gcheaderbuilder.size_gc_header
totalsize = size_gc_header + size
rawtotalsize = raw_malloc_usage(totalsize)
#
# If the object needs a finalizer, ask for a rawmalloc.
# The following check should be constant-folded.
- if needs_finalizer:
+ if needs_finalizer and not is_finalizer_light:
ll_assert(not contains_weakptr,
"'needs_finalizer' and 'contains_weakptr' both specified")
obj = self.external_malloc(typeid, 0, can_make_young=False)
@@ -494,13 +498,14 @@
#
# Build the object.
llarena.arena_reserve(result, totalsize)
+ obj = result + size_gc_header
+ if is_finalizer_light:
+ self.young_objects_with_light_finalizers.append(obj)
self.init_gc_object(result, typeid, flags=0)
#
# If it is a weakref, record it (check constant-folded).
if contains_weakptr:
- self.young_objects_with_weakrefs.append(result+size_gc_header)
- #
- obj = result + size_gc_header
+ self.young_objects_with_weakrefs.append(obj)
#
return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
@@ -1264,6 +1269,8 @@
# weakrefs' targets.
if self.young_objects_with_weakrefs.non_empty():
self.invalidate_young_weakrefs()
+ if self.young_objects_with_light_finalizers.non_empty():
+ self.deal_with_young_objects_with_finalizers()
#
# Clear this mapping.
if self.nursery_objects_shadows.length() > 0:
@@ -1584,6 +1591,9 @@
# Weakref support: clear the weak pointers to dying objects
if self.old_objects_with_weakrefs.non_empty():
self.invalidate_old_weakrefs()
+ if self.old_objects_with_light_finalizers.non_empty():
+ self.deal_with_old_objects_with_finalizers()
+
#
# Walk all rawmalloced objects and free the ones that don't
# have the GCFLAG_VISITED flag.
@@ -1649,8 +1659,7 @@
if self.header(obj).tid & GCFLAG_VISITED:
self.header(obj).tid &= ~GCFLAG_VISITED
return False # survives
- else:
- return True # dies
+ return True # dies
def _reset_gcflag_visited(self, obj, ignored):
self.header(obj).tid &= ~GCFLAG_VISITED
@@ -1829,6 +1838,39 @@
# ----------
# Finalizers
+ def deal_with_young_objects_with_finalizers(self):
+ """ This is a much simpler version of dealing with finalizers
+ and an optimization - we can reasonably assume that those finalizers
+ don't do anything fancy and *just* call them. Among other things
+ they won't resurrect objects
+ """
+ while self.young_objects_with_light_finalizers.non_empty():
+ obj = self.young_objects_with_light_finalizers.pop()
+ if not self.is_forwarded(obj):
+ finalizer = self.getlightfinalizer(self.get_type_id(obj))
+ ll_assert(bool(finalizer), "no light finalizer found")
+ finalizer(obj, llmemory.NULL)
+
+ def deal_with_old_objects_with_finalizers(self):
+ """ This is a much simpler version of dealing with finalizers
+ and an optimization - we can reasonably assume that those finalizers
+ don't do anything fancy and *just* call them. Among other things
+ they won't resurrect objects
+ """
+ new_objects = self.AddressStack()
+ while self.old_objects_with_light_finalizers.non_empty():
+ obj = self.old_objects_with_light_finalizers.pop()
+ if self.header(obj).tid & GCFLAG_VISITED:
+ # surviving
+ new_objects.append(obj)
+ else:
+ # dying
+ finalizer = self.getlightfinalizer(self.get_type_id(obj))
+ ll_assert(bool(finalizer), "no light finalizer found")
+ finalizer(obj, llmemory.NULL)
+ self.old_objects_with_light_finalizers.delete()
+ self.old_objects_with_light_finalizers = new_objects
+
def deal_with_objects_with_finalizers(self):
# Walk over list of objects with finalizers.
# If it is not surviving, add it to the list of to-be-called
@@ -1959,7 +2001,6 @@
#
self.old_objects_with_weakrefs.append(obj)
-
def invalidate_old_weakrefs(self):
"""Called during a major collection."""
# walk over list of objects that contain weakrefs
diff --git a/pypy/rpython/memory/gc/semispace.py
b/pypy/rpython/memory/gc/semispace.py
--- a/pypy/rpython/memory/gc/semispace.py
+++ b/pypy/rpython/memory/gc/semispace.py
@@ -82,6 +82,7 @@
self.free = self.tospace
MovingGCBase.setup(self)
self.objects_with_finalizers = self.AddressDeque()
+ self.objects_with_light_finalizers = self.AddressStack()
self.objects_with_weakrefs = self.AddressStack()
def _teardown(self):
@@ -93,7 +94,9 @@
# because the spaces are filled with zeroes in advance.
def malloc_fixedsize_clear(self, typeid16, size,
- has_finalizer=False, contains_weakptr=False):
+ has_finalizer=False,
+ is_finalizer_light=False,
+ contains_weakptr=False):
size_gc_header = self.gcheaderbuilder.size_gc_header
totalsize = size_gc_header + size
result = self.free
@@ -102,7 +105,9 @@
llarena.arena_reserve(result, totalsize)
self.init_gc_object(result, typeid16)
self.free = result + totalsize
- if has_finalizer:
+ if is_finalizer_light:
+ self.objects_with_light_finalizers.append(result + size_gc_header)
+ elif has_finalizer:
self.objects_with_finalizers.append(result + size_gc_header)
if contains_weakptr:
self.objects_with_weakrefs.append(result + size_gc_header)
@@ -263,6 +268,8 @@
if self.run_finalizers.non_empty():
self.update_run_finalizers()
scan = self.scan_copied(scan)
+ if self.objects_with_light_finalizers.non_empty():
+ self.deal_with_objects_with_light_finalizers()
if self.objects_with_finalizers.non_empty():
scan = self.deal_with_objects_with_finalizers(scan)
if self.objects_with_weakrefs.non_empty():
@@ -471,6 +478,23 @@
# immortal objects always have GCFLAG_FORWARDED set;
# see get_forwarding_address().
+ def deal_with_objects_with_light_finalizers(self):
+ """ This is a much simpler version of dealing with finalizers
+ and an optimization - we can reasonably assume that those finalizers
+ don't do anything fancy and *just* call them. Among other things
+ they won't resurrect objects
+ """
+ new_objects = self.AddressStack()
+ while self.objects_with_light_finalizers.non_empty():
+ obj = self.objects_with_light_finalizers.pop()
+ if self.surviving(obj):
+ new_objects.append(self.get_forwarding_address(obj))
+ else:
+ finalizer = self.getfinalizer(self.get_type_id(obj))
+ finalizer(obj, llmemory.NULL)
+ self.objects_with_light_finalizers.delete()
+ self.objects_with_light_finalizers = new_objects
+
def deal_with_objects_with_finalizers(self, scan):
# walk over list of objects with finalizers
# if it is not copied, add it to the list of to-be-called finalizers
diff --git a/pypy/rpython/memory/gctransform/framework.py
b/pypy/rpython/memory/gctransform/framework.py
--- a/pypy/rpython/memory/gctransform/framework.py
+++ b/pypy/rpython/memory/gctransform/framework.py
@@ -12,6 +12,7 @@
from pypy.rlib.objectmodel import we_are_translated
from pypy.translator.backendopt import graphanalyze
from pypy.translator.backendopt.support import var_needsgc
+from pypy.translator.backendopt.finalizer import FinalizerAnalyzer
from pypy.annotation import model as annmodel
from pypy.rpython import annlowlevel
from pypy.rpython.rbuiltin import gen_cast
@@ -258,6 +259,7 @@
[s_gc, s_typeid16,
annmodel.SomeInteger(nonneg=True),
annmodel.SomeBool(),
+ annmodel.SomeBool(),
annmodel.SomeBool()], s_gcref,
inline = False)
if hasattr(GCClass, 'malloc_fixedsize'):
@@ -267,6 +269,7 @@
[s_gc, s_typeid16,
annmodel.SomeInteger(nonneg=True),
annmodel.SomeBool(),
+ annmodel.SomeBool(),
annmodel.SomeBool()], s_gcref,
inline = False)
else:
@@ -319,7 +322,7 @@
raise NotImplementedError("GC needs write barrier, but does not
provide writebarrier_before_copy functionality")
# in some GCs we can inline the common case of
- # malloc_fixedsize(typeid, size, True, False, False)
+ # malloc_fixedsize(typeid, size, False, False, False)
if getattr(GCClass, 'inline_simple_malloc', False):
# make a copy of this function so that it gets annotated
# independently and the constants are folded inside
@@ -337,7 +340,7 @@
malloc_fast,
[s_gc, s_typeid16,
annmodel.SomeInteger(nonneg=True),
- s_False, s_False], s_gcref,
+ s_False, s_False, s_False], s_gcref,
inline = True)
else:
self.malloc_fast_ptr = None
@@ -668,7 +671,13 @@
kind_and_fptr = self.special_funcptr_for_type(TYPE)
has_finalizer = (kind_and_fptr is not None and
kind_and_fptr[0] == "finalizer")
+ has_light_finalizer = (kind_and_fptr is not None and
+ kind_and_fptr[0] == "light_finalizer")
+ if has_light_finalizer:
+ has_finalizer = True
c_has_finalizer = rmodel.inputconst(lltype.Bool, has_finalizer)
+ c_has_light_finalizer = rmodel.inputconst(lltype.Bool,
+ has_light_finalizer)
if not op.opname.endswith('_varsize') and not flags.get('varsize'):
#malloc_ptr = self.malloc_fixedsize_ptr
@@ -682,7 +691,8 @@
else:
malloc_ptr = self.malloc_fixedsize_ptr
args = [self.c_const_gc, c_type_id, c_size,
- c_has_finalizer, rmodel.inputconst(lltype.Bool, False)]
+ c_has_finalizer, c_has_light_finalizer,
+ rmodel.inputconst(lltype.Bool, False)]
else:
assert not c_has_finalizer.value
info_varsize = self.layoutbuilder.get_info_varsize(type_id)
@@ -847,12 +857,13 @@
# used by the JIT (see pypy.jit.backend.llsupport.gc)
op = hop.spaceop
[v_typeid, v_size,
- v_has_finalizer, v_contains_weakptr] = op.args
+ v_has_finalizer, v_has_light_finalizer, v_contains_weakptr] = op.args
livevars = self.push_roots(hop)
hop.genop("direct_call",
[self.malloc_fixedsize_clear_ptr, self.c_const_gc,
v_typeid, v_size,
- v_has_finalizer, v_contains_weakptr],
+ v_has_finalizer, v_has_light_finalizer,
+ v_contains_weakptr],
resultvar=op.result)
self.pop_roots(hop, livevars)
@@ -912,10 +923,10 @@
info = self.layoutbuilder.get_info(type_id)
c_size = rmodel.inputconst(lltype.Signed, info.fixedsize)
malloc_ptr = self.malloc_fixedsize_ptr
- c_has_finalizer = rmodel.inputconst(lltype.Bool, False)
+ c_false = rmodel.inputconst(lltype.Bool, False)
c_has_weakptr = rmodel.inputconst(lltype.Bool, True)
args = [self.c_const_gc, c_type_id, c_size,
- c_has_finalizer, c_has_weakptr]
+ c_false, c_false, c_has_weakptr]
# push and pop the current live variables *including* the argument
# to the weakref_create operation, which must be kept alive and
@@ -1250,6 +1261,7 @@
lltype2vtable = translator.rtyper.lltype2vtable
else:
lltype2vtable = None
+ self.translator = translator
super(TransformerLayoutBuilder, self).__init__(GCClass, lltype2vtable)
def has_finalizer(self, TYPE):
@@ -1257,6 +1269,10 @@
return rtti is not None and getattr(rtti._obj, 'destructor_funcptr',
None)
+ def has_light_finalizer(self, TYPE):
+ special = self.special_funcptr_for_type(TYPE)
+ return special is not None and special[0] == 'light_finalizer'
+
def has_custom_trace(self, TYPE):
rtti = get_rtti(TYPE)
return rtti is not None and getattr(rtti._obj, 'custom_trace_funcptr',
@@ -1264,7 +1280,7 @@
def make_finalizer_funcptr_for_type(self, TYPE):
if not self.has_finalizer(TYPE):
- return None
+ return None, False
rtti = get_rtti(TYPE)
destrptr = rtti._obj.destructor_funcptr
DESTR_ARG = lltype.typeOf(destrptr).TO.ARGS[0]
@@ -1276,7 +1292,9 @@
return llmemory.NULL
fptr = self.transformer.annotate_finalizer(ll_finalizer,
[llmemory.Address, llmemory.Address], llmemory.Address)
- return fptr
+ g = destrptr._obj.graph
+ light = not
FinalizerAnalyzer(self.translator).analyze_light_finalizer(g)
+ return fptr, light
def make_custom_trace_funcptr_for_type(self, TYPE):
if not self.has_custom_trace(TYPE):
diff --git a/pypy/rpython/memory/gctypelayout.py
b/pypy/rpython/memory/gctypelayout.py
--- a/pypy/rpython/memory/gctypelayout.py
+++ b/pypy/rpython/memory/gctypelayout.py
@@ -1,7 +1,6 @@
from pypy.rpython.lltypesystem import lltype, llmemory, llarena, llgroup
from pypy.rpython.lltypesystem import rclass
from pypy.rpython.lltypesystem.lloperation import llop
-from pypy.rlib.objectmodel import we_are_translated
from pypy.rlib.debug import ll_assert
from pypy.rlib.rarithmetic import intmask
from pypy.tool.identity_dict import identity_dict
@@ -85,6 +84,13 @@
else:
return lltype.nullptr(GCData.FINALIZER_OR_CT_FUNC)
+ def q_light_finalizer(self, typeid):
+ typeinfo = self.get(typeid)
+ if typeinfo.infobits & T_HAS_LIGHTWEIGHT_FINALIZER:
+ return typeinfo.finalizer_or_customtrace
+ else:
+ return lltype.nullptr(GCData.FINALIZER_OR_CT_FUNC)
+
def q_offsets_to_gc_pointers(self, typeid):
return self.get(typeid).ofstoptrs
@@ -142,6 +148,7 @@
self.q_has_gcptr_in_varsize,
self.q_is_gcarrayofgcptr,
self.q_finalizer,
+ self.q_light_finalizer,
self.q_offsets_to_gc_pointers,
self.q_fixed_size,
self.q_varsize_item_sizes,
@@ -157,16 +164,17 @@
# the lowest 16bits are used to store group member index
-T_MEMBER_INDEX = 0xffff
-T_IS_VARSIZE = 0x010000
-T_HAS_GCPTR_IN_VARSIZE = 0x020000
-T_IS_GCARRAY_OF_GCPTR = 0x040000
-T_IS_WEAKREF = 0x080000
-T_IS_RPYTHON_INSTANCE = 0x100000 # the type is a subclass of OBJECT
-T_HAS_FINALIZER = 0x200000
-T_HAS_CUSTOM_TRACE = 0x400000
-T_KEY_MASK = intmask(0xFF000000)
-T_KEY_VALUE = intmask(0x5A000000) # bug detection only
+T_MEMBER_INDEX = 0xffff
+T_IS_VARSIZE = 0x010000
+T_HAS_GCPTR_IN_VARSIZE = 0x020000
+T_IS_GCARRAY_OF_GCPTR = 0x040000
+T_IS_WEAKREF = 0x080000
+T_IS_RPYTHON_INSTANCE = 0x100000 # the type is a subclass of OBJECT
+T_HAS_FINALIZER = 0x200000
+T_HAS_CUSTOM_TRACE = 0x400000
+T_HAS_LIGHTWEIGHT_FINALIZER = 0x800000
+T_KEY_MASK = intmask(0xFF000000)
+T_KEY_VALUE = intmask(0x5A000000) # bug detection only
def _check_valid_type_info(p):
ll_assert(p.infobits & T_KEY_MASK == T_KEY_VALUE, "invalid type_id")
@@ -194,6 +202,8 @@
info.finalizer_or_customtrace = fptr
if kind == "finalizer":
infobits |= T_HAS_FINALIZER
+ elif kind == 'light_finalizer':
+ infobits |= T_HAS_FINALIZER | T_HAS_LIGHTWEIGHT_FINALIZER
elif kind == "custom_trace":
infobits |= T_HAS_CUSTOM_TRACE
else:
@@ -367,12 +377,15 @@
def special_funcptr_for_type(self, TYPE):
if TYPE in self._special_funcptrs:
return self._special_funcptrs[TYPE]
- fptr1 = self.make_finalizer_funcptr_for_type(TYPE)
+ fptr1, is_lightweight = self.make_finalizer_funcptr_for_type(TYPE)
fptr2 = self.make_custom_trace_funcptr_for_type(TYPE)
assert not (fptr1 and fptr2), (
"type %r needs both a finalizer and a custom tracer" % (TYPE,))
if fptr1:
- kind_and_fptr = "finalizer", fptr1
+ if is_lightweight:
+ kind_and_fptr = "light_finalizer", fptr1
+ else:
+ kind_and_fptr = "finalizer", fptr1
elif fptr2:
kind_and_fptr = "custom_trace", fptr2
else:
@@ -382,7 +395,7 @@
def make_finalizer_funcptr_for_type(self, TYPE):
# must be overridden for proper finalizer support
- return None
+ return None, False
def make_custom_trace_funcptr_for_type(self, TYPE):
# must be overridden for proper custom tracer support
diff --git a/pypy/rpython/memory/gcwrapper.py b/pypy/rpython/memory/gcwrapper.py
--- a/pypy/rpython/memory/gcwrapper.py
+++ b/pypy/rpython/memory/gcwrapper.py
@@ -1,3 +1,4 @@
+from pypy.translator.backendopt.finalizer import FinalizerAnalyzer
from pypy.rpython.lltypesystem import lltype, llmemory, llheap
from pypy.rpython import llinterp
from pypy.rpython.annlowlevel import llhelper
@@ -196,9 +197,11 @@
DESTR_ARG = lltype.typeOf(destrptr).TO.ARGS[0]
destrgraph = destrptr._obj.graph
else:
- return None
+ return None, False
assert not type_contains_pyobjs(TYPE), "not implemented"
+ t = self.llinterp.typer.annotator.translator
+ light = not FinalizerAnalyzer(t).analyze_light_finalizer(destrgraph)
def ll_finalizer(addr, dummy):
assert dummy == llmemory.NULL
try:
@@ -208,7 +211,7 @@
raise RuntimeError(
"a finalizer raised an exception, shouldn't happen")
return llmemory.NULL
- return llhelper(gctypelayout.GCData.FINALIZER_OR_CT, ll_finalizer)
+ return llhelper(gctypelayout.GCData.FINALIZER_OR_CT, ll_finalizer),
light
def make_custom_trace_funcptr_for_type(self, TYPE):
from pypy.rpython.memory.gctransform.support import get_rtti, \
diff --git a/pypy/rpython/memory/test/test_gc.py
b/pypy/rpython/memory/test/test_gc.py
--- a/pypy/rpython/memory/test/test_gc.py
+++ b/pypy/rpython/memory/test/test_gc.py
@@ -5,7 +5,6 @@
from pypy.rpython.memory.test import snippet
from pypy.rpython.test.test_llinterp import get_interpreter
from pypy.rpython.lltypesystem import lltype
-from pypy.rpython.lltypesystem.rstr import STR
from pypy.rpython.lltypesystem.lloperation import llop
from pypy.rlib.objectmodel import we_are_translated
from pypy.rlib.objectmodel import compute_unique_id
@@ -57,7 +56,7 @@
while j < 20:
j += 1
a.append(j)
- res = self.interpret(malloc_a_lot, [])
+ self.interpret(malloc_a_lot, [])
#assert simulator.current_size - curr < 16000 * INT_SIZE / 4
#print "size before: %s, size after %s" % (curr,
simulator.current_size)
@@ -73,7 +72,7 @@
while j < 20:
j += 1
b.append((1, j, i))
- res = self.interpret(malloc_a_lot, [])
+ self.interpret(malloc_a_lot, [])
#assert simulator.current_size - curr < 16000 * INT_SIZE / 4
#print "size before: %s, size after %s" % (curr,
simulator.current_size)
@@ -129,7 +128,7 @@
res = self.interpret(concat, [100])
assert res == concat(100)
#assert simulator.current_size - curr < 16000 * INT_SIZE / 4
-
+
def test_finalizer(self):
class B(object):
pass
@@ -278,7 +277,7 @@
self.interpret, f, [])
def test_weakref(self):
- import weakref, gc
+ import weakref
class A(object):
pass
def g():
@@ -299,7 +298,7 @@
assert res
def test_weakref_to_object_with_finalizer(self):
- import weakref, gc
+ import weakref
class A(object):
count = 0
a = A()
@@ -338,7 +337,7 @@
assert res
def test_cycle_with_weakref_and_del(self):
- import weakref, gc
+ import weakref
class A(object):
count = 0
a = A()
@@ -367,7 +366,7 @@
assert res == 11
def test_weakref_to_object_with_finalizer_ordering(self):
- import weakref, gc
+ import weakref
class A(object):
count = 0
a = A()
@@ -616,7 +615,7 @@
assert not rgc.can_move(a)
return 1
return 0
- except Exception, e:
+ except Exception:
return 2
assert self.interpret(func, []) == int(self.GC_CAN_MALLOC_NONMOVABLE)
@@ -647,8 +646,6 @@
assert self.interpret(f, [bigsize, 0, flag]) == 0x62024241
def test_tagged_simple(self):
- from pypy.rlib.objectmodel import UnboxedValue
-
class Unrelated(object):
pass
@@ -689,8 +686,6 @@
assert res == -897
def test_tagged_id(self):
- from pypy.rlib.objectmodel import UnboxedValue, compute_unique_id
-
class Unrelated(object):
pass
diff --git a/pypy/rpython/memory/test/test_transformed_gc.py
b/pypy/rpython/memory/test/test_transformed_gc.py
--- a/pypy/rpython/memory/test/test_transformed_gc.py
+++ b/pypy/rpython/memory/test/test_transformed_gc.py
@@ -345,22 +345,22 @@
b = B()
b.nextid = 0
b.num_deleted = 0
- class A(object):
+ class AAA(object):
def __init__(self):
self.id = b.nextid
b.nextid += 1
def __del__(self):
b.num_deleted += 1
C()
- class C(A):
+ class C(AAA):
def __del__(self):
b.num_deleted += 1
def f(x, y):
- a = A()
+ a = AAA()
i = 0
while i < x:
i += 1
- a = A()
+ a = AAA()
llop.gc__collect(lltype.Void)
llop.gc__collect(lltype.Void)
return b.num_deleted
@@ -807,6 +807,7 @@
op.args = [Constant(type_id, llgroup.HALFWORD),
Constant(llmemory.sizeof(P), lltype.Signed),
Constant(False, lltype.Bool), # has_finalizer
+ Constant(False, lltype.Bool), #
is_finalizer_light
Constant(False, lltype.Bool)] # contains_weakptr
break
else:
@@ -843,6 +844,7 @@
op.args = [Constant(type_id, llgroup.HALFWORD),
Constant(llmemory.sizeof(P), lltype.Signed),
Constant(False, lltype.Bool), # has_finalizer
+ Constant(False, lltype.Bool), #
is_finalizer_light
Constant(False, lltype.Bool)] # contains_weakptr
break
else:
diff --git a/pypy/rpython/rtyper.py b/pypy/rpython/rtyper.py
--- a/pypy/rpython/rtyper.py
+++ b/pypy/rpython/rtyper.py
@@ -717,7 +717,7 @@
raise TyperError("runtime type info function %r returns %r, "
"excepted Ptr(RuntimeTypeInfo)" % (func, s))
funcptr = self.getcallable(graph)
- attachRuntimeTypeInfo(GCSTRUCT, funcptr, destrptr)
+ attachRuntimeTypeInfo(GCSTRUCT, funcptr, destrptr, None)
# register operations from annotation model
RPythonTyper._registeroperations(annmodel)
diff --git a/pypy/rpython/test/test_rclass.py b/pypy/rpython/test/test_rclass.py
--- a/pypy/rpython/test/test_rclass.py
+++ b/pypy/rpython/test/test_rclass.py
@@ -3,7 +3,7 @@
from pypy.translator.translator import TranslationContext, graphof
from pypy.rpython.lltypesystem.lltype import *
from pypy.rpython.ootypesystem import ootype
-from pypy.rlib.rarithmetic import intmask, r_longlong
+from pypy.rlib.rarithmetic import r_longlong
from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
from pypy.rpython.rclass import IR_IMMUTABLE, IR_IMMUTABLE_ARRAY
from pypy.rpython.rclass import IR_QUASIIMMUTABLE, IR_QUASIIMMUTABLE_ARRAY
@@ -972,10 +972,10 @@
graph = graphof(t, f)
TYPE = graph.startblock.operations[0].args[0].value
RTTI = getRuntimeTypeInfo(TYPE)
- queryptr = RTTI._obj.query_funcptr # should not raise
+ RTTI._obj.query_funcptr # should not raise
destrptr = RTTI._obj.destructor_funcptr
assert destrptr is not None
-
+
def test_del_inheritance(self):
from pypy.rlib import rgc
class State:
diff --git a/pypy/translator/backendopt/finalizer.py
b/pypy/translator/backendopt/finalizer.py
new file mode 100644
--- /dev/null
+++ b/pypy/translator/backendopt/finalizer.py
@@ -0,0 +1,46 @@
+
+from pypy.translator.backendopt import graphanalyze
+from pypy.rpython.lltypesystem import lltype
+
+class FinalizerError(Exception):
+ """ __del__ marked as lightweight finalizer, but the analyzer did
+ not agreed
+ """
+
+class FinalizerAnalyzer(graphanalyze.BoolGraphAnalyzer):
+ """ Analyzer that determines whether a finalizer is lightweight enough
+ so it can be called without all the complicated logic in the garbage
+ collector. The set of operations here is restrictive for a good reason
+ - it's better to be safe. Specifically disallowed operations:
+
+ * anything that escapes self
+ * anything that can allocate
+ """
+ ok_operations = ['ptr_nonzero', 'ptr_eq', 'ptr_ne', 'free', 'same_as',
+ 'direct_ptradd', 'force_cast', 'track_alloc_stop',
+ 'raw_free']
+
+ def analyze_light_finalizer(self, graph):
+ result = self.analyze_direct_call(graph)
+ if (result is self.top_result() and
+ getattr(graph.func, '_is_light_finalizer_', False)):
+ raise FinalizerError(FinalizerError.__doc__, graph)
+ return result
+
+ def analyze_simple_operation(self, op, graphinfo):
+ if op.opname in self.ok_operations:
+ return self.bottom_result()
+ if (op.opname.startswith('int_') or op.opname.startswith('float_')
+ or op.opname.startswith('cast_')):
+ return self.bottom_result()
+ if op.opname == 'setfield' or op.opname == 'bare_setfield':
+ TP = op.args[2].concretetype
+ if not isinstance(TP, lltype.Ptr) or TP.TO._gckind == 'raw':
+ # primitive type
+ return self.bottom_result()
+ if op.opname == 'getfield':
+ TP = op.result.concretetype
+ if not isinstance(TP, lltype.Ptr) or TP.TO._gckind == 'raw':
+ # primitive type
+ return self.bottom_result()
+ return self.top_result()
diff --git a/pypy/translator/backendopt/test/test_finalizer.py
b/pypy/translator/backendopt/test/test_finalizer.py
new file mode 100644
--- /dev/null
+++ b/pypy/translator/backendopt/test/test_finalizer.py
@@ -0,0 +1,142 @@
+
+import py
+from pypy.translator.backendopt.finalizer import FinalizerAnalyzer,\
+ FinalizerError
+from pypy.translator.translator import TranslationContext, graphof
+from pypy.translator.backendopt.all import backend_optimizations
+from pypy.translator.unsimplify import varoftype
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.conftest import option
+from pypy.rlib import rgc
+
+
+class BaseFinalizerAnalyzerTests(object):
+ """ Below are typical destructors that we encounter in pypy
+ """
+
+ type_system = None
+
+ def analyze(self, func, sig, func_to_analyze=None, backendopt=False):
+ if func_to_analyze is None:
+ func_to_analyze = func
+ t = TranslationContext()
+ t.buildannotator().build_types(func, sig)
+ t.buildrtyper(type_system=self.type_system).specialize()
+ if backendopt:
+ backend_optimizations(t)
+ if option.view:
+ t.view()
+ a = FinalizerAnalyzer(t)
+ fgraph = graphof(t, func_to_analyze)
+ result = a.analyze_light_finalizer(fgraph)
+ return result
+
+ def test_nothing(self):
+ def f():
+ pass
+ r = self.analyze(f, [])
+ assert not r
+
+def test_various_ops():
+ from pypy.objspace.flow.model import SpaceOperation, Constant
+
+ X = lltype.Ptr(lltype.GcStruct('X'))
+ Z = lltype.Ptr(lltype.Struct('Z'))
+ S = lltype.GcStruct('S', ('x', lltype.Signed),
+ ('y', X),
+ ('z', Z))
+ v1 = varoftype(lltype.Bool)
+ v2 = varoftype(lltype.Signed)
+ f = FinalizerAnalyzer(None)
+ r = f.analyze(SpaceOperation('cast_int_to_bool', [v2],
+ v1))
+ assert not r
+ v1 = varoftype(lltype.Ptr(S))
+ v2 = varoftype(lltype.Signed)
+ v3 = varoftype(X)
+ v4 = varoftype(Z)
+ assert not f.analyze(SpaceOperation('bare_setfield', [v1, Constant('x'),
+ v2], None))
+ assert f.analyze(SpaceOperation('bare_setfield', [v1, Constant('y'),
+ v3], None))
+ assert not f.analyze(SpaceOperation('bare_setfield', [v1, Constant('z'),
+ v4], None))
+
+
+class TestLLType(BaseFinalizerAnalyzerTests):
+ type_system = 'lltype'
+
+ def test_malloc(self):
+ S = lltype.GcStruct('S')
+
+ def f():
+ return lltype.malloc(S)
+
+ r = self.analyze(f, [])
+ assert r
+
+ def test_raw_free_getfield(self):
+ S = lltype.Struct('S')
+
+ class A(object):
+ def __init__(self):
+ self.x = lltype.malloc(S, flavor='raw')
+
+ def __del__(self):
+ if self.x:
+ self.x = lltype.nullptr(S)
+ lltype.free(self.x, flavor='raw')
+
+ def f():
+ return A()
+
+ r = self.analyze(f, [], A.__del__.im_func)
+ assert not r
+
+ def test_c_call(self):
+ C = rffi.CArray(lltype.Signed)
+ c = rffi.llexternal('x', [lltype.Ptr(C)], lltype.Signed)
+
+ def g():
+ p = lltype.malloc(C, 3, flavor='raw')
+ f(p)
+
+ def f(p):
+ c(rffi.ptradd(p, 0))
+ lltype.free(p, flavor='raw')
+
+ r = self.analyze(g, [], f, backendopt=True)
+ assert not r
+
+ def test_chain(self):
+ class B(object):
+ def __init__(self):
+ self.counter = 1
+
+ class A(object):
+ def __init__(self):
+ self.x = B()
+
+ def __del__(self):
+ self.x.counter += 1
+
+ def f():
+ A()
+
+ r = self.analyze(f, [], A.__del__.im_func)
+ assert r
+
+ def test_is_light_finalizer_decorator(self):
+ S = lltype.GcStruct('S')
+
+ @rgc.is_light_finalizer
+ def f():
+ lltype.malloc(S)
+ @rgc.is_light_finalizer
+ def g():
+ pass
+ self.analyze(g, []) # did not explode
+ py.test.raises(FinalizerError, self.analyze, f, [])
+
+class TestOOType(BaseFinalizerAnalyzerTests):
+ type_system = 'ootype'
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit