[pypy-commit] stmgc c8-gil-like: in-progress

arigo noreply at buildbot.pypy.org
Thu Jun 11 11:26:57 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: c8-gil-like
Changeset: r1801:f344eac5014c
Date: 2015-06-11 11:27 +0200
http://bitbucket.org/pypy/stmgc/changeset/f344eac5014c/

Log:	in-progress

diff --git a/c8/demo/demo_random.c b/c8/demo/demo_random.c
--- a/c8/demo/demo_random.c
+++ b/c8/demo/demo_random.c
@@ -347,7 +347,7 @@
 
     objptr_t p;
 
-    stm_start_transaction(&stm_thread_local);
+    stm_enter_transactional_zone(&stm_thread_local);
     assert(td.num_roots >= td.num_roots_at_transaction_start);
     td.num_roots = td.num_roots_at_transaction_start;
     p = NULL;
@@ -367,12 +367,19 @@
 
             long call_fork = (arg != NULL && *(long *)arg);
             if (call_fork == 0) {   /* common case */
-                stm_commit_transaction();
                 td.num_roots_at_transaction_start = td.num_roots;
-                if (get_rand(100) < 98) {
-                    stm_start_transaction(&stm_thread_local);
-                } else {
-                    stm_start_inevitable_transaction(&stm_thread_local);
+                if (get_rand(100) < 50) {
+                    stm_leave_transactional_zone(&stm_thread_local);
+                    /* Nothing here; it's unlikely that a different thread
+                       manages to steal the detached inev transaction.
+                       Give them a little chance with a usleep(). */
+                    fprintf(stderr, "sleep...\n");
+                    usleep(1);
+                    fprintf(stderr, "sleep done\n");
+                    stm_enter_transactional_zone(&stm_thread_local);
+                }
+                else {
+                    stm_force_transaction_break(&stm_thread_local);
                 }
                 td.num_roots = td.num_roots_at_transaction_start;
                 p = NULL;
@@ -401,16 +408,16 @@
         }
     }
     push_roots();
-    stm_commit_transaction();
+    stm_force_transaction_break(&stm_thread_local);
 
     /* even out the shadow stack before leaveframe: */
-    stm_start_inevitable_transaction(&stm_thread_local);
+    stm_become_inevitable(&stm_thread_local, "before leaveframe");
     while (td.num_roots > 0) {
         td.num_roots--;
         objptr_t t;
         STM_POP_ROOT(stm_thread_local, t);
     }
-    stm_commit_transaction();
+    stm_leave_transactional_zone(&stm_thread_local);
 
     stm_rewind_jmp_leaveframe(&stm_thread_local, &rjbuf);
     stm_unregister_thread_local(&stm_thread_local);
diff --git a/c8/stm/atomic.h b/c8/stm/atomic.h
--- a/c8/stm/atomic.h
+++ b/c8/stm/atomic.h
@@ -24,15 +24,21 @@
 
 #if defined(__i386__) || defined(__amd64__)
 
-# define HAVE_FULL_EXCHANGE_INSN
   static inline void spin_loop(void) { asm("pause" : : : "memory"); }
   static inline void write_fence(void) { asm("" : : : "memory"); }
+# define atomic_exchange(ptr, old, new)  do {           \
+        (old) = __sync_lock_test_and_set(ptr, new);     \
+    } while (0)
 
 #else
 
   static inline void spin_loop(void) { asm("" : : : "memory"); }
   static inline void write_fence(void) { __sync_synchronize(); }
 
+# define atomic_exchange(ptr, old, new)  do {           \
+        (old) = *(ptr);                                 \
+    } while (UNLIKELY(!__sync_bool_compare_and_swap(ptr, old, new)));
+
 #endif
 
 
diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -496,8 +496,8 @@
 
 static void wait_for_other_inevitable(struct stm_commit_log_entry_s *old)
 {
-    int detached = fetch_detached_transaction();
-    if (detached >= 0) {
+    intptr_t detached = fetch_detached_transaction();
+    if (detached != 0) {
         commit_fetched_detached_transaction(detached);
         return;
     }
@@ -509,7 +509,7 @@
         usleep(10);    /* XXXXXX */
 
         detached = fetch_detached_transaction();
-        if (detached >= 0) {
+        if (detached != 0) {
             commit_fetched_detached_transaction(detached);
             break;
         }
@@ -1235,7 +1235,8 @@
     list_clear(STM_PSEGMENT->objects_pointing_to_nursery);
     list_clear(STM_PSEGMENT->old_objects_with_cards_set);
     list_clear(STM_PSEGMENT->large_overflow_objects);
-    timing_event(tl, event);
+    if (tl != NULL)
+        timing_event(tl, event);
 
     release_thread_segment(tl);
     /* cannot access STM_SEGMENT or STM_PSEGMENT from here ! */
@@ -1290,19 +1291,24 @@
 
     assert(!_has_mutex());
     assert(STM_PSEGMENT->safe_point == SP_RUNNING);
-    //assert(STM_PSEGMENT->running_pthread == pthread_self());
-    // ^^^ fails if detach.c commits a detached inevitable transaction
+    assert(STM_PSEGMENT->running_pthread == pthread_self());
 
     dprintf(("> stm_commit_transaction()\n"));
     minor_collection(1);
 
+    _core_commit_transaction();
+}
+
+static void _core_commit_transaction(void)
+{
     push_large_overflow_objects_to_other_segments();
     /* push before validate. otherwise they are reachable too early */
 
     bool was_inev = STM_PSEGMENT->transaction_state == TS_INEVITABLE;
     _validate_and_add_to_commit_log();
 
-    stm_rewind_jmp_forget(STM_SEGMENT->running_thread);
+    if (!was_inev)
+        stm_rewind_jmp_forget(STM_SEGMENT->running_thread);
 
     /* XXX do we still need a s_mutex_lock() section here? */
     s_mutex_lock();
@@ -1343,7 +1349,8 @@
 
     /* between transactions, call finalizers. this will execute
        a transaction itself */
-    invoke_general_finalizers(tl);
+    if (tl != NULL)
+        invoke_general_finalizers(tl);
 }
 
 static void reset_modified_from_backup_copies(int segment_num)
diff --git a/c8/stm/core.h b/c8/stm/core.h
--- a/c8/stm/core.h
+++ b/c8/stm/core.h
@@ -300,6 +300,7 @@
 
 static void _signal_handler(int sig, siginfo_t *siginfo, void *context);
 static bool _stm_validate(void);
+static void _core_commit_transaction(void);
 
 static inline bool was_read_remote(char *base, object_t *obj)
 {
diff --git a/c8/stm/detach.c b/c8/stm/detach.c
--- a/c8/stm/detach.c
+++ b/c8/stm/detach.c
@@ -2,21 +2,31 @@
 # error "must be compiled via stmgc.c"
 #endif
 
+/* Idea: if stm_leave_transactional_zone() is quickly followed by
+   stm_enter_transactional_zone() in the same thread, then we should
+   simply try to have one inevitable transaction that does both sides.
+   This is useful if there are many such small interruptions.
 
-/* _stm_detached_inevitable_segnum is:
+   stm_leave_transactional_zone() tries to make sure the transaction
+   is inevitable, and then sticks the current 'stm_thread_local_t *'
+   into _stm_detached_inevitable_from_thread.
+   stm_enter_transactional_zone() has a fast-path if the same
+   'stm_thread_local_t *' is still there.
 
-   - -1: there is no inevitable transaction, or it is not detached
+   If a different thread grabs it, it atomically replaces the value in
+   _stm_detached_inevitable_from_thread with -1, commits it (this part
+   involves reading for example the shadowstack of the thread that
+   originally detached), and at the point where we know the original
+   stm_thread_local_t is no longer relevant, we reset
+   _stm_detached_inevitable_from_thread to 0.
+*/
 
-   - in range(1, NB_SEGMENTS): an inevitable transaction belongs to
-     the segment and was detached.  It might concurrently be
-     reattached at any time, with an XCHG (__sync_lock_test_and_set).
-*/
-volatile int _stm_detached_inevitable_seg_num;
+volatile intptr_t _stm_detached_inevitable_from_thread;
 
 
 static void setup_detach(void)
 {
-    _stm_detached_inevitable_seg_num = -1;
+    _stm_detached_inevitable_from_thread = 0;
 }
 
 
@@ -26,50 +36,108 @@
 
     /* did it work? */
     if (STM_PSEGMENT->transaction_state == TS_INEVITABLE) {   /* yes */
-        _stm_detach_inevitable_transaction(STM_SEGMENT->running_thread);
+        dprintf(("leave_noninevitable_transactional_zone: now inevitable\n"));
+        stm_thread_local_t *tl = STM_SEGMENT->running_thread;
+        _stm_detach_inevitable_transaction(tl);
     }
     else {   /* no */
+        dprintf(("leave_noninevitable_transactional_zone: commit\n"));
         _stm_commit_transaction();
     }
 }
 
-void _stm_reattach_transaction(int old, stm_thread_local_t *tl)
+static void commit_external_inevitable_transaction(void)
 {
-    if (old == -1) {
-        /* there was no detached inevitable transaction */
-        _stm_start_transaction(tl);
+    assert(!_has_mutex());
+    assert(STM_PSEGMENT->safe_point == SP_RUNNING);
+    assert(STM_PSEGMENT->transaction_state == TS_INEVITABLE); /* can't abort */
+
+    exec_local_finalizers();
+    minor_collection(1);
+
+    /* from this point on, unlink the original 'stm_thread_local_t *'
+       from its segment.  Better do it as soon as possible, because
+       other threads might be spin-looping, waiting for the -1 to
+       disappear.  XXX could be done even earlier, as soon as we have
+       read the shadowstack inside the minor collection. */
+    STM_SEGMENT->running_thread = NULL;
+    write_fence();
+    assert(_stm_detached_inevitable_from_thread == -1);
+    _stm_detached_inevitable_from_thread = 0;
+
+    _core_commit_transaction();
+}
+
+void _stm_reattach_transaction(intptr_t old, stm_thread_local_t *tl)
+{
+ restart:
+    if (old != 0) {
+        if (old == -1) {
+            /* busy-loop: wait until _stm_detached_inevitable_from_thread
+               is reset to a value different from -1 */
+            while (_stm_detached_inevitable_from_thread == -1)
+                spin_loop();
+
+            /* then retry */
+            atomic_exchange(&_stm_detached_inevitable_from_thread, old, -1);
+            goto restart;
+        }
+
+        stm_thread_local_t *old_tl = (stm_thread_local_t *)old;
+        int remote_seg_num = old_tl->last_associated_segment_num;
+        dprintf(("reattach_transaction: commit detached from seg %d\n",
+                 remote_seg_num));
+
+        ensure_gs_register(remote_seg_num);
+        commit_external_inevitable_transaction();
     }
     else {
-        /* We took over the inevitable transaction originally detached
-           from a different segment.  We have to fix the %gs register if
-           it is incorrect.
-        */
-        tl->last_associated_segment_num = old;
-        ensure_gs_register(old);
-        assert(STM_PSEGMENT->transaction_state == TS_INEVITABLE);
-        STM_SEGMENT->running_thread = tl;
-
-        stm_safe_point();
+        assert(_stm_detached_inevitable_from_thread == -1);
+        _stm_detached_inevitable_from_thread = 0;
     }
+    dprintf(("reattach_transaction: start a new transaction\n"));
+    _stm_start_transaction(tl);
 }
 
 void stm_force_transaction_break(stm_thread_local_t *tl)
 {
+    dprintf(("> stm_force_transaction_break()\n"));
     assert(STM_SEGMENT->running_thread == tl);
     _stm_commit_transaction();
     _stm_start_transaction(tl);
 }
 
-static int fetch_detached_transaction(void)
+static intptr_t fetch_detached_transaction(void)
 {
-    int cur = _stm_detached_inevitable_seg_num;
-    if (cur != -1)
-        cur = __sync_lock_test_and_set(    /* XCHG */
-            &_stm_detached_inevitable_seg_num, -1);
+    intptr_t cur;
+ restart:
+    cur = _stm_detached_inevitable_from_thread;
+    if (cur == 0) {    /* fast-path */
+        return 0;   /* _stm_detached_inevitable_from_thread not changed */
+    }
+    if (cur != -1) {
+        atomic_exchange(&_stm_detached_inevitable_from_thread, cur, -1);
+        if (cur == 0) {
+            /* found 0, so change from -1 to 0 again and return */
+            _stm_detached_inevitable_from_thread = 0;
+            return 0;
+        }
+    }
+    if (cur == -1) {
+        /* busy-loop: wait until _stm_detached_inevitable_from_thread
+           is reset to a value different from -1 */
+        while (_stm_detached_inevitable_from_thread == -1)
+            spin_loop();
+        goto restart;
+    }
+    /* this is the only case where we grabbed a detached transaction.
+       _stm_detached_inevitable_from_thread is still -1, until
+       commit_fetched_detached_transaction() is called. */
+    assert(_stm_detached_inevitable_from_thread == -1);
     return cur;
 }
 
-static void commit_fetched_detached_transaction(int segnum)
+static void commit_fetched_detached_transaction(intptr_t old)
 {
     /* Here, 'seg_num' is the segment that contains the detached
        inevitable transaction from fetch_detached_transaction(),
@@ -79,13 +147,33 @@
        transaction.  This should guarantee there are not race
        conditions.
     */
+    int segnum = ((stm_thread_local_t *)old)->last_associated_segment_num;
+    dprintf(("commit_fetched_detached_transaction from seg %d\n", segnum));
     assert(segnum > 0);
 
     int mysegnum = STM_SEGMENT->segment_num;
     ensure_gs_register(segnum);
-
-    assert(STM_PSEGMENT->transaction_state == TS_INEVITABLE);
-    _stm_commit_transaction();   /* can't abort */
-
+    commit_external_inevitable_transaction();
     ensure_gs_register(mysegnum);
 }
+
+static void commit_detached_transaction_if_from(stm_thread_local_t *tl)
+{
+    intptr_t old;
+ restart:
+    old = _stm_detached_inevitable_from_thread;
+    if (old == (intptr_t)tl) {
+        if (!__sync_bool_compare_and_swap(&_stm_detached_inevitable_from_thread,
+                                          old, -1))
+            goto restart;
+        commit_fetched_detached_transaction(old);
+        return;
+    }
+    if (old == -1) {
+        /* busy-loop: wait until _stm_detached_inevitable_from_thread
+           is reset to a value different from -1 */
+        while (_stm_detached_inevitable_from_thread == -1)
+            spin_loop();
+        goto restart;
+    }
+}
diff --git a/c8/stm/detach.h b/c8/stm/detach.h
--- a/c8/stm/detach.h
+++ b/c8/stm/detach.h
@@ -1,4 +1,5 @@
 
 static void setup_detach(void);
-static int fetch_detached_transaction(void);
-static void commit_fetched_detached_transaction(int segnum);
+static intptr_t fetch_detached_transaction(void);
+static void commit_fetched_detached_transaction(intptr_t old);
+static void commit_detached_transaction_if_from(stm_thread_local_t *tl);
diff --git a/c8/stm/setup.c b/c8/stm/setup.c
--- a/c8/stm/setup.c
+++ b/c8/stm/setup.c
@@ -264,6 +264,8 @@
 
 void stm_unregister_thread_local(stm_thread_local_t *tl)
 {
+    commit_detached_transaction_if_from(tl);
+
     s_mutex_lock();
     assert(tl->prev != NULL);
     assert(tl->next != NULL);
diff --git a/c8/stm/sync.c b/c8/stm/sync.c
--- a/c8/stm/sync.c
+++ b/c8/stm/sync.c
@@ -215,10 +215,12 @@
         num = (num+1) % (NB_SEGMENTS-1);
         if (sync_ctl.in_use1[num+1] == 0) {
             /* we're getting 'num', a different number. */
-            dprintf(("acquired different segment: %d->%d\n",
-                     tl->last_associated_segment_num, num+1));
+            int old_num = tl->last_associated_segment_num;
+            dprintf(("acquired different segment: %d->%d\n", old_num, num+1));
             tl->last_associated_segment_num = num+1;
             set_gs_register(get_segment_base(num+1));
+            dprintf(("                            %d->%d\n", old_num, num+1));
+            (void)old_num;
             goto got_num;
         }
     }
@@ -245,18 +247,22 @@
 
 static void release_thread_segment(stm_thread_local_t *tl)
 {
+    int segnum;
     assert(_has_mutex());
 
     cond_signal(C_SEGMENT_FREE);
 
     assert(STM_SEGMENT->running_thread == tl);
-    assert(tl->last_associated_segment_num == STM_SEGMENT->segment_num);
-    assert(in_transaction(tl));
-    STM_SEGMENT->running_thread = NULL;
-    assert(!in_transaction(tl));
+    segnum = STM_SEGMENT->segment_num;
+    if (tl != NULL) {
+        assert(tl->last_associated_segment_num == segnum);
+        assert(in_transaction(tl));
+        STM_SEGMENT->running_thread = NULL;
+        assert(!in_transaction(tl));
+    }
 
-    assert(sync_ctl.in_use1[tl->last_associated_segment_num] == 1);
-    sync_ctl.in_use1[tl->last_associated_segment_num] = 0;
+    assert(sync_ctl.in_use1[segnum] == 1);
+    sync_ctl.in_use1[segnum] = 0;
 }
 
 __attribute__((unused))
@@ -414,8 +420,8 @@
        Wait until all other threads are suspended. */
     while (count_other_threads_sp_running() > 0) {
 
-        int detached = fetch_detached_transaction();
-        if (detached >= 0) {
+        intptr_t detached = fetch_detached_transaction();
+        if (detached != 0) {
             remove_requests_for_safe_point();    /* => C_REQUEST_REMOVED */
             commit_fetched_detached_transaction(detached);
             goto restart;
diff --git a/c8/stmgc.h b/c8/stmgc.h
--- a/c8/stmgc.h
+++ b/c8/stmgc.h
@@ -84,16 +84,16 @@
 object_t *_stm_allocate_slowpath(ssize_t);
 object_t *_stm_allocate_external(ssize_t);
 
-extern volatile int _stm_detached_inevitable_seg_num;
+extern volatile intptr_t _stm_detached_inevitable_from_thread;
 long _stm_start_transaction(stm_thread_local_t *tl);
 void _stm_commit_transaction(void);
 void _stm_leave_noninevitable_transactional_zone(void);
-#define _stm_detach_inevitable_transaction(tl)  do {                    \
-    write_fence();                                                      \
-    assert((tl)->last_associated_segment_num == STM_SEGMENT->segment_num); \
-    _stm_detached_inevitable_seg_num = STM_SEGMENT->segment_num;        \
+#define _stm_detach_inevitable_transaction(tl)  do {            \
+    write_fence();                                              \
+    assert(_stm_detached_inevitable_from_thread == 0);          \
+    _stm_detached_inevitable_from_thread = (intptr_t)(tl);      \
 } while (0)
-void _stm_reattach_transaction(int old, stm_thread_local_t *tl);
+void _stm_reattach_transaction(intptr_t old, stm_thread_local_t *tl);
 void _stm_become_inevitable(const char*);
 void _stm_collectable_safe_point(void);
 
@@ -421,12 +421,15 @@
    transactions.
 */
 static inline void stm_enter_transactional_zone(stm_thread_local_t *tl) {
-    int old = __sync_lock_test_and_set(    /* XCHG */
-        &_stm_detached_inevitable_seg_num, -1);
-    if (old == tl->last_associated_segment_num)
-        STM_SEGMENT->running_thread = tl;
-    else
+    intptr_t old;
+    atomic_exchange(&_stm_detached_inevitable_from_thread, old, -1);
+    if (old == (intptr_t)tl) {
+        _stm_detached_inevitable_from_thread = 0;
+    }
+    else {
         _stm_reattach_transaction(old, tl);
+        assert(_stm_detached_inevitable_from_thread == 0);
+    }
 }
 static inline void stm_leave_transactional_zone(stm_thread_local_t *tl) {
     assert(STM_SEGMENT->running_thread == tl);
@@ -458,7 +461,7 @@
     if (!stm_is_inevitable())
         _stm_become_inevitable(msg);
     /* now, we're running the inevitable transaction, so: */
-    assert(_stm_detached_inevitable_seg_num == -1);
+    assert(_stm_detached_inevitable_from_thread == 0);
 }
 
 /* Forces a safe-point if needed.  Normally not needed: this is


More information about the pypy-commit mailing list