[pypy-commit] stm default: Add GCFLAG_OLD. It's very easy to maintain it, and it simplifies things.

arigo noreply at buildbot.pypy.org
Sat May 25 18:01:25 CEST 2013


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r5:b62e0a5f9940
Date: 2013-05-25 18:01 +0200
http://bitbucket.org/pypy/stm/changeset/b62e0a5f9940/

Log:	Add GCFLAG_OLD. It's very easy to maintain it, and it simplifies
	things.

diff --git a/c3/et.c b/c3/et.c
--- a/c3/et.c
+++ b/c3/et.c
@@ -81,7 +81,7 @@
         {
         old_to_young:
           v &= ~2;
-          if (UNLIKELY(!stmgc_is_young(d, (gcptr)v)))
+          if (UNLIKELY(!stmgc_is_young_in(d, (gcptr)v)))
             {
               stmgc_public_to_foreign_protected(R);
               goto retry;
@@ -183,7 +183,7 @@
       if (v & 2)
         {
           v &= ~2;
-          if (!stmgc_is_young(thread_descriptor, (gcptr)v))
+          if (!is_young((gcptr)v))
             {
               stmgc_follow_foreign(R);
               goto retry;
@@ -317,7 +317,7 @@
   /* XXX optimize me based on common patterns */
   R = HeadOfRevisionChainList(d, P);
 
-  switch (stmgc_classify(d, R)) {
+  switch (stmgc_classify(R)) {
   case K_PRIVATE:   W = R;                       break;
   case K_PROTECTED: W = LocalizeProtected(d, R); break;
   case K_PUBLIC:    W = LocalizePublic(d, R);    break;
@@ -358,8 +358,8 @@
           abort();//XXX
           if (R->h_tid & GCFLAG_NURSERY_MOVED)
             {
-              assert(stmgc_is_young(d, R));
-              assert(!stmgc_is_young(d, (gcptr)v));
+              assert(is_young(R));
+              assert(!is_young((gcptr)v));
               R = (gcptr)v;
               goto retry;
             }
@@ -651,6 +651,15 @@
       L->h_tid &= ~GCFLAG_PRIVATE_COPY;
       L->h_revision = new_revision;
 
+      if (is_young(L))
+        {
+          item->val = (gcptr)(((revision_t)L) | 2);
+#ifdef DUMP_EXTRA
+          fprintf(stderr, "PUBLIC-TO-PROTECTED:\n");
+          /*mark*/
+#endif
+        }
+
     } G2L_LOOP_END;
 
   smp_wmb(); /* a memory barrier: make sure the new L->h_revisions are visible
@@ -659,31 +668,19 @@
   G2L_LOOP_FORWARD(d->public_to_private, item)
     {
       gcptr R = item->addr;
-      gcptr L = item->val;
-      revision_t v = (revision_t)L;
+      revision_t v = (revision_t)item->val;
 
       assert(!(R->h_tid & GCFLAG_PRIVATE_COPY));
       assert(R->h_tid & GCFLAG_PUBLIC_TO_PRIVATE);
       assert(!(R->h_tid & GCFLAG_NURSERY_MOVED));
-      assert(!stmgc_is_young(d, R));
+      assert(!is_young(R));
       assert(R->h_revision != localrev);
 
-      if (stmgc_is_young(d, L))
-        {
-          v |= 2;
 #ifdef DUMP_EXTRA
-          fprintf(stderr, "%p->h_revision = %p (UpdateChainHeads2)  "
-                  "<=== PUBLIC-TO-PROTECTED\n", R, (gcptr)v);
-          /*mark*/
+      fprintf(stderr, "%p->h_revision = %p (UpdateChainHeads2)\n",
+              R, (gcptr)v);
+      /*mark*/
 #endif
-        }
-      else
-        {
-#ifdef DUMP_EXTRA
-          fprintf(stderr, "%p->h_revision = %p (UpdateChainHeads2)\n", R, L);
-          /*mark*/
-#endif
-        }
       ACCESS_ONCE(R->h_revision) = v;
 
       if (R->h_tid & GCFLAG_PREBUILT_ORIGINAL)
diff --git a/c3/et.h b/c3/et.h
--- a/c3/et.h
+++ b/c3/et.h
@@ -52,6 +52,8 @@
  * set again at the next minor collection.
  *
  * GCFLAG_NURSERY_MOVED is used temporarily during minor collections.
+ *
+ * GCFLAG_OLD is set on old objects.
  */
 #define GCFLAG_PRIVATE_COPY      (STM_FIRST_GCFLAG << 0)
 #define GCFLAG_VISITED           (STM_FIRST_GCFLAG << 1)
@@ -59,10 +61,12 @@
 #define GCFLAG_PREBUILT_ORIGINAL (STM_FIRST_GCFLAG << 3)
 #define GCFLAG_WRITE_BARRIER     (STM_FIRST_GCFLAG << 4)
 #define GCFLAG_NURSERY_MOVED     (STM_FIRST_GCFLAG << 5)
+#define GCFLAG_OLD               (STM_FIRST_GCFLAG << 6)
 
 /* this value must be reflected in PREBUILT_FLAGS in stmgc.h */
 #define GCFLAG_PREBUILT  (GCFLAG_VISITED           | \
-                          GCFLAG_PREBUILT_ORIGINAL)
+                          GCFLAG_PREBUILT_ORIGINAL | \
+                          GCFLAG_OLD)
 
 #define GC_FLAG_NAMES  { "PRIVATE_COPY",      \
                          "VISITED",           \
@@ -70,6 +74,7 @@
                          "PREBUILT_ORIGINAL", \
                          "WRITE_BARRIER",     \
                          "NURSERY_MOVED",     \
+                         "OLD",               \
                          NULL }
 
 /************************************************************/
diff --git a/c3/gcpage.c b/c3/gcpage.c
--- a/c3/gcpage.c
+++ b/c3/gcpage.c
@@ -119,7 +119,7 @@
 
     struct tx_descriptor *d;
     for (d = tx_head; d; d = d->tx_next) {
-        if (stmgc_is_young(d, L))
+        if (stmgc_is_young_in(d, L))
             goto found;
     }
     assert(0);    /* L is not a young pointer anywhere! */
@@ -396,8 +396,8 @@
 
     G2L_LOOP_FORWARD(d->public_to_private, item) {
 
-        assert(stmgc_classify(d, item->addr) == K_PUBLIC);
-        assert(stmgc_classify(d, item->val)  == K_PRIVATE);
+        assert(stmgc_classify(item->addr) == K_PUBLIC);
+        assert(stmgc_classify(item->val)  == K_PRIVATE);
         item->addr->h_tid |= GCFLAG_PUBLIC_TO_PRIVATE;
 
     } G2L_LOOP_END;
@@ -689,7 +689,8 @@
         /* ^^^ write this line even if the following segfault */
         switch (stm_dbgmem_is_active(obj, 1)) {
         case 1:
-            if (thread_descriptor && stmgc_is_young(thread_descriptor, obj)) {
+            if (thread_descriptor &&
+                    stmgc_is_young_in(thread_descriptor, obj)) {
                 if (g2l_contains(
                        &thread_descriptor->young_objects_outside_nursery, obj))
                     fprintf(stderr, " (young but outside nursery)");
@@ -738,7 +739,7 @@
                    other thread */
                 if (!(obj->h_revision & 2) ||
                         (thread_descriptor &&
-                         stmgc_is_young(thread_descriptor, p))) {
+                         stmgc_is_young_in(thread_descriptor, p))) {
                     gcptrlist_insert(&pending, p);
                 }
                 else {
diff --git a/c3/nursery.c b/c3/nursery.c
--- a/c3/nursery.c
+++ b/c3/nursery.c
@@ -9,18 +9,29 @@
     return (d->nursery <= (char*)obj && ((char*)obj) < d->nursery_end);
 }
 
-int stmgc_is_young(struct tx_descriptor *d, gcptr obj)
+int stmgc_is_young_in(struct tx_descriptor *d, gcptr obj)
 {
+    /* Check if 'obj' is a young object for 'd'.  (So if it's a young
+       object for another thread, returns False.) */
     return is_in_nursery(d, obj) ||
         g2l_contains(&d->young_objects_outside_nursery, obj);
 }
 
-enum protection_class_t stmgc_classify(struct tx_descriptor *d, gcptr obj)
+#ifdef _GC_DEBUG
+int is_young(gcptr obj)
+{
+    int result = (obj->h_tid & GCFLAG_OLD) == 0;
+    assert(result == stmgc_is_young_in(thread_descriptor, obj));
+    return result;
+}
+#endif
+
+enum protection_class_t stmgc_classify(gcptr obj)
 {
     /* note that this function never returns K_OLD_PRIVATE. */
     if (obj->h_revision == stm_local_revision)
         return K_PRIVATE;
-    if (stmgc_is_young(d, obj))
+    if (is_young(obj))
         return K_PROTECTED;
     else
         return K_PUBLIC;
@@ -42,16 +53,21 @@
         G2L_FIND(d->young_objects_outside_nursery, obj, entry,
                  goto not_found);
 
-        if (obj->h_tid & GCFLAG_VISITED)
+        if (obj->h_tid & GCFLAG_OLD)
             e = private ? K_OLD_PRIVATE : K_PUBLIC;
         else
             e = private ? K_PRIVATE : K_PROTECTED;
     }
     assert(stm_dbgmem_is_active(obj, 0));
+    if (e == K_PRIVATE || e == K_PROTECTED)
+        assert((obj->h_tid & GCFLAG_OLD) == 0);
+    else
+        assert((obj->h_tid & GCFLAG_OLD) == GCFLAG_OLD);
     return e;
 
  not_found:
     assert(stm_dbgmem_is_active(obj, 1));
+    assert(obj->h_tid & GCFLAG_OLD);
     return private ? K_OLD_PRIVATE : K_PUBLIC;
 }
 
@@ -91,6 +107,7 @@
     gcptr p = stmgcpage_malloc(size);
     memset(p, 0, size);
     p->h_revision = stm_local_revision;
+    p->h_tid = GCFLAG_OLD;
     return p;
 }
 
@@ -145,7 +162,8 @@
     localobj->h_tid &= ~(GCFLAG_VISITED           |
                          GCFLAG_PUBLIC_TO_PRIVATE |
                          GCFLAG_PREBUILT_ORIGINAL |
-                         GCFLAG_WRITE_BARRIER);
+                         GCFLAG_WRITE_BARRIER     |
+                         GCFLAG_OLD);
     localobj->h_tid |= GCFLAG_PRIVATE_COPY;
     localobj->h_revision = stm_local_revision;
     return localobj;
