Author: Armin Rigo <ar...@tunes.org>
Branch: concurrent-marksweep
Changeset: r47928:2d3fb03f4ad4
Date: 2011-10-11 11:07 +0200
http://bitbucket.org/pypy/pypy/changeset/2d3fb03f4ad4/

Log:    Finalizers, first version.

diff --git a/pypy/doc/discussion/finalizer-order.rst 
b/pypy/doc/discussion/finalizer-order.rst
--- a/pypy/doc/discussion/finalizer-order.rst
+++ b/pypy/doc/discussion/finalizer-order.rst
@@ -96,7 +96,7 @@
   we just skip it ("continue" line) and so it doesn't get marked.
 
 * if x2 is enumerated before x1, then when we process x2 we mark it and
-  set its state to >= 2 (before x2 is in closure(x2)), and then when we
+  set its state to >= 2 (because x2 is in closure(x2)), and then when we
   process x1 we set state[x2] == 3.  So in the final loop x2 gets
   removed from the "marked" list.
 
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
@@ -31,6 +31,7 @@
         self.config = config
         assert isinstance(translated_to_c, bool)
         self.translated_to_c = translated_to_c
+        self.run_finalizers = None
 
     def setup(self):
         # all runtime mutable values' setup should happen here
@@ -297,7 +298,8 @@
         callback2, attrname = _convert_callback_formats(callback)    # :-/
         setattr(self, attrname, arg)
         self.root_walker.walk_roots(callback2, callback2, callback2)
-        self.run_finalizers.foreach(callback, arg)
+        if self.run_finalizers is not None:
+            self.run_finalizers.foreach(callback, arg)
     enumerate_all_roots._annspecialcase_ = 'specialize:arg(1)'
 
     def debug_check_consistency(self):
diff --git a/pypy/rpython/memory/gc/concurrentms.py 
b/pypy/rpython/memory/gc/concurrentms.py
--- a/pypy/rpython/memory/gc/concurrentms.py
+++ b/pypy/rpython/memory/gc/concurrentms.py
@@ -37,8 +37,8 @@
 MARK_VALUE_2      = 0x6B    # 'k', 107
 MARK_VALUE_STATIC = 0x53    # 'S', 83
 # Next lower byte: a combination of flags.
-FL_WITHHASH              = 0x0100
-FL_EXTRA                 = 0x0200
+FL_WITHHASH       = 0x0100
+FL_EXTRA          = 0x0200
 # And the high half of the word contains the numeric typeid.
 
 
@@ -117,11 +117,11 @@
     def _clear_list(self, array):
         i = 0
         while i < self.pagelists_length:
-            array[i] = lltype.nullptr(self.HDR)
+            array[i] = self.NULL
             i += 1
 
     def _initialize(self):
-        self.free_pages = lltype.nullptr(self.HDR)
+        self.free_pages = self.NULL
         #
         # Clear the lists
         self._clear_list(self.nonfree_pages)
@@ -130,6 +130,13 @@
         self._clear_list(self.collect_heads)
         self._clear_list(self.collect_tails)
         #
+        self.finalizer_pages = self.NULL
+        self.collect_finalizer_pages = self.NULL
+        self.collect_finalizer_tails = self.NULL
+        self.collect_run_finalizers_head = self.NULL
+        self.collect_run_finalizers_tail = self.NULL
+        self.run_finalizers = self.NULL
+        #
         # The following character is either MARK_VALUE_1 or MARK_VALUE_2,
         # and represents the character that must be in the 'mark' field
         # of an object header in order for the object to be considered as
@@ -158,7 +165,9 @@
 
     def setup(self):
         "Start the concurrent collector thread."
-        GCBase.setup(self)
+        # don't call GCBase.setup(self), because we don't need
+        # 'run_finalizers' as a deque
+        self.finalizer_lock_count = 0
         #
         self.main_thread_ident = ll_thread.get_ident()
         self.ready_to_start_lock = ll_thread.allocate_ll_lock()
@@ -198,8 +207,15 @@
 
     def malloc_fixedsize_clear(self, typeid, size,
                                needs_finalizer=False, contains_weakptr=False):
-        assert not needs_finalizer  # XXX
         # contains_weakptr: detected during collection
