Author: Armin Rigo <ar...@tunes.org>
Branch: c7-refactor
Changeset: r821:976dbb25fe76
Date: 2014-02-24 12:09 +0100
http://bitbucket.org/pypy/stmgc/changeset/976dbb25fe76/

Log:    progress

diff --git a/c7/stm/core.c b/c7/stm/core.c
--- a/c7/stm/core.c
+++ b/c7/stm/core.c
@@ -49,7 +49,6 @@
         /* We need to privatize the pages containing the object, if they
            are still SHARED_PAGE.  The common case is that there is only
            one page in total. */
-        size_t obj_size = 0;
         uintptr_t first_page = ((uintptr_t)obj) / 4096UL;
 
         /* If the object is in the uniform pages of small objects
@@ -60,12 +59,16 @@
             pages_privatize(first_page, 1, true);
         }
         else {
+            char *realobj;
+            size_t obj_size;
+            uintptr_t end_page;
+
             /* get the size of the object */
-            obj_size = stmcb_size_rounded_up(
-              (struct object_s *)REAL_ADDRESS(STM_SEGMENT->segment_base, obj));
+            realobj = REAL_ADDRESS(STM_SEGMENT->segment_base, obj);
+            obj_size = stmcb_size_rounded_up((struct object_s *)realobj);
 
             /* that's the page *following* the last page with the object */
-            uintptr_t end_page = (((uintptr_t)obj) + obj_size + 4095) / 4096UL;
+            end_page = (((uintptr_t)obj) + obj_size + 4095) / 4096UL;
 
             pages_privatize(first_page, end_page - first_page, true);
         }
@@ -265,19 +268,16 @@
 
 void stm_commit_transaction(void)
 {
-    minor_collection();
+    assert(!_has_mutex());
+    assert(STM_PSEGMENT->safe_point == SP_RUNNING);
+
+    minor_collection(/*commit=*/ true);
 
     mutex_lock();
-
-    assert(STM_PSEGMENT->safe_point = SP_RUNNING);
     STM_PSEGMENT->safe_point = SP_SAFE_POINT_CAN_COLLECT;
 
- restart:
-    abort_if_needed();
-
     /* wait until the other thread is at a safe-point */
-    if (!try_wait_for_other_safe_points(SP_SAFE_POINT_CANNOT_COLLECT))
-        goto restart;
+    wait_for_other_safe_points(SP_SAFE_POINT_CANNOT_COLLECT);
 
     /* the rest of this function runs either atomically without releasing
        the mutex, or it needs to restart. */
diff --git a/c7/stm/core.h b/c7/stm/core.h
--- a/c7/stm/core.h
+++ b/c7/stm/core.h
@@ -96,10 +96,16 @@
        "this segment has modified this old object". */
     uint8_t write_lock_num;
 
-    /* The thread's safe-point state, one of the SP_xxx constants */
+    /* The thread's safe-point state, one of the SP_xxx constants.  The
+       thread is in a "safe point" if it is not concurrently doing any
+       change that might cause race conditions in other threads.  A
+       thread may enter but not *leave* the safe point it is in without
+       getting hold of the mutex.  Broadly speaking, any value other
+       than SP_RUNNING means a safe point of some kind. */
     uint8_t safe_point;
 
-    /* The transaction status, one of the TS_xxx constants */
+    /* The transaction status, one of the TS_xxx constants.  This is
+       only accessed when we hold the mutex. */
     uint8_t transaction_state;
 
     /* In case of abort, we restore the 'shadowstack' field. */
diff --git a/c7/stm/gcpage.c b/c7/stm/gcpage.c
--- a/c7/stm/gcpage.c
+++ b/c7/stm/gcpage.c
@@ -12,7 +12,7 @@
     largemalloc_init_arena(base, length);
 
     uninitialized_page_start = stm_object_pages + END_NURSERY_PAGE * 4096UL;
-    uninitialized_page_stop  = stm_object_pages + (NB_PAGES - 1) * 4096UL;
+    uninitialized_page_stop  = stm_object_pages + NB_PAGES * 4096UL;
 
     assert(GC_MEDIUM_REQUEST >= (1 << 8));
 }
