Author: Remi Meier <[email protected]>
Branch: c8-private-pages
Changeset: r1532:e3e791a65654
Date: 2015-01-15 16:46 +0100
http://bitbucket.org/pypy/stmgc/changeset/e3e791a65654/

Log:    first test passes

diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -189,7 +189,7 @@
 
 static void _signal_handler(int sig, siginfo_t *siginfo, void *context)
 {
-    assert(_stm_segfault_expected);
+    assert(_stm_segfault_expected > 0);
 
     int saved_errno = errno;
     char *addr = siginfo->si_addr;
@@ -1033,19 +1033,14 @@
         --j;
         stm_char *frag = STM_PSEGMENT->sq_fragments[j];
         uintptr_t page = ((uintptr_t)frag) / 4096UL;
-        /* XXX: necessary? */
-        /* if (is_shared_log_page(page)) */
-        /*     continue; */
-
         ssize_t frag_size = STM_PSEGMENT->sq_fragsizes[j];
 
         char *src = REAL_ADDRESS(STM_SEGMENT->segment_base, frag);
-        /* XXX: including the sharing segment? */
         for (i = 0; i < NB_SEGMENTS; i++) {
             if (i == myself)
                 continue;
 
-            if (get_page_status_in(i, page) != PAGE_NO_ACCESS) {
+            if (i == 0 || (get_page_status_in(i, page) != PAGE_NO_ACCESS)) {
                 /* shared or private, but never segfault */
                 char *dst = REAL_ADDRESS(get_segment_base(i), frag);
                 memcpy(dst, src, frag_size);
diff --git a/c8/stm/core.h b/c8/stm/core.h
--- a/c8/stm/core.h
+++ b/c8/stm/core.h
@@ -38,7 +38,7 @@
     GCFLAG_WRITE_BARRIER = _STM_GCFLAG_WRITE_BARRIER,
     GCFLAG_HAS_SHADOW = 0x02,
     GCFLAG_WB_EXECUTED = 0x04,
-    GCFLAG_VISITED = 0x05,
+    GCFLAG_VISITED = 0x08,
 };
 
 
diff --git a/c8/stm/gcpage.c b/c8/stm/gcpage.c
--- a/c8/stm/gcpage.c
+++ b/c8/stm/gcpage.c
@@ -137,7 +137,7 @@
    certain segment if there exist modifications there.
    All other segments' versions should be identical to seg0's
    version and thus don't need tracing. */
-static struct list_s *mark_objects_to_trace;
+static struct list_s *marked_objects_to_trace;
 
 /* we use the sharing seg0's pages for the GCFLAG_VISITED flag */
 
@@ -181,144 +181,220 @@
 
 /************************************************************/
 
-/* static void mark_and_trace(object_t *obj, char *segment_base) */
-/* { */
-/*     assert(list_is_empty(mark_objects_to_trace)); */
+static inline void mark_record_trace(object_t **pobj)
+{
+    /* takes a normal pointer to a thread-local pointer to an object */
+    object_t *obj = *pobj;
 
-/*     while (1) { */
-/*         /\* trace into the object (the version from 'segment_base') *\/ */
-/*         struct object_s *realobj = */
-/*             (struct object_s *)REAL_ADDRESS(segment_base, obj); */
-/*         stmcb_trace(realobj, &mark_record_trace); */
+    /* Note: this obj might be visited already, but from a different
+       segment.  We ignore this case and skip re-visiting the object
+       anyway.  The idea is that such an object is old (not from the
+       current transaction), otherwise it would not be possible to see
+       it in two segments; and moreover it is not modified, otherwise
+       mark_trace() would have been called on two different segments
+       already.  That means that this object is identical in all
+       segments and only needs visiting once.  (It may actually be in a
+       shared page, or maybe not.)
+    */
+    if (obj == NULL || mark_visited_test_and_set(obj))
+        return;    /* already visited this object */
 
-/*         if (list_is_empty(mark_objects_to_trace)) */
-/*             break; */
+    LIST_APPEND(marked_objects_to_trace, obj);
+}
 
-/*         obj = (object_t *)list_pop_item(mark_objects_to_trace); */
-/*     } */
-/* } */
 
-/* static inline void mark_visit_object(object_t *obj, char *segment_base) */
-/* { */
-/*     /\* if already visited, don't trace *\/ */
-/*     if (obj == NULL || mark_visited_test_and_set(obj)) */
-/*         return; */
-/*     mark_and_trace(obj, segment_base); */
-/* } */
+static void mark_and_trace(object_t *obj, char *segment_base)
+{
+    /* mark the obj and trace all reachable objs from it */
 
-/* static void *mark_visit_objects_from_ss(void *_, const void *slice, size_t 
size) */
-/* { */
-/*     const struct stm_shadowentry_s *p, *end; */
-/*     p = (const struct stm_shadowentry_s *)slice; */
-/*     end = (const struct stm_shadowentry_s *)(slice + size); */
-/*     for (; p < end; p++) */
-/*         if ((((uintptr_t)p->ss) & 3) == 0) */
-/*             mark_visit_object(p->ss, stm_object_pages); */
-/*     return NULL; */
-/* } */
+    assert(list_is_empty(marked_objects_to_trace));
 
-/* static void assert_obj_accessible_in(long segnum, object_t *obj) */
-/* { */
-/* #ifndef NDEBUG */
-/*     uintptr_t page = (uintptr_t)item / 4096UL; */
-/*     assert(get_page_status_in(segnum, page) == PAGE_ACCESSIBLE); */
+    while (1) {
+        /* trace into the object (the version from 'segment_base') */
+        struct object_s *realobj =
+            (struct object_s *)REAL_ADDRESS(segment_base, obj);
+        stmcb_trace(realobj, &mark_record_trace);
 
-/*     struct object_s *realobj = */
-/*         (struct object_s *)REAL_ADDRESS(get_segment_base(segnum), obj); */
+        if (list_is_empty(marked_objects_to_trace))
+            break;
 
-/*     size_t obj_size = stmcb_size_rounded_up(realobj); */
-/*     uintptr_t count = obj_size / 4096UL + 1; */
-/*     while (count--> 0) { */
-/*         assert(get_page_status_in(segnum, page) == PAGE_ACCESSIBLE); */
-/*         page++; */
-/*     } */
-/* #endif */
-/* } */
+        obj = (object_t *)list_pop_item(marked_objects_to_trace);
+    }
+}
 
+static inline void mark_visit_object(object_t *obj, char *segment_base)
+{
+    /* if already visited, don't trace */
+    if (obj == NULL || mark_visited_test_and_set(obj))
+        return;
+    mark_and_trace(obj, segment_base);
+}
 
-/* static void mark_visit_from_modified_objects(void) */
-/* { */
-/*     /\* look for modified objects in segments and mark all of them */
-/*        for further tracing (XXX: don't if we are going to share */
-/*        some of the pages) *\/ */
+static void *mark_visit_objects_from_ss(void *_, const void *slice, size_t 
size)
+{
+    const struct stm_shadowentry_s *p, *end;
+    p = (const struct stm_shadowentry_s *)slice;
+    end = (const struct stm_shadowentry_s *)(slice + size);
+    for (; p < end; p++)
+        if ((((uintptr_t)p->ss) & 3) == 0)
+            mark_visit_object(p->ss, stm_object_pages); // seg0
+    return NULL;
+}
 
-/*     long i; */
-/*     for (i = 1; i < NB_SEGMENTS; i++) { */
-/*         char *base = get_segment_base(i); */
+static void assert_obj_accessible_in(long segnum, object_t *obj)
+{
+#ifndef NDEBUG
+    uintptr_t page = (uintptr_t)obj / 4096UL;
+    assert(get_page_status_in(segnum, page) == PAGE_ACCESSIBLE);
 
-/*         LIST_FOREACH_R( */
-/*             get_priv_segment(i)->modified_old_objects, */
-/*             object_t * /\*item*\/, */
-/*             ({ */
-/*                 /\* All modified objs have all pages accessible for now. */
-/*                    This is because we create a backup of the whole obj */
-/*                    and thus make all pages accessible. *\/ */
-/*                 assert_obj_accessible_in(i, item); */
+    struct object_s *realobj =
+        (struct object_s *)REAL_ADDRESS(get_segment_base(segnum), obj);
 
-/*                 mark_visited_test_and_set(item); */
-/*                 mark_and_trace(item, base);              /\* private 
version *\/ */
-/*             })); */
-/*     } */
-/* } */
+    size_t obj_size = stmcb_size_rounded_up(realobj);
+    uintptr_t count = obj_size / 4096UL + 1;
+    while (count--> 0) {
+        assert(get_page_status_in(segnum, page) == PAGE_ACCESSIBLE);
+        page++;
+    }
+#endif
+}
 
-/* static void mark_visit_from_roots(void) */
-/* { */
-/*     if (testing_prebuilt_objs != NULL) { */
-/*         LIST_FOREACH_R(testing_prebuilt_objs, object_t * /\*item*\/, */
-/*                        mark_visit_object(item, stm_object_pages)); */
-/*     } */
 
-/*     stm_thread_local_t *tl = stm_all_thread_locals; */
-/*     do { */
-/*         /\* If 'tl' is currently running, its 'associated_segment_num' */
-/*            field is the segment number.  If not, then the */
-/*            field is still some correct segment number, and it doesn't */
-/*            matter which one we pick. *\/ */
-/*         char *segment_base = get_segment_base(tl->associated_segment_num); 
*/
+static void mark_visit_from_modified_objects(void)
+{
+    /* look for modified objects in segments and mark all of them
+       for further tracing (XXX: don't if we are going to share
+       some of the pages) */
 
-/*         struct stm_shadowentry_s *current = tl->shadowstack; */
-/*         struct stm_shadowentry_s *base = tl->shadowstack_base; */
-/*         while (current-- != base) { */
-/*             if ((((uintptr_t)current->ss) & 3) == 0) */
-/*                 mark_visit_object(current->ss, segment_base); */
-/*         } */
-/*         mark_visit_object(tl->thread_local_obj, segment_base); */
+    long i;
+    for (i = 1; i < NB_SEGMENTS; i++) {
+        char *base = get_segment_base(i);
 
-/*         tl = tl->next; */
-/*     } while (tl != stm_all_thread_locals); */
+        LIST_FOREACH_R(
+            get_priv_segment(i)->modified_old_objects,
+            object_t * /*item*/,
+            ({
+                /* All modified objs have all pages accessible for now.
+                   This is because we create a backup of the whole obj
+                   and thus make all pages accessible. */
+                assert_obj_accessible_in(i, item);
 
-/*     long i; */
-/*     for (i = 1; i <= NB_SEGMENTS; i++) { */
-/*         if (get_priv_segment(i)->transaction_state != TS_NONE) { */
-/*             mark_visit_object( */
-/*                 get_priv_segment(i)->threadlocal_at_start_of_transaction, */
-/*                 get_segment_base(i)); */
-/*             stm_rewind_jmp_enum_shadowstack( */
-/*                 get_segment(i)->running_thread, */
-/*                 mark_visit_objects_from_ss); */
-/*         } */
-/*     } */
-/* } */
+                mark_visited_test_and_set(item);
+                mark_and_trace(item, stm_object_pages);  /* shared, committed 
version */
+                mark_and_trace(item, base);          /* private, modified 
version */
+            }));
+    }
+}
+
+static void mark_visit_from_roots(void)
+{
+    if (testing_prebuilt_objs != NULL) {
+        LIST_FOREACH_R(testing_prebuilt_objs, object_t * /*item*/,
+                       mark_visit_object(item, stm_object_pages)); // seg0
+    }
+
+    stm_thread_local_t *tl = stm_all_thread_locals;
+    do {
+        /* look at all objs on the shadow stack (they are old but may
+           be uncommitted so far, so only exist in the associated_segment_num).
+
+           However, since we just executed a minor collection, they were
+           all synced to the sharing seg0. Thus we can trace them there.
+
+           If they were again modified since then, they were traced
+           by mark_visit_from_modified_object() already.
+        */
+        struct stm_shadowentry_s *current = tl->shadowstack;
+        struct stm_shadowentry_s *base = tl->shadowstack_base;
+        while (current-- != base) {
+            if ((((uintptr_t)current->ss) & 3) == 0)
+                mark_visit_object(current->ss, stm_object_pages);
+        }
+
+        tl = tl->next;
+    } while (tl != stm_all_thread_locals);
+
+    /* also visit all objs in the rewind-shadowstack */
+    long i;
+    for (i = 1; i < NB_SEGMENTS; i++) {
+        if (get_priv_segment(i)->transaction_state != TS_NONE) {
+            stm_rewind_jmp_enum_shadowstack(
+                get_segment(i)->running_thread,
+                mark_visit_objects_from_ss);
+        }
+    }
+}
+
+
+static void clean_up_segment_lists(void)
+{
+#pragma push_macro("STM_PSEGMENT")
+#pragma push_macro("STM_SEGMENT")
+#undef STM_PSEGMENT
+#undef STM_SEGMENT
+
+    long i;
+    for (i = 1; i < NB_SEGMENTS; i++) {
+        struct stm_priv_segment_info_s *pseg = get_priv_segment(i);
+        struct list_s *lst;
+
+        /* 'objects_pointing_to_nursery' should be empty, but isn't
+           necessarily because it also lists objects that have been
+           written to but don't actually point to the nursery.  Clear
+           it up and set GCFLAG_WRITE_BARRIER again on the objects.
+           This is the case for transactions where
+               MINOR_NOTHING_TO_DO() == true
+           but they still did write-barriers on objects
+        */
+        lst = pseg->objects_pointing_to_nursery;
+        if (!list_is_empty(lst)) {
+            abort(); // check that there is a test
+            LIST_FOREACH_R(lst, object_t* /*item*/,
+                ({
+                    struct object_s *realobj = (struct object_s *)
+                        REAL_ADDRESS(pseg->pub.segment_base, (uintptr_t)item);
+
+                    assert(realobj->stm_flags & GCFLAG_WB_EXECUTED);
+                    assert(!(realobj->stm_flags & GCFLAG_WRITE_BARRIER));
+
+                    realobj->stm_flags |= GCFLAG_WRITE_BARRIER;
+                }));
+            list_clear(lst);
+        } else {
+            /* if here MINOR_NOTHING_TO_DO() was true before, it's like
+               we "didn't do a collection" at all. So nothing to do on
+               modified_old_objs. */
+        }
+    }
+#pragma pop_macro("STM_SEGMENT")
+#pragma pop_macro("STM_PSEGMENT")
+}
 
 static inline bool largemalloc_keep_object_at(char *data)
 {
-    /* /\* this is called by _stm_largemalloc_sweep() *\/ */
-    /* object_t *obj = (object_t *)(data - stm_object_pages); */
-    /* if (!mark_visited_test_and_clear(obj)) { */
-    /*     /\* This is actually needed in order to avoid random write-read */
-    /*        conflicts with objects read and freed long in the past. */
-    /*        It is probably rare enough, but still, we want to avoid any */
-    /*        false conflict. (test_random hits it sometimes) *\/ */
-    /*     long i; */
-    /*     for (i = 1; i <= NB_SEGMENTS; i++) { */
-    /*         ((struct stm_read_marker_s *) */
-    /*          (get_segment_base(i) + (((uintptr_t)obj) >> 4)))->rm = 0; */
-    /*     } */
-    /*     return false; */
-    /* } */
+    /* this is called by _stm_largemalloc_sweep() */
+    object_t *obj = (object_t *)(data - stm_object_pages);
+    dprintf(("keep obj %p ? -> %d\n", obj, mark_visited_test(obj)));
+    if (!mark_visited_test_and_clear(obj)) {
+        /* This is actually needed in order to avoid random write-read
+           conflicts with objects read and freed long in the past.
+           It is probably rare enough, but still, we want to avoid any
+           false conflict. (test_random hits it sometimes) */
+        long i;
+        for (i = 1; i < NB_SEGMENTS; i++) {
+            /* reset read marker */
+            *((char *)(get_segment_base(i) + (((uintptr_t)obj) >> 4))) = 0;
+        }
+        return false;
+    }
     return true;
 }
 
+static void sweep_large_objects(void)
+{
+    _stm_largemalloc_sweep();
+}
+
 static void major_collection_now_at_safe_point(void)
 {
     dprintf(("\n"));
@@ -337,16 +413,16 @@
     DEBUG_EXPECT_SEGFAULT(false);
 
     /* marking */
-    /* LIST_CREATE(mark_objects_to_trace); */
-    /* mark_visit_from_modified_objects(); */
-    /* mark_visit_from_roots(); */
-    /* LIST_FREE(mark_objects_to_trace); */
+    LIST_CREATE(marked_objects_to_trace);
+    mark_visit_from_modified_objects();
+    mark_visit_from_roots();
+    LIST_FREE(marked_objects_to_trace);
 
     /* /\* cleanup *\/ */
-    /* clean_up_segment_lists(); */
+    clean_up_segment_lists();
 
     /* /\* sweeping *\/ */
-    /* sweep_large_objects(); */
+    sweep_large_objects();
     /* //sweep_uniform_pages(); */
 
     dprintf((" | used after collection:  %ld\n",
diff --git a/c8/stm/setup.h b/c8/stm/setup.h
--- a/c8/stm/setup.h
+++ b/c8/stm/setup.h
@@ -3,8 +3,8 @@
 static pthread_t *_get_cpth(stm_thread_local_t *);
 
 #ifndef NDEBUG
-static __thread long _stm_segfault_expected = false;
-#define DEBUG_EXPECT_SEGFAULT(v) do {_stm_segfault_expected = (v);} while (0)
+static __thread long _stm_segfault_expected = 0;
+#define DEBUG_EXPECT_SEGFAULT(v) do {if (v) _stm_segfault_expected++; else 
_stm_segfault_expected--;} while (0)
 #else
 #define DEBUG_EXPECT_SEGFAULT(v) {}
 #endif
diff --git a/c8/test/test_gcpage.py b/c8/test/test_gcpage.py
--- a/c8/test/test_gcpage.py
+++ b/c8/test/test_gcpage.py
@@ -126,7 +126,8 @@
         stm_minor_collect()
         assert lib._stm_total_allocated() == 5000 + LMO
 
-        self.pop_root()
+        new = self.pop_root()
+        assert not is_in_nursery(new)
         stm_minor_collect()
         assert lib._stm_total_allocated() == 5000 + LMO
 
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to