Author: Remi Meier <[email protected]>
Branch: cl-collector-thread
Changeset: r1709:caba34127c17
Date: 2015-03-09 15:47 +0100
http://bitbucket.org/pypy/stmgc/changeset/caba34127c17/
Log: fixes, call CL-collection on segment release, do validation of non-
running segments in CL-collection
diff --git a/c8/stm/commitlog.c b/c8/stm/commitlog.c
--- a/c8/stm/commitlog.c
+++ b/c8/stm/commitlog.c
@@ -25,7 +25,13 @@
__sync_add_and_fetch(&cle_allocated, add_or_remove);
}
-uint64_t _stm_cle_allocated(void) {
+static bool is_cle_collection_requested(void)
+{
+ return cle_allocated > CLE_COLLECT_BOUND;
+}
+
+uint64_t _stm_cle_allocated(void)
+{
return cle_allocated;
}
@@ -53,6 +59,7 @@
static void free_cle(struct stm_commit_log_entry_s *e)
{
+ dprintf(("free_cle(%p): written=%lu\n", e, e->written_count));
size_t byte_len = sizeof(struct stm_commit_log_entry_s) +
e->written_count * sizeof(struct stm_undo_s);
add_cle_allocated(-byte_len);
@@ -85,3 +92,110 @@
}
}
}
+
+
+
+void free_commit_log_entries_up_to(struct stm_commit_log_entry_s *end)
+{
+ struct stm_commit_log_entry_s *cl, *next;
+
+ cl = &commit_log_root;
+ assert(cl->next != NULL && cl->next != INEV_RUNNING);
+ assert(end->next != NULL && end->next != INEV_RUNNING);
+ assert(cl != end);
+
+ uint64_t rev_num = -1;
+ next = cl->next; /* guaranteed to exist */
+ do {
+ cl = next;
+ rev_num = cl->rev_num;
+
+ /* free bk copies of entries: */
+ long count = cl->written_count;
+ while (count-->0) {
+ free_bk(&cl->written[count]);
+ }
+
+ next = cl->next;
+ free_cle(cl);
+
+ assert(next != INEV_RUNNING);
+ if (cl == end) {
+ /* was the last one to free */
+ break;
+ }
+ } while (next != NULL);
+
+ /* set the commit_log_root to the last, common cl entry: */
+ commit_log_root.next = next;
+ commit_log_root.rev_num = rev_num;
+}
+
+void maybe_collect_commit_log()
+{
+ /* XXX: maybe use other lock, but right now we must make sure
+ that we do not run a major GC in parallel, since we do a
+ validate in some segments. */
+ assert(_has_mutex());
+
+ if (!is_cle_collection_requested())
+ return;
+
+ /* do validation in segments with no threads running, as some
+ of them may rarely run a thread and thus rarely advance in
+ the commit log. */
+ int original_num = STM_SEGMENT->segment_num;
+ for (long i = 0; i < NB_SEGMENTS; i++) {
+ struct stm_priv_segment_info_s *pseg = get_priv_segment(i);
+
+ if (pseg->pub.running_thread == NULL) {
+ assert(pseg->transaction_state == TS_NONE);
+
+ set_gs_register(get_segment_base(i));
+ bool ok = _stm_validate();
+ OPT_ASSERT(IMPLY(STM_PSEGMENT->transaction_state != TS_NONE,
+ ok));
+ /* only TS_NONE segments may have stale read-marker data
+ that reports a conflict here. but it is fine to ignore it. */
+ }
+ }
+ set_gs_register(get_segment_base(original_num));
+
+
+ /* look for the last common commit log entry. However,
+ don't free the last common one, as it may still be
+ used by running threads. */
+ struct stm_commit_log_entry_s *cl, *next;
+
+ cl = &commit_log_root;
+ next = cl;
+ do {
+ bool found = false;
+ for (int i = 0; i < NB_SEGMENTS; i++) {
+ struct stm_commit_log_entry_s *tmp;
+ tmp = get_priv_segment(i)->last_commit_log_entry;
+ if (next == tmp) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ /* cl is the last one to consider */
+ break;
+ } else {
+ cl = next;
+ }
+ } while ((next = cl->next) && next != INEV_RUNNING);
+
+
+ if (cl == &commit_log_root) {
+ dprintf(("WARN: triggered CLE collection w/o anything to do\n"));
+ return; /* none found that is newer */
+ }
+
+ /* cl should never be the last (common) one */
+ assert(cl->next != NULL && cl->next != INEV_RUNNING);
+
+ free_commit_log_entries_up_to(cl);
+}
diff --git a/c8/stm/commitlog.h b/c8/stm/commitlog.h
--- a/c8/stm/commitlog.h
+++ b/c8/stm/commitlog.h
@@ -1,7 +1,11 @@
/* when to trigger a CLE collection */
-#define CLE_COLLECT_BOUND (1*1024*1024) /* 1 MiB */
+#ifndef STM_TESTS
+#define CLE_COLLECT_BOUND (30*1024*1024) /* 30 MiB */
+#else
+#define CLE_COLLECT_BOUND 0 /* ASAP! */
+#endif
/* Commit Log things */
@@ -37,6 +41,8 @@
static struct stm_commit_log_entry_s commit_log_root;
+static bool is_cle_collection_requested(void);
+
static char *malloc_bk(size_t bk_size);
static void free_bk(struct stm_undo_s *undo);
static struct stm_commit_log_entry_s *malloc_cle(long entries);
diff --git a/c8/stm/gcpage.c b/c8/stm/gcpage.c
--- a/c8/stm/gcpage.c
+++ b/c8/stm/gcpage.c
@@ -573,62 +573,6 @@
_stm_smallmalloc_sweep();
}
-static void clean_up_commit_log_entries()
-{
- struct stm_commit_log_entry_s *cl, *next;
-
-#ifndef NDEBUG
- /* check that all segments are at the same revision: */
- cl = get_priv_segment(0)->last_commit_log_entry;
- for (long i = 0; i < NB_SEGMENTS; i++) {
- struct stm_priv_segment_info_s *pseg = get_priv_segment(i);
- assert(pseg->last_commit_log_entry == cl);
- }
-#endif
-
- /* if there is only one element, we don't have to do anything: */
- cl = &commit_log_root;
-
- if (cl->next == NULL || cl->next == INEV_RUNNING) {
- assert(get_priv_segment(0)->last_commit_log_entry == cl);
- return;
- }
-
- bool was_inev = false;
- uint64_t rev_num = -1;
-
- next = cl->next; /* guaranteed to exist */
- do {
- cl = next;
- rev_num = cl->rev_num;
-
- /* free bk copies of entries: */
- long count = cl->written_count;
- while (count-->0) {
- free_bk(&cl->written[count]);
- }
-
- next = cl->next;
- free_cle(cl);
- if (next == INEV_RUNNING) {
- was_inev = true;
- break;
- }
- } while (next != NULL);
-
- /* set the commit_log_root to the last, common cl entry: */
- commit_log_root.next = was_inev ? INEV_RUNNING : NULL;
- commit_log_root.rev_num = rev_num;
-
- /* update in all segments: */
- for (long i = 0; i < NB_SEGMENTS; i++) {
- get_priv_segment(i)->last_commit_log_entry = &commit_log_root;
- }
-
- assert(_stm_count_cl_entries() == 0);
-}
-
-
static void major_collection_now_at_safe_point(void)
{
@@ -644,37 +588,6 @@
dprintf((" | commit log entries before: %ld\n",
_stm_count_cl_entries()));
-
- /* free all commit log entries. all segments are on the most recent
- revision now. */
- uint64_t allocd_before = pages_ctl.total_allocated;
- clean_up_commit_log_entries();
- /* check if freeing the log entries actually freed a considerable
- amount itself. Then we don't want to also trace the whole heap
- and just leave major gc right here.
- The problem is apparent from raytrace.py, but may disappear if
- we have card marking that also reduces the size of commit log
- entries */
- if ((pages_ctl.total_allocated < pages_ctl.total_allocated_bound)
- && (allocd_before - pages_ctl.total_allocated > 0.3 * allocd_before)) {
- /* 0.3 should mean that we are at about 50% of the way to the
- allocated_bound again */
-#ifndef STM_TESTS
- /* we freed a considerable amount just by freeing commit log entries */
- pages_ctl.major_collection_requested = false; // reset_m_gc_requested
-
- dprintf(("STOP AFTER FREEING CL ENTRIES: -%ld\n",
- (long)(allocd_before - pages_ctl.total_allocated)));
- dprintf((" | used after collection: %ld\n",
- (long)pages_ctl.total_allocated));
- dprintf((" `----------------------------------------------\n"));
- if (must_abort())
- abort_with_mutex();
-
- return;
-#endif
- }
-
/* only necessary because of assert that fails otherwise (XXX) */
acquire_all_privatization_locks();
diff --git a/c8/stm/sync.c b/c8/stm/sync.c
--- a/c8/stm/sync.c
+++ b/c8/stm/sync.c
@@ -166,6 +166,10 @@
assert(sync_ctl.in_use1[tl->last_associated_segment_num] == 1);
sync_ctl.in_use1[tl->last_associated_segment_num] = 0;
+
+ /* check for cle collection here after we released the segment
+ while we still hold the mutex */
+ maybe_collect_commit_log();
}
__attribute__((unused))
diff --git a/c8/test/support.py b/c8/test/support.py
--- a/c8/test/support.py
+++ b/c8/test/support.py
@@ -533,7 +533,6 @@
def stm_major_collect():
res = lib._check_stm_collect(1)
- assert count_commit_log_entries() == 0
if res == 1:
raise Conflict()
return res
diff --git a/c8/test/test_basic.py b/c8/test/test_basic.py
--- a/c8/test/test_basic.py
+++ b/c8/test/test_basic.py
@@ -40,7 +40,7 @@
self.commit_transaction()
assert last_commit_log_entry_objs() == []
- assert count_commit_log_entries() == 2
+ assert count_commit_log_entries() == 1
def test_simple_read(self):
self.start_transaction()
diff --git a/c8/test/test_gcpage.py b/c8/test/test_gcpage.py
--- a/c8/test/test_gcpage.py
+++ b/c8/test/test_gcpage.py
@@ -150,7 +150,8 @@
assert last_commit_log_entry_objs() == []
# 2 CLEs, 1 old object
assert lib._stm_total_allocated() == 5008 + LMO
- assert lib._stm_cle_allocated() == 2*CLEO
+ # however, on commit, we could free 1/2 CLE
+ assert lib._stm_cle_allocated() == 1*CLEO
self.start_transaction()
o = self.pop_root()
@@ -158,18 +159,18 @@
self.push_root(o)
self.commit_transaction()
assert last_commit_log_entry_objs() == [o]*2
- # 3 CLEs, 1 old object
+ # 2 CLEs, 1 old object
# also, 2 slices of bk_copy and thus 2 CLE entries
assert lib._stm_total_allocated() == 5008+LMO
- assert lib._stm_cle_allocated() == 3*CLEO + (5008 + CLEEO*2)
+ # however, on commit, we could free 1/2 CLE
+ assert lib._stm_cle_allocated() == 1*CLEO + (5008 + CLEEO*2)
self.start_transaction()
assert lib._stm_total_allocated() == 5008+LMO
- assert lib._stm_cle_allocated() == 3*CLEO + (5008 + CLEEO*2)
+ assert lib._stm_cle_allocated() == 1*CLEO + (5008 + CLEEO*2)
stm_major_collect()
- # all CLE and CLE entries freed:
assert lib._stm_total_allocated() == (5008+LMO)
- assert lib._stm_cle_allocated() == 0
+ assert lib._stm_cle_allocated() == 1*CLEO + (5008 + CLEEO*2)
self.commit_transaction()
@@ -456,14 +457,16 @@
self.start_transaction()
stm_major_collect()
self.commit_transaction()
+ assert count_commit_log_entries() == 1
self.switch(1)
self.start_transaction()
self.commit_transaction()
+ assert count_commit_log_entries() == 1
+ py.test.xfail("XXX: we never completely free the CLEs anymore")
self.switch(0)
self.start_transaction()
- assert count_commit_log_entries() == 2
stm_major_collect()
assert count_commit_log_entries() == 0
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit