Author: Armin Rigo <[email protected]>
Branch: c7-refactor
Changeset: r755:2248bbaba3b2
Date: 2014-02-16 19:17 +0100
http://bitbucket.org/pypy/stmgc/changeset/2248bbaba3b2/

Log:    in-progress

diff --git a/c7/stm/contention.c b/c7/stm/contention.c
--- a/c7/stm/contention.c
+++ b/c7/stm/contention.c
@@ -5,9 +5,9 @@
 
 static void contention_management(uint8_t other_segment_num, bool wait)
 {
-    /* A simple contention manager.  Called when we do stm_write()
-       on an object, but some other thread already holds the write
-       lock on the same object. */
+    /* A simple contention manager.  Called when some other thread
+       holds the write lock on an object.  The current thread tries
+       to do either a write or a read on it. */
 
     assert_has_mutex();
     assert(other_segment_num != STM_SEGMENT->segment_num);
diff --git a/c7/stm/core.c b/c7/stm/core.c
--- a/c7/stm/core.c
+++ b/c7/stm/core.c
@@ -127,19 +127,7 @@
 # error "The logic in the functions below only works with two segments"
 #endif
 
-static void wait_for_other_safe_points(void)
-{
-    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();
-    }
-}
-
-static void detect_write_read_conflicts(void)
+static bool detect_write_read_conflicts(void)
 {
     long remote_num = 1 - STM_SEGMENT->segment_num;
     char *remote_base = get_segment_base(remote_num);
@@ -148,7 +136,7 @@
     switch (get_priv_segment(remote_num)->transaction_state) {
     case TS_NONE:
     case TS_MUST_ABORT:
-        return;    /* no need to do any check */
+        return false;    /* no need to do any check */
     }
 
     LIST_FOREACH_R(
@@ -161,9 +149,11 @@
 
                 /* If we reach this point, it means we aborted the other
                    thread.  We're done here. */
-                return;
+                return true;
             }
         }));
+
+    return false;
 }
 
 static void push_modified_to_other_segments(void)
@@ -207,8 +197,11 @@
 void stm_commit_transaction(void)
 {
     mutex_lock();
+
     assert(STM_PSEGMENT->safe_point = SP_RUNNING);
+    STM_PSEGMENT->safe_point = SP_SAFE_POINT_CAN_COLLECT;
 
+ restart:
     switch (STM_PSEGMENT->transaction_state) {
 
     case TS_REGULAR:
@@ -223,10 +216,14 @@
     }
 
     /* wait until the other thread is at a safe-point */
-    wait_for_other_safe_points();
+    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. */
 
     /* detect conflicts */
-    detect_write_read_conflicts();
+    if (UNLIKELY(detect_write_read_conflicts()))
+        goto restart;
 
     /* cannot abort any more from here */
     assert(STM_PSEGMENT->transaction_state != TS_MUST_ABORT);
@@ -237,7 +234,7 @@
 
     /* done */
     stm_thread_local_t *tl = STM_SEGMENT->running_thread;
-    release_thread_segment(tl);   /* includes the cond_broadcast(); */
+    release_thread_segment(tl);
     STM_PSEGMENT->safe_point = SP_NO_TRANSACTION;
     STM_PSEGMENT->transaction_state = TS_NONE;
     reset_all_creation_markers();
@@ -312,11 +309,12 @@
 
     stm_jmpbuf_t *jmpbuf_ptr = STM_SEGMENT->jmpbuf_ptr;
     stm_thread_local_t *tl = STM_SEGMENT->running_thread;
-    release_thread_segment(tl);   /* includes the cond_broadcast(); */
+    release_thread_segment(tl);
     STM_PSEGMENT->safe_point = SP_NO_TRANSACTION;
     STM_PSEGMENT->transaction_state = TS_NONE;
     reset_all_creation_markers();
 
+    cond_broadcast();
     mutex_unlock();
 
     assert(jmpbuf_ptr != NULL);
diff --git a/c7/stm/nursery.c b/c7/stm/nursery.c
--- a/c7/stm/nursery.c
+++ b/c7/stm/nursery.c
@@ -33,9 +33,6 @@
     char reserved[64];
 } nursery_ctl __attribute__((aligned(64)));
 
