Author: Remi Meier <[email protected]>
Branch: c8-private-pages
Changeset: r1561:72a83d94df8e
Date: 2015-01-21 13:03 +0100
http://bitbucket.org/pypy/stmgc/changeset/72a83d94df8e/

Log:    Implement what overflow objs in c7 do using WB_EXECUTED. This undoes
        part of the previous commit which should not be necessary anymore.

diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -705,6 +705,7 @@
     }
 
     assert(list_is_empty(STM_PSEGMENT->modified_old_objects));
+    assert(list_is_empty(STM_PSEGMENT->new_objects));
     assert(list_is_empty(STM_PSEGMENT->objects_pointing_to_nursery));
     assert(tree_is_cleared(STM_PSEGMENT->young_outside_nursery));
     assert(tree_is_cleared(STM_PSEGMENT->nursery_objects_shadows));
@@ -756,6 +757,7 @@
     STM_PSEGMENT->safe_point = SP_NO_TRANSACTION;
     STM_PSEGMENT->transaction_state = TS_NONE;
     list_clear(STM_PSEGMENT->objects_pointing_to_nursery);
+    list_clear(STM_PSEGMENT->new_objects);
 
     release_thread_segment(tl);
     /* cannot access STM_SEGMENT or STM_PSEGMENT from here ! */
@@ -775,6 +777,20 @@
 #endif
 }
 
+static void push_new_objects_to_other_segments(void)
+{
+    acquire_privatization_lock(STM_SEGMENT->segment_num);
+    LIST_FOREACH_R(STM_PSEGMENT->new_objects, object_t *,
+        ({
+            assert(item->stm_flags & GCFLAG_WB_EXECUTED);
+            item->stm_flags &= ~GCFLAG_WB_EXECUTED;
+            synchronize_object_enqueue(item);
+        }));
+    synchronize_objects_flush();
+    release_privatization_lock(STM_SEGMENT->segment_num);
+}
+
+
 void stm_commit_transaction(void)
 {
     assert(!_has_mutex());
@@ -784,6 +800,8 @@
     dprintf(("> stm_commit_transaction()\n"));
     minor_collection(1);
 
+    push_new_objects_to_other_segments();
+
     _validate_and_add_to_commit_log();
 
     invoke_and_clear_user_callbacks(0);   /* for commit */
@@ -887,6 +905,7 @@
     tl->last_abort__bytes_in_nursery = bytes_in_nursery;
 
     list_clear(pseg->objects_pointing_to_nursery);
+    list_clear(pseg->new_objects);
 #pragma pop_macro("STM_SEGMENT")
 #pragma pop_macro("STM_PSEGMENT")
 }
@@ -999,6 +1018,8 @@
     assert(!_is_young(obj));
     assert(STM_PSEGMENT->privatization_lock);
     assert(obj->stm_flags & GCFLAG_WRITE_BARRIER);
+    assert(!(obj->stm_flags & GCFLAG_WB_EXECUTED));
+
     ssize_t obj_size = stmcb_size_rounded_up(
         (struct object_s *)REAL_ADDRESS(STM_SEGMENT->segment_base, obj));
     OPT_ASSERT(obj_size >= 16);
diff --git a/c8/stm/core.h b/c8/stm/core.h
--- a/c8/stm/core.h
+++ b/c8/stm/core.h
@@ -74,11 +74,20 @@
     struct tree_s *young_outside_nursery;
     struct tree_s *nursery_objects_shadows;
 
+    /* list of objects created in the current transaction and
+       that survived at least one minor collection. They need
+       to be synchronized to other segments on commit, but they
+       do not need to be in the commit log entry. */
+    struct list_s *new_objects;
+
     uint8_t privatization_lock;  // XXX KILL
 
     uint8_t safe_point;
     uint8_t transaction_state;
 
+    /* Temp for minor collection */
+    bool minor_collect_will_commit_now;
+
     struct tree_s *callbacks_on_commit_and_abort[2];
 
     struct stm_commit_log_entry_s *last_commit_log_entry;
diff --git a/c8/stm/gcpage.c b/c8/stm/gcpage.c
--- a/c8/stm/gcpage.c
+++ b/c8/stm/gcpage.c
@@ -192,6 +192,14 @@
 
 /************************************************************/
 