@@ -243,11 +261,13 @@
     assert(!(obj->h_tid & GCFLAG_WRITE_BARRIER));
     assert(!(obj->h_tid & GCFLAG_PUBLIC_TO_PRIVATE));
     assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL));
+    assert(!(obj->h_tid & GCFLAG_OLD));
     assert(obj->h_revision & 1);    /* odd value so far */
 
     size_t size = stmcb_size(obj);
     gcptr fresh_old_copy = stmgcpage_malloc(size);
     memcpy(fresh_old_copy, obj, size);
+    fresh_old_copy->h_tid |= GCFLAG_OLD;
     obj->h_tid |= GCFLAG_NURSERY_MOVED;
     obj->h_revision = (revision_t)fresh_old_copy;
 
@@ -274,6 +294,11 @@
     struct tx_descriptor *d = thread_descriptor;
 
     recdump1(reason, obj);
+
+    /* Note: it's a good idea here to avoid reading any field of 'obj'
+       before we know it's a young object.  This avoids a lot of cache
+       misses and cache pollution.
+    */
  retry:
     if (!is_in_nursery(d, obj)) {
         /* 'obj' is not from the nursery (or 'obj == NULL') */
@@ -289,11 +314,11 @@
             goto ignore_and_try_again_with_next;
         }
         /* was it already marked? */
