Author: Remi Meier <[email protected]>
Branch: c8-private-pages
Changeset: r1531:4c49f4a4e817
Date: 2015-01-15 15:10 +0100
http://bitbucket.org/pypy/stmgc/changeset/4c49f4a4e817/

Log:    WIP: starting with major collections, add a validation step to all
        segs at the start of it

diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -254,9 +254,11 @@
 
 static void reset_modified_from_backup_copies(int segment_num);  /* forward */
 
-static void _stm_validate(void *free_if_abort)
+static bool _stm_validate()
 {
-    dprintf(("_stm_validate(%p)\n", free_if_abort));
+    /* returns true if we reached a valid state, or false if
+       we need to abort now */
+    dprintf(("_stm_validate()\n"));
     /* go from last known entry in commit log to the
        most current one and apply all changes done
        by other transactions. Abort if we have read one of
@@ -268,7 +270,7 @@
 
     if (STM_PSEGMENT->transaction_state == TS_INEVITABLE) {
         assert(first_cl->next == INEV_RUNNING);
-        return;
+        return true;
     }
 
     bool needs_abort = false;
@@ -284,8 +286,14 @@
         if (first_cl->next == NULL)
             break;
 
-        if (first_cl->next == INEV_RUNNING)
-            _stm_collectable_safe_point();     /* otherwise, we may deadlock */
+        if (first_cl->next == INEV_RUNNING) {
+#if STM_TESTS
+            stm_abort_transaction();
+#endif
+            /* need to reach safe point if an INEV transaction
+               is waiting for us, otherwise deadlock */
+            break;
+        }
 
         /* Find the set of segments we need to copy from and lock them: */
         uint64_t segments_to_lock = 1UL << my_segnum;
@@ -293,8 +301,6 @@
         while ((next_cl = cl->next) != NULL) {
             if (next_cl == INEV_RUNNING) {
 #if STM_TESTS
-                if (free_if_abort != (void *)-1)
-                    free(free_if_abort);
                 stm_abort_transaction();
 #endif
                 /* only validate entries up to INEV */
@@ -381,13 +387,7 @@
         release_privatization_lock(STM_SEGMENT->segment_num);
     }
 
-    if (needs_abort) {
-        if (free_if_abort != (void *)-1)
-            free(free_if_abort);
-        /* pages may be inconsistent */
-
-        stm_abort_transaction();
-    }
+    return !needs_abort;
 }
 
 static struct stm_commit_log_entry_s *_create_commit_log_entry(void)
@@ -416,7 +416,10 @@
     struct stm_commit_log_entry_s *old;
 
     while (1) {
-        _stm_validate(/* free_if_abort =*/ new);
+        if (!_stm_validate()) {
+            free(new);
+            stm_abort_transaction();
+        }
 
         /* try to attach to commit log: */
         old = STM_PSEGMENT->last_commit_log_entry;
@@ -468,7 +471,8 @@
 /* ############# STM ############# */
 void stm_validate()
 {
-    _stm_validate(NULL);
+    if (!_stm_validate())
+        stm_abort_transaction();
 }
 
 
@@ -829,7 +833,7 @@
         stm_rewind_jmp_restore_shadowstack(tl);
     assert(tl->shadowstack == pseg->shadowstack_at_start_of_transaction);
 #endif
-tl->last_abort__bytes_in_nursery = bytes_in_nursery;
+    tl->last_abort__bytes_in_nursery = bytes_in_nursery;
 
 #pragma pop_macro("STM_SEGMENT")
 #pragma pop_macro("STM_PSEGMENT")
diff --git a/c8/stm/core.h b/c8/stm/core.h
--- a/c8/stm/core.h
+++ b/c8/stm/core.h
@@ -38,6 +38,7 @@
     GCFLAG_WRITE_BARRIER = _STM_GCFLAG_WRITE_BARRIER,
     GCFLAG_HAS_SHADOW = 0x02,
     GCFLAG_WB_EXECUTED = 0x04,
+    GCFLAG_VISITED = 0x05,
 };
 
 
@@ -190,7 +191,7 @@
 static void synchronize_objects_flush(void);
 
 static void _signal_handler(int sig, siginfo_t *siginfo, void *context);
-static void _stm_validate(void *free_if_abort);
+static bool _stm_validate();
 
 static inline void _duck(void) {
     /* put a call to _duck() between two instructions that set 0 into
diff --git a/c8/stm/gcpage.c b/c8/stm/gcpage.c
--- a/c8/stm/gcpage.c
+++ b/c8/stm/gcpage.c
@@ -104,3 +104,258 @@
              (uintptr_t)p / 4096UL));
     return o;
 }
+
+
+/************************************************************/
+
+
+static void major_collection_if_requested(void)
+{
+    assert(!_has_mutex());
+    if (!is_major_collection_requested())
+        return;
+
+    s_mutex_lock();
+
+    if (is_major_collection_requested()) {   /* if still true */
+
+        synchronize_all_threads(STOP_OTHERS_UNTIL_MUTEX_UNLOCK);
+
+        if (is_major_collection_requested()) {   /* if *still* true */
+            major_collection_now_at_safe_point();
+        }
+
+    }
+
+    s_mutex_unlock();
+}
+
+
+/************************************************************/
+
+/* objects to trace are traced in the sharing seg0 or in a
+   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;
+
+/* we use the sharing seg0's pages for the GCFLAG_VISITED flag */
+
+static inline struct object_s *mark_loc(object_t *obj)
+{
+    /* uses the memory in seg0 for marking: */
+    struct object_s *result = (struct object_s*)REAL_ADDRESS(stm_object_pages, 
obj);
+    return result;
+}
+
+static inline bool mark_visited_test(object_t *obj)
+{
+    struct object_s *realobj = mark_loc(obj);
+    return !!(realobj->stm_flags & GCFLAG_VISITED);
+}
+
+static inline bool mark_visited_test_and_set(object_t *obj)
+{
+    struct object_s *realobj = mark_loc(obj);
+    if (realobj->stm_flags & GCFLAG_VISITED) {
+        return true;
+    }
+    else {
+        realobj->stm_flags |= GCFLAG_VISITED;
+        return false;
+    }
+}
+
+static inline bool mark_visited_test_and_clear(object_t *obj)
+{
+    struct object_s *realobj = mark_loc(obj);
+    if (realobj->stm_flags & GCFLAG_VISITED) {
+        realobj->stm_flags &= ~GCFLAG_VISITED;
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+
+/************************************************************/
+
+/* static void mark_and_trace(object_t *obj, char *segment_base) */
+/* { */
+/*     assert(list_is_empty(mark_objects_to_trace)); */
+
+/*     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); */
+
+/*         if (list_is_empty(mark_objects_to_trace)) */
+/*             break; */
+
+/*         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_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; */
+/* } */
+
+/* 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); */
+
+/*     struct object_s *realobj = */
+/*         (struct object_s *)REAL_ADDRESS(get_segment_base(segnum), obj); */
+
+/*     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_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) *\/ */
+
+/*     long i; */
+/*     for (i = 1; i < NB_SEGMENTS; i++) { */
+/*         char *base = get_segment_base(i); */
+
+/*         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); */
+
+/*                 mark_visited_test_and_set(item); */
+/*                 mark_and_trace(item, base);              /\* private 
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)); */
+/*     } */
+
+/*     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); 
*/
+
+/*         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); */
+
+/*         tl = tl->next; */
+/*     } while (tl != stm_all_thread_locals); */
+
+/*     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); */
+/*         } */
+/*     } */
+/* } */
+
+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; */
+    /* } */
+    return true;
+}
+
+static void major_collection_now_at_safe_point(void)
+{
+    dprintf(("\n"));
+    dprintf((" .----- major collection -----------------------\n"));
+    assert(_has_mutex());
+
+    /* first, force a minor collection in each of the other segments */
+    major_do_validation_and_minor_collections();
+
+    dprintf((" | used before collection: %ld\n",
+             (long)pages_ctl.total_allocated));
+
+    /* only necessary because of assert that fails otherwise (XXX) */
+    acquire_all_privatization_locks();
+
+    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); */
+
+    /* /\* cleanup *\/ */
+    /* clean_up_segment_lists(); */
+
+    /* /\* sweeping *\/ */
+    /* sweep_large_objects(); */
+    /* //sweep_uniform_pages(); */
+
+    dprintf((" | used after collection:  %ld\n",
+             (long)pages_ctl.total_allocated));
+    dprintf((" `----------------------------------------------\n"));
+
+    reset_major_collection_requested();
+
+    DEBUG_EXPECT_SEGFAULT(true);
+
+    release_all_privatization_locks();
+}
diff --git a/c8/stm/gcpage.h b/c8/stm/gcpage.h
--- a/c8/stm/gcpage.h
+++ b/c8/stm/gcpage.h
@@ -16,3 +16,8 @@
 static void teardown_gcpage(void);
 static void setup_N_pages(char *pages_addr, long num);
 static stm_char *allocate_outside_nursery_large(uint64_t size);
+
+
+static void major_collection_if_requested(void);
+static void major_collection_now_at_safe_point(void);
+static bool largemalloc_keep_object_at(char *data);   /* for largemalloc.c */
diff --git a/c8/stm/largemalloc.c b/c8/stm/largemalloc.c
--- a/c8/stm/largemalloc.c
+++ b/c8/stm/largemalloc.c
@@ -588,8 +588,7 @@
     if (_stm_largemalloc_keep != NULL)
         return _stm_largemalloc_keep((char *)&chunk->d);
 #endif
-    return true;
-    //XXX:    return largemalloc_keep_object_at((char *)&chunk->d);
+    return largemalloc_keep_object_at((char *)&chunk->d);
 }
 
 void _stm_largemalloc_sweep(void)
diff --git a/c8/stm/nursery.c b/c8/stm/nursery.c
--- a/c8/stm/nursery.c
+++ b/c8/stm/nursery.c
@@ -18,10 +18,8 @@
     assert(_STM_FAST_ALLOC <= NURSERY_SIZE);
     _stm_nursery_start = NURSERY_START;
 
-    long i = 0;
-    get_segment(i)->nursery_current = (stm_char *)-1;
-    get_segment(i)->nursery_end = -1;
-    for (i = 1; i < NB_SEGMENTS; i++) {
+    long i;
+    for (i = 0; i < NB_SEGMENTS; i++) {
         get_segment(i)->nursery_current = (stm_char *)NURSERY_START;
         get_segment(i)->nursery_end = NURSERY_END;
     }
@@ -292,10 +290,10 @@
 void stm_collect(long level)
 {
     if (level > 0)
-        abort();
+        force_major_collection_request();
 
     minor_collection(/*commit=*/ false);
-    /* XXX: major_collection_if_requested(); */
+    major_collection_if_requested();
 }
 
 
@@ -382,6 +380,52 @@
 }
 
 
+static void major_do_validation_and_minor_collections(void)
+{
+    int original_num = STM_SEGMENT->segment_num;
+    long i;
+
+    /* including the sharing seg0 */
+    for (i = 0; i < NB_SEGMENTS; i++) {
+        set_gs_register(get_segment_base(i));
+
+        if (!_stm_validate()) {
+            /* tell it to abort when continuing */
+            STM_PSEGMENT->pub.nursery_end = NSE_SIGABORT;
+            assert(must_abort());
+
+            dprintf(("abort data structures\n"));
+            abort_data_structures_from_segment_num(i);
+            continue;
+        }
+
+
+        if (MINOR_NOTHING_TO_DO(STM_PSEGMENT))  /*TS_NONE segments have 
NOTHING_TO_DO*/
+            continue;
+
+        assert(STM_PSEGMENT->transaction_state != TS_NONE);
+        assert(STM_PSEGMENT->safe_point != SP_RUNNING);
+        assert(STM_PSEGMENT->safe_point != SP_NO_TRANSACTION);
+
+
+        /* Other segments that will abort immediately after resuming: we
+           have to ignore them, not try to collect them anyway!
+           Collecting might fail due to invalid state.
+        */
+        if (!must_abort()) {
+            _do_minor_collection(/*commit=*/ false);
+            assert(MINOR_NOTHING_TO_DO(STM_PSEGMENT));
+        }
+        else {
+            dprintf(("abort data structures\n"));
+            abort_data_structures_from_segment_num(i);
+        }
+    }
+
+    set_gs_register(get_segment_base(original_num));
+}
+
+
 static object_t *allocate_shadow(object_t *obj)
 {
     char *realobj = REAL_ADDRESS(STM_SEGMENT->segment_base, obj);
diff --git a/c8/stm/nursery.h b/c8/stm/nursery.h
--- a/c8/stm/nursery.h
+++ b/c8/stm/nursery.h
@@ -1,9 +1,11 @@
 
 #define NSE_SIGPAUSE   _STM_NSE_SIGNAL_MAX
+#define NSE_SIGABORT   _STM_NSE_SIGNAL_ABORT
 
 static void minor_collection(bool commit);
 static void check_nursery_at_transaction_start(void);
 static size_t throw_away_nursery(struct stm_priv_segment_info_s *pseg);
+static void major_do_validation_and_minor_collections(void);
 
 static void assert_memset_zero(void *s, size_t n);
 
diff --git a/c8/stm/pages.c b/c8/stm/pages.c
--- a/c8/stm/pages.c
+++ b/c8/stm/pages.c
@@ -34,6 +34,29 @@
     return ta;
 }
 
+static bool is_major_collection_requested(void)
+{
+    return pages_ctl.major_collection_requested;
+}
+
+static void force_major_collection_request(void)
+{
+    pages_ctl.major_collection_requested = true;
+}
+
+static void reset_major_collection_requested(void)
+{
+    assert(_has_mutex());
+
+    uint64_t next_bound = (uint64_t)((double)pages_ctl.total_allocated *
+                                     GC_MAJOR_COLLECT);
+    if (next_bound < GC_MIN)
+        next_bound = GC_MIN;
+
+    pages_ctl.total_allocated_bound = next_bound;
+    pages_ctl.major_collection_requested = false;
+}
+
 
 /************************************************************/
 
diff --git a/c8/stm/pages.h b/c8/stm/pages.h
--- a/c8/stm/pages.h
+++ b/c8/stm/pages.h
@@ -44,6 +44,9 @@
 static void page_mark_inaccessible(long segnum, uintptr_t pagenum);
 
 static uint64_t increment_total_allocated(ssize_t add_or_remove);
+static bool is_major_collection_requested(void);
+static void force_major_collection_request(void);
+static void reset_major_collection_requested(void);
 
 
 static inline char *get_virtual_page(long segnum, uintptr_t pagenum)
diff --git a/c8/stmgc.h b/c8/stmgc.h
--- a/c8/stmgc.h
+++ b/c8/stmgc.h
@@ -59,7 +59,8 @@
 
 #define _STM_GCFLAG_WRITE_BARRIER      0x01
 #define _STM_FAST_ALLOC           (66*1024)
-#define _STM_NSE_SIGNAL_MAX               1
+#define _STM_NSE_SIGNAL_ABORT             1
+#define _STM_NSE_SIGNAL_MAX               2
 
 void _stm_write_slowpath(object_t *);
 object_t *_stm_allocate_slowpath(ssize_t);
diff --git a/c8/test/support.py b/c8/test/support.py
--- a/c8/test/support.py
+++ b/c8/test/support.py
@@ -302,7 +302,7 @@
                     ],
      undef_macros=['NDEBUG'],
      include_dirs=[parent_dir],
-     extra_compile_args=['-g', '-O0', '-Wall', '-Werror', '-ferror-limit=1'],
+     extra_compile_args=['-g', '-O0', '-Wall', '-Werror', '-ferror-limit=5'],
      extra_link_args=['-g', '-lrt'],
      force_generic_engine=True)
 
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
@@ -60,6 +60,7 @@
         assert ([stm_is_accessible_page(p) for p in pages]
                 == [True, True])
 
+
     def test_partial_alloced_pages(self):
         self.start_transaction()
         new = stm_allocate(16)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to