+
+static bool is_new_object(object_t *obj)
+{
+    struct object_s *realobj = (struct 
object_s*)REAL_ADDRESS(stm_object_pages, obj); /* seg0 */
+    return realobj->stm_flags & GCFLAG_WB_EXECUTED;
+}
+
+
 static inline void mark_record_trace(object_t **pobj)
 {
     /* takes a normal pointer to a thread-local pointer to an object */
@@ -226,11 +234,12 @@
     stmcb_trace(realobj, &mark_record_trace);
 
     /* trace all references found in sharing seg0 (should always be
-       up-to-date and not cause segfaults) */
+       up-to-date and not cause segfaults, except for new objs) */
     while (!list_is_empty(marked_objects_to_trace)) {
         obj = (object_t *)list_pop_item(marked_objects_to_trace);
 
-        realobj = (struct object_s *)REAL_ADDRESS(stm_object_pages, obj);
+        char *base = is_new_object(obj) ? segment_base : stm_object_pages;
+        realobj = (struct object_s *)REAL_ADDRESS(base, obj);
         stmcb_trace(realobj, &mark_record_trace);
     }
 }
@@ -243,14 +252,31 @@
     mark_and_trace(obj, segment_base);
 }
 
+
+static void mark_visit_possibly_new_object(char *segment_base, object_t *obj)
+{
+    /* if newly allocated object, we trace in segment_base, otherwise in
+       the sharing seg0 */
+    if (obj == NULL)
+        return;
+
+    if (is_new_object(obj)) {
+        mark_visit_object(obj, segment_base);
+    } else {
+        mark_visit_object(obj, stm_object_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)
+        if ((((uintptr_t)p->ss) & 3) == 0) {
+            assert(!is_new_object(p->ss));
             mark_visit_object(p->ss, stm_object_pages); // seg0
+        }
     return NULL;
 }
 
@@ -273,6 +299,7 @@
 }
 
 
+
 static void mark_visit_from_modified_objects(void)
 {
     /* look for modified objects in segments and mark all of them
@@ -280,53 +307,29 @@
        some of the pages) */
 
     long i;
-    struct list_s *uniques = list_create();
     for (i = 1; i < NB_SEGMENTS; i++) {
         char *base = get_segment_base(i);
-        OPT_ASSERT(list_is_empty(uniques));
 
-        /* the list of modified_old_objs can be huge and contain a lot
-           of duplicated (same obj, different slice) entries. It seems
-           worth it to build a new list without duplicates.
-           The reason is that newly created objs, when moved out of the
-           nursery, don't have WB_EXECUTED flag. Thus we execute waaay
-           too many write barriers per transaction and add them all
-           to this list (and the commit log). XXXXX */
         struct list_s *lst = get_priv_segment(i)->modified_old_objects;
+        struct stm_undo_s *modified = (struct stm_undo_s *)lst->items;
+        struct stm_undo_s *end = (struct stm_undo_s *)(lst->items + 
lst->count);
 
-        struct stm_undo_s *undo = (struct stm_undo_s *)lst->items;
-        struct stm_undo_s *end = (struct stm_undo_s *)(lst->items + 
lst->count);
-        for (; undo < end; undo++) {
-            object_t *obj = undo->object;
-            struct object_s *dst = (struct object_s*)REAL_ADDRESS(base, obj);
+        for (; modified < end; modified++) {
+            object_t *obj = modified->object;
+            /* 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, obj);
 
-            if (!(dst->stm_flags & GCFLAG_VISITED)) {
-                LIST_APPEND(uniques, obj);
-                dst->stm_flags |= GCFLAG_VISITED;
+            assert(!is_new_object(obj)); /* should never be in that list */
+
+            if (!mark_visited_test_and_set(obj)) {
+                /* trace shared, committed version */
+                mark_and_trace(obj, stm_object_pages);
             }
+            mark_and_trace(obj, base);   /* private, modified version */
         }
-
-
-        LIST_FOREACH_R(uniques, object_t*,
-           ({
-               /* clear the VISITED flags again and actually visit them */
-               struct object_s *dst = (struct object_s*)REAL_ADDRESS(base, 
item);
-               dst->stm_flags &= ~GCFLAG_VISITED;
-
-               /* 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);
-
-               if (!mark_visited_test_and_set(item))
-                   mark_and_trace(item, stm_object_pages);  /* shared, 
committed version */
-               mark_and_trace(item, base);          /* private, modified 
version */
-           }));
-
-
-        list_clear(uniques);
     }
-    LIST_FREE(uniques);
 }
 
 static void mark_visit_from_roots(void)
@@ -341,19 +344,29 @@
         /* 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
+           IF they are uncommitted new objs, trace in the actual segment,
+           otherwise, 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.
         */
+
+        /* only for new, uncommitted objects:
+           If 'tl' is currently running, its 'associated_segment_num'
+           field is the segment number that contains the correct
+           version of its overflowed objects. */
+        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, stm_object_pages);
+            if ((((uintptr_t)current->ss) & 3) == 0) {
+                mark_visit_possibly_new_object(segment_base, current->ss);
+            }
         }