-        if (obj->h_tid & GCFLAG_VISITED) {
+        if (obj->h_tid & GCFLAG_OLD) {
             return;    /* yes, and no '*root' to fix, as it doesn't move */
         }
-        /* otherwise, add GCFLAG_VISITED, and continue below */
-        obj->h_tid |= GCFLAG_VISITED;
+        /* otherwise, add GCFLAG_OLD, and continue below */
+        obj->h_tid |= GCFLAG_OLD;
         fresh_old_copy = obj;
     }
     else {
@@ -336,7 +361,7 @@
         previous_obj = NULL;
     }
     obj = (gcptr)obj->h_revision;
-    assert(stmgc_classify(d, obj) != K_PRIVATE);
+    assert(stmgc_classify(obj) != K_PRIVATE);
     PATCH_ROOT_WITH(obj);
     goto retry;
 }
@@ -345,12 +370,13 @@
 static gcptr young_object_becomes_old(struct tx_descriptor *d, gcptr obj
                                       _REASON(char *reason))
 {
-    assert(stmgc_is_young(d, obj));
+    assert(stmgc_is_young_in(d, obj));
     assert(!(obj->h_tid & GCFLAG_NURSERY_MOVED));
     assert(!(obj->h_tid & GCFLAG_VISITED));
     assert(!(obj->h_tid & GCFLAG_WRITE_BARRIER));
     assert(!(obj->h_tid & GCFLAG_PUBLIC_TO_PRIVATE));
     assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL));
