[pypy-commit] stmgc default: Untested, work in progress

arigo noreply at buildbot.pypy.org
Sun May 26 12:09:45 CEST 2013


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r8:26dca82e7423
Date: 2013-05-26 12:05 +0200
http://bitbucket.org/pypy/stmgc/changeset/26dca82e7423/

Log:	Untested, work in progress

diff --git a/c3/atomic_ops.h b/c3/atomic_ops.h
--- a/c3/atomic_ops.h
+++ b/c3/atomic_ops.h
@@ -115,7 +115,7 @@
     } while (1)
 
 #define spinlock_release(lock)                                  \
-    do { smp_wmb(); assert((lock) == 1); (lock) = 0; } while (0)
+    do { smp_wmb(); assert((lock) != 0); (lock) = 0; } while (0)
 
 
 #endif  /* _SRCSTM_ATOMIC_OPS_ */
diff --git a/c3/et.c b/c3/et.c
--- a/c3/et.c
+++ b/c3/et.c
@@ -314,6 +314,13 @@
   struct tx_descriptor *d = thread_descriptor;
   assert(d->active >= 1);
 
+  /* must normalize the situation now, otherwise we risk that
+     LocalizePublic creates a new private version of a public
+     object that has got one, attached to the equivalent stolen
+     protected object */
+  if (gcptrlist_size(&d->stolen_objects) > 0)
+    stmgc_normalize_stolen_objects();
+
   /* XXX optimize me based on common patterns */
   R = HeadOfRevisionChainList(d, P);
 
@@ -352,11 +359,9 @@
       v = ACCESS_ONCE(R->h_revision);
       if (!(v & 1))               // "is a pointer", i.e.
         {                         //   "has a more recent revision"
-          /* ... unless it is only a GCFLAG_NURSERY_MOVED, which may
-             only come from my thread's nursery; then look at the
-             moved-out object instead */
+          /* ... unless it is a GCFLAG_STOLEN object */
           abort();//XXX
-          if (R->h_tid & GCFLAG_NURSERY_MOVED)
+          if (R->h_tid & GCFLAG_STOLEN)
             {
               assert(is_young(R));
               assert(!is_young((gcptr)v));
@@ -572,6 +577,7 @@
       gcptr R = item->addr;
       revision_t v;
     retry:
+      assert(R->h_tid & GCFLAG_OLD);
       v = ACCESS_ONCE(R->h_revision);
       if (!(v & 1))            // "is a pointer", i.e.
         {                      //   "has a more recent revision"
@@ -642,6 +648,7 @@
       assert(!(L->h_tid & GCFLAG_PUBLIC_TO_PRIVATE));
       assert(!(L->h_tid & GCFLAG_PREBUILT_ORIGINAL));
       assert(!(L->h_tid & GCFLAG_NURSERY_MOVED));
+      assert(!(L->h_tid & GCFLAG_STOLEN));
       assert(L->h_revision != localrev);   /* modified by AcquireLocks() */
 
 #ifdef DUMP_EXTRA
@@ -673,6 +680,7 @@
       assert(!(R->h_tid & GCFLAG_PRIVATE_COPY));
       assert(R->h_tid & GCFLAG_PUBLIC_TO_PRIVATE);
       assert(!(R->h_tid & GCFLAG_NURSERY_MOVED));
+      assert(!(R->h_tid & GCFLAG_STOLEN));
       assert(!is_young(R));
       assert(R->h_revision != localrev);
 
@@ -757,6 +765,7 @@
 
   UpdateChainHeads(d, cur_time, localrev);
 
+  stmgc_committed_transaction(d);
   d->num_commits++;
   d->active = 0;
   stm_stop_sharedlock();
diff --git a/c3/et.h b/c3/et.h
--- a/c3/et.h
+++ b/c3/et.h
@@ -54,6 +54,9 @@
  * GCFLAG_NURSERY_MOVED is used temporarily during minor collections.
  *
  * GCFLAG_OLD is set on old objects.
+ *
+ * GCFLAG_STOLEN is set of protected objects after we notice that they
+ * have been stolen.
  */
 #define GCFLAG_PRIVATE_COPY      (STM_FIRST_GCFLAG << 0)
 #define GCFLAG_VISITED           (STM_FIRST_GCFLAG << 1)
@@ -62,6 +65,7 @@
 #define GCFLAG_WRITE_BARRIER     (STM_FIRST_GCFLAG << 4)
 #define GCFLAG_NURSERY_MOVED     (STM_FIRST_GCFLAG << 5)
 #define GCFLAG_OLD               (STM_FIRST_GCFLAG << 6)
+#define GCFLAG_STOLEN            (STM_FIRST_GCFLAG << 7)
 
 /* this value must be reflected in PREBUILT_FLAGS in stmgc.h */
 #define GCFLAG_PREBUILT  (GCFLAG_VISITED           | \
@@ -75,6 +79,7 @@
                          "WRITE_BARRIER",     \
                          "NURSERY_MOVED",     \
                          "OLD",               \
+                         "STOLEN",            \
                          NULL }
 
 /************************************************************/
diff --git a/c3/gcpage.c b/c3/gcpage.c
--- a/c3/gcpage.c
+++ b/c3/gcpage.c
@@ -557,9 +557,9 @@
              */
             thread_descriptor = d;
             stm_local_revision = *d->local_revision_ref;
-            assert(stmgc_nursery_hiding(d, 0));
+            assert(stmgc_nursery_hiding(0));
             stmgc_minor_collect_no_abort();
-            assert(stmgc_nursery_hiding(d, 1));
+            assert(stmgc_nursery_hiding(1));
         }
     }
     thread_descriptor = saved;
