Author: Remi Meier <remi.me...@gmail.com>
Branch: stmgc-c4
Changeset: r65771:ed40f1172b80
Date: 2013-07-29 08:41 +0200
http://bitbucket.org/pypy/pypy/changeset/ed40f1172b80/

Log:    Merge

diff --git a/rpython/memory/gc/stmgc.py b/rpython/memory/gc/stmgc.py
--- a/rpython/memory/gc/stmgc.py
+++ b/rpython/memory/gc/stmgc.py
@@ -44,15 +44,16 @@
     GCFLAG_PREBUILT_ORIGINAL      = first_gcflag << 3
     GCFLAG_PUBLIC_TO_PRIVATE      = first_gcflag << 4
     GCFLAG_WRITE_BARRIER          = first_gcflag << 5 # stmgc.h
-    GCFLAG_NURSERY_MOVED          = first_gcflag << 6
+    GCFLAG_MOVED                  = first_gcflag << 6
     GCFLAG_BACKUP_COPY            = first_gcflag << 7 # debug
     GCFLAG_STUB                   = first_gcflag << 8 # debug
     GCFLAG_PRIVATE_FROM_PROTECTED = first_gcflag << 9
     GCFLAG_HAS_ID                 = first_gcflag << 10
     GCFLAG_IMMUTABLE              = first_gcflag << 11
     GCFLAG_SMALLSTUB              = first_gcflag << 12
+    GCFLAG_MARKED                 = first_gcflag << 13
     
-    PREBUILT_FLAGS    = first_gcflag * (1 + 2 + 4 + 8)
+    PREBUILT_FLAGS    = first_gcflag * ((1<<0) | (1<<1) | (1<<2) | (1<<3) | 
(1<<13))
     PREBUILT_REVISION = r_uint(1)
     
     FX_MASK = 65535
@@ -109,7 +110,7 @@
         # XXX finalizers are ignored for now
         #ll_assert(not needs_finalizer, 'XXX needs_finalizer')
         #ll_assert(not is_finalizer_light, 'XXX is_finalizer_light')
-        #ll_assert(not contains_weakptr, 'XXX contains_weakptr')
+        ll_assert(not contains_weakptr, 'contains_weakptr: use malloc_weakref')
         # XXX call optimized versions, e.g. if size < GC_NURSERY_SECTION
         return llop.stm_allocate(llmemory.GCREF, size, typeid16)
 