@@ -70,31 +70,31 @@
 }
 
 
-#if 0
-static char *allocate_outside_nursery_large(uint64_t size)
+static object_t *allocate_outside_nursery_large(uint64_t size)
 {
-    abort(); //XXX review
-    /* not thread-safe!  Use only when holding the mutex */
-    assert(_has_mutex());
+    /* thread-safe: use the lock of pages.c to prevent any remapping
+       from occurring under our feet */
+    mutex_pages_lock();
 
-    /* Allocate the object with largemalloc.c from the lower addresses.
-       Assumes that 'size' is at least 256 bytes; it's needed for
-       the creation marker to uniquely identify this object */
-    OPT_ASSERT(size >= (1 << 8));
-    OPT_ASSERT((size & 7) == 0);
-
+    /* Allocate the object with largemalloc.c from the lower addresses. */
     char *addr = large_malloc(size);
 
     if (addr + size > uninitialized_page_start) {
         uintptr_t npages;
         npages = (addr + size - uninitialized_page_start) / 4096UL;
         npages += GCPAGE_NUM_PAGES;
+        if (uninitialized_page_stop - uninitialized_page_start <
+                npages * 4096UL) {
+            stm_fatalerror("out of memory!\n");   /* XXX */
+        }
         setup_N_pages(uninitialized_page_start, npages);
         uninitialized_page_start += npages * 4096UL;
     }
-    return addr;
+
+    mutex_pages_unlock();
+
+    return (object_t *)(addr - stm_object_pages);
 }
-#endif
 
 object_t *_stm_allocate_old(ssize_t size_rounded_up)
 {
diff --git a/c7/stm/gcpage.h b/c7/stm/gcpage.h
--- a/c7/stm/gcpage.h
+++ b/c7/stm/gcpage.h
@@ -27,7 +27,7 @@
 
 static void setup_gcpage(void);
 static void teardown_gcpage(void);
-//static char *allocate_outside_nursery_large(uint64_t size);
+static object_t *allocate_outside_nursery_large(uint64_t size);
 
 
 static char *_allocate_small_slowpath(uint64_t size);
diff --git a/c7/stm/nursery.c b/c7/stm/nursery.c
--- a/c7/stm/nursery.c
+++ b/c7/stm/nursery.c
@@ -47,13 +47,6 @@
     return _is_in_nursery(obj);
 }
 
-#if 0
-static bool _is_young(object_t *obj)
-{
-    return _is_in_nursery(obj);    /* for now */
-}
-#endif
-
 
 /************************************************************/
 
@@ -74,122 +67,46 @@
     }
 }
 