-        mark_visit_object(tl->thread_local_obj, stm_object_pages);
+
+        mark_visit_possibly_new_object(segment_base, tl->thread_local_obj);
 
         tl = tl->next;
     } while (tl != stm_all_thread_locals);
@@ -362,9 +375,10 @@
     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,
-                stm_object_pages);
+            mark_visit_possibly_new_object(
+                get_segment_base(i),
+                get_priv_segment(i)->threadlocal_at_start_of_transaction);
+
             stm_rewind_jmp_enum_shadowstack(
                 get_segment(i)->running_thread,
                 mark_visit_objects_from_ss);
@@ -372,6 +386,46 @@
     }
 }
 
+static void ready_new_objects(void)
+{
+#pragma push_macro("STM_PSEGMENT")
+#pragma push_macro("STM_SEGMENT")
+#undef STM_PSEGMENT
+#undef STM_SEGMENT
+    /* objs in new_objects only have garbage in the sharing seg0,
+       since it is used to mark objs as visited, we must make
+       sure the flag is cleared at the start of a major collection.
+       (XXX: ^^^ may be optional if we have the part below)
+
+       Also, we need to be able to recognize these objects in order
+       to only trace them in the segment they are valid in. So we
+       also make sure to set WB_EXECUTED in the sharing seg0. No
+       other objs than new_objects have WB_EXECUTED in seg0 (since
+       there can only be committed versions there).
+    */
+
+    long i;
+    for (i = 1; i < NB_SEGMENTS; i++) {
+        struct stm_priv_segment_info_s *pseg = get_priv_segment(i);
+        struct list_s *lst = pseg->new_objects;
+
+        LIST_FOREACH_R(lst, object_t* /*item*/,
+            ({
+                struct object_s *realobj;
+                /* WB_EXECUTED always set in this segment */
+                assert(realobj = (struct 
object_s*)REAL_ADDRESS(pseg->pub.segment_base, item));
+                assert(realobj->stm_flags & GCFLAG_WB_EXECUTED);
+
+                /* clear VISITED and ensure WB_EXECUTED in seg0 */
+                mark_visited_test_and_clear(item);
+                realobj = (struct object_s*)REAL_ADDRESS(stm_object_pages, 
item);
+                realobj->stm_flags |= GCFLAG_WB_EXECUTED;
+            }));
+    }
+#pragma pop_macro("STM_SEGMENT")
+#pragma pop_macro("STM_PSEGMENT")
+}
+
 
 static void clean_up_segment_lists(void)
 {
@@ -411,6 +465,16 @@
                we "didn't do a collection" at all. So nothing to do on
                modified_old_objs. */
         }
+
+        /* remove from new_objects all objects that die */
+        lst = pseg->new_objects;
+        uintptr_t n = list_count(lst);
+        while (n-- > 0) {
+            object_t *obj = (object_t *)list_item(lst, n);
+            if (!mark_visited_test(obj)) {
+                list_set_item(lst, n, list_pop_item(lst));
+            }
+        }
     }
 #pragma pop_macro("STM_SEGMENT")
 #pragma pop_macro("STM_PSEGMENT")
@@ -486,6 +550,8 @@
 
     DEBUG_EXPECT_SEGFAULT(false);
 