@@ -131,12 +132,14 @@
         seen by the GC, then it can get collected."""
         tid = self.get_hdr_tid(obj)[0]
         if bool(tid & self.GCFLAG_OLD):
-            return False
+            return False    # XXX wrong so far.  We should add a flag to the
+                            # object that means "don't ever kill this copy"
         return True
         
 
     @classmethod
     def JIT_max_size_of_young_obj(cls):
+        # XXX there is actually a maximum, check
         return None
 
     @classmethod
diff --git a/rpython/translator/stm/src_stm/dbgmem.c 
b/rpython/translator/stm/src_stm/dbgmem.c
--- a/rpython/translator/stm/src_stm/dbgmem.c
+++ b/rpython/translator/stm/src_stm/dbgmem.c
@@ -9,7 +9,7 @@
 #ifdef _GC_DEBUG
 /************************************************************/
 
-#define MMAP_TOTAL  671088640   /* 640MB */
+#define MMAP_TOTAL  1280*1024*1024   /* 1280MB */
 
 static pthread_mutex_t malloc_mutex = PTHREAD_MUTEX_INITIALIZER;
 static char *zone_start, *zone_current = NULL, *zone_end = NULL;
@@ -71,6 +71,10 @@
 
 void stm_free(void *p, size_t sz)
 {
+    if (p == NULL) {
+        assert(sz == 0);
+        return;
+    }
     assert(((intptr_t)((char *)p + sz) & (PAGE_SIZE-1)) == 0);
 
     size_t nb_pages = (sz + PAGE_SIZE - 1) / PAGE_SIZE + 1;
@@ -84,6 +88,14 @@
     _stm_dbgmem(p, sz, PROT_NONE);
 }
 
+void *stm_realloc(void *p, size_t newsz, size_t oldsz)
+{
+    void *r = stm_malloc(newsz);
+    memcpy(r, p, oldsz < newsz ? oldsz : newsz);
+    stm_free(p, oldsz);
+    return r;
+}
+
 int _stm_can_access_memory(char *p)
 {
     long base = ((char *)p - zone_start) / PAGE_SIZE;
diff --git a/rpython/translator/stm/src_stm/dbgmem.h 
b/rpython/translator/stm/src_stm/dbgmem.h
--- a/rpython/translator/stm/src_stm/dbgmem.h
+++ b/rpython/translator/stm/src_stm/dbgmem.h
@@ -7,6 +7,7 @@
 
 void *stm_malloc(size_t);
 void stm_free(void *, size_t);
+void *stm_realloc(void *, size_t, size_t);
 int _stm_can_access_memory(char *);
 void assert_cleared(char *, size_t);
 
@@ -14,6 +15,7 @@
 
 #define stm_malloc(sz)    malloc(sz)
 #define stm_free(p,sz)    free(p)
+#define stm_realloc(p,newsz,oldsz)  realloc(p,newsz)
 #define assert_cleared(p,sz)     do { } while(0)
 
 #endif
diff --git a/rpython/translator/stm/src_stm/et.c 
b/rpython/translator/stm/src_stm/et.c
--- a/rpython/translator/stm/src_stm/et.c
+++ b/rpython/translator/stm/src_stm/et.c
@@ -146,7 +146,7 @@
           gcptr P_prev = P;
           P = (gcptr)v;
           assert((P->h_tid & GCFLAG_PUBLIC) ||
-                 (P_prev->h_tid & GCFLAG_NURSERY_MOVED));
+                 (P_prev->h_tid & GCFLAG_MOVED));
 
           v = ACCESS_ONCE(P->h_revision);
 
@@ -238,7 +238,7 @@
  add_in_recent_reads_cache:
   /* The risks are that the following assert fails, because the flag was
      added just now by a parallel thread during stealing... */
-  /*assert(!(P->h_tid & GCFLAG_NURSERY_MOVED));*/
+  /*assert(!(P->h_tid & GCFLAG_MOVED));*/
   fxcache_add(&d->recent_reads_cache, P);
   return P;
 
@@ -281,7 +281,7 @@
    */
   if (P->h_tid & GCFLAG_PUBLIC)
     {
-      if (P->h_tid & GCFLAG_NURSERY_MOVED)
+      if (P->h_tid & GCFLAG_MOVED)
         {
           P = (gcptr)P->h_revision;
           assert(P->h_tid & GCFLAG_PUBLIC);
@@ -413,7 +413,7 @@
 
       while (v = P->h_revision, IS_POINTER(v))
         {
-          if (P->h_tid & GCFLAG_NURSERY_MOVED)
+          if (P->h_tid & GCFLAG_MOVED)
             dprintf(("nursery_moved "));
 
           if (v & 2)
@@ -510,7 +510,7 @@
 static gcptr LocalizePublic(struct tx_descriptor *d, gcptr R)
 {
   assert(R->h_tid & GCFLAG_PUBLIC);
-  assert(!(R->h_tid & GCFLAG_NURSERY_MOVED));
+  assert(!(R->h_tid & GCFLAG_MOVED));
 
 #ifdef _GC_DEBUG
   wlog_t *entry;
@@ -570,6 +570,13 @@
 gcptr stm_WriteBarrier(gcptr P)
 {
   assert(!(P->h_tid & GCFLAG_IMMUTABLE));
+  assert((P->h_tid & GCFLAG_STUB) ||
+         stmgc_size(P) > sizeof(struct stm_stub_s) - WORD);
+  /* If stmgc_size(P) gives a number <= sizeof(stub)-WORD, then there is a
+     risk of overrunning the object later in gcpage.c when copying a stub
+     over it.  However such objects are so small that they contain no field
+     at all, and so no write barrier should occur on them. */
+
   if (is_private(P))
     {
       /* If we have GCFLAG_WRITE_BARRIER in P, then list it into
@@ -606,7 +613,7 @@
          Add R into the list 'public_with_young_copy', unless W is
          actually an old object, in which case we need to record W.
       */
-      if (R->h_tid & GCFLAG_NURSERY_MOVED)
+      if (R->h_tid & GCFLAG_MOVED)
         {
           /* Bah, the object turned into this kind of stub, possibly
              while we were waiting for the collection_lock, because it
@@ -696,8 +703,8 @@
                   continue;
                 }
             }
-          else if ((R->h_tid & (GCFLAG_PUBLIC | GCFLAG_NURSERY_MOVED))
-                            == (GCFLAG_PUBLIC | GCFLAG_NURSERY_MOVED))
+          else if ((R->h_tid & (GCFLAG_PUBLIC | GCFLAG_MOVED))
+                            == (GCFLAG_PUBLIC | GCFLAG_MOVED))
             {
               /* such an object is identical to the one it points to
                (stolen protected young object with h_revision pointing
@@ -970,6 +977,7 @@
   revision_t my_lock = d->my_lock;
   wlog_t *item;
 
+  dprintf(("acquire_locks\n"));
   assert(!stm_has_got_any_lock(d));
   assert(d->public_descriptor->stolen_objects.size == 0);
 
@@ -982,6 +990,7 @@
       revision_t v;
     retry:
       assert(R->h_tid & GCFLAG_PUBLIC);
+      assert(R->h_tid & GCFLAG_PUBLIC_TO_PRIVATE);
       v = ACCESS_ONCE(R->h_revision);
       if (IS_POINTER(v))     /* "has a more recent revision" */
         {
@@ -1014,7 +1023,7 @@
 static void CancelLocks(struct tx_descriptor *d)
 {
   wlog_t *item;
-
+  dprintf(("cancel_locks\n"));
   if (!g2l_any_entry(&d->public_to_private))
     return;
 
@@ -1107,7 +1116,7 @@
       assert(!(L->h_tid & GCFLAG_VISITED));
       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_MOVED));
       assert(L->h_revision != localrev);   /* modified by AcquireLocks() */
 
 #ifdef DUMP_EXTRA
@@ -1119,7 +1128,9 @@
       gcptr stub = stm_stub_malloc(d->public_descriptor, 0);
       stub->h_tid = (L->h_tid & STM_USER_TID_MASK) | GCFLAG_PUBLIC
                                                    | GCFLAG_STUB
+                                                   | GCFLAG_SMALLSTUB
                                                    | GCFLAG_OLD;
+      dprintf(("et.c: stm_stub_malloc -> %p\n", stub));
       stub->h_revision = ((revision_t)L) | 2;
 
       assert(!(L->h_tid & GCFLAG_HAS_ID));
@@ -1154,7 +1165,7 @@
 
       assert(R->h_tid & GCFLAG_PUBLIC);
       assert(R->h_tid & GCFLAG_PUBLIC_TO_PRIVATE);
-      assert(!(R->h_tid & GCFLAG_NURSERY_MOVED));
+      assert(!(R->h_tid & GCFLAG_MOVED));
       assert(R->h_revision != localrev);
 
 #ifdef DUMP_EXTRA
@@ -1249,7 +1260,7 @@
           assert(!(B->h_tid & GCFLAG_BACKUP_COPY));
           P->h_tid |= GCFLAG_PUBLIC;
           assert(!(P->h_tid & GCFLAG_HAS_ID));
-          if (!(P->h_tid & GCFLAG_OLD)) P->h_tid |= GCFLAG_NURSERY_MOVED;
+          if (!(P->h_tid & GCFLAG_OLD)) P->h_tid |= GCFLAG_MOVED;
           /* P becomes a public outdated object.  It may create an
              exception documented in doc-objects.txt: a public but young
              object.  It's still fine because it should only be seen by
@@ -1282,7 +1293,7 @@
   revision_t cur_time;
   struct tx_descriptor *d = thread_descriptor;
   assert(d->active >= 1);
-
+  dprintf(("CommitTransaction(%p)\n", d));
   spinlock_acquire(d->public_descriptor->collection_lock, 'C');  /*committing*/
   if (d->public_descriptor->stolen_objects.size != 0)
     stm_normalize_stolen_objects(d);
@@ -1366,6 +1377,7 @@
   d->active = 2;
   d->reads_size_limit_nonatomic = 0;
   update_reads_size_limit(d);
+  dprintf(("make_inevitable(%p)\n", d));
 }
 
 static revision_t acquire_inev_mutex_and_mark_global_cur_time(
diff --git a/rpython/translator/stm/src_stm/et.h 
b/rpython/translator/stm/src_stm/et.h
--- a/rpython/translator/stm/src_stm/et.h
+++ b/rpython/translator/stm/src_stm/et.h
@@ -26,7 +26,11 @@
  *
  * GCFLAG_OLD is set on old objects.
  *
- * GCFLAG_VISITED is used temporarily during major collections.
+ * GCFLAG_VISITED and GCFLAG_MARKED are used temporarily during major
+ * collections.  The objects are MARKED|VISITED as soon as they have been
+ * added to 'objects_to_trace', and so will be or have been traced.  The
+ * objects are only MARKED if their memory must be kept alive, but (so far)
+ * we found that tracing them is not useful.
  *
  * GCFLAG_PUBLIC is set on public objects.
  *
@@ -47,7 +51,7 @@
  * the list 'old_objects_to_trace'; it is set again at the next minor
  * collection.
  *
- * GCFLAG_NURSERY_MOVED is used temporarily during minor collections.
+ * GCFLAG_MOVED is used temporarily during minor/major collections.
  *
  * GCFLAG_STUB is set for debugging on stub objects made by stealing or
  * by major collections.  'p_stub->h_revision' might be a value
@@ -68,16 +72,20 @@
 static const revision_t GCFLAG_PREBUILT_ORIGINAL      = STM_FIRST_GCFLAG << 3;
 static const revision_t GCFLAG_PUBLIC_TO_PRIVATE      = STM_FIRST_GCFLAG << 4;
 // in stmgc.h:          GCFLAG_WRITE_BARRIER          = STM_FIRST_GCFLAG << 5;
-static const revision_t GCFLAG_NURSERY_MOVED          = STM_FIRST_GCFLAG << 6;
+static const revision_t GCFLAG_MOVED                  = STM_FIRST_GCFLAG << 6;
 static const revision_t GCFLAG_BACKUP_COPY  /*debug*/ = STM_FIRST_GCFLAG << 7;
 static const revision_t GCFLAG_STUB         /*debug*/ = STM_FIRST_GCFLAG << 8;
 static const revision_t GCFLAG_PRIVATE_FROM_PROTECTED = STM_FIRST_GCFLAG << 9;
 static const revision_t GCFLAG_HAS_ID                 = STM_FIRST_GCFLAG << 10;
 static const revision_t GCFLAG_IMMUTABLE              = STM_FIRST_GCFLAG << 11;
+static const revision_t GCFLAG_SMALLSTUB    /*debug*/ = STM_FIRST_GCFLAG << 12;
+static const revision_t GCFLAG_MARKED                 = STM_FIRST_GCFLAG << 13;
+/* warning, the last flag available is "<< 15" on 32-bit */
 
 
 /* this value must be reflected in PREBUILT_FLAGS in stmgc.h */
 #define GCFLAG_PREBUILT  (GCFLAG_VISITED           | \
+                          GCFLAG_MARKED            | \
                           GCFLAG_PREBUILT_ORIGINAL | \
                           GCFLAG_OLD               | \
                           GCFLAG_PUBLIC)
@@ -88,12 +96,14 @@
                          "PREBUILT_ORIGINAL", \
                          "PUBLIC_TO_PRIVATE", \
                          "WRITE_BARRIER",     \
-                         "NURSERY_MOVED",     \
+                         "MOVED",             \
                          "BACKUP_COPY",       \
                          "STUB",              \
                          "PRIVATE_FROM_PROTECTED", \
-                         "HAS_ID", \
-                         "IMMUTABLE", \
+                         "HAS_ID",            \
+                         "IMMUTABLE",         \
+                         "SMALLSTUB",         \
+                         "MARKED",            \
                          NULL }
 
 #define IS_POINTER(v)    (!((v) & 1))   /* even-valued number */
diff --git a/rpython/translator/stm/src_stm/extra.c 
b/rpython/translator/stm/src_stm/extra.c
--- a/rpython/translator/stm/src_stm/extra.c
+++ b/rpython/translator/stm/src_stm/extra.c
@@ -132,17 +132,16 @@
 
 _Bool stm_pointer_equal(gcptr p1, gcptr p2)
 {
-    /* fast path for two equal pointers */
-    if (p1 == p2)
-        return 1;
-    /* if p1 or p2 is NULL (but not both, because they are different
-       pointers), then return 0 */
-    if (p1 == NULL || p2 == NULL)
-        return 0;
-    /* types must be the same */
-    if ((p1->h_tid & STM_USER_TID_MASK) != (p2->h_tid & STM_USER_TID_MASK))
-        return 0;
-    return stm_id(p1) == stm_id(p2);
+    if (p1 != NULL && p2 != NULL) {
+        /* resolve h_original, but only if !PREBUILT_ORIGINAL */
+        if (p1->h_original && !(p1->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
+            p1 = (gcptr)p1->h_original;
+        }
+        if (p2->h_original && !(p2->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
+            p2 = (gcptr)p2->h_original;
+        }
+    }
+    return (p1 == p2);
 }
 
 /************************************************************/
diff --git a/rpython/translator/stm/src_stm/gcpage.c 
b/rpython/translator/stm/src_stm/gcpage.c
--- a/rpython/translator/stm/src_stm/gcpage.c
+++ b/rpython/translator/stm/src_stm/gcpage.c
@@ -213,157 +213,229 @@
 
 static struct GcPtrList objects_to_trace;
 
-static void keep_original_alive(gcptr obj)
+static gcptr copy_over_original(gcptr obj, gcptr id_copy)
 {
-    /* keep alive the original of a visited object */
-    gcptr id_copy = (gcptr)obj->h_original;
-    /* prebuilt original objects may have a predifined
-       hash in h_original */
-    if (id_copy && !(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
-        if (!(id_copy->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
-            id_copy->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE;
-            /* see fix_outdated() */
-            if (!(id_copy->h_tid & GCFLAG_VISITED)) {
-                id_copy->h_tid |= GCFLAG_VISITED;
+    assert(obj != id_copy);
+    assert(id_copy == (gcptr)obj->h_original);
+    assert(!(id_copy->h_revision & 1)); /* not head-revision itself */
 
-                /* 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
-               and if they are not reachable in any other way,
-               we only ever need their location, not their content */
+    /* check a few flags */
+    assert(obj->h_tid & GCFLAG_PUBLIC);
+    assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL));
+    assert(!(obj->h_tid & GCFLAG_BACKUP_COPY));
+
+    assert(id_copy->h_tid & GCFLAG_PUBLIC);
+    assert(!(id_copy->h_tid & GCFLAG_BACKUP_COPY));
+
+    /* id_copy may be a stub, but in this case, as the original, it
+       should have been allocated with a big enough chunk of memory.
+       Also, obj itself might be a stub. */
+    assert(!(id_copy->h_tid & GCFLAG_SMALLSTUB));
+    if (!(id_copy->h_tid & GCFLAG_STUB) && !(obj->h_tid & GCFLAG_STUB)) {
+        assert(stmgc_size(id_copy) == stmgc_size(obj));
+    }
+
+    /* add the MOVED flag to 'obj' */
+    obj->h_tid |= GCFLAG_MOVED;
+
+    /* copy the object's content */
+    size_t objsize;
+    if (obj->h_tid & GCFLAG_STUB)
+        objsize = sizeof(struct stm_stub_s);
+    else {
+        objsize = stmgc_size(obj);
+        assert(objsize > sizeof(struct stm_stub_s) - WORD);
+    }
+    dprintf(("copy %p over %p (%zd bytes)\n", obj, id_copy, objsize));
+    memcpy(id_copy + 1, obj + 1, objsize - sizeof(struct stm_object_s));
+
+    /* copy the object's h_revision number */
+    id_copy->h_revision = obj->h_revision;
+
+    /* copy the STUB flag */
+    id_copy->h_tid &= ~GCFLAG_STUB;
+    id_copy->h_tid |= (obj->h_tid & GCFLAG_STUB);
+
+    return id_copy;
+}
+
+static void visit_nonpublic(gcptr obj, struct tx_public_descriptor *gcp)
+{
+    /* Visit a protected or private object.  'gcp' must be either NULL or
+       point to the thread that has got the object.  This 'gcp' is only an
+       optimization: it lets us trace (most) private/protected objects
+       and replace pointers to public objects in them with pointers to
+       private/protected objects if they are the most recent ones,
+       provided they belong to the same thread.
+    */
+    assert(!(obj->h_tid & GCFLAG_PUBLIC));
+    assert(!(obj->h_tid & GCFLAG_STUB));
+    assert(!(obj->h_tid & GCFLAG_HAS_ID));
+    assert(!(obj->h_tid & GCFLAG_SMALLSTUB));
+    assert(!(obj->h_tid & GCFLAG_MOVED));
+
+    if (obj->h_tid & GCFLAG_VISITED)
+        return;        /* already visited */
+
+    obj->h_tid |= GCFLAG_VISITED | GCFLAG_MARKED;
+    gcptrlist_insert2(&objects_to_trace, obj, (gcptr)gcp);
+
+    obj = (gcptr)obj->h_original;
+    if (obj != NULL)
+        obj->h_tid |= GCFLAG_MARKED;
+}
+
+static gcptr visit_public(gcptr obj, struct tx_public_descriptor *gcp)
+{
+    /* The goal is to walk to the most recent copy, then copy its
+       content back into the h_original, and finally returns this
+       h_original.  Or, if gcp != NULL and the most recent copy is
+       protected by precisely 'gcp', then we return it instead.
+    */
+    assert(obj->h_tid & GCFLAG_PUBLIC);
+    assert(!(obj->h_tid & GCFLAG_BACKUP_COPY));
+    assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
+
+    gcptr original;
+    if (obj->h_original != 0 &&
+            !(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
+        original = (gcptr)obj->h_original;
+        /* the h_original may be protected, or private_from_protected,
+           in some cases.  Then we can't use it.  We'll use the most
+           recent h_revision which is public. */
+        if (!(original->h_tid & GCFLAG_PUBLIC)) {
+            original->h_tid |= GCFLAG_MARKED;
+            original = NULL;
         }
     }
+    else
+        original = obj;
+
+    /* the original object must not be a small stub. */
+    assert(original == NULL || !(original->h_tid & GCFLAG_SMALLSTUB));
+
+    /* if 'original' was already visited, we are done */
+    if (original != NULL && original->h_tid & GCFLAG_VISITED)
+        return original;
+
+    /* walk to the head of the chained list */
+    while (IS_POINTER(obj->h_revision)) {
+        if (!(obj->h_revision & 2)) {
+            obj = (gcptr)obj->h_revision;
+            assert(obj->h_tid & GCFLAG_PUBLIC);
+            continue;
+        }
+
+        /* it's a stub: check the current stealing status */
+        assert(obj->h_tid & GCFLAG_STUB);
+        gcptr obj2 = (gcptr)(obj->h_revision - 2);
+
+        if (obj2->h_tid & GCFLAG_PUBLIC) {
+            /* the stub target itself was stolen, so is public now.
+               Continue looping from there. */
+            obj = obj2;
+            continue;
+        }
+
+        if (obj2->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) {
+            /* the stub target is a private_from_protected. */
+            gcptr obj3 = (gcptr)obj2->h_revision;
+            if (obj3->h_tid & GCFLAG_PUBLIC) {
+                assert(!(obj3->h_tid & GCFLAG_BACKUP_COPY));
+                /* the backup copy was stolen and is now a regular
+                   public object. */
+                obj = obj3;
+                continue;
+            }
+            else {
+                /* the backup copy was not stolen.  Ignore this pair
+                   obj2/obj3, and the head of the public chain is obj.
+                   The pair obj2/obj3 was or will be handled by
+                   mark_all_stack_roots(). */
+                assert(obj3->h_tid & GCFLAG_BACKUP_COPY);
+
+                assert(STUB_THREAD(obj) != NULL);
+                if (STUB_THREAD(obj) == gcp)
+                    return obj2;
+                break;
+            }
+        }
+        else {
+            /* the stub target is just a protected object.
+               The head of the public chain is obj.  We have to
+               explicitly keep obj2 alive. */
+            assert(!IS_POINTER(obj2->h_revision));
+            visit_nonpublic(obj2, STUB_THREAD(obj));
+
+            assert(STUB_THREAD(obj) != NULL);
+            if (STUB_THREAD(obj) == gcp)
+                return obj2;
+            break;
+        }
+    }
+
+    /* at this point, 'obj' contains the most recent revision which is
+       public. */
+    if (original == NULL) {
+        original = obj;
+        if (original->h_tid & GCFLAG_VISITED)
+            return original;
+    }
+    else if (obj != original) {
+        /* copy obj over original */
+        copy_over_original(obj, original);
+    }
+
+    /* return this original */
+    original->h_tid |= GCFLAG_VISITED | GCFLAG_MARKED;
+    if (!(original->h_tid & GCFLAG_STUB))
+        gcptrlist_insert2(&objects_to_trace, original, NULL);
+    return original;
 }
 
-static void visit(gcptr *pobj);
+static struct tx_public_descriptor *visit_protected_gcp;
 
-gcptr stmgcpage_visit(gcptr obj)
+static void visit_take_protected(gcptr *pobj)
 {
-    visit(&obj);
-    return obj;
-}
-
-static void visit(gcptr *pobj)
-{
+    /* Visits '*pobj', marking it as surviving and possibly adding it to
+       objects_to_trace.  Fixes *pobj to point to the exact copy that
+       survived.  This function will replace *pobj with a protected
+       copy if it belongs to the thread 'visit_protected_gcp', so the
+       latter must be initialized before any call!
+    */
     gcptr obj = *pobj;
     if (obj == NULL)
         return;
 
- restart:
-    if (obj->h_revision & 1) {
-        assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
-        assert(!(obj->h_tid & GCFLAG_STUB));
-        if (!(obj->h_tid & GCFLAG_VISITED)) {
-            obj->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE;  /* see fix_outdated() */
-            obj->h_tid |= GCFLAG_VISITED;
-            gcptrlist_insert(&objects_to_trace, obj);
-
-            keep_original_alive(obj);
-        }
-    }
-    else if (obj->h_tid & GCFLAG_PUBLIC) {
-        /* h_revision is a ptr: we have a more recent version */
-        gcptr prev_obj = obj;
-
-        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;
-                keep_original_alive(prev_obj);
-
-                assert(*pobj == prev_obj);
-                /* recursion, but should be only once */
-                obj = stmgcpage_visit(obj);
-                assert(prev_obj->h_tid & GCFLAG_STUB);
-                prev_obj->h_revision = ((revision_t)obj) | 2;
-                return;
-            }
-        }
-
-        if (!(obj->h_revision & 3)) {
-            /* obj is neither a stub nor a most recent revision:
-               completely ignore obj->h_revision */
-
-            obj = (gcptr)obj->h_revision;
-            assert(obj->h_tid & GCFLAG_PUBLIC);
-            prev_obj->h_revision = (revision_t)obj;
-        }
-        *pobj = obj;
-        goto restart;
-    }
-    else if (obj->h_tid & GCFLAG_VISITED) {
-        dprintf(("[already visited: %p]\n", obj));
-        assert(obj == *pobj);
-        assert((obj->h_revision & 3) ||   /* either odd, or stub */
-               (obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
-        return;    /* already seen */
+    if (!(obj->h_tid & GCFLAG_PUBLIC)) {
+        /* 'obj' is a private or protected copy. */
+        visit_nonpublic(obj, visit_protected_gcp);
     }
     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 (obj->h_original && (gcptr)obj->h_original != B) {
-            /* if B is original, it will be visited anyway */
-            assert(obj->h_original == B->h_original);
-            assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL));
-            keep_original_alive(obj);
-        }
-        
-        obj->h_tid |= GCFLAG_VISITED;
-        B->h_tid |= GCFLAG_VISITED;
-        assert(!(obj->h_tid & GCFLAG_STUB));
-        assert(!(B->h_tid & GCFLAG_STUB));
-        gcptrlist_insert2(&objects_to_trace, obj, B);
-
-        if (IS_POINTER(B->h_revision)) {
-            assert(B->h_tid & GCFLAG_PUBLIC);
-            assert(!(B->h_tid & GCFLAG_BACKUP_COPY));
-            assert(!(B->h_revision & 2));
-
-            pobj = (gcptr *)&B->h_revision;
-            obj = *pobj;
-            goto restart;
-        }
+        *pobj = visit_public(obj, visit_protected_gcp);
     }
 }
 
-
-static void visit_keep(gcptr obj)
+gcptr stmgcpage_visit(gcptr obj)
 {
-    if (!(obj->h_tid & GCFLAG_VISITED)) {
-        obj->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE;  /* see fix_outdated() */
-        obj->h_tid |= GCFLAG_VISITED;
-        gcptrlist_insert(&objects_to_trace, obj);
-
-        if (IS_POINTER(obj->h_revision)) {
-            assert(!(obj->h_revision & 2));
-            visit((gcptr *)&obj->h_revision);
-        }
-        keep_original_alive(obj);
+    if (!(obj->h_tid & GCFLAG_PUBLIC)) {
+        visit_nonpublic(obj, NULL);
     }
+    else {
+        obj = visit_public(obj, NULL);
+    }
+    return obj;
 }
 
 static void visit_all_objects(void)
 {
     while (gcptrlist_size(&objects_to_trace) > 0) {
+        visit_protected_gcp =
+            (struct tx_public_descriptor *)gcptrlist_pop(&objects_to_trace);
         gcptr obj = gcptrlist_pop(&objects_to_trace);
-        stmgc_trace(obj, &visit);
+        stmgc_trace(obj, &visit_take_protected);
     }
+    visit_protected_gcp = NULL;
 }
 
 static void mark_prebuilt_roots(void)
@@ -371,18 +443,20 @@
     /* Note about prebuilt roots: 'stm_prebuilt_gcroots' is a list that
        contains all the ones that have been modified.  Because they are
        themselves not in any page managed by this file, their
-       GCFLAG_VISITED will not be removed at the end of the current
-       collection.  This is fine because the base object cannot contain
-       references to the heap.  So we decided to systematically set
-       GCFLAG_VISITED on prebuilt objects. */
+       GCFLAG_VISITED is not removed at the end of the current
+       collection.  That's why we remove it here.  GCFLAG_MARKED is not
+       relevant for prebuilt objects, but we avoid objects with MARKED
+       but not VISITED, which trigger some asserts. */
     gcptr *pobj = stm_prebuilt_gcroots.items;
     gcptr *pend = stm_prebuilt_gcroots.items + stm_prebuilt_gcroots.size;
-    gcptr obj;
+    gcptr obj, obj2;
     for (; pobj != pend; pobj++) {
         obj = *pobj;
+        obj->h_tid &= ~(GCFLAG_VISITED | GCFLAG_MARKED);
         assert(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL);
-        assert(IS_POINTER(obj->h_revision));
-        visit((gcptr *)&obj->h_revision);
+
+        obj2 = visit_public(obj, NULL);
+        assert(obj2 == obj);    /* it is its own original */
     }
 }
 
@@ -396,7 +470,7 @@
         if (((revision_t)item) & ~((revision_t)END_MARKER_OFF |
                                    (revision_t)END_MARKER_ON)) {
             /* 'item' is a regular, non-null pointer */
-            visit(root);
+            visit_take_protected(root);
             dprintf(("visit stack root: %p -> %p\n", item, *root));
         }
         else if (item == END_MARKER_OFF) {
@@ -409,15 +483,19 @@
 static void mark_all_stack_roots(void)
 {
     struct tx_descriptor *d;
+    struct GcPtrList new_public_to_private;
+    memset(&new_public_to_private, 0, sizeof(new_public_to_private));
+
     for (d = stm_tx_head; d; d = d->tx_next) {
         assert(!stm_has_got_any_lock(d));
+        visit_protected_gcp = d->public_descriptor;
 
         /* the roots pushed on the shadowstack */
         mark_roots(d->shadowstack, *d->shadowstack_end_ref);
 
         /* the thread-local object */
-        visit(d->thread_local_obj_ref);
-        visit(&d->old_thread_local_obj);
+        visit_take_protected(d->thread_local_obj_ref);
+        visit_take_protected(&d->old_thread_local_obj);
 
         /* the current transaction's private copies of public objects */
         wlog_t *item;
@@ -427,37 +505,50 @@
             gcptr R = item->addr;
             gcptr L = item->val;
 
-            /* Objects that were not visited yet must have the PUB_TO_PRIV
-             flag. Except if that transaction will abort anyway, then it
-             may be removed from a previous major collection that didn't
-             fix the PUB_TO_PRIV because the transaction was going to
-             abort anyway:
-             1. minor_collect before major collect (R->L, R is outdated, abort)
-             2. major collect removes flag
-             3. major collect again, same thread, no time to abort
-             4. flag still removed
-            */
-            assert(IMPLIES(!(R->h_tid & GCFLAG_VISITED) && d->active > 0,
-                           R->h_tid & GCFLAG_PUBLIC_TO_PRIVATE));
-            visit_keep(R);
+            /* we visit the public object R.  Must keep a public object
+               here, so we pass NULL as second argument. */
+            gcptr new_R = visit_public(R, NULL);
+            assert(new_R->h_tid & GCFLAG_PUBLIC);
+
+            if (new_R != R) {
+                /* we have to update the key in public_to_private, which
+                   can only be done by deleting the existing key and
+                   (after the loop) re-inserting the new key. */
+                G2L_LOOP_DELETE(item);
+                gcptrlist_insert2(&new_public_to_private, new_R, L);
+            }
+
+            /* we visit the private copy L --- which at this point
+               should be private, possibly private_from_protected,
+               so visit() should return the same private copy */
             if (L != NULL) {
-                /* minor collection found R->L in public_to_young
-                 and R was modified. It then sets item->val to NULL and wants 
-                 to abort later. */
-                revision_t v = L->h_revision;
-                visit_keep(L);
-                /* a bit of custom logic here: if L->h_revision used to
-                   point exactly to R, as set by stealing, then we must
-                   keep this property, even though visit_keep(L) might
-                   decide it would be better to make it point to a more
-                   recent copy. */
-                if (v == (revision_t)R) {
-                    assert(L->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
-                    L->h_revision = v;   /* restore */
-                }
+                visit_nonpublic(L, visit_protected_gcp);
             }
+
         } G2L_LOOP_END;
 
+        /* reinsert to real pub_to_priv */
+        long i, size = new_public_to_private.size;
+        gcptr *items = new_public_to_private.items;
+        for (i = 0; i < size; i += 2) {
+            g2l_insert(&d->public_to_private, items[i], items[i + 1]);
+        }
+        gcptrlist_clear(&new_public_to_private);
+
+        /* the current transaction's private copies of protected objects */
+        items = d->private_from_protected.items;
+        for (i = d->private_from_protected.size - 1; i >= 0; i--) {
+            gcptr obj = items[i];
+            assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
+            visit_nonpublic(obj, visit_protected_gcp);
+
+            gcptr backup_obj = (gcptr)obj->h_revision;
+            if (!(backup_obj->h_tid & GCFLAG_PUBLIC))
+                visit_nonpublic(backup_obj, visit_protected_gcp);
+            else
+                obj->h_revision = (revision_t)visit_public(backup_obj, NULL);
+        }
+
         /* make sure that the other lists are empty */
         assert(gcptrlist_size(&d->public_with_young_copy) == 0);
         assert(gcptrlist_size(&d->public_descriptor->stolen_objects) == 0);
@@ -473,27 +564,16 @@
         assert(gcptrlist_size(&d->private_from_protected) ==
                d->num_private_from_protected_known_old);
     }
+
+    visit_protected_gcp = NULL;
+    gcptrlist_delete(&new_public_to_private);
 }
 
 static void cleanup_for_thread(struct tx_descriptor *d)
 {
     long i;
     gcptr *items;
-
-    /* It can occur that 'private_from_protected' contains an object that
-     * has not been visited at all (maybe only in inevitable
-     * transactions). 
-     */
-    items = d->private_from_protected.items;
-    for (i = d->private_from_protected.size - 1; i >= 0; i--) {
-        gcptr obj = items[i];
-        assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
-
-        if (!(obj->h_tid & GCFLAG_VISITED)) {
-            /* forget 'obj' */
-            items[i] = items[--d->private_from_protected.size];
-        }
-    }
+    assert(d->old_objects_to_trace.size == 0);
 
     /* If we're aborting this transaction anyway, we don't need to do
      * more here.
@@ -516,21 +596,29 @@
     items = d->list_of_read_objects.items;
     for (i = d->list_of_read_objects.size - 1; i >= 0; --i) {
         gcptr obj = items[i];
-        assert(!(obj->h_tid & GCFLAG_STUB));
 
-        /* Warning: in case the object listed is outdated and has been
-           replaced with a more recent revision, then it might be the
-           case that obj->h_revision doesn't have GCFLAG_VISITED, but
-           just removing it is very wrong --- we want 'd' to abort.
-        */
-        if (obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) {
+        if (obj->h_tid & GCFLAG_MOVED) {
+            assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
+            assert(IS_POINTER(obj->h_original));
+            obj = (gcptr)obj->h_original;
+            items[i] = obj;
+        }
+        else if (obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) {
+            /* Warning: in case the object listed is outdated and has been
+               replaced with a more recent revision, then it might be the
+               case that obj->h_revision doesn't have GCFLAG_VISITED, but
+               just removing it is very wrong --- we want 'd' to abort.
+            */
             /* follow obj to its backup */
             assert(IS_POINTER(obj->h_revision));
             obj = (gcptr)obj->h_revision;
+
+            /* the backup-ptr should already be updated: */
+            assert(!(obj->h_tid & GCFLAG_MOVED));
         }
 
         revision_t v = obj->h_revision;
-        if (IS_POINTER(v)) {
+        if ((obj->h_tid & GCFLAG_STUB) || IS_POINTER(v)) {
             /* has a more recent revision.  Oups. */
             dprintf(("ABRT_COLLECT_MAJOR %p: "
                      "%p was read but modified already\n", d, obj));
@@ -572,7 +660,7 @@
     G2L_LOOP_FORWARD(d->public_to_private, item) {
         assert(item->addr->h_tid & GCFLAG_VISITED);
         assert(item->val->h_tid & GCFLAG_VISITED);
-
+        assert(!(item->addr->h_tid & GCFLAG_MOVED));
         assert(item->addr->h_tid & GCFLAG_PUBLIC);
         /* assert(is_private(item->val)); but in the other thread,
            which becomes: */
@@ -611,7 +699,9 @@
            and the flag is removed; other locations are marked as free. */
         p = (gcptr)(lpage + 1);
         for (j = 0; j < objs_per_page; j++) {
-            if (p->h_tid & GCFLAG_VISITED)
+            assert(IMPLIES(p->h_tid & GCFLAG_VISITED,
+                           p->h_tid & GCFLAG_MARKED));
+            if (p->h_tid & GCFLAG_MARKED)
                 break;  /* first object that stays alive */
             p = (gcptr)(((char *)p) + obj_size);
         }
@@ -621,8 +711,10 @@
             surviving_pages = lpage;
             p = (gcptr)(lpage + 1);
             for (j = 0; j < objs_per_page; j++) {
-                if (p->h_tid & GCFLAG_VISITED) {
-                    p->h_tid &= ~GCFLAG_VISITED;
+                assert(IMPLIES(p->h_tid & GCFLAG_VISITED,
+                               p->h_tid & GCFLAG_MARKED));
+                if (p->h_tid & GCFLAG_MARKED) {
+                    p->h_tid &= ~(GCFLAG_VISITED | GCFLAG_MARKED);
                     mc_total_in_use += obj_size;
                 }
                 else {
@@ -648,6 +740,7 @@
             p = (gcptr)(lpage + 1);
             for (j = 0; j < objs_per_page; j++) {
                 assert(!(p->h_tid & GCFLAG_VISITED));
+                assert(!(p->h_tid & GCFLAG_MARKED));
                 if (p->h_tid != DEBUG_WORD(0xDD)) {
                     dprintf(("| freeing %p (with page %p)\n", p, lpage));
                 }
@@ -677,8 +770,10 @@
     G2L_LOOP_FORWARD(gcp->nonsmall_objects, item) {
 
         gcptr p = item->addr;
-        if (p->h_tid & GCFLAG_VISITED) {
-            p->h_tid &= ~GCFLAG_VISITED;
+        assert(IMPLIES(p->h_tid & GCFLAG_VISITED,
+                       p->h_tid & GCFLAG_MARKED));
+        if (p->h_tid & GCFLAG_MARKED) {
+            p->h_tid &= ~(GCFLAG_VISITED | GCFLAG_MARKED);
         }
         else {
             G2L_LOOP_DELETE(item);
diff --git a/rpython/translator/stm/src_stm/lists.c 
b/rpython/translator/stm/src_stm/lists.c
--- a/rpython/translator/stm/src_stm/lists.c
+++ b/rpython/translator/stm/src_stm/lists.c
@@ -19,7 +19,7 @@
 
 void g2l_delete(struct G2L *g2l)
 {
-  free(g2l->raw_start);
+  stm_free(g2l->raw_start, g2l->raw_end - g2l->raw_start);
   memset(g2l, 0, sizeof(struct G2L));
 }
 
@@ -57,7 +57,7 @@
   long alloc = g2l->raw_end - g2l->raw_start;
   long newalloc = (alloc + extra + (alloc >> 2) + 31) & ~15;
   //fprintf(stderr, "growth: %ld\n", newalloc);
-  char *newitems = malloc(newalloc);
+  char *newitems = stm_malloc(newalloc);
   newg2l.raw_start = newitems;
   newg2l.raw_current = newitems;
   newg2l.raw_end = newitems + newalloc;
@@ -66,7 +66,7 @@
     {
       g2l_insert(&newg2l, item->addr, item->val);
     } G2L_LOOP_END;
-  free(g2l->raw_start);
+  stm_free(g2l->raw_start, g2l->raw_end - g2l->raw_start);
   *g2l = newg2l;
 }
 
@@ -152,7 +152,7 @@
     //fprintf(stderr, "list %p deleted (%ld KB)\n",
     //gcptrlist, gcptrlist->alloc * sizeof(gcptr) / 1024);
   gcptrlist->size = 0;
-  free(gcptrlist->items);
+  stm_free(gcptrlist->items, gcptrlist->alloc * sizeof(gcptr));
   gcptrlist->items = NULL;
   gcptrlist->alloc = 0;
 }
@@ -163,7 +163,8 @@
     return;
 
   size_t nsize = gcptrlist->size * sizeof(gcptr);
-  gcptr *newitems = realloc(gcptrlist->items, nsize);
+  gcptr *newitems = stm_realloc(gcptrlist->items, nsize,
+                                gcptrlist->alloc * sizeof(gcptr));
   if (newitems != NULL || nsize == 0)
     {
       gcptrlist->items = newitems;
@@ -178,11 +179,11 @@
   //fprintf(stderr, "list %p growth to %ld items (%ld KB)\n",
   //          gcptrlist, newalloc, newalloc * sizeof(gcptr) / 1024);
 
-  gcptr *newitems = malloc(newalloc * sizeof(gcptr));
+  gcptr *newitems = stm_malloc(newalloc * sizeof(gcptr));
   long i;
   for (i=0; i<gcptrlist->size; i++)
     newitems[i] = gcptrlist->items[i];
-  free(gcptrlist->items);
+  stm_free(gcptrlist->items, gcptrlist->alloc * sizeof(gcptr));
   gcptrlist->items = newitems;
   gcptrlist->alloc = newalloc;
 }
diff --git a/rpython/translator/stm/src_stm/lists.h 
b/rpython/translator/stm/src_stm/lists.h
--- a/rpython/translator/stm/src_stm/lists.h
+++ b/rpython/translator/stm/src_stm/lists.h
@@ -2,6 +2,8 @@
 #ifndef _SRCSTM_LISTS_H
 #define _SRCSTM_LISTS_H
 
+#include "dbgmem.h"
+
 /************************************************************/
 
 /* The g2l_xx functions ("global_to_local") are implemented as a tree,
@@ -37,7 +39,7 @@
 void g2l_clear(struct G2L *g2l);
 void g2l_delete(struct G2L *g2l);
 static inline void g2l_delete_not_used_any_more(struct G2L *g2l) {
-    free(g2l->raw_start);
+    stm_free(g2l->raw_start, g2l->raw_end - g2l->raw_start);
 }
 
 static inline int g2l_any_entry(struct G2L *g2l) {
diff --git a/rpython/translator/stm/src_stm/nursery.c 
b/rpython/translator/stm/src_stm/nursery.c
--- a/rpython/translator/stm/src_stm/nursery.c
+++ b/rpython/translator/stm/src_stm/nursery.c
@@ -137,7 +137,7 @@
 static inline gcptr create_old_object_copy(gcptr obj)
 {
     assert(!(obj->h_tid & GCFLAG_PUBLIC));
-    assert(!(obj->h_tid & GCFLAG_NURSERY_MOVED));
+    assert(!(obj->h_tid & GCFLAG_MOVED));
     assert(!(obj->h_tid & GCFLAG_VISITED));
     assert(!(obj->h_tid & GCFLAG_WRITE_BARRIER));
     assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL));
@@ -160,9 +160,9 @@
     }
     else {
         /* it's a nursery object.  Was it already moved? */
-        if (UNLIKELY(obj->h_tid & GCFLAG_NURSERY_MOVED)) {
+        if (UNLIKELY(obj->h_tid & GCFLAG_MOVED)) {
             /* yes.  Such an object can be a public object in the nursery
-               too (such objects are always NURSERY_MOVED).  For all cases,
+               too (such objects are always MOVED).  For all cases,
                we can just fix the ref. 
                Can be stolen objects or those we already moved.
             */
@@ -183,7 +183,7 @@
             fresh_old_copy = create_old_object_copy(obj);
         }
         
-        obj->h_tid |= GCFLAG_NURSERY_MOVED;
+        obj->h_tid |= GCFLAG_MOVED;
         obj->h_revision = (revision_t)fresh_old_copy;
 
         /* fix the original reference */
@@ -233,8 +233,23 @@
         assert(items[i]->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
         assert(IS_POINTER(items[i]->h_revision));
 
+        /* if items[i] is young, move it, update the pointer, and
+           schedule the object for later consideration by
+           visit_all_outside_objects() (which will for example ensure
+           that the WRITE_BARRIER flag is added to it).
+        */
         visit_if_young(&items[i]);
 
+        /* the backup copy is always allocated outside the nursery,
+           but we have to trace it as well, as it may contain its own
+           young pointers.
+           
+           but only once: if the transaction was running for long enough
+           to have num_private_from_protected_known_old > 0, then the
+           backup copies of known-old objects have already been traced
+           in a previous minor collection, and as they are read-only,
+           they cannot contain young pointers any more.
+        */
         stmgc_trace((gcptr)items[i]->h_revision, &visit_if_young);
     }
 
@@ -386,13 +401,13 @@
             /* non-young or visited young objects are kept */
             continue;
         }
-        else if (obj->h_tid & GCFLAG_NURSERY_MOVED) {
+        else if (obj->h_tid & GCFLAG_MOVED) {
             /* visited nursery objects are kept and updated */
             items[i] = (gcptr)obj->h_revision;
             assert(!(items[i]->h_tid & GCFLAG_STUB));
             continue;
         }
-        /* Sanity check: a nursery object without the NURSERY_MOVED flag
+        /* Sanity check: a nursery object without the MOVED flag
            is necessarily a private-without-backup object, or a protected
            object; it cannot be a public object. */
         assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
@@ -432,7 +447,7 @@
     setup_minor_collect(d);
 
     /* first do this, which asserts that some objects are private ---
-       which fails if they have already been GCFLAG_NURSERY_MOVED */
+       which fails if they have already been GCFLAG_MOVED */
     mark_public_to_young(d);
 
     mark_young_roots(d);
diff --git a/rpython/translator/stm/src_stm/revision 
b/rpython/translator/stm/src_stm/revision
--- a/rpython/translator/stm/src_stm/revision
+++ b/rpython/translator/stm/src_stm/revision
@@ -1,1 +1,1 @@
-4cad3aa5a20b
+c528da482152
diff --git a/rpython/translator/stm/src_stm/steal.c 
b/rpython/translator/stm/src_stm/steal.c
--- a/rpython/translator/stm/src_stm/steal.c
+++ b/rpython/translator/stm/src_stm/steal.c
@@ -31,7 +31,7 @@
         assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
         if (obj->h_tid & GCFLAG_PUBLIC) {
             /* young public, replace with stolen old copy */
-            assert(obj->h_tid & GCFLAG_NURSERY_MOVED);
+            assert(obj->h_tid & GCFLAG_MOVED);
             assert(IS_POINTER(obj->h_revision));
             stub = (gcptr)obj->h_revision;
             assert(!IS_POINTER(stub->h_revision)); /* not outdated */
@@ -56,7 +56,7 @@
                 if (!(obj->h_original))
                     obj->h_original = (revision_t)O;
             }
-            obj->h_tid |= (GCFLAG_NURSERY_MOVED | GCFLAG_PUBLIC);
+            obj->h_tid |= (GCFLAG_MOVED | GCFLAG_PUBLIC);
             obj->h_revision = (revision_t)O;
             
             O->h_tid |= GCFLAG_PUBLIC;
@@ -105,6 +105,8 @@
     stub->h_tid = (obj->h_tid & STM_USER_TID_MASK) | GCFLAG_PUBLIC
                                                    | GCFLAG_STUB
                                                    | GCFLAG_OLD;
+    if (size == 0)
+        stub->h_tid |= GCFLAG_SMALLSTUB;
     stub->h_revision = ((revision_t)obj) | 2;
     if (obj->h_original) {
         stub->h_original = obj->h_original;
@@ -206,7 +208,7 @@
 
             /* note that we should follow h_revision at least one more
                step: it is necessary if L is public but young (and then
-               has GCFLAG_NURSERY_MOVED), but it is fine to do it more
+               has GCFLAG_MOVED), but it is fine to do it more
                generally. */
             v = ACCESS_ONCE(L->h_revision);
             if (IS_POINTER(v)) {
@@ -239,7 +241,7 @@
             }
             L->h_revision = (revision_t)O;
 
-            L->h_tid |= GCFLAG_PUBLIC | GCFLAG_NURSERY_MOVED;
+            L->h_tid |= GCFLAG_PUBLIC | GCFLAG_MOVED;
             /* subtle: we need to remove L from the fxcache of the target
                thread, otherwise its read barrier might not trigger on it.
                It is mostly fine because it is anyway identical to O.  But
diff --git a/rpython/translator/stm/src_stm/stmgc.h 
b/rpython/translator/stm/src_stm/stmgc.h
--- a/rpython/translator/stm/src_stm/stmgc.h
+++ b/rpython/translator/stm/src_stm/stmgc.h
@@ -24,7 +24,8 @@
 #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 * (1 + 2 + 4 + 8))
+#define PREBUILT_FLAGS             (STM_FIRST_GCFLAG * ((1<<0) | (1<<1) |    \
+                                               (1<<2) | (1<<3) | (1<<13)))
 #define PREBUILT_REVISION          1
 
 
diff --git a/rpython/translator/stm/src_stm/stmsync.c 
b/rpython/translator/stm/src_stm/stmsync.c
--- a/rpython/translator/stm/src_stm/stmsync.c
+++ b/rpython/translator/stm/src_stm/stmsync.c
@@ -53,7 +53,7 @@
 static void init_shadowstack(void)
 {
     struct tx_descriptor *d = thread_descriptor;
-    d->shadowstack = malloc(sizeof(gcptr) * LENGTH_SHADOW_STACK);
+    d->shadowstack = stm_malloc(sizeof(gcptr) * LENGTH_SHADOW_STACK);
     if (!d->shadowstack) {
         stm_fatalerror("out of memory: shadowstack\n");
     }
@@ -69,7 +69,7 @@
     assert(x == END_MARKER_ON);
     assert(stm_shadowstack == d->shadowstack);
     stm_shadowstack = NULL;
-    free(d->shadowstack);
+    stm_free(d->shadowstack, sizeof(gcptr) * LENGTH_SHADOW_STACK);
 }
 
 void stm_set_max_aborts(int max_aborts)
diff --git a/rpython/translator/stm/src_stm/weakref.c 
b/rpython/translator/stm/src_stm/weakref.c
--- a/rpython/translator/stm/src_stm/weakref.c
+++ b/rpython/translator/stm/src_stm/weakref.c
@@ -28,7 +28,7 @@
     */
     while (gcptrlist_size(&d->young_weakrefs) > 0) {
         gcptr weakref = gcptrlist_pop(&d->young_weakrefs);
-        if (!(weakref->h_tid & GCFLAG_NURSERY_MOVED))
+        if (!(weakref->h_tid & GCFLAG_MOVED))
             continue;   /* the weakref itself dies */
 
         weakref = (gcptr)weakref->h_revision;
@@ -37,7 +37,7 @@
         assert(pointing_to != NULL);
 
         if (stmgc_is_in_nursery(d, pointing_to)) {
-            if (pointing_to->h_tid & GCFLAG_NURSERY_MOVED) {
+            if (pointing_to->h_tid & GCFLAG_MOVED) {
                 dprintf(("weakref ptr moved %p->%p\n", 
                          WEAKREF_PTR(weakref, size),
                          (gcptr)pointing_to->h_revision));
@@ -69,49 +69,25 @@
 
 static _Bool is_partially_visited(gcptr obj)
 {
-    /* 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.
+    /* Based on gcpage.c:visit_public().  Check the code here if we change
+       visit_public().  Returns True or False depending on whether we find any
+       version of 'obj' to be MARKED or not.
     */
- restart:
-    if (obj->h_tid & GCFLAG_VISITED)
+    assert(IMPLIES(obj->h_tid & GCFLAG_VISITED,
+                   obj->h_tid & GCFLAG_MARKED));
+    if (obj->h_tid & GCFLAG_MARKED)
         return 1;
 
-    if (obj->h_revision & 1) {
-        assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
-        assert(!(obj->h_tid & GCFLAG_STUB));
+    if (!(obj->h_tid & GCFLAG_PUBLIC))
         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)
+
+    if (obj->h_original != 0 &&
+            !(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
+        gcptr original = (gcptr)obj->h_original;
+        assert(IMPLIES(original->h_tid & GCFLAG_VISITED,
+                       original->h_tid & GCFLAG_MARKED));
+        if (original->h_tid & GCFLAG_MARKED)
             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;
 }
diff --git a/rpython/translator/stm/test/test_ztranslated.py 
b/rpython/translator/stm/test/test_ztranslated.py
--- a/rpython/translator/stm/test/test_ztranslated.py
+++ b/rpython/translator/stm/test/test_ztranslated.py
@@ -1,5 +1,5 @@
 import py
-from rpython.rlib import rstm, rgc
+from rpython.rlib import rstm, rgc, objectmodel
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rclass
 from rpython.rtyper.lltypesystem.lloperation import llop
 from rpython.rtyper.annlowlevel import cast_instance_to_base_ptr
@@ -276,3 +276,30 @@
         t, cbuilder = self.compile(main)
         data = cbuilder.cmdexec('a b')
         assert 'li102ee10:hi there 3e\n0\n' in data
+
+    def test_weakref(self):
+        import weakref
+        class Foo(object):
+            pass
+
+        def f(argv):
+            foo = Foo()
+            foo.n = argv
+            w = weakref.ref(foo)
+            assert w() is foo
+            objectmodel.keepalive_until_here(foo)
+            return w
+        f._dont_inline_ = True
+
+        def main(argv):
+            w = f(argv)
+            assert w() is not None
+            assert len(w().n) == len(argv)
+            rgc.collect()
+            assert w() is None
+            print 'test ok'
+            return 0
+
+        t, cbuilder = self.compile(main)
+        data = cbuilder.cmdexec('a b')
+        assert 'test ok\n' in data
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to