-#if 0
 static void minor_trace_if_young(object_t **pobj)
 {
-    abort(); //...
     /* takes a normal pointer to a thread-local pointer to an object */
     object_t *obj = *pobj;
     if (obj == NULL)
         return;
-    if (!_is_young(obj))
+    if (!_is_in_nursery(obj))
         return;
 
     /* If the object was already seen here, its first word was set
        to GCWORD_MOVED.  In that case, the forwarding location, i.e.
        where the object moved to, is stored in the second word in 'obj'. */
-    char *realobj = (char *)REAL_ADDRESS(stm_object_pages, obj);
-    object_t **pforwarded_array = (object_t **)realobj;
+    object_t *TLPREFIX *pforwarded_array = (object_t *TLPREFIX *)obj;
 
     if (pforwarded_array[0] == GCWORD_MOVED) {
         *pobj = pforwarded_array[1];    /* already moved */
         return;
     }
 
-    /* We need to make a copy of this object.  There are three different
-       places where the copy can be located, based on four criteria.
-
-       1) object larger than GC_MEDIUM_REQUEST        => largemalloc.c
-       2) otherwise, object from current transaction  => page S
-       3) otherwise, object with the write lock       => page W
-       4) otherwise, object without the write lock    => page S
-
-       The pages S or W above are both pages of uniform sizes obtained
-       from the end of the address space.  The difference is that page S
-       can be shared, but page W needs to be privatized.  Moreover,
-       cases 2 and 4 differ in the creation_marker they need to put,
-       which has a granularity of 256 bytes.
+    /* We need to make a copy of this object.  It goes either in
+       a largemalloc.c-managed area, or if it's small enough, in
+       one of the small uniform pages from gcpage.c.
     */
+    char *realobj = REAL_ADDRESS(STM_SEGMENT->segment_base, obj);
     size_t size = stmcb_size_rounded_up((struct object_s *)realobj);
-    uintptr_t lock_idx = (((uintptr_t)obj) >> 4) - WRITELOCK_START;
-    uint8_t write_lock = write_locks[lock_idx];
     object_t *nobj;
-    long i;
 
     if (1 /*size >= GC_MEDIUM_REQUEST*/) {
 
-        /* case 1: object is larger than GC_MEDIUM_REQUEST.
+        /* case 1: object is not small enough.
            Ask gcpage.c for an allocation via largemalloc. */
-        char *copyobj;
-        copyobj = allocate_outside_nursery_large(size < 256 ? 256 : size);  // 
XXX temp
+        nobj = allocate_outside_nursery_large(size);
 
-        /* Copy the object to segment 0 (as a first step) */
-        memcpy(copyobj, realobj, size);
-        ((struct object_s *)copyobj)->stm_flags |= GCFLAG_WRITE_BARRIER_CALLED;
-
-        nobj = (object_t *)(copyobj - stm_object_pages);
-
-        if (write_lock == 0) {
-            /* The object is not write-locked, so any copy should be
-               identical.  Now some pages of the destination might be
-               private already (because of some older action); if so, we
-               need to replicate the corresponding parts.  The hope is
-               that it's relatively uncommon. */
-            uintptr_t p, pend = ((uintptr_t)(copyobj + size - 1)) & ~4095;
-            for (p = (uintptr_t)copyobj; p < pend; p = (p + 4096) & ~4095) {
-                minor_copy_in_page_to_other_segments(p, 4096 - (p & 4095));
-            }
-            minor_copy_in_page_to_other_segments(p, ((size-1) & 4095) + 1);
-        }
-        else {
-            /* The object has the write lock.  We need to privatize the
-               pages, and repeat the write lock in the new copy. */
-            uintptr_t dataofs = (uintptr_t)nobj;
-            uintptr_t pagenum = dataofs / 4096UL;
-            uintptr_t lastpage= (dataofs + size - 1) / 4096UL;
-            pages_privatize(pagenum, lastpage - pagenum + 1, false);
-
-            lock_idx = (dataofs >> 4) - WRITELOCK_START;
-            assert(write_locks[lock_idx] == 0);
-            write_locks[lock_idx] = write_lock;
-
-            /* Then, for each segment > 0, we need to repeat the
-               memcpy() done above.  XXX This could be optimized if
-               NB_SEGMENTS > 2 by reading all non-written copies from the
-               same segment, instead of reading really all segments. */
-            for (i = 1; i < NB_SEGMENTS; i++) {
-                uintptr_t diff = get_segment_base(i) - stm_object_pages;
-                memcpy(copyobj + diff, realobj + diff, size);
-                ((struct object_s *)(copyobj + diff))->stm_flags |=
-                    GCFLAG_WRITE_BARRIER_CALLED;
-            }
-        }
-
-        /* If the source creation marker is CM_CURRENT_TRANSACTION_IN_NURSERY,
-           write CM_CURRENT_TRANSACTION_OUTSIDE_NURSERY in the destination */
-        uintptr_t cmaddr = ((uintptr_t)obj) >> 8;
-
-        for (i = 0; i < NB_SEGMENTS; i++) {
-            char *absaddr = get_segment_base(i) + cmaddr;
-            if (((struct stm_creation_marker_s *)absaddr)->cm != 0) {
-                uintptr_t ncmaddr = ((uintptr_t)nobj) >> 8;
-                absaddr = get_segment_base(i) + ncmaddr;
-                ((struct stm_creation_marker_s *)absaddr)->cm =
-                    CM_CURRENT_TRANSACTION_OUTSIDE_NURSERY;
-            }
-        }
+        /* Copy the object  */
+        char *realnobj = REAL_ADDRESS(STM_SEGMENT->segment_base, nobj);
+        memcpy(realnobj, realobj, size);
     }
     else {
-        /* cases 2 to 4 */
+        /* case "small enough" */
         abort();  //...
-        allocate_outside_nursery_small(small_alloc_shared, size);
-        allocate_outside_nursery_small(small_alloc_privtz, size);
-    }
-
-    /* Copy the read markers */
-    for (i = 0; i < NB_SEGMENTS; i++) {
-        uint8_t rm = get_segment_base(i)[((uintptr_t)obj) >> 4];
-        get_segment_base(i)[((uintptr_t)nobj) >> 4] = rm;
     }
 
     /* Done copying the object. */
@@ -199,173 +116,87 @@
     *pobj = nobj;
 
     /* Must trace the object later */
-    LIST_APPEND(old_objects_pointing_to_young, nobj);
+    LIST_APPEND(STM_PSEGMENT->old_objects_pointing_to_nursery, nobj);
 }
 
 static void collect_roots_in_nursery(void)
 {
-    stm_thread_local_t *tl = stm_all_thread_locals;
-    do {
-        object_t **current = tl->shadowstack;
-        object_t **base = tl->shadowstack_base;
-        while (current-- != base) {
-            minor_trace_if_young(current);
-        }
-        tl = tl->next;
-    } while (tl != stm_all_thread_locals);
-}
-
-static void trace_and_drag_out_of_nursery(object_t *obj)
-{
-    long i;
-    for (i = 0; i < NB_SEGMENTS; i++) {
-        struct object_s *realobj =
-            (struct object_s *)REAL_ADDRESS(get_segment_base(i), obj);
-
-        realobj->stm_flags |= GCFLAG_WRITE_BARRIER;
-
-        stmcb_trace((struct object_s *)realobj, &minor_trace_if_young);
-
-        if (i == 0 && is_in_shared_pages(obj)) {
-            /* the object needs fixing only in one copy, because all copies
-               are shared and identical. */
-            break;
-        }
+    stm_thread_local_t *tl = STM_SEGMENT->running_thread;
+    object_t **current = tl->shadowstack;
+    object_t **base = tl->shadowstack_base;
+    while (current-- != base) {
+        minor_trace_if_young(current);
     }
 }
 
-static void collect_oldrefs_to_nursery(struct list_s *lst)
+static void collect_oldrefs_to_nursery(void)
 {
+    struct list_s *lst = STM_PSEGMENT->old_objects_pointing_to_nursery;
+
     while (!list_is_empty(lst)) {
         object_t *obj = (object_t *)list_pop_item(lst);
         assert(!_is_in_nursery(obj));
 
-        /* We must have GCFLAG_WRITE_BARRIER_CALLED so far.  If we
-           don't, it's because the same object was stored in several
-           segment's old_objects_pointing_to_young.  It's fine to
-           ignore duplicates. */
-        abort();//...
-        //if ((obj->stm_flags & GCFLAG_WRITE_BARRIER_CALLED) == 0)
-        //    continue;
-
-        /* The flag GCFLAG_WRITE_BARRIER_CALLED is going to be removed:
-           no live object should have this flag set after a nursery
-           collection.  It is done in either one or NB_SEGMENTS copies. */
+        /* We must not have GCFLAG_WRITE_BARRIER so far.  Add it now. */
+        assert(!(obj->stm_flags & GCFLAG_WRITE_BARRIER));
+        obj->stm_flags |= GCFLAG_WRITE_BARRIER;
 
         /* Trace the 'obj' to replace pointers to nursery with pointers
            outside the nursery, possibly forcing nursery objects out
-           and adding them to 'old_objects_pointing_to_young' as well. */
-        trace_and_drag_out_of_nursery(obj);
+           and adding them to 'old_objects_pointing_to_nursery' as well. */
+        char *realobj = REAL_ADDRESS(STM_SEGMENT->segment_base, obj);
+        stmcb_trace((struct object_s *)realobj, &minor_trace_if_young);
     }
 }
 
 static void reset_nursery(void)
 {
-    abort();//...
-    /* reset the global amount-of-nursery-used-so-far */
-    nursery_ctl.used = nursery_ctl.initial_value_of_used;
+    /* reset the nursery by zeroing it */
+    size_t size;
+    char *realnursery;
 
-    long i;
-    for (i = 0; i < NB_SEGMENTS; i++) {
-        struct stm_priv_segment_info_s *other_pseg = get_priv_segment(i);
-        /* no race condition here, because all other threads are paused
-           in safe points, so cannot be e.g. in _stm_allocate_slowpath() */
-        uintptr_t old_end = other_pseg->real_nursery_section_end;
-        other_pseg->real_nursery_section_end = 0;
-        other_pseg->pub.v_nursery_section_end = 0;
+    realnursery = REAL_ADDRESS(STM_SEGMENT->segment_base, _stm_nursery_start);
+    size = STM_SEGMENT->nursery_current - (stm_char *)_stm_nursery_start;
+    memset(realnursery, 0, size);
 
-        /* we don't need to actually reset the read markers, unless
-           we run too many nursery collections in the same transaction:
-           in the normal case it is enough to increase
-           'transaction_read_version' without changing
-           'min_read_version_outside_nursery'.
-        */
-        if (other_pseg->transaction_state == TS_NONE) {
-            /* no transaction running now, nothing to do */
-        }
-        else if (other_pseg->pub.transaction_read_version < 0xff) {
-            other_pseg->pub.transaction_read_version++;
-            abort();//...
-            /*assert(0 < other_pseg->min_read_version_outside_nursery &&
-                   other_pseg->min_read_version_outside_nursery
-                   < other_pseg->pub.transaction_read_version);*/
-        }
-        else {
-            /* however, when the value 0xff is reached, we are stuck
-               and we need to clean all the nursery read markers.
-               We'll be un-stuck when this transaction finishes. */
-            char *read_markers = REAL_ADDRESS(other_pseg->pub.segment_base,
-                                              NURSERY_START >> 4);
-            memset(read_markers, 0, NURSERY_SIZE >> 4);
-        }
+    STM_SEGMENT->nursery_current = (stm_char *)_stm_nursery_start;
+}
 
-        /* reset the creation markers */
-        if (old_end > NURSERY_START) {
-            char *creation_markers = REAL_ADDRESS(other_pseg->pub.segment_base,
-                                                  NURSERY_START >> 8);
-            assert(old_end <= NURSERY_END);
-            memset(creation_markers, 0, (old_end - NURSERY_START) >> 8);
-        }
-        else {
-            assert(old_end == 0 || old_end == NURSERY_START);
-        }
-    }
-}
-#endif
-
-static void minor_collection(void)
+static void minor_collection(bool commit)
 {
     assert(!_has_mutex());
     abort_if_needed();
 
-    dprintf(("minor_collection\n"));
+    /* We must move out of the nursery any object found within the
+       nursery.  All objects touched are either from the current
+       transaction, or are from 'old_objects_pointing_to_young'.
+       In all cases, we should only read and change objects belonging
+       to the current segment.
 
-    abort();//...
-#if 0
+       XXX improve: it might be possible to run this function in
+       a safe-point but without the mutex, if we are careful
+    */
 
-    /* List of what we need to do and invariants we need to preserve
-       -------------------------------------------------------------
+    dprintf(("minor_collection commit=%d\n", (int)commit));
 
-       We must move out of the nursery any object found within the
-       nursery.  This requires either one or NB_SEGMENTS copies,
-       depending on the current write-state of the object.
-
-       We need to move the mark stored in the write_locks, read_markers
-       and creation_markers arrays.  The creation_markers need some care
-       because they work at a coarser granularity of 256 bytes, so
-       objects with an "on" mark should not be moved too close to
-       objects with an "off" mark and vice-versa.
-
-       Then we must trace (= look inside) some objects outside the
-       nursery, and fix any pointer found that goes to a nursery object.
-       This tracing itself needs to be done either once or NB_SEGMENTS
-       times, depending on whether the object is fully in shared pages
-       or not.  We assume that 'stmcb_size_rounded_up' produce the same
-       results on all copies (i.e. don't depend on modifiable
-       information).
-    */
+    if (STM_PSEGMENT->old_objects_pointing_to_nursery == NULL)
+        STM_PSEGMENT->old_objects_pointing_to_nursery = list_create();
 
     collect_roots_in_nursery();
 
-    long i;
-    for (i = 0; i < NB_SEGMENTS; i++) {
-        struct stm_priv_segment_info_s *other_pseg = get_priv_segment(i);
-        collect_oldrefs_to_nursery(other_pseg->old_objects_pointing_to_young);
-    }
-
-    collect_oldrefs_to_nursery(old_objects_pointing_to_young);
+    collect_oldrefs_to_nursery();
 
     reset_nursery();
 
-    pages_make_shared_again(FIRST_NURSERY_PAGE, NB_NURSERY_PAGES);
-#endif
+    assert(list_is_empty(STM_PSEGMENT->old_objects_pointing_to_nursery));
+    if (!commit && STM_PSEGMENT->overflow_objects_pointing_to_nursery == NULL)
+        STM_PSEGMENT->overflow_objects_pointing_to_nursery = list_create();
 }
 
