[pypy-commit] stmgc implement-id: merge

Raemi noreply at buildbot.pypy.org
Fri Jun 21 13:46:51 CEST 2013


Author: Remi Meier <meierrem at student.ethz.ch>
Branch: implement-id
Changeset: r217:74a48c97a3f6
Date: 2013-06-20 16:53 +0200
http://bitbucket.org/pypy/stmgc/changeset/74a48c97a3f6/

Log:	merge

diff --git a/c4/demo2.c b/c4/demo2.c
--- a/c4/demo2.c
+++ b/c4/demo2.c
@@ -105,7 +105,7 @@
                  stm_read_barrier((gcptr)r_next))) {
             asm volatile ("pause":::"memory");  /* smp_spinloop() */
             i++;
-            assert(i < 1000);
+            assert(i < 1000000);
         }
         // for now:
         assert(((nodeptr)stm_read_barrier((gcptr)r_prev->next))->value 
diff --git a/c4/et.c b/c4/et.c
--- a/c4/et.c
+++ b/c4/et.c
@@ -972,7 +972,7 @@
     } G2L_LOOP_END;
 }
 
-//static pthread_mutex_t mutex_prebuilt_gcroots = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t mutex_prebuilt_gcroots = PTHREAD_MUTEX_INITIALIZER;
 
 static void UpdateChainHeads(struct tx_descriptor *d, revision_t cur_time,
                              revision_t localrev)
@@ -1028,16 +1028,13 @@
 #endif
       ACCESS_ONCE(R->h_revision) = v;
 
-#if 0
       if (R->h_tid & GCFLAG_PREBUILT_ORIGINAL)
         {
           /* cannot possibly get here more than once for a given value of R */
           pthread_mutex_lock(&mutex_prebuilt_gcroots);
           gcptrlist_insert(&stm_prebuilt_gcroots, R);
           pthread_mutex_unlock(&mutex_prebuilt_gcroots);
-          /*mark*/
         }
-#endif
 
     } G2L_LOOP_END;
 
@@ -1433,6 +1430,8 @@
           /* we are reusing 'pd' */
           descriptor_array_free_list = pd->free_list_next;
           assert(descriptor_array_free_list >= 0);
+          assert(pd->stolen_objects.size == 0);
+          assert(pd->stolen_young_stubs.size == 0);
           assert(pd->collection_lock == 0 || pd->collection_lock == -1);
           pd->collection_lock = 0;
       }
@@ -1483,8 +1482,12 @@
     assert(d->active == 0);
     stmgcpage_acquire_global_lock();
 
-    /* our nursery is empty at this point */
+    /* our nursery is empty at this point.  The list 'stolen_objects'
+       should have been emptied at the previous minor collection and
+       should remain empty because we don't have any young object. */
+    assert(d->public_descriptor->stolen_objects.size == 0);
     assert(d->public_descriptor->stolen_young_stubs.size == 0);
+    gcptrlist_delete(&d->public_descriptor->stolen_objects);
     gcptrlist_delete(&d->public_descriptor->stolen_young_stubs);
 
     stmgcpage_done_tls();
diff --git a/c4/gcpage.c b/c4/gcpage.c
--- a/c4/gcpage.c
+++ b/c4/gcpage.c
@@ -199,21 +199,56 @@
         return;    /* already seen */
 
     if (obj->h_revision & 1) {
+        assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
         obj->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE;  /* see also fix_outdated() */
     }
