[pypy-commit] stmgc default: Use shadowstack markers to avoid re-scanning the complete stack at every

arigo noreply at buildbot.pypy.org
Tue Apr 15 18:47:35 CEST 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r1159:918b1901b1f9
Date: 2014-04-15 18:47 +0200
http://bitbucket.org/pypy/stmgc/changeset/918b1901b1f9/

Log:	Use shadowstack markers to avoid re-scanning the complete stack at
	every minor collection. Needs to be supported explicitly by pypy.

diff --git a/c7/demo/demo2.c b/c7/demo/demo2.c
--- a/c7/demo/demo2.c
+++ b/c7/demo/demo2.c
@@ -194,6 +194,7 @@
 {
     int status;
     stm_register_thread_local(&stm_thread_local);
+    char *org = (char *)stm_thread_local.shadowstack;
 
     STM_PUSH_ROOT(stm_thread_local, global_chained_list);  /* remains forever in the shadow stack */
 
@@ -202,7 +203,7 @@
     }
 
     STM_POP_ROOT(stm_thread_local, global_chained_list);
-    assert(stm_thread_local.shadowstack == stm_thread_local.shadowstack_base);
+    assert(org == (char *)stm_thread_local.shadowstack);
 
     unregister_thread_local();
     status = sem_post(&done); assert(status == 0);
diff --git a/c7/demo/demo_simple.c b/c7/demo/demo_simple.c
--- a/c7/demo/demo_simple.c
+++ b/c7/demo/demo_simple.c
@@ -50,6 +50,7 @@
 {
     int status;
     stm_register_thread_local(&stm_thread_local);
+    char *org = (char *)stm_thread_local.shadowstack;
     tl_counter = 0;
 
     object_t *tmp;
@@ -65,7 +66,7 @@
         i++;
     }
 
-    assert(stm_thread_local.shadowstack == stm_thread_local.shadowstack_base);
+    assert(org == (char *)stm_thread_local.shadowstack);
 
     stm_unregister_thread_local(&stm_thread_local);
     status = sem_post(&done); assert(status == 0);
diff --git a/c7/stm/gcpage.c b/c7/stm/gcpage.c
--- a/c7/stm/gcpage.c
+++ b/c7/stm/gcpage.c
@@ -379,8 +379,8 @@
         struct stm_shadowentry_s *current = tl->shadowstack;
         struct stm_shadowentry_s *base = tl->shadowstack_base;
         while (current-- != base) {
-            assert(current->ss != (object_t *)-1);
-            mark_visit_object(current->ss, segment_base);
+            if (((uintptr_t)current->ss) > STM_STACK_MARKER_OLD)
+                mark_visit_object(current->ss, segment_base);
         }
         mark_visit_object(tl->thread_local_obj, segment_base);
 
diff --git a/c7/stm/nursery.c b/c7/stm/nursery.c
--- a/c7/stm/nursery.c
+++ b/c7/stm/nursery.c
@@ -156,10 +156,32 @@
     stm_thread_local_t *tl = STM_SEGMENT->running_thread;
     struct stm_shadowentry_s *current = tl->shadowstack;
     struct stm_shadowentry_s *base = tl->shadowstack_base;
-    while (current-- != base) {
-        assert(current->ss != (object_t *)-1);
-        minor_trace_if_young(&current->ss);
+    while (1) {
+        --current;
+        OPT_ASSERT(current >= base);
+
+        switch ((uintptr_t)current->ss) {
+
+        case 0:   /* NULL */
+            continue;
+
+        case STM_STACK_MARKER_NEW:
+            /* the marker was not already seen: mark it as seen,
+               but continue looking more deeply in the shadowstack */
+            current->ss = (object_t *)STM_STACK_MARKER_OLD;
+            continue;
+
+        case STM_STACK_MARKER_OLD:
+            /* the marker was already seen: we can stop the
+               root stack tracing at this point */
+            goto interrupt;
+
+        default:
+            /* the stack entry is a regular pointer */
+            minor_trace_if_young(&current->ss);
+        }
     }
+ interrupt:
     minor_trace_if_young(&tl->thread_local_obj);
 }
 
diff --git a/c7/stm/setup.c b/c7/stm/setup.c
--- a/c7/stm/setup.c
+++ b/c7/stm/setup.c
@@ -153,11 +153,13 @@
     struct stm_shadowentry_s *s = (struct stm_shadowentry_s *)start;
     tl->shadowstack = s;
     tl->shadowstack_base = s;
