[pypy-commit] stmgc nonmovable-int-ref: implement stm_allocate_public_integer_address(). They need to be explicitely unregistered (freed), otherwise they survive everything.

Raemi noreply at buildbot.pypy.org
Thu Aug 15 13:08:34 CEST 2013


Author: Remi Meier <remi.meier at gmail.com>
Branch: nonmovable-int-ref
Changeset: r477:6a465701aaa1
Date: 2013-08-15 13:07 +0200
http://bitbucket.org/pypy/stmgc/changeset/6a465701aaa1/

Log:	implement stm_allocate_public_integer_address(). They need to be
	explicitely unregistered (freed), otherwise they survive everything.

diff --git a/c4/demo_random.c b/c4/demo_random.c
--- a/c4/demo_random.c
+++ b/c4/demo_random.c
@@ -20,6 +20,7 @@
 #define MAXROOTS 1000
 #define SHARED_ROOTS 5 // shared by threads
 #define DO_MAJOR_COLLECTS 1
+#define MAX_PUBLIC_INTS 5
 
 
 
@@ -82,6 +83,8 @@
     int interruptible;
     int atomic;
     char to_clear_on_abort[20];
+    intptr_t public_ints[MAX_PUBLIC_INTS];
+    int num_public_ints;
 };
 __thread struct thread_data td;
 
@@ -266,6 +269,38 @@
     }
 }
 
+void check_public_ints()
+{
+    int i;
+    for (i = 0; i < td.num_public_ints; i++) {
+        intptr_t ip = td.public_ints[i];
+        gcptr obj = (gcptr)ip;
+        assert(obj->h_tid & GCFLAG_PUBLIC);
+        assert(obj->h_tid & GCFLAG_SMALLSTUB);
+        check(obj);
+        check((gcptr)(obj->h_revision - 2));
+    }
+}
+
+void add_as_public_int(gcptr p)
+{
+    if (!p || td.num_public_ints >= MAX_PUBLIC_INTS)
+        return;
+
+    push_roots();
+    intptr_t ip = stm_allocate_public_integer_address(p);
+    pop_roots();
+    td.public_ints[td.num_public_ints++] = ip;
+}
+
+void pop_public_int()
+{
+    if (td.num_public_ints == 0)
+        return;
+
+    stm_unregister_integer_address(td.public_ints[--td.num_public_ints]);
+}
+
 gcptr read_barrier(gcptr p)
 {
     gcptr r = p;
@@ -401,6 +436,7 @@
 
 gcptr rare_events(gcptr p, gcptr _r, gcptr _sr)
 {
+    check_public_ints();
     int k = get_rand(100);
     if (k < 10) {
         push_roots();
@@ -408,13 +444,22 @@
         stm_become_inevitable("fun");
         p = stm_pop_root();
         pop_roots();
-    } 
+    }
     else if (k < 40) {
         push_roots();
         stmgc_minor_collect();
         pop_roots();
         p = NULL;
-    } else if (k < 41 && DO_MAJOR_COLLECTS) {
+    }
+    else if (k < 50) {
+        add_as_public_int(p);
+        p = NULL;
+    }
+    else if (k < 60) {
+        pop_public_int();
+        p = NULL;
+    }
+    else if (k < 61 && DO_MAJOR_COLLECTS) {
         fprintf(stdout, "major collect\n");
         push_roots();
         stmgcpage_possibly_major_collect(1);
diff --git a/c4/et.c b/c4/et.c
--- a/c4/et.c
+++ b/c4/et.c
@@ -6,7 +6,6 @@
  */
 #include "stmimpl.h"
 
-#ifdef _GC_DEBUG
 char tmp_buf[128];
 char* stm_dbg_get_hdr_str(gcptr obj)
 {
@@ -26,7 +25,6 @@
     cur += sprintf(cur, "tid=%ld", stm_get_tid(obj));
     return tmp_buf;
 }
-#endif
 
 
 
diff --git a/c4/extra.c b/c4/extra.c
--- a/c4/extra.c
+++ b/c4/extra.c
@@ -23,6 +23,51 @@
     stm_bytes_to_clear_on_abort = bytes;
 }
 
+
+intptr_t stm_allocate_public_integer_address(gcptr obj)
+{
+    struct tx_descriptor *d = thread_descriptor;
+    gcptr stub;
+    intptr_t result;
+    /* plan: we allocate a small stub whose reference
+       we never give to the caller except in the form 
+       of an integer.
+       During major collections, we visit them and update
+       their references. */
+
+    /* we don't want to deal with young objs */
+    if (!(obj->h_tid & GCFLAG_OLD)) {
+        stm_push_root(obj);
+        stm_minor_collect();
+        obj = stm_pop_root();
+    }
+    
+    spinlock_acquire(d->public_descriptor->collection_lock, 'P');
+
+    stub = stm_stub_malloc(d->public_descriptor, 0);
+    stub->h_tid = (obj->h_tid & STM_USER_TID_MASK)
+        | GCFLAG_PUBLIC | GCFLAG_STUB | GCFLAG_SMALLSTUB
+        | GCFLAG_OLD;
+
+    stub->h_revision = ((revision_t)obj) | 2;
+    if (!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL) && obj->h_original) {
+        stub->h_original = obj->h_original;
+    }
+    else {
+        stub->h_original = (revision_t)obj;
+    }
+
+    result = (intptr_t)stub;
+    spinlock_release(d->public_descriptor->collection_lock);
+    stm_register_integer_address(result);
+    return result;
+}
+
+
+
+
+
+
 /************************************************************/
 /* Each object has a h_original pointer to an old copy of 
    the same object (e.g. an old revision), the "original". 
diff --git a/c4/gcpage.c b/c4/gcpage.c
--- a/c4/gcpage.c
+++ b/c4/gcpage.c
@@ -22,6 +22,9 @@
 /* Only computed during a major collection */
 static size_t mc_total_in_use, mc_total_reserved;
 
+/* keeps track of registered smallstubs that will survive unless unregistered */
+static struct G2L registered_stubs;
+
 /* For tests */
 long stmgcpage_count(int quantity)
 {
@@ -62,6 +65,8 @@
         nblocks_for_size[i] =
             (GC_PAGE_SIZE - sizeof(page_header_t)) / (WORD * i);
     }
+
+    memset(&registered_stubs, 0, sizeof(registered_stubs));
 }
 
 void stmgcpage_init_tls(void)
@@ -208,6 +213,34 @@
 }
 
 
+/***** registering of small stubs as integer addresses *****/
+
+void stm_register_integer_address(intptr_t adr)
+{
+    gcptr obj = (gcptr)adr;
+    assert(obj->h_tid & GCFLAG_SMALLSTUB);
+    assert(obj->h_tid & GCFLAG_PUBLIC);
+
+    stmgcpage_acquire_global_lock();
+    g2l_insert(&registered_stubs, obj, NULL);
+    stmgcpage_release_global_lock();
+    dprintf(("registered %p\n", obj));
+}
+
+void stm_unregister_integer_address(intptr_t adr)
+{
+    gcptr obj = (gcptr)adr;
+    assert(obj->h_tid & GCFLAG_SMALLSTUB);
+    assert(obj->h_tid & GCFLAG_PUBLIC);
+
+    stmgcpage_acquire_global_lock();
+    g2l_delete_item(&registered_stubs, obj);
+    stmgcpage_release_global_lock();
+    dprintf(("unregistered %p\n", obj));
+}
+
+
+
 /***** Major collections: marking *****/
 
 static struct GcPtrList objects_to_trace;
@@ -459,6 +492,27 @@
     }
 }
 