+    ready_new_objects();
+
     /* marking */
     LIST_CREATE(marked_objects_to_trace);
     mark_visit_from_modified_objects();
diff --git a/c8/stm/nursery.c b/c8/stm/nursery.c
--- a/c8/stm/nursery.c
+++ b/c8/stm/nursery.c
@@ -125,8 +125,15 @@
         /* a young object outside the nursery */
         nobj = obj;
         tree_delete_item(STM_PSEGMENT->young_outside_nursery, (uintptr_t)nobj);
+        nobj_sync_now = ((uintptr_t)nobj) | FLAG_SYNC_LARGE;
+    }
 
-        nobj_sync_now = ((uintptr_t)nobj) | FLAG_SYNC_LARGE;
+    /* if this is not during commit, we will add them to the new_objects
+       list and push them to other segments on commit. Thus we can add
+       the WB_EXECUTED flag so that they don't end up in modified_old_objects 
*/
+    assert(!(nobj->stm_flags & GCFLAG_WB_EXECUTED));
+    if (!STM_PSEGMENT->minor_collect_will_commit_now) {
+        nobj->stm_flags |= GCFLAG_WB_EXECUTED;
     }
 
     /* Must trace the object later */
@@ -192,20 +199,31 @@
         _collect_now(obj);
 
         if (obj_sync_now & FLAG_SYNC_LARGE) {
-            /* this is a newly allocated object. We must synchronize it
-               to other segments (after we added WRITE_BARRIER). */
-            acquire_privatization_lock(STM_SEGMENT->segment_num);
-            synchronize_object_enqueue(obj);
-            release_privatization_lock(STM_SEGMENT->segment_num);
+            /* this is a newly allocated obj in this transaction. We must
+               either synchronize the object to other segments now, or
+               add the object to new_objects list */
+            if (STM_PSEGMENT->minor_collect_will_commit_now) {
+                acquire_privatization_lock(STM_SEGMENT->segment_num);
+                synchronize_object_enqueue(obj);
+                release_privatization_lock(STM_SEGMENT->segment_num);
+            } else {
+                LIST_APPEND(STM_PSEGMENT->new_objects, obj);
+            }
         }
 
         /* the list could have moved while appending */
         lst = STM_PSEGMENT->objects_pointing_to_nursery;
     }
 
-    acquire_privatization_lock(STM_SEGMENT->segment_num);
-    synchronize_objects_flush();
-    release_privatization_lock(STM_SEGMENT->segment_num);
+    /* flush all new objects to other segments now */
+    if (STM_PSEGMENT->minor_collect_will_commit_now) {
+        acquire_privatization_lock(STM_SEGMENT->segment_num);
+        synchronize_objects_flush();
+        release_privatization_lock(STM_SEGMENT->segment_num);
+    } else {
+        /* nothing in the queue when not committing */
+        assert(STM_PSEGMENT->sq_len == 0);
+    }
 }
 
 
@@ -273,6 +291,8 @@
 {
     dprintf(("minor_collection commit=%d\n", (int)commit));
 
+    STM_PSEGMENT->minor_collect_will_commit_now = commit;
+
     collect_roots_in_nursery();
 
     collect_oldrefs_to_nursery();
diff --git a/c8/stm/setup.c b/c8/stm/setup.c
--- a/c8/stm/setup.c
+++ b/c8/stm/setup.c
@@ -95,6 +95,7 @@
         pr->pub.segment_num = i;
         pr->pub.segment_base = segment_base;
         pr->modified_old_objects = list_create();
+        pr->new_objects = list_create();
         pr->objects_pointing_to_nursery = list_create();
         pr->young_outside_nursery = tree_create();
         pr->nursery_objects_shadows = tree_create();
@@ -134,6 +135,8 @@
         assert(list_is_empty(pr->objects_pointing_to_nursery));
         list_free(pr->objects_pointing_to_nursery);
         list_free(pr->modified_old_objects);
+        assert(list_is_empty(pr->new_objects));
+        list_free(pr->new_objects);
         tree_free(pr->young_outside_nursery);
         tree_free(pr->nursery_objects_shadows);
         tree_free(pr->callbacks_on_commit_and_abort[0]);
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to