+    STM_PUSH_ROOT(*tl, STM_STACK_MARKER_OLD);
 }
 
 static void _done_shadow_stack(stm_thread_local_t *tl)
 {
-    assert(tl->shadowstack >= tl->shadowstack_base);
+    assert(tl->shadowstack > tl->shadowstack_base);
+    assert(tl->shadowstack_base->ss == (object_t *)STM_STACK_MARKER_OLD);
 
     char *start = (char *)tl->shadowstack_base;
     _shadowstack_trap_page(start, PROT_READ | PROT_WRITE);
diff --git a/c7/stm/timing.c b/c7/stm/timing.c
--- a/c7/stm/timing.c
+++ b/c7/stm/timing.c
@@ -70,7 +70,7 @@
         s_mutex_lock();
         fprintf(stderr, "thread %p:\n", tl);
         for (i = 0; i < _STM_TIME_N; i++) {
-            fprintf(stderr, "    %-24s %9u  %.3f s\n",
+            fprintf(stderr, "    %-24s %9u %8.3f s\n",
                     timer_names[i], tl->events[i], (double)tl->timing[i]);
         }
         s_mutex_unlock();
diff --git a/c7/stmgc.h b/c7/stmgc.h
--- a/c7/stmgc.h
+++ b/c7/stmgc.h
@@ -264,6 +264,8 @@
 #define STM_PUSH_ROOT(tl, p)   ((tl).shadowstack++->ss = (object_t *)(p))
 #define STM_POP_ROOT(tl, p)    ((p) = (typeof(p))((--(tl).shadowstack)->ss))
 #define STM_POP_ROOT_RET(tl)   ((--(tl).shadowstack)->ss)
+#define STM_STACK_MARKER_NEW   1
+#define STM_STACK_MARKER_OLD   2
 
 
 /* Every thread needs to have a corresponding stm_thread_local_t
diff --git a/c7/test/support.py b/c7/test/support.py
--- a/c7/test/support.py
+++ b/c7/test/support.py
@@ -12,6 +12,8 @@
 #define STM_NB_SEGMENTS ...
 #define _STM_FAST_ALLOC ...
 #define _STM_GCFLAG_WRITE_BARRIER ...
+#define STM_STACK_MARKER_NEW ...
+#define STM_STACK_MARKER_OLD ...
 
 struct stm_shadowentry_s {
     object_t *ss;
@@ -504,7 +506,8 @@
     def pop_root(self):
         tl = self.tls[self.current_thread]
         curlength = tl.shadowstack - tl.shadowstack_base
-        if curlength == 0:
+        assert curlength >= 1
+        if curlength == 1:
             raise EmptyStack
         assert 0 < curlength <= SHADOWSTACK_LENGTH
         tl.shadowstack -= 1
diff --git a/c7/test/test_gcpage.py b/c7/test/test_gcpage.py
--- a/c7/test/test_gcpage.py
+++ b/c7/test/test_gcpage.py
@@ -228,3 +228,22 @@
 
         self.start_transaction()
         assert stm_get_char(self.get_thread_local_obj()) == 'L'
+
+    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 *", lib.STM_STACK_MARKER_NEW))
+        p2 = stm_allocate(600)
+        stm_set_char(p2, 't')
+        self.push_root(p2)
+        stm_major_collect()
+        assert lib._stm_total_allocated() == 2 * 616
+        #
+        p2 = self.pop_root()
+        m = self.pop_root()
+        assert m == ffi.cast("object_t *", lib.STM_STACK_MARKER_OLD)
+        p1 = self.pop_root()
+        assert stm_get_char(p1) == 'o'
+        assert stm_get_char(p2) == 't'
diff --git a/c7/test/test_nursery.py b/c7/test/test_nursery.py
--- a/c7/test/test_nursery.py
+++ b/c7/test/test_nursery.py
@@ -197,3 +197,42 @@
 
         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 *", lib.STM_STACK_MARKER_NEW))
+        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 *", lib.STM_STACK_MARKER_OLD)
+        p1 = self.pop_root()
+        assert stm_get_char(p1) == 'o'
+        assert stm_get_char(p2) == 't'
+
+    def test_marker_2(self):
+        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.


More information about the pypy-commit mailing list