[pypy-commit] stmgc c7-refactor: Carefully synchronize the threads in order to run a minor collection

arigo noreply at buildbot.pypy.org
Sun Feb 16 15:30:16 CET 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: c7-refactor
Changeset: r754:4fa29629edfa
Date: 2014-02-16 15:30 +0100
http://bitbucket.org/pypy/stmgc/changeset/4fa29629edfa/

Log:	Carefully synchronize the threads in order to run a minor collection

diff --git a/c7/stm/contention.c b/c7/stm/contention.c
--- a/c7/stm/contention.c
+++ b/c7/stm/contention.c
@@ -38,7 +38,7 @@
     }
     else if (wait) {
         /* otherwise, we will issue a safe point and wait: */
-        STM_PSEGMENT->safe_point = SP_SAFE_POINT;
+        STM_PSEGMENT->safe_point = SP_SAFE_POINT_CANNOT_COLLECT;
 
         /* signal the other thread; it must abort */
         cond_broadcast();
diff --git a/c7/stm/core.c b/c7/stm/core.c
--- a/c7/stm/core.c
+++ b/c7/stm/core.c
@@ -131,6 +131,10 @@
 {
     long remote_num = 1 - STM_SEGMENT->segment_num;
     while (get_priv_segment(remote_num)->safe_point == SP_RUNNING) {
+
+        /* we have the mutex here */
+        get_segment(remote_num)->nursery_section_end = NSE_SIGNAL;
+
         cond_wait();
     }
 }
diff --git a/c7/stm/core.h b/c7/stm/core.h
--- a/c7/stm/core.h
+++ b/c7/stm/core.h
@@ -60,12 +60,13 @@
     uint8_t write_lock_num;
     uint8_t safe_point;         /* one of the SP_xxx constants */
     uint8_t transaction_state;  /* one of the TS_xxx constants */
+    uintptr_t real_nursery_section_end;
 };
 
 enum {
     SP_NO_TRANSACTION=0,
     SP_RUNNING,
-    SP_SAFE_POINT,
+    SP_SAFE_POINT_CANNOT_COLLECT,
     SP_SAFE_POINT_CAN_COLLECT,
 };
 enum {
diff --git a/c7/stm/nursery.c b/c7/stm/nursery.c
--- a/c7/stm/nursery.c
+++ b/c7/stm/nursery.c
@@ -25,6 +25,7 @@
 
 /************************************************************/
 
+
 static union {
     struct {
         uint64_t used;    /* number of bytes from the nursery used so far */
@@ -32,6 +33,10 @@
     char reserved[64];
 } nursery_ctl __attribute__((aligned(64)));
 
+static uint64_t requested_minor_collections = 0;
+static uint64_t completed_minor_collections = 0;
+
+
 /************************************************************/
 
 static void setup_nursery(void)
@@ -50,32 +55,142 @@
 }
 
 
+/************************************************************/
+
+
+static void minor_collection(void)
+{
+    fprintf(stderr, "minor_collection\n");
+    abort(); //...;
+
+    assert(requested_minor_collections == completed_minor_collections + 1);
+    completed_minor_collections += 1;
+    nursery_ctl.used = 0;
+}
+
+
+static void sync_point_for_collection(void)
+{
+    mutex_lock();
+
+    STM_PSEGMENT->safe_point = SP_SAFE_POINT_CAN_COLLECT;
+
+ restart:
+    if (requested_minor_collections == completed_minor_collections) {
+        if (nursery_ctl.used < NURSERY_SIZE)
+            goto exit;
+
+        requested_minor_collections++;
+    }
+
+    /* are all threads in a safe-point? */
+    long i;
+    bool must_wait = false;
+    for (i = 0; i < NB_SEGMENTS; i++) {
+        struct stm_priv_segment_info_s *other_pseg = get_priv_segment(i);
+
+        if (other_pseg->safe_point != SP_NO_TRANSACTION &&
+            other_pseg->safe_point != SP_SAFE_POINT_CAN_COLLECT) {
+            /* segment i is not at a safe point, or at one where
+               collection is not possible (SP_SAFE_POINT_CANNOT_COLLECT) */
+
+            /* we have the mutex here */
+            other_pseg->pub.nursery_section_end = NSE_SIGNAL;
+            must_wait = true;
+        }
+    }
+    if (must_wait) {
+        /* wait until all threads are indeed in a safe-point that allows
+           collection */
+        cond_wait();
+        goto restart;
+    }
+
+    /* now we can run minor collection */
+    minor_collection();
+
+ exit:
+    /* we have the mutex here, and at this point there is no
+       pending requested minor collection, so we simply reset
+       our value of nursery_section_end and return. */
+    STM_SEGMENT->nursery_section_end =
+        STM_PSEGMENT->real_nursery_section_end;
+
+    STM_PSEGMENT->safe_point = SP_RUNNING;
+
+    mutex_unlock();
+}
+
+
+/************************************************************/
+
 #define NURSERY_ALIGN(bytes)  \
     (((bytes) + NURSERY_LINE - 1) & ~(NURSERY_LINE - 1))
 
 static stm_char *allocate_from_nursery(uint64_t bytes)
 {
+    /* may collect! */
     /* thread-safe; allocate a chunk of memory from the nursery */
     bytes = NURSERY_ALIGN(bytes);
-    uint64_t p = __sync_fetch_and_add(&nursery_ctl.used, bytes);
-    if (p + bytes > NURSERY_SIZE) {
-        //major_collection();
-        abort();
+    while (1) {
+        uint64_t p = __sync_fetch_and_add(&nursery_ctl.used, bytes);
+        if (LIKELY(p + bytes <= NURSERY_SIZE)) {
+            return (stm_char *)(NURSERY_START + p);
+        }
+        sync_point_for_collection();
     }
-    return (stm_char *)(NURSERY_START + p);
 }
 
 
 stm_char *_stm_allocate_slowpath(ssize_t size_rounded_up)
 {
+    /* may collect! */
+    STM_SEGMENT->nursery_current -= size_rounded_up;  /* restore correct val */
+
+ restart:
+    if (UNLIKELY(STM_SEGMENT->nursery_section_end == NSE_SIGNAL)) {
+
+        /* If nursery_section_end was set to NSE_SIGNAL by another thread,
+           we end up here as soon as we try to call stm_allocate(). */
+        sync_point_for_collection();
+
+        /* Once the sync point is done, retry. */
+        goto restart;
+    }
+
     if (size_rounded_up < MEDIUM_OBJECT) {
-        /* This is a small object.  The current section is simply full.
+        /* This is a small object.  We first try to check if the current
+           section really doesn't fit the object; maybe all we were called
+           for was the sync point above */
+        stm_char *p1 = STM_SEGMENT->nursery_current;
+        stm_char *end1 = p1 + size_rounded_up;
+        if ((uintptr_t)end1 <= STM_PSEGMENT->real_nursery_section_end) {
+            /* fits */
+            STM_SEGMENT->nursery_current = end1;
+            return p1;
+        }
+
+        /* Otherwise, the current section is really full.
            Allocate the next section and initialize it with zeroes. */
         stm_char *p = allocate_from_nursery(NURSERY_SECTION_SIZE);
+        STM_SEGMENT->nursery_current = p + size_rounded_up;
+
+        /* Set nursery_section_end, but carefully: another thread may
+           have forced it to be equal to NSE_SIGNAL. */
+        uintptr_t end = (uintptr_t)p + NURSERY_SECTION_SIZE;
+
+        if (UNLIKELY(!__sync_bool_compare_and_swap(
+               &STM_SEGMENT->nursery_section_end,
+               STM_PSEGMENT->real_nursery_section_end,
+               end))) {
+            assert(STM_SEGMENT->nursery_section_end == NSE_SIGNAL);
+            goto restart;
+        }
+
+        STM_PSEGMENT->real_nursery_section_end = end;
+
         memset(REAL_ADDRESS(STM_SEGMENT->segment_base, p), 0,
                NURSERY_SECTION_SIZE);
-        STM_SEGMENT->nursery_current = p + size_rounded_up;
-        STM_SEGMENT->nursery_section_end = (uintptr_t)p + NURSERY_SECTION_SIZE;
 
         /* Also fill the corresponding creation markers with 0xff. */
         set_creation_markers(p, NURSERY_SECTION_SIZE,
@@ -109,9 +224,18 @@
     c = NURSERY_ALIGN(c);
     STM_SEGMENT->nursery_current = (stm_char *)c;
 
-    uint64_t size = STM_SEGMENT->nursery_section_end - c;
+    uint64_t size = STM_PSEGMENT->real_nursery_section_end - c;
     if (size > 0) {
         set_creation_markers((stm_char *)c, size,
                              CM_CURRENT_TRANSACTION_IN_NURSERY);
     }
 }
+
+#ifdef STM_TESTS
+void _stm_set_nursery_free_count(uint64_t free_count)
+{
+    assert(free_count == NURSERY_ALIGN(free_count));
+    assert(nursery_ctl.used <= NURSERY_SIZE - free_count);
+    nursery_ctl.used = NURSERY_SIZE - free_count;
+}
+#endif
diff --git a/c7/stm/nursery.h b/c7/stm/nursery.h
--- a/c7/stm/nursery.h
+++ b/c7/stm/nursery.h
@@ -1,2 +1,4 @@
+
+#define NSE_SIGNAL   1
 
 static void align_nursery_at_transaction_start(void);
diff --git a/c7/stm/pages.c b/c7/stm/pages.c
--- a/c7/stm/pages.c
+++ b/c7/stm/pages.c
@@ -8,7 +8,7 @@
     /* call remap_file_pages() to make all pages in the
        range(pagenum, pagenum+count) refer to the same
        physical range of pages from segment 0 */
-    long i;
+    uintptr_t i;
     for (i = 1; i < NB_SEGMENTS; i++) {
         char *segment_base = get_segment_base(i);
         int res = remap_file_pages(segment_base + pagenum * 4096UL,
diff --git a/c7/stm/sync.c b/c7/stm/sync.c
--- a/c7/stm/sync.c
+++ b/c7/stm/sync.c
@@ -67,6 +67,9 @@
 
 static inline void mutex_unlock(void)
 {
+    assert(STM_PSEGMENT->safe_point == SP_NO_TRANSACTION ||
+           STM_PSEGMENT->safe_point == SP_RUNNING);
+
     if (UNLIKELY(pthread_mutex_unlock(&sync_ctl.global_mutex) != 0)) {
         perror("pthread_mutex_unlock");
         abort();
diff --git a/c7/stmgc.h b/c7/stmgc.h
--- a/c7/stmgc.h
+++ b/c7/stmgc.h
@@ -62,7 +62,8 @@
     int segment_num;
     char *segment_base;
     stm_char *nursery_current;
-    uintptr_t nursery_section_end;
+    uintptr_t nursery_section_end;  /* forced to 1 by
+                                       sync_all_threads_for_collection() */
     struct stm_thread_local_s *running_thread;
     stm_jmpbuf_t *jmpbuf_ptr;
 };
@@ -96,6 +97,7 @@
 void _stm_large_dump(void);
 void _stm_start_safe_point(void);
 void _stm_stop_safe_point(void);
+void _stm_set_nursery_free_count(uint64_t free_count);
 #endif
 
 #define _STM_GCFLAG_WRITE_BARRIER_CALLED  0x80
diff --git a/c7/test/support.py b/c7/test/support.py
--- a/c7/test/support.py
+++ b/c7/test/support.py
@@ -69,6 +69,8 @@
 
 void _stm_start_safe_point(void);
 bool _check_stop_safe_point(void);
+
+void _stm_set_nursery_free_count(uint64_t free_count);
 """)
 
 
diff --git a/c7/test/test_nursery.py b/c7/test/test_nursery.py
--- a/c7/test/test_nursery.py
+++ b/c7/test/test_nursery.py
@@ -49,3 +49,15 @@
         self.commit_transaction()
         assert stm_creation_marker(lp1) == 0
         assert stm_creation_marker(lp2) == 0
+
+    def test_nursery_full(self):
+        lib._stm_set_nursery_free_count((SOME_MEDIUM_SIZE + 255) & ~255)
+        self.push_root_no_gc()
+        self.start_transaction()
+        lp1 = stm_allocate(SOME_MEDIUM_SIZE)
+        self.pop_root()
+        #
+        self.push_root(lp1)
+        lp2 = stm_allocate(16)
+        lp1b = self.pop_root()
+        assert lp1b != lp1      # collection occurred


More information about the pypy-commit mailing list