diff --git a/c3/nursery.c b/c3/nursery.c
--- a/c3/nursery.c
+++ b/c3/nursery.c
@@ -63,6 +63,8 @@
         assert((obj->h_tid & GCFLAG_OLD) == 0);
     else
         assert((obj->h_tid & GCFLAG_OLD) == GCFLAG_OLD);
+    if (e != K_PROTECTED)
+        assert(!(obj->h_tid & GCFLAG_STOLEN));
     return e;
 
  not_found:
@@ -169,6 +171,14 @@
     return localobj;
 }
 
+static revision_t fetch_extra_word(gcptr L)
+{
+    size_t size = stmcb_size(L);
+    revision_t extra_word = *(revision_t *)(((char*)L) + size);
+    assert(extra_word & 1);   /* "is not a pointer", should be a rev number */
+    return extra_word;
+}
+
 void stmgc_start_transaction(struct tx_descriptor *d)
 {
     assert(!gcptrlist_size(&d->protected_with_private_copy));
@@ -179,6 +189,7 @@
 }
 
 static void fix_new_public_to_protected_references(struct tx_descriptor *d);
+static void normalize_stolen_objects(struct tx_descriptor *d);
 
 void stmgc_stop_transaction(struct tx_descriptor *d)
 {
@@ -189,13 +200,19 @@
         fix_new_public_to_protected_references(d);
 
     spinlock_acquire(d->collection_lock);
+    d->collection_lock = 'C';   /* Committing */
+
     if (gcptrlist_size(&d->stolen_objects) > 0)
-        abort();  //XXX XXX also is it fine if stolen_objects grows just after
-    spinlock_release(d->collection_lock);
+        normalize_stolen_objects(d);
 
     fprintf(stderr, "stop transaction\n");
 }
 
+void stmgc_committed_transaction(struct tx_descriptor *d)
+{
+    spinlock_release(d->collection_lock);
+}
+
 void stmgc_abort_transaction(struct tx_descriptor *d)
 {
     /* cancel the change to the h_revision done in LocalizeProtected() */
@@ -209,20 +226,22 @@
        other thread is busy stealing (which includes reading the extra
        word immediately after private objects).
     */
-    spinlock_acquire(d->collection_lock);
+    if (d->collection_lock != 'C')
+        spinlock_acquire(d->collection_lock);
 
     for (i = 0; i < size; i++) {
         gcptr R = items[i];
         assert(dclassify(R) == K_PROTECTED);
         assert(!(R->h_revision & 1));    /* "is a pointer" */
         gcptr L = (gcptr)R->h_revision;
+
+        if (R->h_tid & GCFLAG_STOLEN) {    /* ignore stolen objects */
+            assert(dclassify(L) == K_PUBLIC);
+            continue;
+        }
         assert(dclassify(L) == K_PRIVATE);
 
-        size_t size = stmcb_size(R);
-        revision_t extra_word = *(revision_t *)(((char*)L) + size);
-
-        assert(extra_word & 1);
-        R->h_revision = extra_word;
+        R->h_revision = fetch_extra_word(L);
         abort();//XXX
     }
     gcptrlist_clear(&d->protected_with_private_copy);
@@ -251,8 +270,7 @@
 #  define recdump1(msg, obj)  /* removed */
 #endif
 
-static inline gcptr create_old_object_copy(struct tx_descriptor *d, gcptr obj
-                                           _REASON(char *reason))
+static inline gcptr create_old_object_copy(gcptr obj  _REASON(char *reason))
 {
     assert(!(obj->h_tid & GCFLAG_NURSERY_MOVED));
     assert(!(obj->h_tid & GCFLAG_VISITED));
@@ -335,7 +353,7 @@
             }
         }
         /* case C */
