Author: Armin Rigo <[email protected]>
Branch: c7-refactor
Changeset: r818:8c254c425ce5
Date: 2014-02-24 10:29 +0100
http://bitbucket.org/pypy/stmgc/changeset/8c254c425ce5/

Log:    Reimplement the _stm_write_slowpath().

diff --git a/c7/stm/core.c b/c7/stm/core.c
--- a/c7/stm/core.c
+++ b/c7/stm/core.c
@@ -13,71 +13,93 @@
 {
     assert(_running_transaction());
     assert(!_is_in_nursery(obj));
-    abort();//...
-#if 0
 
-    /* for old objects from the same transaction, we are done now */
-    if (obj_from_same_transaction(obj)) {
-        obj->stm_flags |= GCFLAG_WRITE_BARRIER_CALLED;
-        LIST_APPEND(STM_PSEGMENT->old_objects_pointing_to_young, obj);
+    /* is this an object from the same transaction, outside the nursery? */
+    if ((obj->stm_flags & -GCFLAG_OVERFLOW_NUMBER_bit0) ==
+            STM_PSEGMENT->overflow_number) {
+
+        obj->stm_flags &= ~GCFLAG_WRITE_BARRIER;
+        assert(STM_PSEGMENT->overflow_objects_pointing_to_nursery != NULL);
+        LIST_APPEND(STM_PSEGMENT->overflow_objects_pointing_to_nursery, obj);
         return;
     }
 
+    /* do a read-barrier now.  Note that this must occur before the
+       safepoints that may be issued in contention_management(). */
+    stm_read(obj);
 
-    /* otherwise, 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;
+    /* claim the write-lock for this object.  In case we're running the
+       same transaction since a long while, the object can be already in
+       'modified_old_objects' (but, because it had GCFLAG_WRITE_BARRIER,
+       not in 'old_objects_pointing_to_nursery').  We'll detect this case
+       by finding that we already own the write-lock. */
+    uintptr_t lock_idx = (((uintptr_t)obj) >> 4) - WRITELOCK_START;
+    uint8_t lock_num = STM_PSEGMENT->write_lock_num;
+    assert((intptr_t)lock_idx >= 0);
+ retry:
+    if (write_locks[lock_idx] == 0) {
+        if (UNLIKELY(!__sync_bool_compare_and_swap(&write_locks[lock_idx],
+                                                   0, lock_num)))
+            goto retry;
 
-    /* If the object is in the uniform pages of small objects (outside the
-       nursery), then it fits into one page.  Otherwise, we need to compute
-       it based on its location and size. */
-    if ((obj->stm_flags & GCFLAG_SMALL_UNIFORM) != 0) {
-        pages_privatize(first_page, 1, true);
+        /* First change to this old object from this transaction.
+           Add it to the list 'modified_old_objects'. */
+        LIST_APPEND(STM_PSEGMENT->modified_old_objects, obj);
+
+        /* 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
+           (outside the nursery), then it fits into one page.  This is
+           the common case. Otherwise, we need to compute it based on
+           its location and size. */
+        if ((obj->stm_flags & GCFLAG_SMALL_UNIFORM) != 0) {
+            pages_privatize(first_page, 1, true);
+        }
+        else {
+            /* get the size of the object */
+            obj_size = stmcb_size_rounded_up(
+              (struct object_s *)REAL_ADDRESS(STM_SEGMENT->segment_base, obj));
+
+            /* that's the page *following* the last page with the object */
+            uintptr_t end_page = (((uintptr_t)obj) + obj_size + 4095) / 4096UL;
+
+            pages_privatize(first_page, end_page - first_page, true);
+        }
+    }
+    else if (write_locks[lock_idx] == lock_num) {
+        OPT_ASSERT(STM_PSEGMENT->old_objects_pointing_to_nursery != NULL);
+#ifdef STM_TESTS
+        bool found = false;
+        LIST_FOREACH_R(STM_PSEGMENT->modified_old_objects, object_t *,
+                       ({ if (item == obj) { found = true; break; } }));
+        assert(found);
+#endif
     }
     else {
-        /* get the size of the object */
-        obj_size = stmcb_size_rounded_up(
-            (struct object_s *)REAL_ADDRESS(STM_SEGMENT->segment_base, obj));
-
-        /* that's the page *following* the last page with the object */
-        uintptr_t end_page = (((uintptr_t)obj) + obj_size + 4095) / 4096UL;
-
-        pages_privatize(first_page, end_page - first_page, true);
-    }
-
-
-    /* do a read-barrier *before* the safepoints that may be issued in
-       contention_management() */
-    stm_read(obj);
-
-    /* claim the write-lock for this object */
- retry:;
-    uintptr_t lock_idx = (((uintptr_t)obj) >> 4) - WRITELOCK_START;
-    uint8_t lock_num = STM_PSEGMENT->write_lock_num;
-    uint8_t prev_owner;
-    assert((intptr_t)lock_idx >= 0);
-    prev_owner = __sync_val_compare_and_swap(&write_locks[lock_idx],
-                                             0, lock_num);
-
-    /* if there was no lock-holder, we are done; otherwise... */
-    if (UNLIKELY(prev_owner != 0)) {
-        /* otherwise, call the contention manager, and then possibly retry.
-           By construction it should not be possible that the owner
-           of the object is already us */
+        /* call the contention manager, and then retry (unless we were
+           aborted). */
         mutex_lock();
-        contention_management(prev_owner - 1, true);
+        uint8_t prev_owner = ((volatile uint8_t *)write_locks)[lock_idx];
+        if (prev_owner != 0 && prev_owner != lock_num)
+            contention_management(prev_owner - 1, true);
         mutex_unlock();
         goto retry;
     }
 
+    /* A common case for write_locks[] that was either 0 or lock_num:
+       we need to add the object to 'old_objects_pointing_to_nursery'
+       if there is such a list. */
+    if (STM_PSEGMENT->old_objects_pointing_to_nursery != NULL)
+        LIST_APPEND(STM_PSEGMENT->old_objects_pointing_to_nursery, obj);
+
     /* add the write-barrier-already-called flag ONLY if we succeeded in
        getting the write-lock */
-    assert(!(obj->stm_flags & GCFLAG_WRITE_BARRIER_CALLED));
-    obj->stm_flags |= GCFLAG_WRITE_BARRIER_CALLED;
-    LIST_APPEND(STM_PSEGMENT->modified_objects, obj);
-#endif
+    assert(obj->stm_flags & GCFLAG_WRITE_BARRIER);
+    obj->stm_flags &= ~GCFLAG_WRITE_BARRIER;
 }
 
 static void reset_transaction_read_version(void)
@@ -143,6 +165,8 @@
     }
 
     assert(list_is_empty(STM_PSEGMENT->modified_old_objects));
+    assert(STM_PSEGMENT->old_objects_pointing_to_nursery == NULL);
+    assert(STM_PSEGMENT->overflow_objects_pointing_to_nursery == NULL);
 
 #ifdef STM_TESTS
     check_nursery_at_transaction_start();
diff --git a/c7/stm/core.h b/c7/stm/core.h
--- a/c7/stm/core.h
+++ b/c7/stm/core.h
@@ -61,15 +61,27 @@
 struct stm_priv_segment_info_s {
     struct stm_segment_info_s pub;
 
+    /* List of old objects (older than the current transaction) that the
+       current transaction attempts to modify.  This is used to track
+       the STM status: it's old objects that where written to and that
+       need to be copied to other segments upon commit. */
+    struct list_s *modified_old_objects;
+
+    /* List of the modified old objects that may point to the nursery.
+       If the current transaction didn't span a minor collection so far,
+       this is NULL, understood as meaning implicitly "this is the same
+       as 'modified_old_objects'".  Otherwise, this list is a subset of
+       'modified_old_objects'. */
+    struct list_s *old_objects_pointing_to_nursery;
+
     /* List of overflowed objects (from the same transaction but outside
        the nursery) on which the write-barrier was triggered, so that
-       they likely contain a pointer to a nursery object */
+       they likely contain a pointer to a nursery object.  This is used
+       by the GC: it's roots for the next minor collection.  This is
+       NULL if the current transaction didn't span a minor collection
+       so far. */
     struct list_s *overflow_objects_pointing_to_nursery;
 
-    /* List of old objects (older than the current transaction) that the
-       current transaction attempts to modify */
-    struct list_s *modified_old_objects;
-
     /* Start time: to know approximately for how long a transaction has
        been running, in contention management */
     uint64_t start_time;
diff --git a/c7/stm/setup.c b/c7/stm/setup.c
--- a/c7/stm/setup.c
+++ b/c7/stm/setup.c
@@ -87,6 +87,7 @@
     for (i = 0; i < NB_SEGMENTS; i++) {
         struct stm_priv_segment_info_s *pr = get_priv_segment(i);
         assert(pr->overflow_objects_pointing_to_nursery == NULL);
+        assert(pr->old_objects_pointing_to_nursery == NULL);
         list_free(pr->modified_old_objects);
     }
 
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to