[pypy-commit] pypy concurrent-marksweep: Weakrefs.
arigo
noreply at buildbot.pypy.org
Thu Jan 12 12:17:53 CET 2012
Author: Armin Rigo <arigo at tunes.org>
Branch: concurrent-marksweep
Changeset: r51258:2ae56d12bba9
Date: 2012-01-12 12:17 +0100
http://bitbucket.org/pypy/pypy/changeset/2ae56d12bba9/
Log: Weakrefs.
diff --git a/pypy/rpython/memory/gc/concurrentgen.py b/pypy/rpython/memory/gc/concurrentgen.py
--- a/pypy/rpython/memory/gc/concurrentgen.py
+++ b/pypy/rpython/memory/gc/concurrentgen.py
@@ -40,7 +40,7 @@
class ConcurrentGenGC(GCBase):
_alloc_flavor_ = "raw"
- #inline_simple_malloc = True
+ inline_simple_malloc = True
#inline_simple_malloc_varsize = True
needs_deletion_barrier = True
needs_weakref_read_barrier = True
@@ -109,8 +109,10 @@
# contains the objects that the write barrier re-marked as young
# (so they are "old young objects").
self.new_young_objects = self.NULL
+ self.new_young_objects_wr = self.NULL # weakrefs
self.new_young_objects_size = r_uint(0)
self.old_objects = self.NULL
+ self.old_objects_wr = self.NULL
self.old_objects_size = r_uint(0) # total size of self.old_objects
#
# See concurrentgen.txt for more information about these fields.
@@ -215,6 +217,7 @@
needs_finalizer=False,
finalizer_is_light=False,
contains_weakptr=False):
+ # Generic function to allocate any fixed-size object.
#
# Case of finalizers (test constant-folded)
if needs_finalizer:
@@ -225,32 +228,16 @@
#
# Case of weakreferences (test constant-folded)
if contains_weakptr:
- raise NotImplementedError
return self._malloc_weakref(typeid, size)
#
# Regular case
- size_gc_header = self.gcheaderbuilder.size_gc_header
- totalsize = size_gc_header + size
- rawtotalsize = raw_malloc_usage(totalsize)
- adr = llarena.arena_malloc(rawtotalsize, 2)
- if adr == llmemory.NULL:
- raise MemoryError
- llarena.arena_reserve(adr, totalsize)
- obj = adr + size_gc_header
- hdr = self.header(obj)
- hdr.tid = self.combine(typeid, self.current_young_marker, 0)
- hdr.next = self.new_young_objects
- #debug_print("malloc:", rawtotalsize, obj)
- self.new_young_objects = hdr
- self.new_young_objects_size += r_uint(rawtotalsize)
- if self.new_young_objects_size > self.nursery_limit:
- self.nursery_overflowed(obj)
- return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
+ return self._malloc_regular(typeid, size)
def malloc_varsize_clear(self, typeid, length, size, itemsize,
offset_to_length):
- size_gc_header = self.gcheaderbuilder.size_gc_header
- nonvarsize = size_gc_header + size
+ # Generic function to allocate any variable-size object.
+ #
+ nonvarsize = self.gcheaderbuilder.size_gc_header + size
#
if length < 0:
raise MemoryError
@@ -259,24 +246,49 @@
except OverflowError:
raise MemoryError
#
+ return self._do_malloc(typeid, totalsize, offset_to_length, length, 0)
+
+
+ def _malloc_regular(self, typeid, size):
+ totalsize = self.gcheaderbuilder.size_gc_header + size
+ return self._do_malloc(typeid, totalsize, -1, -1, 0)
+ _malloc_regular._dont_inline_ = True
+
+ def _malloc_weakref(self, typeid, size):
+ totalsize = self.gcheaderbuilder.size_gc_header + size
+ return self._do_malloc(typeid, totalsize, -1, -1, 1)
+ _malloc_weakref._dont_inline_ = True
+
+
+ def _do_malloc(self, typeid, totalsize, offset_to_length, length,
+ linked_list_number):
+ # Generic function to perform allocation. Inlined in its few callers,
+ # so that some checks like 'offset_to_length >= 0' are removed.
rawtotalsize = raw_malloc_usage(totalsize)
adr = llarena.arena_malloc(rawtotalsize, 2)
if adr == llmemory.NULL:
raise MemoryError
llarena.arena_reserve(adr, totalsize)
- obj = adr + size_gc_header
- (obj + offset_to_length).signed[0] = length
+ obj = adr + self.gcheaderbuilder.size_gc_header
+ if offset_to_length >= 0:
+ (obj + offset_to_length).signed[0] = length
+ totalsize = llarena.round_up_for_allocation(totalsize)
+ rawtotalsize = raw_malloc_usage(totalsize)
hdr = self.header(obj)
hdr.tid = self.combine(typeid, self.current_young_marker, 0)
- hdr.next = self.new_young_objects
- totalsize = llarena.round_up_for_allocation(totalsize)
- rawtotalsize = raw_malloc_usage(totalsize)
- #debug_print("malloc:", rawtotalsize, obj)
- self.new_young_objects = hdr
+ if linked_list_number == 0:
+ hdr.next = self.new_young_objects
+ self.new_young_objects = hdr
+ elif linked_list_number == 1:
+ hdr.next = self.new_young_objects_wr
+ self.new_young_objects_wr = hdr
+ else:
+ raise AssertionError(linked_list_number)
self.new_young_objects_size += r_uint(rawtotalsize)
if self.new_young_objects_size > self.nursery_limit:
self.nursery_overflowed(obj)
return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
+ _do_malloc._always_inline_ = True
# ----------
# Other functions in the GC API
@@ -582,8 +594,10 @@
self.current_aging_marker = other
#
# Copy a few 'mutator' fields to 'collector' fields
- self.collector.aging_objects = self.new_young_objects
+ self.collector.aging_objects = self.new_young_objects
+ self.collector.aging_objects_wr = self.new_young_objects_wr
self.new_young_objects = self.NULL
+ self.new_young_objects_wr = self.NULL
self.new_young_objects_size = r_uint(0)
#self.collect_weakref_pages = self.weakref_pages
#self.collect_finalizer_pages = self.finalizer_pages
@@ -611,6 +625,8 @@
"flagged_objects should be empty here")
ll_assert(self.new_young_objects == self.NULL,
"new_young_obejcts should be empty here")
+ ll_assert(self.new_young_objects_wr == self.NULL,
+ "new_young_obejcts_wr should be empty here")
#
# Keep this newest_obj alive
if newest_obj:
@@ -635,8 +651,11 @@
#
# Copy a few 'mutator' fields to 'collector' fields
self.collector.delayed_aging_objects = self.collector.aging_objects
+ self.collector.delayed_aging_objects_wr=self.collector.aging_objects_wr
self.collector.aging_objects = self.old_objects
+ self.collector.aging_objects_wr = self.old_objects_wr
self.old_objects = self.NULL
+ self.old_objects_wr = self.NULL
self.old_objects_size = r_uint(0)
#self.collect_weakref_pages = self.weakref_pages
#self.collect_finalizer_pages = self.finalizer_pages
@@ -686,12 +705,20 @@
self.collector.gray_objects.append(obj)
def debug_check_lists(self):
- # just check that they are correct, non-infinite linked lists
- self.debug_check_list(self.new_young_objects,
- self.new_young_objects_size)
- self.debug_check_list(self.old_objects, self.old_objects_size)
+ # check that they are correct, non-infinite linked lists,
+ # and check that the total size of objects in the lists corresponds
+ # precisely to the value recorded
+ size = self.debug_check_list(self.new_young_objects)
+ size += self.debug_check_list(self.new_young_objects_wr)
+ ll_assert(size == self.new_young_objects_size,
+ "bogus total size in new_young_objects")
+ #
+ size = self.debug_check_list(self.old_objects)
+ size += self.debug_check_list(self.old_objects_wr)
+ ll_assert(size == self.old_objects_size,
+ "bogus total size in old_objects")
- def debug_check_list(self, list, totalsize):
+ def debug_check_list(self, list):
previous = self.NULL
count = 0
size = r_uint(0)
@@ -708,8 +735,7 @@
previous = list # detect loops of any size
list = list.next
#print "\tTOTAL:", size
- ll_assert(size == totalsize, "bogus total size in linked list")
- return count
+ return size
def acquire(self, lock):
if we_are_translated():
@@ -748,7 +774,6 @@
# Weakrefs
def weakref_deref(self, wrobj):
- raise NotImplementedError
# Weakrefs need some care. This code acts as a read barrier.
# The only way I found is to acquire the mutex_lock to prevent
# the collection thread from going from collector.running==1
@@ -775,7 +800,7 @@
# collector phase was already finished (deal_with_weakrefs).
# Otherwise we would be returning an object that is about to
# be swept away.
- if not self.is_marked_or_static(targetobj, self.current_mark):
+ if not self.collector.is_marked_or_static(targetobj):
targetobj = llmemory.NULL
#
else:
@@ -841,7 +866,9 @@
# when the collection starts, we make all young objects aging and
# move 'new_young_objects' into 'aging_objects'
self.aging_objects = self.NULL
+ self.aging_objects_wr = self.NULL
self.delayed_aging_objects = self.NULL
+ self.delayed_aging_objects_wr = self.NULL
def setup(self):
self.ready_to_start_lock = self.gc.ready_to_start_lock
@@ -994,7 +1021,7 @@
def _collect_add_pending(self, root, ignored):
obj = root.address[0]
- # these 'get_mark(obj) are here for debugging invalid marks.
+ # these 'get_mark(obj)' are here for debugging invalid marks.
# XXX check that the C compiler removes them if lldebug is off
self.get_mark(obj)
self.gray_objects.append(obj)
@@ -1003,10 +1030,17 @@
def collector_sweep(self):
if self.major_collection_phase != 1: # no sweeping during phase 1
self.update_size = self.gc.old_objects_size
+ #
lst = self._collect_do_sweep(self.aging_objects,
self.current_aging_marker,
self.gc.old_objects)
self.gc.old_objects = lst
+ #
+ lst = self._collect_do_sweep(self.aging_objects_wr,
+ self.current_aging_marker,
+ self.gc.old_objects_wr)
+ self.gc.old_objects_wr = lst
+ #
self.gc.old_objects_size = self.update_size
#
self.running = -1
@@ -1023,6 +1057,12 @@
self.aging_objects)
self.aging_objects = lst
self.delayed_aging_objects = self.NULL
+ #
+ lst = self._collect_do_sweep(self.delayed_aging_objects_wr,
+ self.current_old_marker,
+ self.aging_objects_wr)
+ self.aging_objects_wr = lst
+ self.delayed_aging_objects_wr = self.NULL
def _collect_do_sweep(self, hdr, still_not_marked, linked_list):
size_gc_header = self.gc.gcheaderbuilder.size_gc_header
@@ -1059,43 +1099,59 @@
# -------------------------
# CollectorThread: Weakrefs
+ def is_marked_or_static(self, obj):
+ return self.get_mark(obj) != self.current_aging_marker
+
def deal_with_weakrefs(self):
- self.running = 3; return
- # ^XXX^
- size_gc_header = self.gcheaderbuilder.size_gc_header
- current_mark = self.current_mark
- weakref_page = self.collect_weakref_pages
- self.collect_weakref_pages = self.NULL
- self.collect_weakref_tails = self.NULL
- while weakref_page != self.NULL:
- next_page = list_next(weakref_page)
+ # For simplicity, we do the minimal amount of work here: if a weakref
+ # dies or points to a dying object, we clear it and move it from
+ # 'aging_objects_wr' to 'aging_objects'. Otherwise, we keep it in
+ # 'aging_objects_wr'.
+ size_gc_header = self.gc.gcheaderbuilder.size_gc_header
+ linked_list = self.aging_objects
+ linked_list_wr = self.NULL
+ #
+ hdr = self.aging_objects_wr
+ while hdr != self.NULL:
+ nexthdr = hdr.next
#
- # If the weakref points to a dead object, make it point to NULL.
- x = llmemory.cast_ptr_to_adr(weakref_page)
- x = llarena.getfakearenaaddress(x) + 8
- hdr = llmemory.cast_adr_to_ptr(x, self.HDRPTR)
- type_id = llop.extract_high_ushort(llgroup.HALFWORD, hdr.tid)
- offset = self.weakpointer_offset(type_id)
- ll_assert(offset >= 0, "bad weakref")
- obj = x + size_gc_header
- pointing_to = (obj + offset).address[0]
- ll_assert(pointing_to != llmemory.NULL, "null weakref?")
- if not self.is_marked_or_static(pointing_to, current_mark):
- # 'pointing_to' dies: relink to self.collect_pages[0]
- (obj + offset).address[0] = llmemory.NULL
- set_next(weakref_page, self.collect_pages[0])
- self.collect_pages[0] = weakref_page
+ mark = hdr.tid & 0xFF
+ if mark == self.current_aging_marker:
+ # the weakref object itself is not referenced any more
+ valid = False
+ #
else:
- # the weakref stays alive
- set_next(weakref_page, self.collect_weakref_pages)
- self.collect_weakref_pages = weakref_page
- if self.collect_weakref_tails == self.NULL:
- self.collect_weakref_tails = weakref_page
+ #
+ type_id = llop.extract_high_ushort(llgroup.HALFWORD, hdr.tid)
+ offset = self.gc.weakpointer_offset(type_id)
+ ll_assert(offset >= 0, "bad weakref")
+ obj = llmemory.cast_ptr_to_adr(hdr) + size_gc_header
+ pointing_to = (obj + offset).address[0]
+ if pointing_to == llmemory.NULL:
+ # special case only for fresh new weakrefs not yet filled
+ valid = True
+ #
+ elif not self.is_marked_or_static(pointing_to):
+ # 'pointing_to' dies
+ (obj + offset).address[0] = llmemory.NULL
+ valid = False
+ else:
+ valid = True
#
- weakref_page = next_page
+ if valid:
+ hdr.next = linked_list_wr
+ linked_list = linked_list_wr
+ else:
+ hdr.next = linked_list
+ linked_list = hdr.next
+ #
+ hdr = nexthdr
+ #
+ self.aging_objects = linked_list
+ self.aging_objects_wr = linked_list_wr
#
self.acquire(self.mutex_lock)
- self.collector.running = 3
+ self.running = 3
#debug_print("collector.running = 3")
self.release(self.mutex_lock)
More information about the pypy-commit
mailing list