-static uint64_t requested_minor_collections = 0;
-static uint64_t completed_minor_collections = 0;
-
 
 /************************************************************/
 
@@ -58,64 +55,82 @@
 /************************************************************/
 
 
-static void minor_collection(void)
+
+static void minor_trace_roots(void)
 {
+    stm_thread_local_t *tl = stm_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_thread_locals);
+}
+
+static void do_minor_collection(void)
+{
+    minor_trace_roots();
+
+            /* visit shadowstack & add to old_obj_to_trace */
+    object_t **current = _STM_TL->shadow_stack;
+    object_t **base = _STM_TL->shadow_stack_base;
+    while (current-- != base) {
+        trace_if_young(current);
+    }
+
+    
+    
+    
     fprintf(stderr, "minor_collection\n");
     abort(); //...;
 
+
+    /* reset all segments' nursery_section_end, as well as nursery_ctl.used */
+    long i;
+    for (i = 0; i < NB_SEGMENTS; i++) {
+        get_segment(i)->nursery_section_end = 0;
+        get_priv_segment(i)->real_nursery_section_end = 0;
+    }
+    nursery_ctl.used = 0;
+
+    /* done */
     assert(requested_minor_collections == completed_minor_collections + 1);
     completed_minor_collections += 1;
-    nursery_ctl.used = 0;
 }
 
 
-static void sync_point_for_collection(void)
+static void restore_nursery_section_end(uintptr_t prev_value)
 {
+    __sync_bool_compare_and_swap(&STM_SEGMENT->v_nursery_section_end,
+                                 prev_value,
+                                 STM_PSEGMENT->real_nursery_section_end);
+}
+
+static void stm_minor_collection(uint64_t request_size)
+{
+    /* Run a minor collection --- but only if we can't get 'request_size'
+       bytes out of the nursery; if we can, no-op. */
     mutex_lock();
 
+    assert(STM_PSEGMENT->safe_point == SP_RUNNING);
     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;
+    /* We just waited here, either from mutex_lock() or from cond_wait(),
+       so we should check again if another thread did the minor
+       collection itself */
+    if (nursery_ctl.used + bytes <= NURSERY_SIZE)
+        goto exit;
 
-        requested_minor_collections++;
-    }
+    if (!try_wait_for_other_safe_points(SP_SAFE_POINT_CAN_COLLECT))
+        goto restart;
 
-    /* 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();
+    /* now we can run our minor collection */
+    do_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();
@@ -137,7 +152,9 @@
         if (LIKELY(p + bytes <= NURSERY_SIZE)) {
             return (stm_char *)(NURSERY_START + p);
         }
-        sync_point_for_collection();
+
+        /* nursery full! */
+        stm_minor_collection(bytes);
     }
 }
 
@@ -147,47 +164,21 @@
     /* 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 (collectable_safe_point())
+        return stm_allocate(size_rounded_up);
 
     if (size_rounded_up < MEDIUM_OBJECT) {
-        /* 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.
+        /* This is a small object.  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
+        /* Set v_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;
-        }
-
+        uintptr_t prev_end = STM_PSEGMENT->real_nursery_section_end;
         STM_PSEGMENT->real_nursery_section_end = end;
+        restore_nursery_section_end(prev_end);
 
         memset(REAL_ADDRESS(STM_SEGMENT->segment_base, p), 0,
                NURSERY_SECTION_SIZE);
diff --git a/c7/stm/nursery.h b/c7/stm/nursery.h
--- a/c7/stm/nursery.h
+++ b/c7/stm/nursery.h
@@ -1,4 +1,7 @@
 
-#define NSE_SIGNAL   1
+/* special values of 'v_nursery_section_end' */
+#define NSE_SIGNAL        1
+#define NSE_SIGNAL_DONE   2
 
 static void align_nursery_at_transaction_start(void);