+static void mark_registered_stubs(void)
+{
+    wlog_t *item;
+    G2L_LOOP_FORWARD(registered_stubs, item) {
+        gcptr R = item->addr;
+        assert(R->h_tid & GCFLAG_SMALLSTUB);
+
+        R->h_tid |= (GCFLAG_MARKED | GCFLAG_VISITED);
+
+        gcptr L = (gcptr)(R->h_revision - 2);
+        L = stmgcpage_visit(L);
+        R->h_revision = ((revision_t)L) | 2;
+
+        /* h_original will be kept up-to-date because
+           it is either == L or L's h_original. And
+           h_originals don't move */
+    } G2L_LOOP_END;
+
+}
+
+
 static void mark_roots(gcptr *root, gcptr *end)
 {
     assert(*root == END_MARKER_ON);
@@ -889,6 +943,7 @@
 
     assert(gcptrlist_size(&objects_to_trace) == 0);
     mark_prebuilt_roots();
+    mark_registered_stubs();
     mark_all_stack_roots();
     do {
         visit_all_objects();
diff --git a/c4/steal.c b/c4/steal.c
--- a/c4/steal.c
+++ b/c4/steal.c
@@ -19,6 +19,59 @@
 };
 static __thread struct tx_steal_data *steal_data;
 
+static void replace_ptr_to_immutable_with_stub(gcptr * pobj)
+{
+    gcptr stub, obj = *pobj;
+    assert(obj->h_tid & GCFLAG_IMMUTABLE);
+    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_MOVED);
+        assert(IS_POINTER(obj->h_revision));
+        stub = (gcptr)obj->h_revision;
+        assert(!IS_POINTER(stub->h_revision)); /* not outdated */
+        goto done;
+    }
+
+    /* old or young protected! mark as PUBLIC */
+    if (!(obj->h_tid & GCFLAG_OLD)) {
+        /* young protected */
+        gcptr O;
+
+        if (obj->h_tid & GCFLAG_HAS_ID) {
+            /* use id-copy for us */
+            O = (gcptr)obj->h_original;
+            obj->h_tid &= ~GCFLAG_HAS_ID;
+            stm_copy_to_old_id_copy(obj, O);
+            O->h_original = 0;
+        } else {
+            O = stmgc_duplicate_old(obj);
+
+            /* young and without original? */
+            if (!(obj->h_original))
+                obj->h_original = (revision_t)O;
+        }
+        obj->h_tid |= (GCFLAG_MOVED | GCFLAG_PUBLIC);
+        obj->h_revision = (revision_t)O;
+
+        O->h_tid |= GCFLAG_PUBLIC;
+        /* here it is fine if it stays in read caches because
+           the object is immutable anyway and there are no
+           write_barriers allowed. */
+        dprintf(("steal prot immutable -> public: %p -> %p\n", obj, O));
+        stub = O;
+        goto done;
+    }
+    /* old protected: */
+    dprintf(("prot immutable -> public: %p\n", obj));
+    obj->h_tid |= GCFLAG_PUBLIC;
+
+    return;
+ done:
+    *pobj = stub;
+    dprintf(("  stolen: fixing *%p: %p -> %p\n", pobj, obj, stub));
+}
+
 static void replace_ptr_to_protected_with_stub(gcptr *pobj)
 {
     gcptr stub, obj = *pobj;
@@ -27,49 +80,7 @@
         return;
 
     if (obj->h_tid & GCFLAG_IMMUTABLE) {
-        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_MOVED);
-            assert(IS_POINTER(obj->h_revision));
-            stub = (gcptr)obj->h_revision;
-            assert(!IS_POINTER(stub->h_revision)); /* not outdated */
-            goto done;
-        }
-
-        /* old or young protected! mark as PUBLIC */
-        if (!(obj->h_tid & GCFLAG_OLD)) {
-            /* young protected */
-            gcptr O;
-            
-            if (obj->h_tid & GCFLAG_HAS_ID) {
-                /* use id-copy for us */
-                O = (gcptr)obj->h_original;
-                obj->h_tid &= ~GCFLAG_HAS_ID;
-                stm_copy_to_old_id_copy(obj, O);
-                O->h_original = 0;
-            } else {
-                O = stmgc_duplicate_old(obj);
-                
-                /* young and without original? */
-                if (!(obj->h_original))
-                    obj->h_original = (revision_t)O;
-            }
-            obj->h_tid |= (GCFLAG_MOVED | GCFLAG_PUBLIC);
-            obj->h_revision = (revision_t)O;
-            
-            O->h_tid |= GCFLAG_PUBLIC;
-            /* here it is fine if it stays in read caches because
-               the object is immutable anyway and there are no
-               write_barriers allowed. */
-            dprintf(("steal prot immutable -> public: %p -> %p\n", obj, O));
-            stub = O;
-            goto done;
-        }
-        /* old protected: */
-        dprintf(("prot immutable -> public: %p\n", obj));
-        obj->h_tid |= GCFLAG_PUBLIC;
-        
+        replace_ptr_to_immutable_with_stub(pobj);
         return;
     }
     