-
 void stm_collect(long level)
 {
     assert(level == 0);
-    minor_collection();
+    minor_collection(/*commit=*/ false);
 }
 
 
@@ -391,7 +222,7 @@
         return (object_t *)p;
     }
 
-    minor_collection();
+    minor_collection(/*commit=*/ false);
     goto restart;
 }
 
diff --git a/c7/stm/nursery.h b/c7/stm/nursery.h
--- a/c7/stm/nursery.h
+++ b/c7/stm/nursery.h
@@ -5,4 +5,5 @@
 
 static uint32_t highest_overflow_number;
 
+static void minor_collection(bool commit);
 static void check_nursery_at_transaction_start(void) __attribute__((unused));
diff --git a/c7/stm/pages.c b/c7/stm/pages.c
--- a/c7/stm/pages.c
+++ b/c7/stm/pages.c
@@ -3,12 +3,35 @@
 #endif
 
 
+/************************************************************/
+
+static union {
+    uint8_t mutex_pages;
+    char reserved[64];
+} pages_ctl __attribute__((aligned(64)));
+
+static void mutex_pages_lock(void)
+{
+    while (__sync_lock_test_and_set(&pages_ctl.mutex_pages, 1) != 0) {
+        spin_loop();
+    }
+}
+
+static void mutex_pages_unlock(void)
+{
+    __sync_lock_release(&pages_ctl.mutex_pages);
+}
+
+/************************************************************/
+
+
 static void pages_initialize_shared(uintptr_t pagenum, uintptr_t count)
 {
     /* 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. */
     uintptr_t i;
+    mutex_pages_lock();
     for (i = 1; i < NB_SEGMENTS; i++) {
         char *segment_base = get_segment_base(i);
         int res = remap_file_pages(segment_base + pagenum * 4096UL,
@@ -21,6 +44,7 @@
     }
     for (i = 0; i < count; i++)
         flag_page_private[pagenum + i] = SHARED_PAGE;
+    mutex_pages_unlock();
 }
 
 #if 0
@@ -45,8 +69,7 @@
 }
 #endif
 
