[pypy-commit] stmgc default: start with test_nursery (fix missing GCWORD_MOVED test)

Raemi noreply at buildbot.pypy.org
Tue Sep 9 10:45:26 CEST 2014


Author: Remi Meier <remi.meier at inf.ethz.ch>
Branch: 
Changeset: r1372:f7891821148c
Date: 2014-09-09 10:15 +0200
http://bitbucket.org/pypy/stmgc/changeset/f7891821148c/

Log:	start with test_nursery (fix missing GCWORD_MOVED test)

diff --git a/c8/stm/gcpage.c b/c8/stm/gcpage.c
--- a/c8/stm/gcpage.c
+++ b/c8/stm/gcpage.c
@@ -38,6 +38,10 @@
             stm_fatalerror("uninitialized_page_start changed?");
         }
     }
+    dprintf(("allocate_outside_nursery_large(%lu): %p, seg=%d, page=%lu\n",
+             size, addr, get_segment_of_linear_address(addr),
+             (addr - STM_SEGMENT->segment_base) / 4096UL));
+
 
     return addr;
 }
diff --git a/c8/stm/nursery.c b/c8/stm/nursery.c
--- a/c8/stm/nursery.c
+++ b/c8/stm/nursery.c
@@ -63,6 +63,11 @@
            where the object moved to, is stored in the second word in 'obj'. */
         object_t *TLPREFIX *pforwarded_array = (object_t *TLPREFIX *)obj;
 
+        if (LIKELY(pforwarded_array[0] == GCWORD_MOVED)) {
+            *pobj = pforwarded_array[1];    /* already moved */
+            return;
+        }
+
         realobj = REAL_ADDRESS(STM_SEGMENT->segment_base, obj);
         size = stmcb_size_rounded_up((struct object_s *)realobj);
 
@@ -199,6 +204,16 @@
     _do_minor_collection(commit);
 }
 
+void stm_collect(long level)
+{
+    if (level > 0)
+        abort();
+
+    minor_collection(/*commit=*/ false);
+    /* XXX: major_collection_if_requested(); */
+}
+
+
 
 /************************************************************/
 
@@ -220,7 +235,7 @@
         return (object_t *)p;
     }
 
-    abort();//stm_collect(0);
+    stm_collect(0);
     goto restart;
 }
 
diff --git a/c8/stmgc.h b/c8/stmgc.h
--- a/c8/stmgc.h
+++ b/c8/stmgc.h
@@ -181,6 +181,8 @@
 void stm_commit_transaction(void);
 void stm_abort_transaction(void) __attribute__((noreturn));
 
+void stm_collect(long level);
+
 
 #ifdef STM_NO_AUTOMATIC_SETJMP
 int stm_is_inevitable(void);
