Author: Armin Rigo <[email protected]>
Branch: stm-gc-2
Changeset: r63500:6fe3b9967ab2
Date: 2013-04-19 13:35 +0200
http://bitbucket.org/pypy/pypy/changeset/6fe3b9967ab2/
Log: In-progress.
diff --git a/rpython/memory/gc/stmgc.py b/rpython/memory/gc/stmgc.py
--- a/rpython/memory/gc/stmgc.py
+++ b/rpython/memory/gc/stmgc.py
@@ -2,11 +2,11 @@
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
@@ -183,9 +183,8 @@
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)
@@ -335,10 +334,9 @@
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()
@@ -412,32 +410,41 @@
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
diff --git a/rpython/memory/gc/stmshared.py b/rpython/memory/gc/stmshared.py
--- a/rpython/memory/gc/stmshared.py
+++ b/rpython/memory/gc/stmshared.py
@@ -2,6 +2,7 @@
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
@@ -78,6 +79,7 @@
#
# 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)
@@ -92,6 +94,31 @@
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")
+
# ------------------------------------------------------------
diff --git a/rpython/memory/gc/stmtls.py b/rpython/memory/gc/stmtls.py
--- a/rpython/memory/gc/stmtls.py
+++ b/rpython/memory/gc/stmtls.py
@@ -274,8 +274,8 @@
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
diff --git a/rpython/memory/gc/test/test_stmtls.py
b/rpython/memory/gc/test/test_stmtls.py
--- a/rpython/memory/gc/test/test_stmtls.py
+++ b/rpython/memory/gc/test/test_stmtls.py
@@ -140,6 +140,9 @@
if addr.address[0]:
callback(addr, arg)
+ def maybe_major_collection(self):
+ pass
+
class TestStmGCTLS(object):
diff --git a/rpython/memory/gctransform/stmframework.py
b/rpython/memory/gctransform/stmframework.py
--- a/rpython/memory/gctransform/stmframework.py
+++ b/rpython/memory/gctransform/stmframework.py
@@ -163,8 +163,7 @@
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):
diff --git a/rpython/rlib/atomic_ops.py b/rpython/rlib/atomic_ops.py
--- a/rpython/rlib/atomic_ops.py
+++ b/rpython/rlib/atomic_ops.py
@@ -22,8 +22,8 @@
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)
diff --git a/rpython/translator/stm/src_stm/rpyintf.c
b/rpython/translator/stm/src_stm/rpyintf.c
--- a/rpython/translator/stm/src_stm/rpyintf.c
+++ b/rpython/translator/stm/src_stm/rpyintf.c
@@ -65,6 +65,7 @@
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);
@@ -215,7 +216,7 @@
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);
@@ -244,22 +245,19 @@
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)
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit