[pypy-commit] stmgc c8-new-page-handling: in-progress: break everything :-) The goal is to use backups that are

arigo noreply at buildbot.pypy.org
Tue Sep 23 18:41:56 CEST 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: c8-new-page-handling
Changeset: r1410:61d043d6d6f9
Date: 2014-09-23 18:42 +0200
http://bitbucket.org/pypy/stmgc/changeset/61d043d6d6f9/

Log:	in-progress: break everything :-) The goal is to use backups that
	are longer-lived than one transaction, in order to allow the
	segfault handler to cleanly reconstruct any missing page in all
	cases.

diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -29,48 +29,6 @@
 }
 
 
-static void _update_obj_from(int from_seg, object_t *obj, uintptr_t only_page)
-{
-    /* updates 'obj' in our accessible pages from another segment's
-       page or bk copy. (never touch PROT_NONE memory)
-       only_page = -1 means update whole obj, only_page=pagenum means only
-       update memory in page 'pagenum'
-    */
-    /* XXXXXXX: are we sure everything is readable in from_seg??? */
-    size_t obj_size;
-
-    dprintf(("_update_obj_from(%d, %p)\n", from_seg, obj));
-    assert(get_priv_segment(from_seg)->privatization_lock);
-
-    /* look the obj up in the other segment's modified_old_objects to
-       get its backup copy: */
-    acquire_modified_objs_lock(from_seg);
-
-    wlog_t *item;
-    struct tree_s *tree = get_priv_segment(from_seg)->modified_old_objects;
-    TREE_FIND(tree, (uintptr_t)obj, item, goto not_found);
-
-    obj_size = stmcb_size_rounded_up((struct object_s*)item->val);
-
-    memcpy_to_accessible_pages(STM_SEGMENT->segment_num, obj,
-                               (char*)item->val, obj_size, only_page);
-
-    release_modified_objs_lock(from_seg);
-    return;
-
- not_found:
-    /* copy from page directly (obj is unmodified) */
-    obj_size = stmcb_size_rounded_up(
-        (struct object_s*)REAL_ADDRESS(get_segment_base(from_seg), obj));
-
-    memcpy_to_accessible_pages(STM_SEGMENT->segment_num, obj,
-                               REAL_ADDRESS(get_segment_base(from_seg), obj),
-                               obj_size, only_page);
-
-    release_modified_objs_lock(from_seg);
-}
-
-
 static void copy_bk_objs_in_page_from(int from_segnum, uintptr_t pagenum)
 {
     /* looks at all bk copies of objects overlapping page 'pagenum' and
@@ -78,7 +36,8 @@
     dprintf(("copy_bk_objs_in_page_from(%d, %lu)\n", from_segnum, pagenum));
 
     acquire_modified_objs_lock(from_segnum);
-    struct tree_s *tree = get_priv_segment(from_segnum)->modified_old_objects;
+    abort();
+    struct tree_s *tree = NULL; // get_priv_segment(from_segnum)->modified_old_objects;
     wlog_t *item;
     TREE_LOOP_FORWARD(tree, item); {
         object_t *obj = (object_t*)item->addr;
@@ -111,15 +70,15 @@
         return;
 
     while ((cl = cl->next)) {
-        if ((uintptr_t)cl == -1)
+        if (cl == (void *)-1)
             return;
 
         OPT_ASSERT(cl->segment_num >= 0 && cl->segment_num < NB_SEGMENTS);
 
         object_t *obj;
         size_t i = 0;
-        while ((obj = cl->written[i])) {
-            _update_obj_from(cl->segment_num, obj, pagenum);
+        while ((obj = cl->written[i].object)) {
+            abort(); //_update_obj_from(cl->segment_num, obj, pagenum);
 
             i++;
         };
@@ -177,7 +136,7 @@
 
     /* in case page is already newer, validate everything now to have a common
        revision for all pages */
-    _stm_validate(NULL, true);
+    //_stm_validate(NULL, true);
 }
 
 static void _signal_handler(int sig, siginfo_t *siginfo, void *context)
