Commits

Armin Rigo committed 6fe3b99

In-progress.

Comments (0)

Files changed (7)

rpython/memory/gc/stmgc.py

 from rpython.rtyper.lltypesystem.lloperation import llop
 from rpython.rtyper.lltypesystem.llmemory import raw_malloc_usage, raw_memcopy
 from rpython.memory.gc.base import GCBase, MovingGCBase
+from rpython.memory.gc.env import addressable_size
 from rpython.memory.support import mangle_hash
 from rpython.rtyper.annlowlevel import llhelper
 from rpython.rlib.rarithmetic import LONG_BIT, r_uint
-from rpython.rlib.debug import ll_assert, debug_start, debug_stop, fatalerror
-from rpython.rlib.debug import debug_print
+from rpython.rlib.debug import ll_assert, fatalerror
 from rpython.rlib import rthread
 from rpython.memory.gc import stmshared
 
         self.stm_operations = stm_operations
         self.nursery_size = nursery_size
         self.major_collection_threshold = 1.82     # xxx
-        self.min_heap_size = r_uint(8 * self.nursery_size)
-        self.real_limit_for_major_gc = self.min_heap_pages
-        self.dyn_limit_for_major_gc = self.real_limit_for_major_gc
+        self.min_heap_size = 8.0 * self.nursery_size
+        self.limit_for_major_gc = r_uint(self.min_heap_size)
         #self.maximum_extra_threshold = 0
         self.sharedarea = stmshared.StmGCSharedArea(self, page_size,
                                                     small_request_threshold)
 
 
     def collect(self, gen=1):
-        if gen <= 0:
-            self.get_tls().local_collection()
-        else:
-            self.major_collection(force=True)
+        self.get_tls().local_collection()
+        if gen > 0:
+            self.major_collection()
 
     def start_transaction(self):
         self.get_tls().start_transaction()
     def maybe_major_collection(self):
         """Check the memory usage, and maybe do a major GC collection."""
         if (self.sharedarea.fetch_count_total_bytes() >=
-                self.dyn_limit_for_major_gc):
+                self.limit_for_major_gc):
             self.major_collection()
-            return True
-        else:
-            return False
 
-    def major_collection(self, force=False):
+    def major_collection(self):
         """Do a major collection.  This uses a stop-the-world system."""
         #
-        # When the present function is called we know we'll do at least
-        # one major GC.  Setting this limit to 0 now will invite other
-        # threads to enter major_collection() soon too.
-        self.dyn_limit_for_major_gc = r_uint(0)
-        #
-        # While still running multithreaded, do a local collection.
-        # This is not strictly needed.
-        self.get_tls().local_collection(run_finalizers=False)
+        # Setting this limit to 0 now will invite other threads to enter
+        # major_collection() soon too.  Note that this doesn't create a
+        # race, because all threads are first going to run until they are
+        # all at the start_single_thread() below, or in C code at a
+        # reached_safe_point() or  outside a transaction.
+        self.limit_for_major_gc = r_uint(0)
         #
         # Now wait until we can acquire the RW lock in exclusive mode.
         self.stm_operations.start_single_thread()
         #
-        # At this point all other threads should be blocked or running
-        # external C code
-        if (self.sharedarea.fetch_count_total_bytes() >=
-                self.limit_for_major_collection):
-            xxxxxxx
+        # If several threads were blocked on the previous line, the first
+        # one to proceed sees 0 in this variable.  It's the thread that
+        # will do the major collection.  Afterwards the other threads will
+        # also acquire the RW lock in exclusive mode, but won't do anything.
+        if self.limit_for_major_gc == r_uint(0):
+            #
+            # do the major collection
+            self.sharedarea.do_major_collection()
+            #
+            # reset 'limit_for_major_gc' to the correct value at which the
+            # following major collection should take place.
+            in_use = self.sharedarea.fetch_count_total_bytes()
+            threshold = in_use * self.major_collection_threshold
+            if threshold < self.min_heap_size:
+                threshold = self.min_heap_size
+            if threshold > addressable_size * 0.99:
+                threshold = addressable_size * 0.99
+            self.limit_for_major_gc = r_uint(threshold)
+        #
         self.stm_operations.stop_single_thread()
     major_collection._dont_inline_ = True
 

rpython/memory/gc/stmshared.py

 from rpython.rlib.rarithmetic import LONG_BIT, r_uint
 from rpython.rlib.objectmodel import free_non_gc_object, we_are_translated
 from rpython.rlib.debug import ll_assert, fatalerror
+from rpython.rlib.debug import debug_start, debug_stop, debug_print
 from rpython.rlib import rthread, atomic_ops
 
 WORD = LONG_BIT // 8
         #
         # Counters for statistics
         self.count_global_pages = 0