-        fresh_old_copy = create_old_object_copy(d, obj  _REASON("visit"));
+        fresh_old_copy = create_old_object_copy(obj  _REASON("visit"));
         obj->h_tid |= GCFLAG_NURSERY_MOVED;
         obj->h_revision = (revision_t)fresh_old_copy;
 
@@ -410,14 +428,15 @@
         assert(dclassify(R) == K_PROTECTED);
         assert(!(R->h_revision & 1));    /* "is a pointer" */
         gcptr L = (gcptr)R->h_revision;
+
+        if (R->h_tid & GCFLAG_STOLEN) {    /* ignore stolen objects */
+            assert(dclassify(L) == K_PUBLIC);
+            continue;
+        }
         assert(dclassify(L) == K_PRIVATE);
 
         /* we first detach the link, restoring the original R->h_revision */
-        size_t size = stmcb_size(R);
-        revision_t extra_word = *(revision_t *)(((char*)L) + size);
-
-        assert(extra_word & 1);
-        R->h_revision = extra_word;
+        R->h_revision = fetch_extra_word(L);
 
         /* then we turn the protected object in a public one */
         R = young_object_becomes_old(d, R
@@ -601,7 +620,7 @@
         else {
             /* second case */
             abort();//XXX
-            /* ... check
+            /* ABRT_COLLECT_MINOR ... check
                for stolen object */
         }
     }
@@ -686,6 +705,9 @@
     /* acquire the "collection lock" first */
     setup_minor_collect(d);
 
+    if (gcptrlist_size(&d->stolen_objects) > 0)
+        normalize_stolen_objects(d);
+
     mark_protected_with_private_copy(d);
 
     mark_public_to_young(d);
@@ -775,9 +797,10 @@
     gcptrlist_insert(&d->private_old_pointing_to_young, obj);
 }
 