diff --git a/c4/stmgc.h b/c4/stmgc.h
--- a/c4/stmgc.h
+++ b/c4/stmgc.h
@@ -28,12 +28,21 @@
 #define PREBUILT_REVISION          1
 
 
+/* push roots around allocating functions! */
+
 /* allocate an object out of the local nursery */
 gcptr stm_allocate(size_t size, unsigned long tid);
 /* allocate an object that is be immutable. it cannot be changed with
    a stm_write_barrier() or after the next commit */
 gcptr stm_allocate_immutable(size_t size, unsigned long tid);
 
+/* allocates a public reference to the object that will 
+   not be freed until stm_unregister_integer_address is 
+   called on the result */
+intptr_t stm_allocate_public_integer_address(gcptr);
+void stm_unregister_integer_address(intptr_t);
+
+
 /* returns a never changing hash for the object */
 revision_t stm_hash(gcptr);
 /* returns a number for the object which is unique during its lifetime */
@@ -166,6 +175,8 @@
 extern __thread void *stm_to_clear_on_abort;
 extern __thread size_t stm_bytes_to_clear_on_abort;
 
+/* only user currently is stm_allocate_public_integer_address() */
+void stm_register_integer_address(intptr_t);
 
 /* macro functionality */
 
diff --git a/c4/test/support.py b/c4/test/support.py
--- a/c4/test/support.py
+++ b/c4/test/support.py
@@ -47,6 +47,9 @@
     #define PREBUILT_REVISION      ...
 
     gcptr stm_allocate(size_t size, unsigned long tid);
+    gcptr stm_allocate_immutable(size_t size, unsigned long tid);
+    intptr_t stm_allocate_public_integer_address(gcptr adr);
+    void stm_unregister_integer_address(intptr_t adr);
     revision_t stm_hash(gcptr);
     revision_t stm_id(gcptr);
     _Bool stm_pointer_equal(gcptr, gcptr);
diff --git a/c4/test/test_extra.py b/c4/test/test_extra.py
--- a/c4/test/test_extra.py
+++ b/c4/test/test_extra.py
@@ -156,3 +156,47 @@
                    0, 0,
                    0, 0,
                    0, 0]
+
+
+
+def test_allocate_public_integer_address():
+    p1 = palloc(HDR)
+    p2 = oalloc(HDR)
+    p3 = nalloc(HDR)
+    lib.stm_push_root(p3)
+    p3p = lib.stm_allocate_public_integer_address(p3)
+    p1p = lib.stm_allocate_public_integer_address(p1)
+    p2p = lib.stm_allocate_public_integer_address(p2)
+
+    # p3 stub points to p3o:
+    p3o = lib.stm_pop_root()
+    p3po = ffi.cast("gcptr", p3p)
+    assert ffi.cast("gcptr", p3po.h_revision - 2) == p3o
+
+    # we have stubs here:
+    assert ffi.cast("gcptr", p1p).h_tid & GCFLAG_PUBLIC
+    assert classify(ffi.cast("gcptr", p1p)) == 'stub'
+    assert classify(ffi.cast("gcptr", p2p)) == 'stub'
+    assert classify(ffi.cast("gcptr", p3p)) == 'stub'
+
+    major_collect()
+
+    # kept alive through stubs:
+    check_not_free(p3o)
+    check_not_free(p2)
+
+    check_not_free(ffi.cast("gcptr", p1p))
+    check_not_free(ffi.cast("gcptr", p2p))
+    check_not_free(ffi.cast("gcptr", p3p))
+
+    lib.stm_unregister_integer_address(p1p)
+    lib.stm_unregister_integer_address(p2p)
+    lib.stm_unregister_integer_address(p3p)
+
+    major_collect()
+    major_collect()
+    
+    check_free_old(p3o)
+    check_free_old(p2)
+    
+    


More information about the pypy-commit mailing list