diff --git a/c8/test/test_nursery.py b/c8/test/test_nursery.py
new file mode 100644
--- /dev/null
+++ b/c8/test/test_nursery.py
@@ -0,0 +1,250 @@
+from support import *
+import py
+
+class TestNursery(BaseTest):
+
+    def test_nursery_full(self):
+        lib._stm_set_nursery_free_count(2048)
+        self.start_transaction()
+        self.push_root_no_gc()
+        lp1 = stm_allocate(2048)    # no collection here
+        self.pop_root()
+        #
+        self.push_root(lp1)
+        lp2 = stm_allocate(2048)
+        lp1b = self.pop_root()
+        assert lp1b != lp1      # collection occurred
+
+    def test_several_minor_collections(self):
+        # make a long, ever-growing linked list of objects, in one transaction
+        lib._stm_set_nursery_free_count(2048)
+        self.start_transaction()
+        lp1 = stm_allocate_refs(1)
+        self.push_root(lp1)
+        prev = lp1
+        prevprev = None
+        FIT = 2048 / 16 - 1   # without 'lp1' above
+        N = 4096 / 16 + 41
+        for i in range(N):
+            if prevprev:
+                assert stm_get_ref(prevprev, 0) == prev
+                self.push_root(prevprev)
+            self.push_root(prev)
+            lp3 = stm_allocate_refs(1)
+            prev = self.pop_root()
+            if prevprev:
+                prevprev = self.pop_root()
+                assert prevprev != prev
+            stm_set_ref(prev, 0, lp3)
+
+            #assert modified_old_objects() == []    # only 1 transaction
+            opn = objects_pointing_to_nursery()
+            if i < FIT:
+                assert opn == []      # no minor collection so far
+            else:
+                assert len(opn) == 1
+
+            prevprev = prev
+            prev = lp3
+
+        lp1 = self.pop_root()
+        #assert modified_old_objects() == []
+
+        lp2 = lp1
+        for i in range(N):
+            assert lp2
+            prev = lp2
+            lp2 = stm_get_ref(lp2, 0)
+        assert lp2 == lp3
+
+    def test_many_allocs(self):
+        lib._stm_set_nursery_free_count(32768)
+        obj_size = 512
+        num = 65536 / obj_size + 41
+
+        self.start_transaction()
+        for i in range(num):
+            new = stm_allocate(obj_size)
+            stm_set_char(new, chr(i % 255))
+            self.push_root(new)
+
+        old = []
+        young = []
+        for i in reversed(range(num)):
+            r = self.pop_root()
+            assert stm_get_char(r) == chr(i % 255)
+            if is_in_nursery(r):
+                young.append(r)
+            else:
+                old.append(r)
+
+        assert old
+        assert young
+
+    def test_larger_than_limit_for_nursery_die(self):
+        obj_size = lib._STM_FAST_ALLOC + 16
+
+        self.start_transaction()
+        assert lib._stm_total_allocated() == 0
+        seen = set()
+        for i in range(10):
+            stm_minor_collect()
+            new = stm_allocate(obj_size)
+            assert not is_in_nursery(new)
+            assert lib._stm_total_allocated() == obj_size + 16
+            seen.add(new)
+        assert len(seen) < 5     # addresses are reused
+        stm_minor_collect()
+        assert lib._stm_total_allocated() == 0
+
+    def test_larger_than_limit_for_nursery_dont_die(self):
+        obj_nrefs = (lib._STM_FAST_ALLOC + 16) // 8
+
+        self.start_transaction()
+        lp1 = ffi.cast("object_t *", 0)
+        seen = set()
+        for i in range(100):
+            self.push_root(lp1)
+            stm_minor_collect()
+            lp1 = self.pop_root()
+            new = stm_allocate_refs(obj_nrefs)
+            assert not is_in_nursery(new)
+            seen.add(new)
+            stm_set_ref(new, i, lp1)
+            lp1 = new
+        assert len(seen) == 100     # addresses are not reused
+
+        for i in reversed(range(100)):
+            assert lp1
+            lp1 = stm_get_ref(lp1, i)
+        assert not lp1
+
+    def test_account_for_privatized_page(self):
+        self.start_transaction()
+        obj = stm_allocate(16)
+        self.push_root(obj)
+        self.commit_transaction()
+        obj = self.pop_root()
+        base = lib._stm_total_allocated()
+        assert base <= 4096
+
+        self.start_transaction()
+        stm_write(obj)
+        assert lib._stm_total_allocated() == base + 4096
+
+    def test_reset_partial_alloc_pages(self):
+        py.test.skip("a would-be-nice feature, but not actually needed: "
+                     "the next major GC will take care of it")
+        self.start_transaction()
+        new = stm_allocate(16)
+        stm_set_char(new, 'a')
+        self.push_root(new)
+        stm_minor_collect()
+        new = self.pop_root()
+        self.abort_transaction()
+
+        self.start_transaction()
+        newer = stm_allocate(16)
+        self.push_root(newer)
+        stm_minor_collect()
+        newer = self.pop_root()
+        assert stm_get_real_address(new) == stm_get_real_address(newer)
+        assert stm_get_char(newer) == '\0'
+
+    def test_reuse_page(self):
+        py.test.skip("a would-be-nice feature, but not actually needed: "
+                     "the next major GC will take care of it")
+        self.start_transaction()
+        new = stm_allocate(16)
+        self.push_root(new)
+        stm_minor_collect()
+        new = self.pop_root()
+        # assert stm_get_page_flag(stm_get_obj_pages(new)[0]) == lib.UNCOMMITTED_SHARED_PAGE
+        self.abort_transaction()
+
+        self.start_transaction()
+        newer = stm_allocate(16)
+        self.push_root(newer)
+        stm_minor_collect()
+        newer = self.pop_root()
+        assert new == newer
+
+    def test_write_to_old_after_minor(self):
+        self.start_transaction()
+        new = stm_allocate(16)
+        self.push_root(new)
+        stm_minor_collect()
+        old = self.pop_root()
+        self.commit_transaction()
+
+        self.start_transaction()
+        stm_write(old) # old objs to trace
+        stm_set_char(old, 'x')
+        stm_minor_collect()
+        stm_write(old) # old objs to trace
+        stm_set_char(old, 'y')
+        self.commit_transaction()
+
+    def test_can_move(self):
+        self.start_transaction()
+        new = stm_allocate(16)
+        assert lib.stm_can_move(new) == 1
+        self.push_root(new)
+        stm_minor_collect()
+        old = self.pop_root()
+        assert lib.stm_can_move(old) == 0
+        self.commit_transaction()
+
+        self.start_transaction()
+        assert lib.stm_can_move(old) == 0
+
+    def test_marker_1(self):
+        self.start_transaction()
+        p1 = stm_allocate(600)
+        stm_set_char(p1, 'o')
+        self.push_root(p1)
+        self.push_root(ffi.cast("object_t *", 123))
+        p2 = stm_allocate(600)
+        stm_set_char(p2, 't')
+        self.push_root(p2)
+        stm_minor_collect()
+        assert lib._stm_total_allocated() == 2 * 616
+        #
+        p2 = self.pop_root()
+        m = self.pop_root()
+        assert m == ffi.cast("object_t *", 123)
+        p1 = self.pop_root()
+        assert stm_get_char(p1) == 'o'
+        assert stm_get_char(p2) == 't'
+
+    def test_marker_2(self):
+        py.test.skip("testing this requires working shadowstack saving logic")
+        self.start_transaction()
+        p1 = stm_allocate(600)
+        stm_set_char(p1, 'o')
+        self.push_root(p1)
+        self.push_root(ffi.cast("object_t *", lib.STM_STACK_MARKER_OLD))
+        p2 = stm_allocate(600)
+        stm_set_char(p2, 't')
+        self.push_root(p2)
+        stm_minor_collect()
+        assert lib._stm_total_allocated() == 1 * 616
+        #
+        p2 = self.pop_root()
+        m = self.pop_root()
+        assert m == ffi.cast("object_t *", lib.STM_STACK_MARKER_OLD)
+        assert stm_get_char(p2) == 't'
+        # the 'p1' reference is invalid now, don't try to read it.
+        # we check that it's invalid because _stm_total_allocated()
+        # only records one of the two objects.
+
+    def test_clear_read_marker_for_external_young(self):
+        self.start_transaction()
+        big = stm_allocate(FAST_ALLOC + 1000) # young outside nursery
+        stm_read(big)
+        assert stm_was_read(big)
+        stm_minor_collect() # free young outside
+        assert not stm_was_read(big)
+        # if the read marker is not cleared, we get false conflicts
+        # with later transactions using the same large-malloced slot
+        # as our outside-nursery-obj


More information about the pypy-commit mailing list