Author: Armin Rigo <[email protected]>
Branch: weakref
Changeset: r407:69291ba64476
Date: 2013-07-17 19:49 +0200
http://bitbucket.org/pypy/stmgc/changeset/69291ba64476/

Log:    Weakrefs in major collections. Tests are a bit light here given
        that there are a lot of possible corner cases.

diff --git a/c4/gcpage.c b/c4/gcpage.c
--- a/c4/gcpage.c
+++ b/c4/gcpage.c
@@ -222,11 +222,13 @@
         if (!(id_copy->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
             id_copy->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE;
             /* see fix_outdated() */
-            id_copy->h_tid |= GCFLAG_VISITED;
+            if (!(id_copy->h_tid & GCFLAG_VISITED)) {
+                id_copy->h_tid |= GCFLAG_VISITED;
 
-            /* XXX: may not always need tracing? */
-            if (!(id_copy->h_tid & GCFLAG_STUB))
-                gcptrlist_insert(&objects_to_trace, id_copy);
+                /* XXX: may not always need tracing? */
+                if (!(id_copy->h_tid & GCFLAG_STUB))
+                    gcptrlist_insert(&objects_to_trace, id_copy);
+            }
         } 
         else {
             /* prebuilt originals won't get collected anyway
@@ -236,6 +238,14 @@
     }
 }
 
+static void visit(gcptr *pobj);
+
+gcptr stmgcpage_visit(gcptr obj)
+{
+    visit(&obj);
+    return obj;
+}
+
 static void visit(gcptr *pobj)
 {
     gcptr obj = *pobj;
@@ -275,10 +285,10 @@
                 keep_original_alive(prev_obj);
 
                 assert(*pobj == prev_obj);
-                gcptr obj1 = obj;
-                visit(&obj1);       /* recursion, but should be only once */
+                /* recursion, but should be only once */
+                obj = stmgcpage_visit(obj);
                 assert(prev_obj->h_tid & GCFLAG_STUB);
-                prev_obj->h_revision = ((revision_t)obj1) | 2;
+                prev_obj->h_revision = ((revision_t)obj) | 2;
                 return;
             }
         }
@@ -649,8 +659,6 @@
     int i;
     wlog_t *item;
 
-    stm_invalidate_old_weakrefs(gcp);
-
     for (i = 1; i < GC_SMALL_REQUESTS; i++) {
         sweep_pages(gcp, i);
     }
@@ -777,9 +785,13 @@
     assert(gcptrlist_size(&objects_to_trace) == 0);
     mark_prebuilt_roots();
     mark_all_stack_roots();
-    visit_all_objects();
+    do {
+        visit_all_objects();
+        stm_visit_old_weakrefs();
+    } while (gcptrlist_size(&objects_to_trace) != 0);
     gcptrlist_delete(&objects_to_trace);
     clean_up_lists_of_read_objects_and_fix_outdated_flags();
+    stm_clean_old_weakrefs();
 
     mc_total_in_use = mc_total_reserved = 0;
     free_all_unused_local_pages();
diff --git a/c4/gcpage.h b/c4/gcpage.h
--- a/c4/gcpage.h
+++ b/c4/gcpage.h
@@ -84,6 +84,7 @@
 void stmgcpage_add_prebuilt_root(gcptr obj);
 void stmgcpage_possibly_major_collect(int force);
 long stmgcpage_count(int quantity);
+gcptr stmgcpage_visit(gcptr);
 
 extern struct GcPtrList stm_prebuilt_gcroots;
 
diff --git a/c4/test/test_weakref.py b/c4/test/test_weakref.py
--- a/c4/test/test_weakref.py
+++ b/c4/test/test_weakref.py
@@ -115,6 +115,6 @@
         major_collect()
         p2b = lib.stm_pop_root()
         p1 = lib.stm_pop_root()
-        assert lib.rawgetptr(p1, 0) == p2
+        assert lib.rawgetptr(p1, 0) == p2b
         assert p2b != p2
-        assert lib.rawgetlong(p2b, 0) == 912809218
+        assert lib.getlong(p2b, 0) == 912809218
diff --git a/c4/weakref.c b/c4/weakref.c
--- a/c4/weakref.c
+++ b/c4/weakref.c
@@ -64,29 +64,140 @@
 
 /***** Major collection *****/
 