+        #
+        # Case of finalizers (test constant-folded)
+        if needs_finalizer:
+            ll_assert(not contains_weakptr,
+                     "'needs_finalizer' and 'contains_weakptr' both specified")
+            return self._malloc_with_finalizer(typeid, size)
+        #
+        # Regular case
         size_gc_header = self.gcheaderbuilder.size_gc_header
         totalsize = size_gc_header + size
         rawtotalsize = raw_malloc_usage(totalsize)
@@ -377,6 +393,32 @@
         return result
     _malloc_varsize_slowpath._dont_inline_ = True
 
+    def _malloc_with_finalizer(self, typeid, size):
+        # XXX a lot of copy and paste from _malloc_slowpath()
+        size_gc_header = self.gcheaderbuilder.size_gc_header
+        totalsize = size_gc_header + size
+        rawtotalsize = raw_malloc_usage(totalsize)
+        ll_assert(rawtotalsize & (WORD - 1) == 0,
+                  "malloc_with_finalizer: non-rounded size")
+        rawtotalsize += 8
+        block = llarena.arena_malloc(rawtotalsize, 2)
+        if not block:
+            raise MemoryError
+        llarena.arena_reserve(block, self.HDRSIZE)
+        blockhdr = llmemory.cast_adr_to_ptr(block, self.HDRPTR)
+        # the changes are only in the following two lines: we add the block
+        # to a different linked list
+        blockhdr.tid = self.cast_hdrptr_to_int(self.finalizer_pages)
+        self.finalizer_pages = blockhdr
+        result = block + 8
+        #
+        llarena.arena_reserve(result, totalsize)
+        hdr = llmemory.cast_adr_to_ptr(result, self.HDRPTR)
+        hdr.tid = self.combine(typeid, self.current_mark, 0)
+        #
+        obj = result + size_gc_header
+        return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
+
     # ----------
     # Other functions in the GC API
 
@@ -509,10 +551,50 @@
                     self.nonfree_pages[0])
                 self.nonfree_pages[0] = self.collect_heads[0]
             #
+            # Do the same with 'collect_finalizer_pages/tails'
+            if self.collect_finalizer_tails != self.NULL:
+                self.collect_finalizer_tails.tid = self.cast_hdrptr_to_int(
+                    self.finalizer_pages)
+                self.finalizer_pages = self.collect_finalizer_pages
+            #
+            # Do the same with 'collect_run_finalizers_head/tail'
+            if self.collect_run_finalizers_tail != self.NULL:
+                self.collect_run_finalizers_tail.tid = self.cast_hdrptr_to_int(
+                    self.run_finalizers)
+                self.run_finalizers = self.collect_run_finalizers_head
+            #
             if self.DEBUG:
                 self.debug_check_lists()
             #
             debug_stop("gc-stop")
+            #
+            self.execute_finalizers_ll()
+
+
+    def execute_finalizers_ll(self):
+        self.finalizer_lock_count += 1
+        try:
+##            print 'execute_finalizers_ll: %d' % self.finalizer_lock_count
+##            p = self.run_finalizers
+##            while p != self.NULL:
+##                print p
+##                p = self.cast_int_to_hdrptr(p.tid)
+            
+            while self.run_finalizers != self.NULL:
+                if self.finalizer_lock_count > 1:
+                    # the outer invocation of execute_finalizers() will do it
+                    break
+                #
+                x = llmemory.cast_ptr_to_adr(self.run_finalizers)
+                x = llarena.getfakearenaaddress(x) + 8
+                obj = x + self.gcheaderbuilder.size_gc_header
+                self.run_finalizers = self.cast_int_to_hdrptr(
+                    self.run_finalizers.tid)
+                #
+                finalizer = self.getfinalizer(self.get_type_id(obj))
+                finalizer(obj, llmemory.NULL)
+        finally:
+            self.finalizer_lock_count -= 1
 
 
     def collect(self, gen=2):
@@ -550,6 +632,14 @@
         # Add the prebuilt root objects that have been written to
         self.prebuilt_root_objects.foreach(self._add_prebuilt_root, None)
         #
+        # Add the objects still waiting in 'run_finalizers'
+        p = self.run_finalizers
+        while p != self.NULL:
+            x = llmemory.cast_ptr_to_adr(p)
+            x = llarena.getfakearenaaddress(x) + 8
+            self.gray_objects.append(x + self.gcheaderbuilder.size_gc_header)
+            p = self.cast_int_to_hdrptr(p.tid)
+        #
         # Invert this global variable, which has the effect that on all
         # objects' state go instantly from "marked" to "non marked"
         self.current_mark = self.other_mark(self.current_mark)