@@ -219,101 +178,111 @@
 
 void _dbg_print_commit_log()
 {
-    volatile struct stm_commit_log_entry_s *cl;
-    cl = (volatile struct stm_commit_log_entry_s *)&commit_log_root;
+    struct stm_commit_log_entry_s *cl = &commit_log_root;
 
-    fprintf(stderr, "root (%p, %d)\n", cl->next, cl->segment_num);
+    fprintf(stderr, "commit log root (%p, %d)\n", cl->next, cl->segment_num);
     while ((cl = cl->next)) {
-        if ((uintptr_t)cl == -1) {
-            fprintf(stderr, "INEVITABLE\n");
+        if (cl == (void *)-1) {
+            fprintf(stderr, "  INEVITABLE\n");
             return;
         }
-        size_t i = 0;
         fprintf(stderr, "  elem (%p, %d)\n", cl->next, cl->segment_num);
-        object_t *obj;
-        while ((obj = cl->written[i])) {
-            fprintf(stderr, "-> %p\n", obj);
-            i++;
-        };
+        struct stm_undo_s *undo = cl->written;
+        struct stm_undo_s *end = undo + cl->written_count;
+        for (; undo < end; undo++) {
+            fprintf(stderr, "    obj %p, size %d, ofs %lu\n", undo->object,
+                    SLICE_SIZE(undo->slice), SLICE_OFFSET(undo->slice));
+        }
     }
 }
 
+static void reapply_undo_log(struct stm_undo_s *undo)
+{
+    /* read the object (or object slice) described by 'undo', and
+       re-applies it to our current segment.
+    */
+    dprintf(("_update_obj_from_undo(obj=%p, size=%d, ofs=%lu)\n",
+             undo->object, SLICE_SIZE(undo->slice), SLICE_OFFSET(undo->slice)));
 
-static void _stm_validate(void *free_if_abort, bool locks_acquired)
+    size_t ofs = SLICE_OFFSET(undo->slice);
+    size_t size = SLICE_SIZE(undo->slice);
+    stm_char *slice_start = ((stm_char *)undo->object) + ofs;
+    stm_char *slice_end = slice_start + size;
+
+    uintptr_t page_start = ((uintptr_t)slice_start) / 4096;
+    if ((uintptr_t)slice_end <= (page_start + 1) * 4096) {
+
+        /* the object fits inside a single page: fast path */
+        if (get_page_status_in(STM_SEGMENT->segment_num, page_start)
+            == PAGE_NO_ACCESS) {
+            return;   /* ignore the object: it is in a NO_ACCESS page */
+        }
+
+        char *src = undo->backup;
+        char *dst = REAL_ADDRESS(STM_SEGMENT->segment_base, slice_start);
+        memcpy(dst, src, size);
+    }
+    else {
+        abort(); //XXX
+    }
+}
+
+static void reset_modified_from_backup_copies(int segment_num);  /* forward */
+
+static void _stm_validate(void *free_if_abort)
 {
     /* go from last known entry in commit log to the
        most current one and apply all changes done
        by other transactions. Abort if we read one of
        the committed objs. */
-    if (STM_PSEGMENT->transaction_state == TS_INEVITABLE) {
-        assert((uintptr_t)STM_PSEGMENT->last_commit_log_entry->next == -1);
-        if (locks_acquired)
-            release_all_privatization_locks();
-        return;
-    }
-
-    if (locks_acquired)
-        assert(all_privatization_locks_acquired());
-    else
-        acquire_all_privatization_locks();
-
-    volatile struct stm_commit_log_entry_s *cl, *prev_cl;
-    cl = prev_cl = (volatile struct stm_commit_log_entry_s *)
-        STM_PSEGMENT->last_commit_log_entry;
+    struct stm_commit_log_entry_s *cl = STM_PSEGMENT->last_commit_log_entry;
+    struct stm_commit_log_entry_s *next_cl;
+    /* Don't check this 'cl'. This entry is already checked */
 
     bool needs_abort = false;
-    /* Don't check 'cl'. This entry is already checked */
-    while ((cl = cl->next)) {
-        if ((uintptr_t)cl == -1) {
+    while ((next_cl = cl->next) != NULL) {
+        if (next_cl == (void *)-1) {
             /* there is an inevitable transaction running */
-            release_all_privatization_locks();
 #if STM_TESTS
-            free(free_if_abort);
+            if (free_if_abort != (void *)-1)
+                free(free_if_abort);
             stm_abort_transaction();
 #endif
-            cl = prev_cl;
+            abort();  /* XXX non-busy wait here */
             _stm_collectable_safe_point();
             acquire_all_privatization_locks();
             continue;
         }
-        prev_cl = cl;
+        cl = next_cl;
 
-        OPT_ASSERT(cl->segment_num >= 0 && cl->segment_num < NB_SEGMENTS);
+        /*int srcsegnum = cl->segment_num;
+          OPT_ASSERT(srcsegnum >= 0 && srcsegnum < NB_SEGMENTS);*/
 
-        object_t *obj;
-        size_t i = 0;
-        while ((obj = cl->written[i])) {
-            _update_obj_from(cl->segment_num, obj, -1);
+        struct stm_undo_s *undo = cl->written;
+        struct stm_undo_s *end = cl->written + cl->written_count;
 
-            if (_stm_was_read(obj)) {
-                needs_abort = true;
+        for (; undo < end; undo++) {
 
-                /* if we wrote this obj, we need to free its backup and
-                   remove it from modified_old_objects because
-                   we would otherwise overwrite the updated obj on abort */
-                acquire_modified_objs_lock(STM_SEGMENT->segment_num);
-                wlog_t *item;
-                struct tree_s *tree = STM_PSEGMENT->modified_old_objects;
-                TREE_FIND(tree, (uintptr_t)obj, item, goto not_found);
-
-                free((void*)item->val);
-                TREE_FIND_DELETE(tree, item);
-
-            not_found:
-                /* nothing todo */
-                release_modified_objs_lock(STM_SEGMENT->segment_num);
+            if (_stm_was_read(undo->object)) {
+                /* first reset all modified objects from the backup
+                   copies as soon as the first conflict is detected;
+                   then we will proceed below to update our segment from
+                   the old (but unmodified) version to the newer version. */
+                if (!needs_abort) {
+                    reset_modified_from_backup_copies(STM_SEGMENT->segment_num);
+                    needs_abort = true;
+                }
             }
-
-            i++;
-        };
+            reapply_undo_log(undo);
+        }
 
         /* last fully validated entry */
-        STM_PSEGMENT->last_commit_log_entry = (struct stm_commit_log_entry_s *)cl;
+        STM_PSEGMENT->last_commit_log_entry = cl;
     }
 
-    release_all_privatization_locks();
     if (needs_abort) {
-        free(free_if_abort);
+        if (free_if_abort != (void *)-1)
+            free(free_if_abort);
         stm_abort_transaction();
     }
 }
@@ -324,69 +293,61 @@
 
     // we don't need the privatization lock, as we are only
     // reading from modified_old_objs and nobody but us can change it
-    struct tree_s *tree = STM_PSEGMENT->modified_old_objects;
-    size_t count = tree_count(tree);
-    size_t byte_len = sizeof(struct stm_commit_log_entry_s) + (count + 1) * sizeof(object_t*);
+    struct list_s *list = STM_PSEGMENT->modified_old_objects;
+    OPT_ASSERT((list_count(list) % 3) == 0);
+    size_t count = list_count(list) / 3;
+    size_t byte_len = sizeof(struct stm_commit_log_entry_s) +
+        count * sizeof(struct stm_undo_s);
     struct stm_commit_log_entry_s *result = malloc(byte_len);
 
     result->next = NULL;
     result->segment_num = STM_SEGMENT->segment_num;
-
-    int i = 0;
-    wlog_t *item;
-    TREE_LOOP_FORWARD(tree, item); {
-        result->written[i] = (object_t*)item->addr;
-        i++;
-    } TREE_LOOP_END;
-
-    OPT_ASSERT(count == i);
-    result->written[count] = NULL;
-
+    result->written_count = count;
+    memcpy(result->written, list->items, count * sizeof(struct stm_undo_s));
     return result;
 }
 
-static void _validate_and_add_to_commit_log()
+static void _validate_and_attach(struct stm_commit_log_entry_s *new)
 {
-    struct stm_commit_log_entry_s *new;
-    volatile struct stm_commit_log_entry_s **to;
+    struct stm_commit_log_entry_s *old;
+
+    while (1) {
+        _stm_validate(/* free_if_abort =*/ new);
+
+        /* try to attach to commit log: */
+        old = STM_PSEGMENT->last_commit_log_entry;
+        if (old->next == NULL &&
+                __sync_bool_compare_and_swap(&old->next, NULL, new))
+            break;   /* success! */
+    }
+}
+
+static void _validate_and_turn_inevitable(void)
+{
+    _validate_and_attach((struct stm_commit_log_entry_s *)-1);
+}
+
+static void _validate_and_add_to_commit_log(void)
+{
+    struct stm_commit_log_entry_s *old, *new;
 
     new = _create_commit_log_entry();
     if (STM_PSEGMENT->transaction_state == TS_INEVITABLE) {
-        OPT_ASSERT((uintptr_t)STM_PSEGMENT->last_commit_log_entry->next == -1);
+        old = STM_PSEGMENT->last_commit_log_entry;
+        OPT_ASSERT(old->next == (void *)-1);
 
-        to = &(STM_PSEGMENT->last_commit_log_entry->next);
-        bool yes = __sync_bool_compare_and_swap(to, (void*)-1, new);
+        bool yes = __sync_bool_compare_and_swap(&old->next, (void*)-1, new);
         OPT_ASSERT(yes);
-        return;
     }
-
-    /* regular transaction: */
-    do {
-        _stm_validate(new, false);
-
-        /* try attaching to commit log: */
-        to = &(STM_PSEGMENT->last_commit_log_entry->next);
-    } while (!__sync_bool_compare_and_swap(to, NULL, new));
-}
-
-static void _validate_and_turn_inevitable()
-{
-    struct stm_commit_log_entry_s *new;
-    volatile struct stm_commit_log_entry_s **to;
-
-    new = (struct stm_commit_log_entry_s*)-1;
-    do {
-        stm_validate();
-
-        /* try attaching to commit log: */
-        to = &(STM_PSEGMENT->last_commit_log_entry->next);
-    } while (!__sync_bool_compare_and_swap(to, NULL, new));
+    else {
+        _validate_and_attach(new);
+    }
 }
 
 /* ############# STM ############# */
 void stm_validate()
 {
-    _stm_validate(NULL, false);
+    _stm_validate(NULL);
 }
 
 
@@ -441,7 +402,7 @@
            choosing to make us PRIVATE is harder because then nobody must ever
            update the shared page in stm_validate() except if it is the sole
            reader of it. But then we don't actually know which revision the page is at. */
-        long i;
+        int i;
         for (i = 0; i < NB_SEGMENTS; i++) {
             if (i == my_segnum)
                 continue;
@@ -466,12 +427,13 @@
     /* phew, now add the obj to the write-set and register the
        backup copy. */
     /* XXX: possibly slow check; try overflow objs again? */
-    if (!tree_contains(STM_PSEGMENT->modified_old_objects, (uintptr_t)obj)) {
+    abort();
+    /*if (!tree_contains(STM_PSEGMENT->modified_old_objects, (uintptr_t)obj)) {
         acquire_modified_objs_lock(my_segnum);
         tree_insert(STM_PSEGMENT->modified_old_objects,
                     (uintptr_t)obj, (uintptr_t)bk_obj);
         release_modified_objs_lock(my_segnum);
-    }
+    }*/
     /* XXX else... what occurs with bk_obj? */
 
     /* done fiddling with protection and privatization */
@@ -529,7 +491,7 @@
         reset_transaction_read_version();
     }
 
-    assert(tree_is_cleared(STM_PSEGMENT->modified_old_objects));
+    assert(list_is_empty(STM_PSEGMENT->modified_old_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));
@@ -601,7 +563,8 @@
        and clear the tree: */
     acquire_modified_objs_lock(STM_SEGMENT->segment_num);
 
-    struct tree_s *tree = STM_PSEGMENT->modified_old_objects;
+    abort();
+    struct tree_s *tree = NULL; //XXX STM_PSEGMENT->modified_old_objects;
     wlog_t *item;
     TREE_LOOP_FORWARD(tree, item); {
         object_t *obj = (object_t*)item->addr;
@@ -632,7 +595,7 @@
     s_mutex_unlock();
 }
 
-void reset_modified_from_backup_copies(int segment_num)
+static void reset_modified_from_backup_copies(int segment_num)
 {
 #pragma push_macro("STM_PSEGMENT")
 #pragma push_macro("STM_SEGMENT")
@@ -641,7 +604,8 @@
     acquire_modified_objs_lock(segment_num);
 
     struct stm_priv_segment_info_s *pseg = get_priv_segment(segment_num);
-    struct tree_s *tree = pseg->modified_old_objects;
+    abort();
+    struct tree_s *tree = NULL; //XXX pseg->modified_old_objects;
     wlog_t *item;
     TREE_LOOP_FORWARD(tree, item); {
         object_t *obj = (object_t*)item->addr;
diff --git a/c8/stm/core.h b/c8/stm/core.h
--- a/c8/stm/core.h
+++ b/c8/stm/core.h
@@ -51,7 +51,16 @@
     struct stm_segment_info_s pub;
 
     uint8_t modified_objs_lock;
-    struct tree_s *modified_old_objects;
+
+    /* All the old objects (older than the current transaction) that
+       the current transaction attempts to modify.  This is used to
+       track the STM status: these are old objects that where written
+       to and that will need to be recorded in the commit log.  The
+       list contains three entries for every such object, in the same
+       format as 'struct stm_undo_s' below.
+    */
+    struct list_s *modified_old_objects;
+
     struct list_s *objects_pointing_to_nursery;
     struct tree_s *young_outside_nursery;
     struct tree_s *nursery_objects_shadows;
@@ -90,12 +99,24 @@
 };
 
 /* Commit Log things */
+struct stm_undo_s {
+    object_t *object;   /* the object that is modified */
+    char *backup;       /* some backup data (a slice of the original obj) */
+    uint64_t slice;     /* location and size of this slice (== the whole
+                           object, unless card marking is enabled).  The
+                           size is in the lower 2 bytes, and the offset
+                           in the remaining 6 bytes. */
+};
+#define SLICE_OFFSET(slice)  ((slice) >> 16)
+#define SLICE_SIZE(slice)    ((int)((slice) & 0xFFFF))
+
 struct stm_commit_log_entry_s {
-    volatile struct stm_commit_log_entry_s *next;
+    struct stm_commit_log_entry_s *volatile next;
     int segment_num;
-    object_t *written[];        /* terminated with a NULL ptr */
+    size_t written_count;
+    struct stm_undo_s written[];
 };
-static struct stm_commit_log_entry_s commit_log_root = {NULL, -1};
+static struct stm_commit_log_entry_s commit_log_root = {NULL, -1, 0};
 
 
 static char *stm_object_pages;
@@ -137,7 +158,7 @@
 static void abort_data_structures_from_segment_num(int segment_num);
 
 static void _signal_handler(int sig, siginfo_t *siginfo, void *context);
-static void _stm_validate(void *free_if_abort, bool locks_acquired);
+static void _stm_validate(void *free_if_abort);
 
 static inline void _duck(void) {
     /* put a call to _duck() between two instructions that set 0 into
diff --git a/c8/stm/misc.c b/c8/stm/misc.c
--- a/c8/stm/misc.c
+++ b/c8/stm/misc.c
@@ -46,8 +46,9 @@
 long _stm_count_modified_old_objects(void)
 {
     assert(STM_PSEGMENT->modified_old_objects);
-    assert(tree_count(STM_PSEGMENT->modified_old_objects) < 10000);
-    return tree_count(STM_PSEGMENT->modified_old_objects);
+    assert(list_count(STM_PSEGMENT->modified_old_objects) < 30000);
+    assert((list_count(STM_PSEGMENT->modified_old_objects) % 3) == 0);
+    return list_count(STM_PSEGMENT->modified_old_objects) / 3;
 }
 
 long _stm_count_objects_pointing_to_nursery(void)
@@ -59,8 +60,8 @@
 
 object_t *_stm_enum_modified_old_objects(long index)
 {
-    wlog_t* entry = tree_item(STM_PSEGMENT->modified_old_objects, index);
-    return (object_t*)entry->addr;
+    return (object_t *)list_item(
+        STM_PSEGMENT->modified_old_objects, index * 3);
 }
 
 object_t *_stm_enum_objects_pointing_to_nursery(long index)
@@ -86,7 +87,7 @@
 object_t *_stm_next_last_cl_entry()
 {
     if (_last_cl_entry != &commit_log_root)
-        return _last_cl_entry->written[_last_cl_entry_index++];
+        return _last_cl_entry->written[_last_cl_entry_index++].object;
     return NULL;
 }
 #endif
diff --git a/c8/stm/setup.c b/c8/stm/setup.c
--- a/c8/stm/setup.c
+++ b/c8/stm/setup.c
@@ -136,7 +136,7 @@
         assert(0 <= i && i < 255);   /* 255 is WL_VISITED in gcpage.c */
         pr->pub.segment_num = i;
         pr->pub.segment_base = segment_base;
-        pr->modified_old_objects = tree_create();
+        pr->modified_old_objects = list_create();
         pr->objects_pointing_to_nursery = list_create();
         pr->young_outside_nursery = tree_create();
         pr->nursery_objects_shadows = tree_create();
@@ -173,7 +173,7 @@
         struct stm_priv_segment_info_s *pr = get_priv_segment(i);
         assert(list_is_empty(pr->objects_pointing_to_nursery));
         list_free(pr->objects_pointing_to_nursery);
-        tree_free(pr->modified_old_objects);
+        list_free(pr->modified_old_objects);
         tree_free(pr->young_outside_nursery);
         tree_free(pr->nursery_objects_shadows);
         tree_free(pr->callbacks_on_commit_and_abort[0]);


More information about the pypy-commit mailing list