-void stm_invalidate_old_weakrefs(struct tx_public_descriptor *gcp)
+static _Bool is_partially_visited(gcptr obj)
 {
-    /* walk over list of objects that contain weakrefs.  If the
-       object it references does not survive, invalidate the weakref */
-    long i;
+    /* Based on gcpage.c:visit().  Check the code here if we simplify
+       visit().  Returns True or False depending on whether we find any
+       version of 'obj' to be VISITED or not.
+    */
+ restart:
+    if (obj->h_tid & GCFLAG_VISITED)
+        return 1;
+
+    if (obj->h_revision & 1) {
+        assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
+        assert(!(obj->h_tid & GCFLAG_STUB));
+        return 0;
+    }
+    else if (obj->h_tid & GCFLAG_PUBLIC) {
+        /* h_revision is a ptr: we have a more recent version */
+        if (!(obj->h_revision & 2)) {
+            /* go visit the more recent version */
+            obj = (gcptr)obj->h_revision;
+        }
+        else {
+            /* it's a stub */
+            assert(obj->h_tid & GCFLAG_STUB);
+            obj = (gcptr)(obj->h_revision - 2);
+        }
+        goto restart;
+    }
+    else {
+        assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
+        gcptr B = (gcptr)obj->h_revision;
+        assert(B->h_tid & (GCFLAG_PUBLIC | GCFLAG_BACKUP_COPY));
+        if (B->h_tid & GCFLAG_VISITED)
+            return 1;
+        assert(!(obj->h_tid & GCFLAG_STUB));
+        assert(!(B->h_tid & GCFLAG_STUB));
+
+        if (IS_POINTER(B->h_revision)) {
+            assert(B->h_tid & GCFLAG_PUBLIC);
+            assert(!(B->h_tid & GCFLAG_BACKUP_COPY));
+            assert(!(B->h_revision & 2));
+
+            obj = (gcptr)B->h_revision;
+            goto restart;
+        }
+    }
+    return 0;
+}
+
+static void visit_old_weakrefs(struct tx_public_descriptor *gcp)
+{
+    /* Note: it's possible that a weakref points to a public stub to a
+       protected object, and only the protected object was marked as
+       VISITED so far.  In this case, this function needs to mark the
+       public stub as VISITED too.
+    */
+    long i, size = gcp->old_weakrefs.size;
     gcptr *items = gcp->old_weakrefs.items;
 
-    for (i = gcp->old_weakrefs.size - 1; i >= 0; i--) {
+    for (i = 0; i < size; i++) {
         gcptr weakref = items[i];
 
+        /* weakrefs are immutable: during a major collection, they
+           cannot be in the nursery, and so there should be only one
+           version of each weakref object.  XXX relying on this is
+           a bit fragile, but simplifies things a lot... */
+        assert(weakref->h_revision & 1);
+
         if (!(weakref->h_tid & GCFLAG_VISITED)) {
-            /* weakref itself dies */
+            /* the weakref itself dies */
         }
         else {
             size_t size = stmgc_size(weakref);
             gcptr pointing_to = WEAKREF_PTR(weakref, size);
-            //...;
-            abort();
+            assert(pointing_to != NULL);
+            if (is_partially_visited(pointing_to)) {
+                pointing_to = stmgcpage_visit(pointing_to);
+                assert(pointing_to->h_tid & GCFLAG_VISITED);
+                WEAKREF_PTR(weakref, size) = pointing_to;
+            }
+            else {
+                /* the weakref appears to be pointing to a dying object,
+                   but we don't know for sure now.  Clearing it is left
+                   to clean_old_weakrefs(). */
+            }
         }
+    }
+}
 
+static void clean_old_weakrefs(struct tx_public_descriptor *gcp)
+{
+    long i, size = gcp->old_weakrefs.size;
+    gcptr *items = gcp->old_weakrefs.items;
+
+    for (i = size - 1; i >= 0; i--) {
+        gcptr weakref = items[i];
+        assert(weakref->h_revision & 1);
+        if (weakref->h_tid & GCFLAG_VISITED) {
+            size_t size = stmgc_size(weakref);
+            gcptr pointing_to = WEAKREF_PTR(weakref, size);
+            if (pointing_to->h_tid & GCFLAG_VISITED) {
+                continue;   /* the target stays alive, the weakref remains */
+            }
+            WEAKREF_PTR(weakref, size) = NULL;  /* the target dies */
+        }
         /* remove this weakref from the list */
         items[i] = items[--gcp->old_weakrefs.size];
     }
-
     gcptrlist_compress(&gcp->old_weakrefs);
 }
+
+static void for_each_public_descriptor(
+                                  void visit(struct tx_public_descriptor *)) {
+    struct tx_descriptor *d;
+    for (d = stm_tx_head; d; d = d->tx_next)
+        visit(d->public_descriptor);
+
+    struct tx_public_descriptor *gcp;
+    revision_t index = -1;
+    while ((gcp = stm_get_free_public_descriptor(&index)) != NULL)
+        visit(gcp);
+}
+
+void stm_visit_old_weakrefs(void)
+{
+    /* Figure out which weakrefs survive, which possibly
+       adds more objects to 'objects_to_trace'.
+    */
+    for_each_public_descriptor(visit_old_weakrefs);
+}
+
+void stm_clean_old_weakrefs(void)
+{
+    /* Clean up the non-surviving weakrefs
+     */
+    for_each_public_descriptor(clean_old_weakrefs);
+}
diff --git a/c4/weakref.h b/c4/weakref.h
--- a/c4/weakref.h
+++ b/c4/weakref.h
@@ -3,7 +3,8 @@
 
 
 void stm_move_young_weakrefs(struct tx_descriptor *);
-void stm_invalidate_old_weakrefs(struct tx_public_descriptor *);
+void stm_visit_old_weakrefs(void);
+void stm_clean_old_weakrefs(void);
 
 
 #endif
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to