-static void privatize_range_and_unlock(uintptr_t pagenum, uintptr_t count,
-                                       bool full)
+static void privatize_range(uintptr_t pagenum, uintptr_t count, bool full)
 {
     ssize_t pgoff1 = pagenum;
     ssize_t pgoff2 = pagenum + NB_PAGES;
@@ -73,61 +96,41 @@
             pagecopy(localpg + 4096 * (count-1), otherpg + 4096 * (count-1));
     }
     write_fence();
-    for (i = 0; i < count; i++) {
-        assert(flag_page_private[pagenum + i] == REMAPPING_PAGE);
-        flag_page_private[pagenum + i] = PRIVATE_PAGE;
-    }
+    memset(flag_page_private + pagenum, PRIVATE_PAGE, count);
 }
 
 static void _pages_privatize(uintptr_t pagenum, uintptr_t count, bool full)
 {
-    uintptr_t page_start_range = pagenum;
-    uintptr_t pagestop = pagenum + count;
-
     while (flag_page_private[pagenum + count - 1] == PRIVATE_PAGE) {
         if (!--count)
             return;
     }
 
+    mutex_pages_lock();
+
+    uintptr_t page_start_range = pagenum;
+    uintptr_t pagestop = pagenum + count;
+
     for (; pagenum < pagestop; pagenum++) {
-#ifdef HAVE_FULL_EXCHANGE_INSN
-        /* use __sync_lock_test_and_set() as a cheaper alternative to
-           __sync_bool_compare_and_swap(). */
-        int prev = __sync_lock_test_and_set(&flag_page_private[pagenum],
-                                            REMAPPING_PAGE);
-        assert(prev != FREE_PAGE);
-        if (prev == PRIVATE_PAGE) {
-            flag_page_private[pagenum] = PRIVATE_PAGE;
-        }
-        bool was_shared = (prev == SHARED_PAGE);
-#else
-        bool was_shared = __sync_bool_compare_and_swap(
-                                            &flag_page_private[pagenum + cnt1],
-                                            SHARED_PAGE, REMAPPING_PAGE);
-#endif
-        if (!was_shared) {
+        uint8_t prev = flag_page_private[pagenum];
+        if (prev == SHARED_PAGE) {
             if (pagenum > page_start_range) {
-                privatize_range_and_unlock(page_start_range,
-                                           pagenum - page_start_range, full);
+                privatize_range(page_start_range,
+                                pagenum - page_start_range, full);
             }
             page_start_range = pagenum + 1;
-
-            while (1) {
-                uint8_t state;
-                state = ((uint8_t volatile *)flag_page_private)[pagenum];
-                if (state != REMAPPING_PAGE) {
-                    assert(state == PRIVATE_PAGE);
-                    break;
-                }
-                spin_loop();
-            }
+        }
+        else {
+            assert(prev == PRIVATE_PAGE);
         }
     }
 
     if (pagenum > page_start_range) {
-        privatize_range_and_unlock(page_start_range,
-                                   pagenum - page_start_range, full);
+        privatize_range(page_start_range,
+                        pagenum - page_start_range, full);
     }
+
+    mutex_pages_unlock();
 }
 
 #if 0
diff --git a/c7/stm/pages.h b/c7/stm/pages.h
--- a/c7/stm/pages.h
+++ b/c7/stm/pages.h
@@ -30,4 +30,7 @@
     _pages_privatize(pagenum, count, full);
 }
 