+        self.num_major_collects = 0
         self.v_count_total_bytes = lltype.malloc(rffi.CArray(lltype.Unsigned),
                                                  1, flavor='raw',
                                                  immortal=True, zero=True)
         adr = rffi.cast(llmemory.Address, self.v_count_total_bytes)
         return r_uint(atomic_ops.fetch_and_add(adr, increment))
 
+    def do_major_collection(self):
+        """Perform a major collection."""
+        # At this point all other threads should be blocked or running
+        # external C code or careful non-GC-using code, with all GC roots
+        # in their shadow stack.  Even if some nurseries are not empty
+        # we can still trace through them: a major collection does not
+        # move any object.  The point is only that after the "sweep" phase,
+        # we have identified all locations that are now free, and added
+        # them to the chained lists of StmGCSharedArea for reuse.
+        debug_start("gc-collect")
+        debug_print()
+        debug_print(".----------- Full collection ------------------")
+        debug_print("| used before collection:",
+                    self.fetch_count_total_bytes(), "bytes")
+        #
+        fatalerror("do_major_collection: in-progress")
+        #
+        self.num_major_collects += 1
+        debug_print("| used after collection:",
+                    self.fetch_count_total_bytes(), "bytes")
+        debug_print("| number of major collects:        ",
+                    self.num_major_collects)
+        debug_print("`----------------------------------------------")
+        debug_stop("gc-collect")
+
 
 # ------------------------------------------------------------
 

rpython/memory/gc/stmtls.py

         if llmemory.raw_malloc_usage(size) > self.nursery_size // 8 * 7:
             fatalerror("XXX object too large to ever fit in the nursery")
         #
-        if not self.gc.maybe_major_collection():
-            self.local_collection(run_finalizers=True)
+        self.local_collection(run_finalizers=True)
+        self.gc.maybe_major_collection()
         #
         # if we have now enough space, return it
         free = self.nursery_free

rpython/memory/gc/test/test_stmtls.py

             if addr.address[0]:
                 callback(addr, arg)
 
+    def maybe_major_collection(self):
+        pass
+
 
 class TestStmGCTLS(object):
 

rpython/memory/gctransform/stmframework.py

         base = self.stackgcdata.root_stack_base
         llmemory.raw_free(base)
 
-    def walk_roots(self, *args):
-        "NOT_RPYTHON"
+    def walk_roots(self, c1, c2, c3):
         raise NotImplementedError
 
     def walk_stack_roots(self, *args):

rpython/rlib/atomic_ops.py

 
 
 bool_cas = rffi.llexternal('pypy_bool_cas', [llmemory.Address]*3, lltype.Bool,
-                           compilation_info=eci, macro=True)
+                           compilation_info=eci, macro=True, _nowrapper=True)
 fetch_and_add = rffi.llexternal('pypy_fetch_and_add', [llmemory.Address,
                                                        lltype.Signed],
-                                lltype.Signed,
-                                compilation_info=eci, macro=True)
+                                lltype.Signed, compilation_info=eci,
+                                macro=True, _nowrapper=True)

rpython/translator/stm/src_stm/rpyintf.c

 static unsigned long stm_regular_length_limit = ULONG_MAX;
 
 /* sync_required is either 0 or 0xffffffff */
+#define SYNC_REQUIRED  ((unsigned long)-1)
 static volatile unsigned long sync_required = 0;
 static void reached_safe_point(void);
 
      Warning, may block waiting for rwlock_in_transaction while another
      thread runs a major GC itself! */
   int err;
-  sync_required = (unsigned long)-1;
+  sync_required = SYNC_REQUIRED;
   err = pthread_rwlock_unlock(&rwlock_in_transaction);
   assert(err == 0);
   err = pthread_rwlock_wrlock(&rwlock_in_transaction);
 
 static void reached_safe_point(void)
 {
-  /* Warning: all places that call this function from RPython code
-     must do so with a llop with canmallocgc=True!  The release of
-     the rwlock_in_transaction below means a major GC could run in
-     another thread! */
+  /* Warning, may block waiting for rwlock_in_transaction while another
+     thread runs a major GC */
   int err;
   struct tx_descriptor *d = thread_descriptor;
+  assert(d->active);
   assert(in_single_thread != d);
-  if (d->active)
-    {
-      err = pthread_rwlock_unlock(&rwlock_in_transaction);
-      assert(err == 0);
-      /* another thread should be waiting in pthread_rwlock_wrlock(),
-         which takes priority here */
-      err = pthread_rwlock_rdlock(&rwlock_in_transaction);
-      assert(err == 0);
-    }
+
+  err = pthread_rwlock_unlock(&rwlock_in_transaction);
+  assert(err == 0);
+  /* another thread should be waiting in pthread_rwlock_wrlock(),
+     which takes priority here */
+  err = pthread_rwlock_rdlock(&rwlock_in_transaction);
+  assert(err == 0);
 }
 
 void stm_abort_and_retry(void)