@@ -562,7 +652,13 @@
         while n < self.pagelists_length:
             self.collect_pages[n] = self.nonfree_pages[n]
             n += 1
+        self.collect_finalizer_pages = self.finalizer_pages
+        #
+        # Clear the following lists.  When the collector thread finishes,
+        # it will give back (in collect_{pages,tails}[0] and
+        # collect_finalizer_{pages,tails}) all the original items that survive.
         self.nonfree_pages[0] = self.NULL
+        self.finalizer_pages = self.NULL
         #
         # Start the collector thread
         self.collection_running = 1
@@ -584,15 +680,20 @@
         while n < self.pagelists_length:
             self.debug_check_list(self.free_lists[n])
             n += 1
+        self.debug_check_list(self.finalizer_pages)
+        self.debug_check_list(self.run_finalizers)
 
     def debug_check_list(self, page):
         try:
             previous_page = self.NULL
+            count = 0
             while page != self.NULL:
                 # prevent constant-folding, and detects loops of length 1
                 ll_assert(page != previous_page, "loop!")
                 previous_page = page
                 page = self.cast_int_to_hdrptr(page.tid)
+                count += 1
+            return count
         except KeyboardInterrupt:
             ll_assert(False, "interrupted")
             raise
@@ -663,6 +764,8 @@
             self.collector_mark()
             self.collection_running = 2
             #
+            self.deal_with_objects_with_finalizers()
+            #
             # Sweep
             self.collector_sweep()
             #
@@ -837,6 +940,51 @@
         self.collect_heads[n] = linked_list
         self.collect_tails[n] = first_loc_in_linked_list
 
+    # ----------
+    # Finalizers
+
+    def deal_with_objects_with_finalizers(self):
+        # XXX needs to be done correctly; for now we'll call finalizers
+        # in random order
+        size_gc_header = self.gcheaderbuilder.size_gc_header
+        marked = self.current_mark
+        finalizer_page = self.collect_finalizer_pages
+        self.collect_run_finalizers_head = self.NULL
+        self.collect_run_finalizers_tail = self.NULL
+        self.collect_finalizer_pages = self.NULL
+        self.collect_finalizer_tails = self.NULL
+        while finalizer_page != self.NULL:
+            next_page = self.cast_int_to_hdrptr(finalizer_page.tid)
+            #
+            x = llmemory.cast_ptr_to_adr(finalizer_page)
+            x = llarena.getfakearenaaddress(x) + 8
+            hdr = llmemory.cast_adr_to_ptr(x, self.HDRPTR)
+            if (hdr.tid & 0xFF) != marked:
+                # non-marked: add to collect_run_finalizers,
+                # and mark the object and its dependencies
+                finalizer_page.tid = self.cast_hdrptr_to_int(
+                    self.collect_run_finalizers_head)
+                self.collect_run_finalizers_head = finalizer_page
+                if self.collect_run_finalizers_tail == self.NULL:
+                    self.collect_run_finalizers_tail = finalizer_page
+                obj = x + size_gc_header
+                self.gray_objects.append(obj)
+            else:
+                # marked: relink into the collect_finalizer_pages list
+                finalizer_page.tid = self.cast_hdrptr_to_int(
+                    self.collect_finalizer_pages)
+                self.collect_finalizer_pages = finalizer_page
+                if self.collect_finalizer_tails != self.NULL:
+                    self.collect_finalizer_tails = finalizer_page
+            #
+            finalizer_page = next_page
+        #
+        self._collect_mark()
+        #
+        ll_assert(not self.extra_objects_to_mark.non_empty(),
+                  "should not see objects only reachable from finalizers "
+                  "before we run them")
+
 
 # ____________________________________________________________
 #
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
@@ -931,3 +931,6 @@
 class TestMostlyConcurrentMarkSweepGC(GCTest):
     from pypy.rpython.memory.gc.concurrentms \
             import MostlyConcurrentMarkSweepGC as GCClass
+
+    def test_finalizer_calls_malloc_during_minor_collect(self):
+        py.test.skip("check if this test is valid here")
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to