Commits

Armin Rigo committed f48a4df

Progress, but annoyed by the constant awkward interfacing between
C code and various pieces of RPython. :-/

Comments (0)

Files changed (4)

rpython/memory/gc/stmgc.py

         stmtls = self.get_tls()
         stmtls.stop_transaction()
         self.stm_operations.commit_transaction()
+        self.stm_operations.begin_inevitable_transaction()
         self.remove_from_linked_list(stmtls)
-        self.stm_operations.begin_inevitable_transaction()
         stmtls.delete()
     teardown_thread._dont_inline_ = True
 
         return mangle_hash(i)
 
     def can_move(self, addr):
+        raise NotImplementedError("stmgc.can_move")
         tls = self.get_tls()
         if tls.is_in_nursery(addr):
             return True

rpython/memory/gc/stmshared.py

         debug_start("gc-collect")
         debug_print()
         debug_print(".----------- Full collection ------------------")
-        debug_print("| used before collection:",
+        debug_print("| used before collection:   ",
                     self.fetch_count_total_bytes(), "bytes")
         #
-        fatalerror("do_major_collection: in-progress")
+        # Note that a major collection is non-moving.  The goal is only to
+        # find and free some of the objects allocated by the ArenaCollection.
+        # We first visit all objects and set the flag GCFLAG_VISITED on them.
+        self.objects_to_trace = self.AddressStack()
+        #
+        # The stacks...
+        self.collect_stack_roots_from_every_thread()
         #
         self.num_major_collects += 1
-        debug_print("| used after collection:",
+        debug_print("| used after collection:    ",
                     self.fetch_count_total_bytes(), "bytes")
-        debug_print("| number of major collects:        ",
+        debug_print("| number of major collects: ",
                     self.num_major_collects)
         debug_print("`----------------------------------------------")
         debug_stop("gc-collect")
 
+    def collect_stack_roots_from_every_thread(self):
+        self.gc.root_walker.walk_all_stack_roots(self._collect_stack_root,
+                                                 None)
+
+    def _collect_stack_root(self, ignored, root):
+        self.visit(root.address[0])
+
+    def visit(self, obj):
+        hdr = self.gc.header(obj)
+        XXX
+
 
 # ------------------------------------------------------------
 
     def __init__(self, sharedarea):
         self.gc = sharedarea.gc
         self.sharedarea = sharedarea
-        self.chained_list = NULL
         #
         # The array 'pages_for_size' contains 'length' chained lists
         # of pages currently managed by this thread.
         llarena.arena_reserve(addr, _dummy_size(totalsize))
         return addr + self.gc.gcheaderbuilder.size_gc_header
 
-    def add_regular(self, obj):
-        """After malloc_object(), register the object in the internal chained
-        list.  For objects whose 'revision' field is not otherwise needed."""
-        self.gc.set_obj_revision(obj, self.chained_list)
-        self.chained_list = obj
-
     def free_object(self, obj):
         adr1 = obj - self.gc.gcheaderbuilder.size_gc_header
         totalsize = (self.gc.gcheaderbuilder.size_gc_header +
             self.sharedarea.fetch_count_total_bytes_and_add(-totalsize)
             llarena.arena_free(llarena.getfakearenaaddress(adr1))
 
-    def free_and_clear(self):
-        obj = self.chained_list
-        self.chained_list = NULL
-        while obj:
-            next = self.gc.obj_revision(obj)
-            self.free_object(obj)
-            obj = next
-
-    def free_and_clear_list(self, lst):
-        while lst.non_empty():
-            self.free_object(lst.pop())
-
     def gift_all_pages_to_shared_area(self):
         """Send to the shared area all my pages.  For now we don't extract
         the information about which locations are free or not; we just stick

rpython/memory/gc/stmtls.py

                 llmemory.Address)
             self.adr_of_stack_top  = llop.gc_adr_of_root_stack_top(
                 llmemory.Address)
-        if StmGCThreadLocalAllocator is None:
-            from rpython.memory.gc.stmshared import StmGCThreadLocalAllocator
-        self.StmGCThreadLocalAllocator = StmGCThreadLocalAllocator
         #
         # --- current position, or NULL when mallocs are forbidden
         self.nursery_free = NULL
         self.nursery_top  = self.nursery_stop
         #
         # --- a thread-local allocator for the shared area
-        self.sharedarea_tls = self.StmGCThreadLocalAllocator(
+        if StmGCThreadLocalAllocator is None:
+            from rpython.memory.gc.stmshared import StmGCThreadLocalAllocator
+        self.sharedarea_tls = StmGCThreadLocalAllocator(
             self.gc.sharedarea)
+        # --- a chained list of regular local objects, linked via 'revision'.
+        self.regular_chained_list = NULL
         # --- the LOCAL objects which are weakrefs.  They are also listed
         #     in the appropriate place, like sharedarea_tls, if needed.
         self.local_weakrefs = self.AddressStack()
         else:
             self.detect_flag_combination = -1
         #
-        # Move away the previous sharedarea_tls and start a new one.
-        previous_sharedarea_tls = self.sharedarea_tls
-        self.sharedarea_tls = self.StmGCThreadLocalAllocator(
-            self.gc.sharedarea)
+        # Move away the previous chained_list and start a new one.
+        previous_chained_list = self.regular_chained_list
+        self.regular_chained_list = NULL
         #
         # List of LOCAL objects pending a visit.  Note that no GLOBAL
         # object can at any point contain a reference to a LOCAL object.
         #
         # Visit all previous OLD objects.  Free the ones that have not been
         # visited above, and reset GCFLAG_VISITED on the others.
-        self.mass_free_old_local(previous_sharedarea_tls)
+        self.mass_free_old_local(previous_chained_list)
         #
         # Note that the last step guarantees the invariant that between
         # collections, all the objects linked within 'self.sharedarea_tls'
     def _promote_locals_to_globals(self):
         ll_assert(self.local_nursery_is_empty(), "nursery must be empty [1]")
         #
-        # Promote all objects in sharedarea_tls to global.
+        # Promote all objects in 'regular_chained_list' to global.
         # This is the "real" equivalent of _FakeReach() in et.c.
-        obj = self.sharedarea_tls.chained_list
-        self.sharedarea_tls.chained_list = NULL
+        obj = self.regular_chained_list
+        self.regular_chained_list = NULL
         #
         while obj:
             hdr = self.gc.header(obj)
         #if self.rawmalloced_objects:
         #    xxx     # free the rawmalloced_objects still around
 
-        # free the old unused local objects still allocated in the
-        # StmGCThreadLocalAllocator
-        self.sharedarea_tls.free_and_clear()
+        # free the old unused local objects still in the chained list
+        obj = self.regular_chained_list
+        self.regular_chained_list = NULL
+        while obj:
+            next = self.gc.obj_revision(obj)
+            self.sharedarea_tls.free_object(obj)
+            obj = next
         # forget the local weakrefs.
         self.local_weakrefs.clear()
 
         #
         # Register the object here, not before the memcopy() that would
         # overwrite its 'revision' field
-        self._register_newly_malloced_obj(newobj)
+        self._register_regular_obj(newobj)
         #
         # Set the YOUNG copy's GCFLAG_VISITED and set its revision to
         # point to the OLD copy.
         #
         return newobj
 
-    def _register_newly_malloced_obj(self, obj):
-        self.sharedarea_tls.add_regular(obj)
+    def _register_regular_obj(self, obj):
+        self.gc.set_obj_revision(obj, self.regular_chained_list)
+        self.regular_chained_list = obj
 
     def collect_roots_from_tldict(self):
         if not we_are_translated():
         self.local_weakrefs = new
         old.delete()
 
-    def mass_free_old_local(self, previous_sharedarea_tls):
-        obj = previous_sharedarea_tls.chained_list
-        previous_sharedarea_tls.delete()
+    def mass_free_old_local(self, obj):
         while obj != NULL:
             hdr = self.gc.header(obj)
             next = hdr_revision(hdr)
             if hdr.tid & GCFLAG_VISITED:
-                # survives: relink in the new sharedarea_tls
+                # survives: relink in the new chained list
                 hdr.tid -= GCFLAG_VISITED
-                self.sharedarea_tls.add_regular(obj)
+                self._register_regular_obj(obj)
             else:
                 # dies
                 self.sharedarea_tls.free_object(obj)

rpython/memory/gc/test/test_stmshared.py

 from rpython.memory.gc.stmshared import WORD
 from rpython.memory.gc.stmshared import StmGCSharedArea
 from rpython.memory.gc.stmshared import StmGCThreadLocalAllocator
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
 
 
 SGH = 3
     def release_global_lock(self):
         pass
 
+class FakeRootWalker:
+    def __init__(self, rootobjlist):
+        self._rootobjlist = rootobjlist
+    def walk_all_stack_roots(self, collect_stack_root, arg):
+        for obj in self._rootobjlist:
+            p = lltype.malloc(rffi.CArray(llmemory.Address), 1, flavor='raw')
+            p[0] = obj
+            collect_stack_root(arg, llmemory.cast_ptr_to_adr(p))
+            assert p[0] == obj   # should not move
+            lltype.free(p)
+
 
 def test_simple():
     gc = FakeGC()
     assert shared.count_global_pages == 0
     thl1.delete()
     assert shared.count_global_pages == 1
+
+def test_do_major_collection():
+    gc = FakeGC()
+    shared = StmGCSharedArea(gc, 9*WORD, 2*WORD)
+    shared.setup()
+    thl1 = StmGCThreadLocalAllocator(shared)
+    obj1 = thl1.malloc_object(2*WORD-SGH)
+    obj2 = thl1.malloc_object(2*WORD-SGH)
+    #
+    gc.root_walker = FakeRootWalker([obj2])
+    shared.do_major_collection()
+    #
+    xxx