+    assert(!(obj->h_tid & GCFLAG_OLD));
     assert(obj->h_revision & 1);    /* odd value so far */
 
     visit_if_young(&obj  _REASON(reason));
@@ -544,8 +570,10 @@
 
         if (!is_in_nursery(d, obj)) {
             if (!g2l_contains(&d->young_objects_outside_nursery, obj) ||
-                (obj->h_tid & GCFLAG_VISITED)) {
-                /* non-young or visited young objects are kept */
+                (obj->h_tid & GCFLAG_OLD)) {
+                /* non-young or visited young objects are kept (the
+                   first line of this check could be removed, but it is
+                   better this way to avoid cache pollution) */
                 continue;
             }
         }
@@ -605,7 +633,7 @@
         return;
 
     struct tx_descriptor *d = thread_descriptor;
-    if (!stmgc_is_young(d, obj))
+    if (!stmgc_is_young_in(d, obj))
         return;
 
     /* xxx try to avoid duplicate stubs for the same object */
@@ -635,11 +663,7 @@
     G2L_LOOP_FORWARD(d->young_objects_outside_nursery, item) {
 
         gcptr obj = item->addr;
-        if (obj->h_tid & GCFLAG_VISITED) {
-            /* survives */
-            obj->h_tid &= ~GCFLAG_VISITED;
-        }
-        else {
+        if (!(obj->h_tid & GCFLAG_OLD)) {
             /* dies */
             stmgcpage_free(obj);
         }
@@ -670,16 +694,12 @@
 
     /* now all surviving nursery objects have been moved out, and all
        surviving young-but-outside-the-nursery objects have been flagged
-       with GCFLAG_VISITED */
+       with GCFLAG_OLD */
+    finish_public_to_young(d);
+
     if (g2l_any_entry(&d->young_objects_outside_nursery))
         free_unvisited_young_objects_outside_nursery(d);
 
-    /* now all previously young objects won't be changed any more and are
-       considered old (this is why we have to do this after
-       free_unvisited_young_objects_outside_nursery(), which clears
-       the VISITED flags and clears 'd->young_objects_outside_nursery') */
-    finish_public_to_young(d);
-
     teardown_minor_collect(d);
 
     /* clear the nursery */
@@ -787,18 +807,121 @@
 
 void stmgc_public_to_foreign_protected(gcptr R)
 {
-    abort();  //XXX
+    abort();//XXX
+#if 0
+    /* R 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;
+
+    /* repeat the checks in the caller, to avoid passing more than one
+       argument here */
+    revision_t v = ACCESS_ONCE(R->h_revision);
+    assert(!(v & 1));    /* "is a pointer" */
+    if (!(v & 2))
+        return;   /* changed already, retry */
+
+    gcptr L = (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);
+    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
+       modified in the meantime */
+    if (ACCESS_ONCE(R->h_revision) != v) {
+        spinlock_release(source_d->collection_lock);
+        return;   /* changed already, retry */
+    }
+
+    /* debugging support: "activate" the foreign nursery */
+    int was_active = stm_dbgmem_is_active(source_d->nursery, 0);
+    if (!was_active) assert(stmgc_nursery_hiding(0));
+
+    /* walk to the head of the chain in the foreign nursery */
+    while (1) {
+        ...
+
+    if (!is_in_nursery(d, obj)) {
+        /* 'obj' is not from the nursery (or 'obj == NULL') */
+        if (obj == NULL || !g2l_contains(
+                               &d->young_objects_outside_nursery, obj)) {
+            return;   /* then it's an old object or NULL, nothing to do */
+        }
+        /* it's a young object outside the nursery */
+
+        /* is it a protected object with a more recent revision?
+           (this test fails automatically if it's a private object) */
+        if (!(obj->h_revision & 1)) {
+            goto ignore_and_try_again_with_next;
+        }
+        /* was it already marked? */
+        if (obj->h_tid & GCFLAG_VISITED) {
+            return;    /* yes, and no '*root' to fix, as it doesn't move */
+        }
+        /* otherwise, add GCFLAG_VISITED, and continue below */
+        obj->h_tid |= GCFLAG_VISITED;
+        fresh_old_copy = obj;
+    }
+    else {
+        /* it's a nursery object.  Is it:
+           A. an already-moved nursery object?
+           B. a protected object with a more recent revision?
+           C. common case: first visit to an object to copy outside
+        */
+        if (!(obj->h_revision & 1)) {
+            
+            if (obj->h_tid & GCFLAG_NURSERY_MOVED) {
+                /* case A: just fix the ref. */
+                PATCH_ROOT_WITH((gcptr)obj->h_revision);
+                return;
+            }
+            else {
+                /* case B */
+                goto ignore_and_try_again_with_next;
+            }
+        }
+        /* case C */
+        fresh_old_copy = copy_outside_nursery(d, obj  _REASON("visit"));
+
+        /* fix the original reference */
+        PATCH_ROOT_WITH(fresh_old_copy);
+    }
+
+    /* add 'fresh_old_copy' to the list of objects to trace */
+    assert(!(fresh_old_copy->h_tid & GCFLAG_WRITE_BARRIER));
+    gcptrlist_insert(&d->old_objects_to_trace, fresh_old_copy);
+    recdump1("MOVED TO", fresh_old_copy);
+    return;
+
+ ignore_and_try_again_with_next:
+    if (previous_obj == NULL) {
+        previous_obj = obj;
+    }
+    else {
+        previous_obj->h_revision = obj->h_revision;    /* compress chain */
+        previous_obj = NULL;
+    }
+    obj = (gcptr)obj->h_revision;
+    assert(stmgc_classify(d, obj) != K_PRIVATE);
+    PATCH_ROOT_WITH(obj);
+    goto retry;
+
+    ...;
+#endif
 }
 
 #if 0
 void stmgc_follow_foreign(gcptr R)
 {
-    /* R is a global old object, which contains in h_revision a pointer
-       to a global *young* object --- but it is young for 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 *d = thread_descriptor;
 
     /* repeat the checks in the caller, to avoid passing more than one
diff --git a/c3/nursery.h b/c3/nursery.h
--- a/c3/nursery.h
+++ b/c3/nursery.h
@@ -42,9 +42,15 @@
 void stmgc_minor_collect_no_abort(void);
 int stmgc_minor_collect_anything_to_do(struct tx_descriptor *);
 void stmgc_write_barrier(gcptr);
-enum protection_class_t stmgc_classify(struct tx_descriptor *, gcptr);
-int stmgc_is_young(struct tx_descriptor *, gcptr);
+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(int);
 
+#ifdef _GC_DEBUG
+int is_young(gcptr);
+#else
+#  define is_young(o)  (((o)->h_tid & GCFLAG_OLD) == 0)
 #endif
+
+#endif
diff --git a/c3/stmgc.h b/c3/stmgc.h
--- a/c3/stmgc.h
+++ b/c3/stmgc.h
@@ -21,7 +21,7 @@
 #define STM_SIZE_OF_USER_TID       (sizeof(revision_t) / 2)    /* in bytes */
 #define STM_FIRST_GCFLAG           (1L << (8 * STM_SIZE_OF_USER_TID))
 #define STM_USER_TID_MASK          (STM_FIRST_GCFLAG - 1)
-#define PREBUILT_FLAGS             (STM_FIRST_GCFLAG * (2 + 8))
+#define PREBUILT_FLAGS             (STM_FIRST_GCFLAG * (2 + 8 + 64))
 #define PREBUILT_REVISION          1
 
 
diff --git a/c3/stmsync.c b/c3/stmsync.c
--- a/c3/stmsync.c
+++ b/c3/stmsync.c
@@ -104,7 +104,7 @@
 {
     gcptr result = _stm_allocate_object_of_size_old(size);
     assert(tid == (tid & STM_USER_TID_MASK));
-    result->h_tid = tid;
+    result->h_tid = tid | GCFLAG_OLD;
     return result;
 }
 
diff --git a/c3/test/support.py b/c3/test/support.py
--- a/c3/test/support.py
+++ b/c3/test/support.py
@@ -96,6 +96,7 @@
     #define GCFLAG_PREBUILT_ORIGINAL ...
     #define GCFLAG_WRITE_BARRIER     ...
     #define GCFLAG_NURSERY_MOVED     ...
+    #define GCFLAG_OLD               ...
     #define ABRT_MANUAL              ...
     typedef struct { ...; } page_header_t;
 ''')
@@ -372,7 +373,7 @@
 def oalloc(size):
     "Allocate an 'old' object, i.e. outside any nursery"
     p = lib.stmgcpage_malloc(size)
-    p.h_tid = GCFLAG_WRITE_BARRIER
+    p.h_tid = GCFLAG_WRITE_BARRIER | GCFLAG_OLD
     p.h_revision = lib.get_local_revision()
     lib.settid(p, 42 + size)
     return p
@@ -382,7 +383,7 @@
 def oalloc_refs(nrefs):
     "Allocate an 'old' object, i.e. outside any nursery, with nrefs pointers"
     p = lib.stmgcpage_malloc(HDR + WORD * nrefs)
-    p.h_tid = GCFLAG_WRITE_BARRIER
+    p.h_tid = GCFLAG_WRITE_BARRIER | GCFLAG_OLD
     p.h_revision = lib.get_local_revision()
     lib.settid(p, 421 + nrefs)
     for i in range(nrefs):
diff --git a/c3/test/test_gcpage.py b/c3/test/test_gcpage.py
--- a/c3/test/test_gcpage.py
+++ b/c3/test/test_gcpage.py
@@ -162,6 +162,7 @@
 
 def test_local_copy_from_global_obj():
     p1 = oalloc(HDR)
+    assert p1.h_tid & GCFLAG_OLD
     assert lib.stm_write_barrier(p1) == p1
     make_global(p1)
     p2n = lib.stm_write_barrier(p1)


More information about the pypy-commit mailing list