+static void mutex_pages_lock(void);
+static void mutex_pages_unlock(void);
+
 //static bool is_in_shared_pages(object_t *obj);
diff --git a/c7/stm/sync.c b/c7/stm/sync.c
--- a/c7/stm/sync.c
+++ b/c7/stm/sync.c
@@ -62,6 +62,9 @@
         perror("pthread_mutex_lock");
         abort();
     }
+
+    if (STM_PSEGMENT->transaction_state == TS_MUST_ABORT)
+        abort_with_mutex();
 }
 
 static inline void mutex_unlock(void)
@@ -77,7 +80,13 @@
 
 static inline bool _has_mutex(void)
 {
-    return pthread_mutex_trylock(&sync_ctl.global_mutex) == EBUSY;
+    if (pthread_mutex_trylock(&sync_ctl.global_mutex) == EBUSY) {
+        return true;
+    }
+    else {
+        pthread_mutex_unlock(&sync_ctl.global_mutex);
+        return false;
+    }
 }
 
 static inline void cond_wait(void)
@@ -197,38 +206,33 @@
 #endif
 
 
-static bool try_wait_for_other_safe_points(int requested_safe_point_kind)
+static void wait_for_other_safe_points(int requested_safe_point_kind)
 {
-    /* Must be called with the mutex.  If all other threads are in a
-       safe point of at least the requested kind, returns true.  Otherwise,
-       asks them to enter a safe point, issues a cond_wait(), and returns
-       false; you can call repeatedly this function in this case.
+    /* Must be called with the mutex.  When all other threads are in a
+       safe point of at least the requested kind, returns.  Otherwise,
+       asks them to enter a safe point, issues a cond_wait(), and wait.
 
-       When this function returns true, the other threads are all
-       blocked at safe points as requested.  They may be either in their
-       own cond_wait(), or running at SP_NO_TRANSACTION, in which case
-       they should not do anything related to stm until the next time
-       they call mutex_lock().
+       When this function returns, the other threads are all blocked at
+       safe points as requested.  They may be either in their own
+       cond_wait(), or running at SP_NO_TRANSACTION, in which case they
+       should not do anything related to stm until the next time they
+       call mutex_lock().
 
        The next time we unlock the mutex (with mutex_unlock() or
        cond_wait()), they will proceed.
 
        This function requires that the calling thread is in a safe-point
        right now, so there is no deadlock if one thread calls
-       try_wait_for_other_safe_points() while another is currently blocked
+       wait_for_other_safe_points() while another is currently blocked
        in the cond_wait() in this same function.
     */
-    abort();//...
-#if 0
+
+ restart:
     assert(_has_mutex());
     assert(STM_PSEGMENT->safe_point == SP_SAFE_POINT_CAN_COLLECT);
 
     long i;
-    bool must_wait = false;
     for (i = 0; i < NB_SEGMENTS; i++) {
-        if (i == STM_SEGMENT->segment_num)
-            continue;    /* ignore myself */
-
         /* If the other thread is SP_NO_TRANSACTION, then it can be
            ignored here: as long as we have the mutex, it will remain
            SP_NO_TRANSACTION.  If it is already at a suitable safe point,
@@ -241,32 +245,18 @@
             (requested_safe_point_kind == SP_SAFE_POINT_CAN_COLLECT &&
                 other_pseg->safe_point == SP_SAFE_POINT_CANNOT_COLLECT)) {
 
-            /* we need to wait for this thread.  Use NSE_SIGNAL to
-               ask it to enter a safe-point soon. */
-            other_pseg->pub.v_nursery_section_end = NSE_SIGNAL;
-            must_wait = true;
+            /* we need to wait for this thread.  Use NSE_SIGNAL to ask
+               it (and possibly all other threads in the same case) to
+               enter a safe-point soon. */
+            _stm_nursery_end = NSE_SIGNAL;
+            cond_wait();
+            goto restart;
         }
     }