-int stmgc_nursery_hiding(struct tx_descriptor *d, int hide)
+int stmgc_nursery_hiding(int hide)
 {
 #ifdef _GC_DEBUG
+    struct tx_descriptor *d = thread_descriptor;
     if (hide) {
         stm_dbgmem_not_used(d->nursery, GC_NURSERY, 1);
     }
@@ -808,101 +831,165 @@
 
 /************************************************************/
 
-static gcptr extract_from_foreign_nursery(struct tx_descriptor *source_d,
-                                          gcptr L)
+static gcptr extract_from_foreign_nursery(gcptr R)
 {
-    /* "Stealing": this function follows a chain of protected objects
-       in the foreign nursery of the thread 'source_d'.  It copies the
-       last one outside the nursery, and return it. */
-    gcptr L2, N;
+    /* "Stealing": this function follows a chain of protected objects in
+       the foreign nursery of the thread temporarily in
+       'thread_descriptor'.  It copies the last one outside the nursery,
+       and return it. */
+    gcptr R2, N;
     revision_t source_local_rev, v;
 
-    source_local_rev = ACCESS_ONCE(*source_d->local_revision_ref);
-    v = ACCESS_ONCE(L->h_revision);
+    source_local_rev = stm_local_revision;
+    v = ACCESS_ONCE(R->h_revision);
 
-    /* check that L is a protected object */
-    assert(!(L->h_tid & GCFLAG_OLD));
+    /* check that R is a protected object */
+    assert(!(R->h_tid & GCFLAG_OLD));
     assert(v != source_local_rev);
 
     /* walk to the head of the chain in the foreign nursery
      */
     while (!(v & 1)) {     /* "is a pointer" */
-        L2 = (gcptr)v;
-        v = ACCESS_ONCE(L2->h_revision);
+        R2 = (gcptr)v;
+        v = ACCESS_ONCE(R2->h_revision);
         if (v == source_local_rev) {
-            /* L->h_revision is a pointer, but the target is a private
-               object.  We ignore private objects, so we stay at L; but
-               have to fetch L's real revision off-line from the extra
-               word that follows L2 */
-            size_t size = stmcb_size(L2);
-            v = *(revision_t *)(((char*)L2) + size);
-            assert(v & 1);   /* "is not a pointer" */
+            /* R->h_revision is a pointer, but the target is a private
+               object.  We ignore private objects, so we stay at R; but
+               have to fetch R's real revision off-line from the extra
+               word that follows R2 */
+            v = fetch_extra_word(R2);
             break;
         }
-        else if (L2->h_tid & GCFLAG_OLD) {
+        else if (R2->h_tid & GCFLAG_OLD) {
             /* we find a public object again: easy case, just return it */
-            return L2;
+            return R2;
         }
         else {
             /* the chain continues with another protected object, go on */
-            L = L2;
+            R = R2;
         }
     }
 
-    /* L is now the protected object to move outside, with revision v. */
-    N = create_old_object_copy(source_d, L  _REASON("stolen copy"));
+    /* R is now the protected object to move outside, with revision v. */
+    N = create_old_object_copy(R  _REASON("stolen copy"));
     N->h_revision = v;
-    gcptrlist_insert2(&source_d->stolen_objects, L, N);
+    gcptrlist_insert2(&thread_descriptor->stolen_objects, R, N);
 
-    smp_wmb();
+    /* there might be references in N going to protected objects.  We
+       must fix them with stubs. */
+    stmcb_trace(N, &create_yo_stubs);
 
     return N;
 }
 
-void stmgc_public_to_foreign_protected(gcptr R)
+void stmgc_public_to_foreign_protected(gcptr P)
 {
-    /* R is a public object, which contains in h_revision a pointer to a
+    /* P is a public object, which contains in h_revision a pointer to a
        protected object --- but it is protectd by another thread,
        i.e. it likely lives in a foreign nursery.  We have to copy the
        object out ourselves.  This is necessary: we can't simply wait
        for the other thread to do a minor collection, because it might
        be blocked in a system call or whatever. */
     struct tx_descriptor *my_d = thread_descriptor;
+    revision_t my_local_rev = stm_local_revision;
 
     /* repeat the checks in the caller, to avoid passing more than one
        argument here */
-    revision_t v = ACCESS_ONCE(R->h_revision);
+    revision_t v = ACCESS_ONCE(P->h_revision);
     assert(!(v & 1));    /* "is a pointer" */
     if (!(v & 2))
         return;   /* changed already, retry */
 
-    gcptr L = (gcptr)(v & ~2);
+    gcptr R = (gcptr)(v & ~2);
 
     /* We need to look up which thread it belongs to and lock this
        thread's minor collection lock.  This also prevents several
        threads from getting on each other's toes trying to extract
        objects from the same nursery */
-    struct tx_descriptor *source_d = stm_find_thread_containing_pointer(L);
+    struct tx_descriptor *source_d = stm_find_thread_containing_pointer(R);
     assert(source_d != my_d);
 
     spinlock_acquire(source_d->collection_lock);
 
-    /* now that we have the lock, check again that R->h_revision was not
+    /* now that we have the lock, check again that P->h_revision was not
        modified in the meantime.  If it did change, we do nothing and will
        retry.
     */
-    if (R->h_revision == v) {
+    if (P->h_revision == v) {
+        /* temporarily take the identity of source_d */
+        thread_descriptor = source_d;
+        stm_local_revision = *source_d->local_revision_ref;
+
         /* debugging support: "activate" the foreign nursery */
         int was_active = stm_dbgmem_is_active(source_d->nursery, 0);
-        if (!was_active) assert(stmgc_nursery_hiding(source_d, 0));
+        if (!was_active) assert(stmgc_nursery_hiding(0));
 
-        gcptr N = extract_from_foreign_nursery(source_d, L);
-        ACCESS_ONCE(R->h_revision) = (revision_t)N;
+        /* copy the protected source object */
+        gcptr N = extract_from_foreign_nursery(R);
+
+        /* make sure the copy N is visible to other threads before we
+           change P->h_revision */
+        smp_wmb();
+
+        /* do the change to P->h_revision */
+        ACCESS_ONCE(P->h_revision) = (revision_t)N;
         fprintf(stderr, "STEALING: %p->h_revision changed from %p to %p\n",
-                R, L, N);
+                P, R, N);
 
         /* debugging support: "deactivate" the foreign nursery again */
-        if (!was_active) assert(stmgc_nursery_hiding(source_d, 1));
+        if (!was_active) assert(stmgc_nursery_hiding(1));
+
+        /* restore my own identity */
+        stm_local_revision = my_local_rev;
+        thread_descriptor = my_d;
     }
     spinlock_release(source_d->collection_lock);
 }
+
+static void normalize_stolen_objects(struct tx_descriptor *d)
+{
+    /* d->stolen_objects lists pairs (R, N) with R being a protected
+       object, and N a public object at the _same_ revision (and with
+       identical content).  The protected object R can have a private
+       copy, but it cannot have another already-committed 'h_revision'.
+    */
+    long i, size = d->stolen_objects.size;
+    gcptr *items = d->stolen_objects.items;
+
+    for (i = 0; i < size; i += 2) {
+        gcptr R = items[i];
+        gcptr N = items[i + 1];
+
+        assert(dclassify(R) == K_PROTECTED);
+        assert(dclassify(N) == K_PUBLIC);
+
+        revision_t v = R->h_revision;
+        R->h_tid |= GCFLAG_STOLEN;
+        R->h_revision = (revision_t)N;
+
+        if (!(v & 1)) {   /* "is a pointer" */
+            gcptr L = (gcptr)v;
+            assert(dclassify(L) == K_PRIVATE);
+
+            /* R has got a private copy L.  This means that R is listed
+               in protected_with_private_copy.  Where?  We don't know.
+               The other places that scan protected_with_private_copy
+               must carefully ignore GCFLAG_STOLEN entries. */
+
+            /* we re-insert L as a private copy of the public object N */
+            N->h_tid |= GCFLAG_PUBLIC_TO_PRIVATE;
+            g2l_insert(&d->public_to_private, N, L);
+            gcptrlist_insert(&d->public_to_young, N);
+        }
+    }
+    gcptrlist_clear(&d->stolen_objects);
+    abort();//XXX
+}
+
+void stmgc_normalize_stolen_objects(void)
+{
+    struct tx_descriptor *d = thread_descriptor;
+    spinlock_acquire(d->collection_lock);
+    normalize_stolen_objects(d);
+    spinlock_release(d->collection_lock);
+}
diff --git a/c3/nursery.h b/c3/nursery.h
--- a/c3/nursery.h
+++ b/c3/nursery.h
@@ -35,6 +35,7 @@
 gcptr stmgc_duplicate(gcptr, revision_t);
 void stmgc_start_transaction(struct tx_descriptor *);
 void stmgc_stop_transaction(struct tx_descriptor *);
+void stmgc_committed_transaction(struct tx_descriptor *);
 void stmgc_abort_transaction(struct tx_descriptor *);
 void stmgc_init_tls(void);
 void stmgc_done_tls(void);
@@ -45,7 +46,8 @@
 enum protection_class_t stmgc_classify(gcptr);
 int stmgc_is_young_in(struct tx_descriptor *, gcptr);
 void stmgc_public_to_foreign_protected(gcptr);
-int stmgc_nursery_hiding(struct tx_descriptor *, int);
+int stmgc_nursery_hiding(int);
+void stmgc_normalize_stolen_objects(void);
 
 #ifdef _GC_DEBUG
 int is_young(gcptr);
diff --git a/c3/stmsync.c b/c3/stmsync.c
--- a/c3/stmsync.c
+++ b/c3/stmsync.c
@@ -236,12 +236,12 @@
 {
     int err = pthread_rwlock_rdlock(&rwlock_shared);
     assert(err == 0);
-    assert(stmgc_nursery_hiding(thread_descriptor, 0));
+    assert(stmgc_nursery_hiding(0));
 }
 
 void stm_stop_sharedlock(void)
 {
-    assert(stmgc_nursery_hiding(thread_descriptor, 1));
+    assert(stmgc_nursery_hiding(1));
     int err = pthread_rwlock_unlock(&rwlock_shared);
     assert(err == 0);
 }


More information about the pypy-commit mailing list