-    else {
+    else if (obj->h_tid & GCFLAG_PUBLIC) {
         /* h_revision is a ptr: we have a more recent version */
-        assert(!(obj->h_tid & GCFLAG_STUB));
         gcptr prev_obj = obj;
-        obj = (gcptr)obj->h_revision;   /* go visit the more recent version */
 
-        if (!(obj->h_tid & GCFLAG_VISITED) && IS_POINTER(obj->h_revision)) {
+        if (!(obj->h_revision & 2)) {
+            /* go visit the more recent version */
             obj = (gcptr)obj->h_revision;
+        }
+        else {
+            /* it's a stub: keep it if it points to a protected version,
+               because we need to keep the effect of stealing if it is
+               later accessed by the wrong thread.  If it points to a
+               public object (possibly outdated), we can ignore the stub.
+            */
+            assert(obj->h_tid & GCFLAG_STUB);
+            obj = (gcptr)(obj->h_revision - 2);
+            if (!(obj->h_tid & GCFLAG_PUBLIC)) {
+                prev_obj->h_tid |= GCFLAG_VISITED;
+                assert(*pobj == prev_obj);
+                gcptr obj1 = obj;
+                visit(&obj1);       /* recursion, but should be only once */
+                prev_obj->h_revision = ((revision_t)obj1) + 2;
+                return;
+            }
+        }
+
+        if (!(obj->h_revision & 3)) {
+            obj = (gcptr)obj->h_revision;
+            assert(obj->h_tid & GCFLAG_PUBLIC);
             prev_obj->h_revision = (revision_t)obj;
         }
         *pobj = obj;
         goto restart;
     }
+    else {
+        assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
+        gcptr B = (gcptr)obj->h_revision;
+        if (!(B->h_tid & GCFLAG_PUBLIC)) {
+            /* a regular private_from_protected object with a backup copy B */
+            assert(B->h_tid & GCFLAG_BACKUP_COPY);
+            B->h_tid |= GCFLAG_VISITED;
+        }
+        else {
+            assert(!(B->h_tid & GCFLAG_BACKUP_COPY));
+            abort();  // XXX
+        }
+    }
     obj->h_tid |= GCFLAG_VISITED;
     gcptrlist_insert(&objects_to_trace, obj);
 }
@@ -304,6 +339,11 @@
     if (d->active < 0)
         return;       /* already "aborted" during forced minor collection */
 
+    if (d->active == 2) {
+        /* inevitable transaction: clear the list of read objects */
+        gcptrlist_clear(&d->list_of_read_objects);
+    }
+
     for (i = d->list_of_read_objects.size - 1; i >= 0; --i) {
         gcptr obj = items[i];
 
@@ -313,7 +353,8 @@
            just removing it is very wrong --- we want 'd' to abort.
         */
         revision_t v = obj->h_revision;
-        if (IS_POINTER(v)) {    /* has a more recent revision.  Oups. */
+        if (IS_POINTER(v) && !(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)) {
+            /* has a more recent revision.  Oups. */
             fprintf(stderr,
                     "ABRT_COLLECT_MAJOR: %p was read but modified already\n",
                     obj);
@@ -321,16 +362,13 @@
             return;
         }
 
-        /* on the other hand, if we see a non-visited object in the read
-           list, then we need to remove it --- it's wrong to just abort.
-           Consider the following case: the transaction is inevitable,
-           and since it started, it popped objects out of its shadow
-           stack.  Some popped objects might become free even if they
-           have been read from.  We must not abort such transactions
-           (and cannot anyway: they are inevitable!). */
-        if (!(obj->h_tid & GCFLAG_VISITED)) {
-            items[i] = items[--d->list_of_read_objects.size];
-        }
+        /* It should not be possible to see a non-visited object in the
+           read list.  I think the only case is: the transaction is
+           inevitable, and since it started, it popped objects out of
+           its shadow stack.  Some popped objects might become free even
+           if they have been read from.  But for inevitable transactions,
+           we clear the 'list_of_read_objects' above anyway. */
+        assert(obj->h_tid & GCFLAG_VISITED);
     }
 
     d->num_read_objects_known_old = d->list_of_read_objects.size;
@@ -483,6 +521,36 @@
 }
 
 
+/***** Major collections: forcing minor collections *****/
+
+void force_minor_collections(void)
+{
+    struct tx_descriptor *d;
+    struct tx_descriptor *saved = thread_descriptor;
+    revision_t saved_private_rev = stm_private_rev_num;
+    assert(saved_private_rev == *saved->private_revision_ref);
+
+    for (d = stm_tx_head; d; d = d->tx_next) {
+        /* Force a minor collection to run in the thread 'd'.
+           Usually not needed, but it may be the case that this major
+           collection was not preceeded by a minor collection if the
+           thread is busy in a system call for example.
+        */
+        if (stmgc_minor_collect_anything_to_do(d)) {
+            /* Hack: temporarily pretend that we "are" the other thread...
+             */
+            thread_descriptor = d;
+            stm_private_rev_num = *d->private_revision_ref;
+            //assert(stmgc_nursery_hiding(d, 0));
+            stmgc_minor_collect_no_abort();
+            //assert(stmgc_nursery_hiding(d, 1));
+            thread_descriptor = saved;
+            stm_private_rev_num = saved_private_rev;
+        }
+    }
+}
+
+
 /***** Major collections: main *****/
 
 void update_next_threshold(void)
@@ -519,11 +587,9 @@
     stmgcpage_acquire_global_lock();
     fprintf(stderr, ",-----\n| running major collection...\n");
 
-#if 0
     force_minor_collections();
 
     assert(gcptrlist_size(&objects_to_trace) == 0);
-#endif
     mark_prebuilt_roots();
     mark_all_stack_roots();
     visit_all_objects();
diff --git a/c4/nursery.c b/c4/nursery.c
--- a/c4/nursery.c
+++ b/c4/nursery.c
@@ -357,6 +357,11 @@
     gcptr *items = d->list_of_read_objects.items;
     assert(d->list_of_read_objects.size >= limit);
 
+    if (d->active == 2) {
+        /* inevitable transaction: clear the list of read objects */
+        gcptrlist_clear(&d->list_of_read_objects);
+    }
+
     for (i = d->list_of_read_objects.size - 1; i >= limit; --i) {
         gcptr obj = items[i];
 
@@ -444,6 +449,12 @@
     AbortNowIfDelayed();
 }
 
+void stmgc_minor_collect_no_abort(void)
+{
+    struct tx_descriptor *d = thread_descriptor;
+    minor_collect(d);
+}
+
 int stmgc_minor_collect_anything_to_do(struct tx_descriptor *d)
 {
     if (d->nursery_current == d->nursery_base /*&&
diff --git a/c4/nursery.h b/c4/nursery.h
--- a/c4/nursery.h
+++ b/c4/nursery.h
@@ -40,6 +40,7 @@
 void stmgc_init_nursery(void);
 void stmgc_done_nursery(void);
 void stmgc_minor_collect(void);
+void stmgc_minor_collect_no_abort(void);
 int stmgc_minor_collect_anything_to_do(struct tx_descriptor *);
 gcptr stmgc_duplicate(gcptr);
 gcptr stmgc_duplicate_old(gcptr);
diff --git a/c4/test/test_gcpage.py b/c4/test/test_gcpage.py
--- a/c4/test/test_gcpage.py
+++ b/c4/test/test_gcpage.py
@@ -265,3 +265,51 @@
     check_prebuilt(p1)
     check_free_old(p2)
     check_not_free(p3)     # XXX replace with p1
+
+def test_prebuilt_version_to_protected():
+    p1 = lib.pseudoprebuilt(HDR, 42 + HDR)
+    p2 = lib.stm_write_barrier(p1)
+    lib.stm_commit_transaction()
+    lib.stm_begin_inevitable_transaction()
+    minor_collect()
+    p2 = lib.stm_read_barrier(p1)
+    assert p2 != p1
+    minor_collect()
+    major_collect()
+    check_prebuilt(p1)
+    check_not_free(p2)     # XXX replace with p1
+
+def test_private():
+    p1 = nalloc(HDR)
+    lib.stm_push_root(p1)
+    minor_collect()
+    major_collect()
+    p1 = lib.stm_pop_root()
+    assert not lib.in_nursery(p1)
+    check_not_free(p1)
+
+def test_major_collect_first_does_minor_collect():
+    p1 = nalloc(HDR)
+    lib.stm_push_root(p1)
+    major_collect()
+    p1 = lib.stm_pop_root()
+    assert not lib.in_nursery(p1)
+    check_not_free(p1)
+
+def test_private_from_protected_young():
+    p1 = nalloc(HDR)
+    lib.stm_commit_transaction()
+    lib.stm_begin_inevitable_transaction()
+    p1b = lib.stm_write_barrier(p1)
+    assert p1b == p1
+    check_not_free(follow_revision(p1))
+    assert follow_revision(p1).h_tid & GCFLAG_BACKUP_COPY
+    lib.stm_push_root(p1)
+    major_collect()
+    p1 = lib.stm_pop_root()
+    assert not lib.in_nursery(p1)
+    check_not_free(p1)
+    p1b = lib.stm_write_barrier(p1)
+    assert p1b == p1
+    check_not_free(follow_revision(p1))
+    assert follow_revision(p1).h_tid & GCFLAG_BACKUP_COPY


More information about the pypy-commit mailing list