+static void restore_nursery_section_end(uintptr_t prev_value);
diff --git a/c7/stm/sync.c b/c7/stm/sync.c
--- a/c7/stm/sync.c
+++ b/c7/stm/sync.c
@@ -152,8 +152,6 @@
 
     assert(sync_ctl.in_use[tl->associated_segment_num] == 1);
     sync_ctl.in_use[tl->associated_segment_num] = 0;
-
-    cond_broadcast();
 }
 
 static bool _running_transaction(void)
@@ -189,7 +187,103 @@
     assert(STM_PSEGMENT->safe_point == SP_SAFE_POINT_CAN_COLLECT);
     STM_PSEGMENT->safe_point = SP_RUNNING;
 
+    restore_nursery_section_end(NSE_SIGNAL_DONE);
     if (STM_PSEGMENT->transaction_state == TS_MUST_ABORT)
         stm_abort_transaction();
 }
 #endif
+
+
+static bool try_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.
+
+       When this function returns true, the other threads are all
+       blocked at safe points as requested, until the next time we
+       unlock the mutex (with mutex_unlock() or cond_wait()).
+
+       This function requires that the calling thread is in a safe-point
+       right now, so there is no deadlock if one thread calls
+       wait_for_other_safe_points() while another is currently blocked
+       in the cond_wait() in this same function.
+    */
+    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 */
+
+        struct stm_priv_segment_info_s *other_pseg = get_priv_segment(i);
+        if (other_pseg->safe_point == SP_RUNNING ||
+            (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;
+        }
+    }
+    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_priv_segment_info_s *other_pseg = get_priv_segment(i);
+        if (other_pseg->v_nursery_section_end == NSE_SIGNAL)
+            other_pseg->v_nursery_section_end = NSE_SIGNAL_DONE;
+    }
+    cond_broadcast();   /* to wake up the other threads, but later,
+                           when they get the mutex again */
+    return true;
+}
+
+static void wait_for_other_safe_points(int requested_safe_point_kind)
+{
+    while (!try_wait_for_other_safe_points(requested_safe_point_kind))
+        /* repeat */;
+}
+
+static bool collectable_safe_point(void)
+{
+    bool any_operation = false;
+ restart:;
+    switch (STM_SEGMENT->v_nursery_section_end) {
+
+    case 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().
+           See try_wait_for_other_safe_points() for details. */
+        mutex_lock();
+        STM_PSEGMENT->safe_point = SP_SAFE_POINT_CAN_COLLECT;
+        cond_broadcast();
+        cond_wait();
+        STM_PSEGMENT->safe_point = SP_RUNNING;
+        mutex_unlock();
+
+        /* Once the sync point is done, retry. */
+        any_operation = true;
+        goto restart;
+
+    case NSE_SIGNAL_DONE:
+        restore_nursery_section_end(NSE_SIGNAL_DONE);
+        any_operation = true;
+        break;
+
+    default:;
+    }
+    return any_operation;
+}
diff --git a/c7/stm/sync.h b/c7/stm/sync.h
--- a/c7/stm/sync.h
+++ b/c7/stm/sync.h
@@ -13,3 +13,8 @@
    (must have the mutex acquired!) */
 static void acquire_thread_segment(stm_thread_local_t *tl);
 static void release_thread_segment(stm_thread_local_t *tl);
+
+/* see the source for an exact description */
+static void wait_for_other_safe_points(int requested_safe_point_kind);
+static bool try_wait_for_other_safe_points(int requested_safe_point_kind);
+static bool collectable_safe_point(void);
diff --git a/c7/stmgc.h b/c7/stmgc.h
--- a/c7/stmgc.h
+++ b/c7/stmgc.h
@@ -62,8 +62,7 @@
     int segment_num;
     char *segment_base;
     stm_char *nursery_current;
-    uintptr_t nursery_section_end;  /* forced to 1 by
-                                       sync_all_threads_for_collection() */
+    volatile uintptr_t v_nursery_section_end;  /* see nursery.h */
     struct stm_thread_local_s *running_thread;
     stm_jmpbuf_t *jmpbuf_ptr;
 };
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to