-    if (must_wait) {
-        cond_wait();
-        return false;
-    }
 
-    /* done!  All NSE_SIGNAL threads become NSE_SIGNAL_DONE now, which
-       mean they will actually run again the next time they grab the
-       mutex. */
-    for (i = 0; i < NB_SEGMENTS; i++) {
-        if (i == STM_SEGMENT->segment_num)
-            continue;    /* ignore myself */
-
-        struct stm_segment_info_s *other_seg = get_segment(i);
-        if (other_seg->v_nursery_section_end == NSE_SIGNAL)
-            other_seg->v_nursery_section_end = NSE_SIGNAL_DONE;
-    }
+    /* all threads are at a safe-point now. */
     cond_broadcast();   /* to wake up the other threads, but later,
                            when they get the mutex again */
-    return true;
-#endif
 }
 
 void _stm_collectable_safe_point(void)
@@ -274,7 +264,7 @@
     /* 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() or do
        a call to stm_safe_point().
-       See try_wait_for_other_safe_points() for details.
+       See wait_for_other_safe_points() for details.
     */
     mutex_lock();
     assert(STM_PSEGMENT->safe_point == SP_RUNNING);
diff --git a/c7/stm/sync.h b/c7/stm/sync.h
--- a/c7/stm/sync.h
+++ b/c7/stm/sync.h
@@ -16,4 +16,4 @@
 static void release_thread_segment(stm_thread_local_t *tl);
 
 /* see the source for an exact description */
-static bool try_wait_for_other_safe_points(int requested_safe_point_kind);
+static void wait_for_other_safe_points(int requested_safe_point_kind);
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to