[pypy-commit] stmgc default: hg merge c8-marker

arigo noreply at buildbot.pypy.org
Tue Mar 10 17:39:36 CET 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r1711:cf7b3fc03a25
Date: 2015-03-10 17:40 +0100
http://bitbucket.org/pypy/stmgc/changeset/cf7b3fc03a25/

Log:	hg merge c8-marker

	Reintroduce "markers", produced by pypy-stm if the PYPYSTM
	environment variable is set to a file name.

diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -5,6 +5,7 @@
 /* *** MISC *** */
 static void free_bk(struct stm_undo_s *undo)
 {
+    assert(undo->type != TYPE_POSITION_MARKER);
     free(undo->backup);
     assert(undo->backup = (char*)-88);
     increment_total_allocated(-SLICE_SIZE(undo->slice));
@@ -49,6 +50,8 @@
 
     DEBUG_EXPECT_SEGFAULT(false);
     for (; undo < end; undo++) {
+        if (undo->type == TYPE_POSITION_MARKER)
+            continue;
         object_t *obj = undo->object;
         stm_char *oslice = ((stm_char *)obj) + SLICE_OFFSET(undo->slice);
         uintptr_t current_page_num = ((uintptr_t)oslice) / 4096;
@@ -269,6 +272,11 @@
         struct stm_undo_s *undo = cl->written;
         struct stm_undo_s *end = undo + cl->written_count;
         for (; undo < end; undo++) {
+            if (undo->type == TYPE_POSITION_MARKER) {
+                fprintf(stderr, "    marker %p %lu\n",
+                        undo->marker_object, undo->marker_odd_number);
+                continue;
+            }
             fprintf(stderr, "    obj %p, size %d, ofs %lu: ", undo->object,
                     SLICE_SIZE(undo->slice), SLICE_OFFSET(undo->slice));
             /* long i; */
@@ -312,6 +320,12 @@
 
     bool needs_abort = false;
 
+    if (STM_PSEGMENT->transaction_state == TS_NONE) {
+        /* can be seen from major_do_validation_and_minor_collections();
+           don't try to detect pseudo-conflicts in this case */
+        needs_abort = true;
+    }
+
     while(1) {
         /* retry IF: */
         /* if at the time of "HERE" (s.b.) there happen to be
@@ -362,6 +376,8 @@
                     struct stm_undo_s *undo = cl->written;
                     struct stm_undo_s *end = cl->written + cl->written_count;
                     for (; undo < end; undo++) {
+                        if (undo->type == TYPE_POSITION_MARKER)
+                            continue;
                         if (_stm_was_read(undo->object)) {
                             /* first reset all modified objects from the backup
                                copies as soon as the first conflict is detected;
@@ -369,6 +385,7 @@
                                the old (but unmodified) version to the newer version.
                             */
                             reset_modified_from_backup_copies(my_segnum);
+                            timing_write_read_contention(cl->written, undo);
                             needs_abort = true;
 
                             dprintf(("_stm_validate() failed for obj %p\n", undo->object));
@@ -445,6 +462,17 @@
 }
 
 
+static void wait_for_other_inevitable(struct stm_commit_log_entry_s *old)
+{
+    timing_event(STM_SEGMENT->running_thread, STM_WAIT_OTHER_INEVITABLE);
+
+    while (old->next == INEV_RUNNING && !safe_point_requested()) {
+        spin_loop();
+        usleep(10);    /* XXXXXX */
+    }
+    timing_event(STM_SEGMENT->running_thread, STM_WAIT_DONE);
+}
+
 static void reset_wb_executed_flags(void);
 static void readd_wb_executed_flags(void);
 static void check_all_write_barrier_flags(char *segbase, struct list_s *list);
@@ -496,13 +524,6 @@
 
             if (__sync_bool_compare_and_swap(&old->next, NULL, new))
                 break;   /* success! */
-        } else if (old->next == INEV_RUNNING) {
-            /* we failed because there is an INEV transaction running */
-            /* XXXXXX for now just sleep (XXX with the lock acquired?
-               isn't it a bad idea?).  We should really ask to inev
-               transaction to do the commit for us, and then we can
-               continue running. */
-            usleep(10);
         }
 
         if (is_commit) {
@@ -513,6 +534,16 @@
             readd_wb_executed_flags();
         }
 
+        if (old->next == INEV_RUNNING && !safe_point_requested()) {
+            /* we failed because there is an INEV transaction running */
+            /* XXXXXX for now just sleep.  We should really ask to inev
+               transaction to do the commit for us, and then we can
+               continue running. */
+            dprintf(("_validate_and_attach(%p) failed, "
+                     "waiting for inevitable\n", new));
+            wait_for_other_inevitable(old);
+        }
+
         dprintf(("_validate_and_attach(%p) failed, enter safepoint\n", new));
 
         /* check for requested safe point. otherwise an INEV transaction
@@ -603,6 +634,8 @@
 {
     dprintf(("make_bk_slices_for_range(%p, %lu, %lu)\n",
              obj, start - (stm_char*)obj, end - start));
+    timing_record_write_position();
+
     char *realobj = REAL_ADDRESS(STM_SEGMENT->segment_base, obj);
     uintptr_t first_page = ((uintptr_t)start) / 4096UL;
     uintptr_t end_page = ((uintptr_t)end) / 4096UL;
@@ -1025,6 +1058,8 @@
     struct stm_undo_s *end = (struct stm_undo_s *)(list->items + list->count);
 
     for (; undo < end; undo++) {
+        if (undo->type == TYPE_POSITION_MARKER)
+            continue;
         object_t *obj = undo->object;
         obj->stm_flags &= ~GCFLAG_WB_EXECUTED;
     }
@@ -1038,6 +1073,8 @@
     struct stm_undo_s *end = (struct stm_undo_s *)(list->items + list->count);
 
     for (; undo < end; undo++) {
+        if (undo->type == TYPE_POSITION_MARKER)
+            continue;
         object_t *obj = undo->object;
         obj->stm_flags |= GCFLAG_WB_EXECUTED;
     }
@@ -1055,6 +1092,7 @@
 
     assert(STM_PSEGMENT->safe_point == SP_NO_TRANSACTION);
     assert(STM_PSEGMENT->transaction_state == TS_NONE);
+    timing_event(tl, STM_TRANSACTION_START);
     STM_PSEGMENT->transaction_state = TS_REGULAR;
     STM_PSEGMENT->safe_point = SP_RUNNING;
 #ifndef NDEBUG
@@ -1084,6 +1122,10 @@
     assert(tree_is_cleared(STM_PSEGMENT->callbacks_on_commit_and_abort[1]));
     assert(list_is_empty(STM_PSEGMENT->young_objects_with_light_finalizers));
     assert(STM_PSEGMENT->finalizers == NULL);
+#ifndef NDEBUG
+    /* this should not be used when objects_pointing_to_nursery == NULL */
+    STM_PSEGMENT->position_markers_len_old = 99999999999999999L;
+#endif
 
     check_nursery_at_transaction_start();
 
@@ -1129,7 +1171,7 @@
 
 /************************************************************/
 
-static void _finish_transaction()
+static void _finish_transaction(enum stm_event_e event)
 {
     stm_thread_local_t *tl = STM_SEGMENT->running_thread;
 
@@ -1140,6 +1182,7 @@
     list_clear(STM_PSEGMENT->objects_pointing_to_nursery);
     list_clear(STM_PSEGMENT->old_objects_with_cards_set);
     list_clear(STM_PSEGMENT->large_overflow_objects);
+    timing_event(tl, event);
 
     release_thread_segment(tl);
     /* cannot access STM_SEGMENT or STM_PSEGMENT from here ! */
@@ -1151,6 +1194,8 @@
     struct stm_undo_s *undo = (struct stm_undo_s *)list->items;
     struct stm_undo_s *end = (struct stm_undo_s *)(list->items + list->count);
     for (; undo < end; undo++) {
+        if (undo->type == TYPE_POSITION_MARKER)
+            continue;
         object_t *obj = undo->object;
         struct object_s *dst = (struct object_s*)REAL_ADDRESS(segbase, obj);
         assert(dst->stm_flags & GCFLAG_WRITE_BARRIER);
@@ -1199,6 +1244,7 @@
 
     push_large_overflow_objects_to_other_segments();
     /* push before validate. otherwise they are reachable too early */
+
     bool was_inev = STM_PSEGMENT->transaction_state == TS_INEVITABLE;
     _validate_and_add_to_commit_log();
 
@@ -1225,11 +1271,7 @@
 
     /* if a major collection is required, do it here */
     if (is_major_collection_requested()) {
-        synchronize_all_threads(STOP_OTHERS_UNTIL_MUTEX_UNLOCK);
-
-        if (is_major_collection_requested()) {   /* if *still* true */
-            major_collection_now_at_safe_point();
-        }
+        major_collection_with_mutex();
     }
 
     _verify_cards_cleared_in_all_lists(get_priv_segment(STM_SEGMENT->segment_num));
@@ -1240,7 +1282,7 @@
 
     /* done */
     stm_thread_local_t *tl = STM_SEGMENT->running_thread;
-    _finish_transaction();
+    _finish_transaction(STM_TRANSACTION_COMMIT);
     /* cannot access STM_SEGMENT or STM_PSEGMENT from here ! */
 
     s_mutex_unlock();
@@ -1264,6 +1306,8 @@
     struct stm_undo_s *end = (struct stm_undo_s *)(list->items + list->count);
 
     for (; undo < end; undo++) {
+        if (undo->type == TYPE_POSITION_MARKER)
+            continue;
         object_t *obj = undo->object;
         char *dst = REAL_ADDRESS(pseg->pub.segment_base, obj);
 
@@ -1373,7 +1417,7 @@
                                                    : NURSERY_END;
     }
 
-    _finish_transaction();
+    _finish_transaction(STM_TRANSACTION_ABORT);
     /* cannot access STM_SEGMENT or STM_PSEGMENT from here ! */
 
     return tl;
@@ -1412,9 +1456,11 @@
     if (STM_PSEGMENT->transaction_state == TS_REGULAR) {
         dprintf(("become_inevitable: %s\n", msg));
         _stm_collectable_safe_point();
+        timing_become_inevitable();
 
         _validate_and_turn_inevitable();
         STM_PSEGMENT->transaction_state = TS_INEVITABLE;
+
         stm_rewind_jmp_forget(STM_SEGMENT->running_thread);
         invoke_and_clear_user_callbacks(0);   /* for commit */
     }
diff --git a/c8/stm/core.h b/c8/stm/core.h
--- a/c8/stm/core.h
+++ b/c8/stm/core.h
@@ -84,9 +84,12 @@
        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.
+       format as 'struct stm_undo_s' below.  It can also represent a
+       position marker, like 'struct stm_undo_s'.
     */
     struct list_s *modified_old_objects;
+    uintptr_t position_markers_last;     /* index of most recent pos marker */
+    uintptr_t position_markers_len_old;  /* length of list at last minor col */
 
     struct list_s *objects_pointing_to_nursery;
     struct list_s *old_objects_with_cards_set;
@@ -174,18 +177,27 @@
 
 /* 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 (cannot cross
-                           pages).  The size is in the lower 2 bytes, and
-                           the offset in the remaining 6 bytes. */
+  union {
+    struct {
+        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 (cannot cross
+                               pages).  The size is in the lower 2 bytes, and
+                               the offset in the remaining 6 bytes. */
+    };
+    struct {
+        intptr_t type;               /* TYPE_POSITION_MARKER */
+        uintptr_t marker_odd_number; /* the odd number part of the marker */
+        object_t *marker_object;     /* the object part of the marker */
+    };
+  };
 };
+#define TYPE_POSITION_MARKER    (-1)
 #define SLICE_OFFSET(slice)  ((slice) >> 16)
 #define SLICE_SIZE(slice)    ((int)((slice) & 0xFFFF))
 #define NEW_SLICE(offset, size) (((uint64_t)(offset)) << 16 | (size))
 
 
-
 /* The model is: we have a global chained list, from 'commit_log_root',
    of 'struct stm_commit_log_entry_s' entries.  Every one is fully
    read-only apart from the 'next' field.  Every one stands for one
diff --git a/c8/stm/gcpage.c b/c8/stm/gcpage.c
--- a/c8/stm/gcpage.c
+++ b/c8/stm/gcpage.c
@@ -131,6 +131,19 @@
 /************************************************************/
 
 
+static void major_collection_with_mutex(void)
+{
+    timing_event(STM_SEGMENT->running_thread, STM_GC_MAJOR_START);
+
+    synchronize_all_threads(STOP_OTHERS_UNTIL_MUTEX_UNLOCK);
+
+    if (is_major_collection_requested()) {   /* if *still* true */
+        major_collection_now_at_safe_point();
+    }
+
+    timing_event(STM_SEGMENT->running_thread, STM_GC_MAJOR_DONE);
+}
+
 static void major_collection_if_requested(void)
 {
     assert(!_has_mutex());
@@ -140,13 +153,7 @@
     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();
-        }
-
+        major_collection_with_mutex();
     }
 
     s_mutex_unlock();
@@ -348,6 +355,8 @@
         struct stm_undo_s *modified = (struct stm_undo_s *)lst->items;
         struct stm_undo_s *end = (struct stm_undo_s *)(lst->items + lst->count);
         for (; modified < end; modified++) {
+            if (modified->type == TYPE_POSITION_MARKER)
+                continue;
             object_t *obj = modified->object;
             struct object_s *dst = (struct object_s*)REAL_ADDRESS(base, obj);
 
@@ -386,6 +395,22 @@
     LIST_FREE(uniques);
 }
 
+static void mark_visit_from_markers(void)
+{
+    long i;
+    for (i = 1; i < NB_SEGMENTS; i++) {
+        struct stm_priv_segment_info_s *pseg = get_priv_segment(i);
+        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);
+        for (; modified < end; modified++) {
+            if (modified->type == TYPE_POSITION_MARKER)
+                mark_visit_possibly_new_object(modified->marker_object, pseg);
+        }
+    }
+}
+
 static void mark_visit_from_roots(void)
 {
     if (testing_prebuilt_objs != NULL) {
@@ -605,7 +630,8 @@
         /* free bk copies of entries: */
         long count = cl->written_count;
         while (count-->0) {
-            free_bk(&cl->written[count]);
+            if (cl->written[count].type != TYPE_POSITION_MARKER)
+                free_bk(&cl->written[count]);
         }
 
         next = cl->next;
@@ -683,6 +709,7 @@
     /* marking */
     LIST_CREATE(marked_objects_to_trace);
     mark_visit_from_modified_objects();
+    mark_visit_from_markers();
     mark_visit_from_roots();
     mark_visit_from_finalizer_pending();
 
diff --git a/c8/stm/gcpage.h b/c8/stm/gcpage.h
--- a/c8/stm/gcpage.h
+++ b/c8/stm/gcpage.h
@@ -19,5 +19,6 @@
 
 static void major_collection_if_requested(void);
 static void major_collection_now_at_safe_point(void);
+static void major_collection_with_mutex(void);
 static bool largemalloc_keep_object_at(char *data);   /* for largemalloc.c */
 static bool smallmalloc_keep_object_at(char *data);   /* for smallmalloc.c */
diff --git a/c7/stm/marker.c b/c8/stm/marker.c
copy from c7/stm/marker.c
copy to c8/stm/marker.c
--- a/c7/stm/marker.c
+++ b/c8/stm/marker.c
@@ -3,11 +3,10 @@
 #endif
 
 
-static void marker_fetch(stm_loc_marker_t *out_marker)
+static bool marker_fetch(stm_thread_local_t *tl, stm_loc_marker_t *out_marker)
 {
-    /* Fetch the current marker from the 'out_marker->tl's shadow stack,
+    /* Fetch the current marker from tl's shadow stack,
        and return it in 'out_marker->odd_number' and 'out_marker->object'. */
-    stm_thread_local_t *tl = out_marker->tl;
     struct stm_shadowentry_s *current = tl->shadowstack - 1;
     struct stm_shadowentry_s *base = tl->shadowstack_base;
 
@@ -23,121 +22,82 @@
         /* found the odd marker */
         out_marker->odd_number = (uintptr_t)current[0].ss;
         out_marker->object = current[1].ss;
+        return true;
     }
     else {
         /* no marker found */
         out_marker->odd_number = 0;
         out_marker->object = NULL;
+        return false;
     }
 }
 
-static void _timing_fetch_inev(void)
+static void marker_fetch_obj_write(struct stm_undo_s *start,
+                                   struct stm_undo_s *contention,
+                                   stm_loc_marker_t *out_marker)
 {
-    stm_loc_marker_t marker;
-    marker.tl = STM_SEGMENT->running_thread;
-    marker_fetch(&marker);
-    STM_PSEGMENT->marker_inev.odd_number = marker.odd_number;
-    STM_PSEGMENT->marker_inev.object = marker.object;
-}
-
-static void marker_fetch_obj_write(object_t *obj, stm_loc_marker_t *out_marker)
-{
-    /* From 'out_marker->tl', fill in 'out_marker->segment_base' and
-       'out_marker->odd_number' and 'out_marker->object' from the
-       marker associated with writing the 'obj'.
+    /* Fill out 'out_marker->odd_number' and 'out_marker->object' from
+       the marker just before 'contention' in the list starting at
+       'start'.
     */
-    assert(_has_mutex());
-
-    /* here, we acquired the other thread's marker_lock, which means that:
-
-       (1) it has finished filling 'modified_old_objects' after it sets
-           up the write_locks[] value that we're conflicting with
-
-       (2) it is not mutating 'modified_old_objects' right now (we have
-           the global mutex_lock at this point too).
-    */
-    long i;
-    int in_segment_num = out_marker->tl->associated_segment_num;
-    assert(in_segment_num >= 1);
-    struct stm_priv_segment_info_s *pseg = get_priv_segment(in_segment_num);
-    struct list_s *mlst = pseg->modified_old_objects;
-    struct list_s *mlstm = pseg->modified_old_objects_markers;
-    assert(list_count(mlstm) <= 2 * list_count(mlst));
-    for (i = list_count(mlstm) / 2; --i >= 0; ) {
-        if (list_item(mlst, i) == (uintptr_t)obj) {
-            out_marker->odd_number = list_item(mlstm, i * 2 + 0);
-            out_marker->object = (object_t *)list_item(mlstm, i * 2 + 1);
+    while (contention != start) {
+        --contention;
+        if (contention->type == TYPE_POSITION_MARKER) {
+            out_marker->odd_number = contention->marker_odd_number;
+            out_marker->object = contention->marker_object;
             return;
         }
     }
+    /* no position marker found... */
     out_marker->odd_number = 0;
     out_marker->object = NULL;
 }
 
-static void _timing_record_write(void)
+static void _timing_record_write_position(void)
 {
     stm_loc_marker_t marker;
-    marker.tl = STM_SEGMENT->running_thread;
-    marker_fetch(&marker);
+    if (!marker_fetch(STM_SEGMENT->running_thread, &marker))
+        return;
 
-    long base_count = list_count(STM_PSEGMENT->modified_old_objects);
-    struct list_s *mlstm = STM_PSEGMENT->modified_old_objects_markers;
-    while (list_count(mlstm) < 2 * base_count) {
-        mlstm = list_append2(mlstm, 0, 0);
+    struct list_s *list = STM_PSEGMENT->modified_old_objects;
+    uintptr_t i = STM_PSEGMENT->position_markers_last;
+    if (i < list_count(list)) {
+        struct stm_undo_s *undo = (struct stm_undo_s *)(list->items + i);
+        if (undo->type == TYPE_POSITION_MARKER &&
+            undo->marker_odd_number == marker.odd_number &&
+            undo->marker_object == marker.object)
+            return;    /* already up-to-date */
     }
-    mlstm = list_append2(mlstm, marker.odd_number, (uintptr_t)marker.object);
-    STM_PSEGMENT->modified_old_objects_markers = mlstm;
+
+    STM_PSEGMENT->position_markers_last = list_count(list);
+    STM_PSEGMENT->modified_old_objects = list_append3(
+        list,
+        TYPE_POSITION_MARKER,         /* type */
+        marker.odd_number,            /* marker_odd_number */
+        (uintptr_t)marker.object);    /* marker_object */
 }
 
-static void _timing_contention(enum stm_event_e kind,
-                               uint8_t other_segment_num, object_t *obj)
+static void timing_write_read_contention(struct stm_undo_s *start,
+                                         struct stm_undo_s *contention)
 {
-    struct stm_priv_segment_info_s *other_pseg;
-    other_pseg = get_priv_segment(other_segment_num);
+    if (stmcb_timing_event == NULL)
+        return;
 
-    char *other_segment_base = other_pseg->pub.segment_base;
-    acquire_marker_lock(other_segment_base);
+    stm_loc_marker_t marker;
+    marker_fetch_obj_write(start, contention, &marker);
+    stmcb_timing_event(STM_SEGMENT->running_thread,
+                       STM_CONTENTION_WRITE_READ, &marker);
+}
 
-    stm_loc_marker_t markers[2];
-
-    /* Collect the location for myself.  It's usually the current
-       location, except in a write-read abort, in which case it's the
-       older location of the write. */
-    markers[0].tl = STM_SEGMENT->running_thread;
-    markers[0].segment_base = STM_SEGMENT->segment_base;
-
-    if (kind == STM_CONTENTION_WRITE_READ)
-        marker_fetch_obj_write(obj, &markers[0]);
-    else
-        marker_fetch(&markers[0]);
-
-    /* For some categories, we can also collect the relevant information
-       for the other segment. */
-    markers[1].tl = other_pseg->pub.running_thread;
-    markers[1].segment_base = other_pseg->pub.segment_base;
-
-    switch (kind) {
-    case STM_CONTENTION_WRITE_WRITE:
-        marker_fetch_obj_write(obj, &markers[1]);
-        break;
-    case STM_CONTENTION_INEVITABLE:
-        markers[1].odd_number = other_pseg->marker_inev.odd_number;
-        markers[1].object = other_pseg->marker_inev.object;
-        break;
-    default:
-        markers[1].odd_number = 0;
-        markers[1].object = NULL;
-        break;
-    }
-
-    stmcb_timing_event(markers[0].tl, kind, markers);
-
-    /* only release the lock after stmcb_timing_event(), otherwise it could
-       run into race conditions trying to interpret 'markers[1].object' */
-    release_marker_lock(other_segment_base);
+static void _timing_become_inevitable(void)
+{
+    stm_loc_marker_t marker;
+    marker_fetch(STM_SEGMENT->running_thread, &marker);
+    stmcb_timing_event(STM_SEGMENT->running_thread,
+                       STM_BECOME_INEVITABLE, &marker);
 }
 
 
 void (*stmcb_timing_event)(stm_thread_local_t *tl, /* the local thread */
                            enum stm_event_e event,
-                           stm_loc_marker_t *markers);
+                           stm_loc_marker_t *marker);
diff --git a/c7/stm/marker.h b/c8/stm/marker.h
copy from c7/stm/marker.h
copy to c8/stm/marker.h
--- a/c7/stm/marker.h
+++ b/c8/stm/marker.h
@@ -1,19 +1,17 @@
 
-static void _timing_record_write(void);
-static void _timing_fetch_inev(void);
-static void _timing_contention(enum stm_event_e kind,
-                               uint8_t other_segment_num, object_t *obj);
+static void _timing_record_write_position(void);
+static void timing_write_read_contention(struct stm_undo_s *start,
+                                         struct stm_undo_s *contention);
+static void _timing_become_inevitable(void);
 
 
+#define timing_enabled()   (stmcb_timing_event != NULL)
+
 #define timing_event(tl, event)                                         \
-    (stmcb_timing_event != NULL ? stmcb_timing_event(tl, event, NULL) : (void)0)
+    (timing_enabled() ? stmcb_timing_event(tl, event, NULL) : (void)0)
 
-#define timing_record_write()                                           \
-    (stmcb_timing_event != NULL ? _timing_record_write() : (void)0)
+#define timing_record_write_position()                                  \
+    (timing_enabled() ? _timing_record_write_position() : (void)0)
 
-#define timing_fetch_inev()                                             \
-    (stmcb_timing_event != NULL ? _timing_fetch_inev() : (void)0)
-
-#define timing_contention(kind, other_segnum, obj)                      \
-    (stmcb_timing_event != NULL ?                                       \
-        _timing_contention(kind, other_segnum, obj) : (void)0)
+#define timing_become_inevitable()                                      \
+    (timing_enabled() ? _timing_become_inevitable() : (void)0)
diff --git a/c8/stm/nursery.c b/c8/stm/nursery.c
--- a/c8/stm/nursery.c
+++ b/c8/stm/nursery.c
@@ -185,6 +185,8 @@
     struct stm_undo_s *end = (struct stm_undo_s *)(list->items + list->count);
 
     for (; undo < end; undo++) {
+        if (undo->type == TYPE_POSITION_MARKER)
+            continue;
         _cards_cleared_in_object(pseg, undo->object, false);
     }
     LIST_FOREACH_R(
@@ -410,6 +412,20 @@
     }
 }
 
+static void collect_roots_from_markers(uintptr_t len_old)
+{
+    dprintf(("collect_roots_from_markers\n"));
+
+    /* visit the marker objects */
+    struct list_s *list = STM_PSEGMENT->modified_old_objects;
+    struct stm_undo_s *undo = (struct stm_undo_s *)(list->items + len_old);
+    struct stm_undo_s *end = (struct stm_undo_s *)(list->items + list->count);
+
+    for (; undo < end; undo++) {
+        if (undo->type == TYPE_POSITION_MARKER)
+            minor_trace_if_young(&undo->marker_object);
+    }
+}
 
 static void collect_objs_still_young_but_with_finalizers(void)
 {
@@ -494,14 +510,25 @@
     dprintf(("minor_collection commit=%d\n", (int)commit));
 
     STM_PSEGMENT->minor_collect_will_commit_now = commit;
+
+    uintptr_t len_old;
+    if (STM_PSEGMENT->overflow_number_has_been_used)
+        len_old = STM_PSEGMENT->position_markers_len_old;
+    else
+        len_old = 0;
+
     if (!commit) {
         /* 'STM_PSEGMENT->overflow_number' is used now by this collection,
            in the sense that it's copied to the overflow objects */
         STM_PSEGMENT->overflow_number_has_been_used = true;
+        STM_PSEGMENT->position_markers_len_old =
+            list_count(STM_PSEGMENT->modified_old_objects);
     }
 
     collect_cardrefs_to_nursery();
 
+    collect_roots_from_markers(len_old);
+
     collect_roots_in_nursery();
 
     if (STM_PSEGMENT->finalizers != NULL)
@@ -529,7 +556,11 @@
 
     stm_safe_point();
 
+    timing_event(STM_SEGMENT->running_thread, STM_GC_MINOR_START);
+
     _do_minor_collection(commit);
+
+    timing_event(STM_SEGMENT->running_thread, STM_GC_MINOR_DONE);
 }
 
 void stm_collect(long level)
@@ -652,14 +683,13 @@
         assert(get_priv_segment(i)->last_commit_log_entry->next == NULL
                || get_priv_segment(i)->last_commit_log_entry->next == INEV_RUNNING);
         if (!ok) {
-            assert(i != 0);     /* sharing seg0 should never need an abort */
-
             if (STM_PSEGMENT->transaction_state == TS_NONE) {
                 /* we found a segment that has stale read-marker data and thus
                    is in conflict with committed objs. Since it is not running
                    currently, it's fine to ignore it. */
                 continue;
             }
+            assert(i != 0);     /* sharing seg0 should never need an abort */
 
             /* tell it to abort when continuing */
             STM_PSEGMENT->pub.nursery_end = NSE_SIGABORT;
@@ -671,7 +701,7 @@
         }
 
 
-        if (MINOR_NOTHING_TO_DO(STM_PSEGMENT))  /*TS_NONE segments have NOTHING_TO_DO*/
+        if (MINOR_NOTHING_TO_DO(STM_PSEGMENT))
             continue;
 
         assert(STM_PSEGMENT->transaction_state != TS_NONE);
diff --git a/c8/stm/prof.c b/c8/stm/prof.c
new file mode 100644
--- /dev/null
+++ b/c8/stm/prof.c
@@ -0,0 +1,122 @@
+#include <stdio.h>
+#include <time.h>
+
+
+static FILE *profiling_file;
+static char *profiling_basefn = NULL;
+static stm_expand_marker_fn profiling_expand_marker;
+
+#define MARKER_LEN_MAX   160
+
+
+static bool close_timing_log(void);   /* forward */
+
+static void _stm_profiling_event(stm_thread_local_t *tl,
+                                 enum stm_event_e event,
+                                 stm_loc_marker_t *marker)
+{
+    struct buf_s {
+        uint32_t tv_sec;
+        uint32_t tv_nsec;
+        uint32_t thread_num;
+        uint8_t event;
+        uint8_t marker_length;
+        char extra[MARKER_LEN_MAX+1];
+    } __attribute__((packed));
+
+    struct buf_s buf;
+    struct timespec t;
+    clock_gettime(CLOCK_MONOTONIC, &t);
+    buf.tv_sec = t.tv_sec;
+    buf.tv_nsec = t.tv_nsec;
+    buf.thread_num = tl->thread_local_counter;
+    buf.event = event;
+    buf.marker_length = 0;
+
+    if (marker != NULL && marker->odd_number != 0) {
+        buf.marker_length = profiling_expand_marker(get_segment_base(0),
+                                                    marker,
+                                                    buf.extra, MARKER_LEN_MAX);
+    }
+
+    if (fwrite(&buf, offsetof(struct buf_s, extra) + buf.marker_length,
+               1, profiling_file) != 1) {
+        fprintf(stderr, "stmgc: profiling log file closed unexpectedly: %m\n");
+        close_timing_log();
+    }
+}
+
+static int default_expand_marker(char *b, stm_loc_marker_t *m, char *p, int s)
+{
+    *(uintptr_t *)p = m->odd_number;
+    return sizeof(uintptr_t);
+}
+
+static bool open_timing_log(const char *filename)
+{
+    profiling_file = fopen(filename, "w");
+    if (profiling_file == NULL)
+        return false;
+
+    fwrite("STMGC-C8-PROF01\n", 16, 1, profiling_file);
+    stmcb_timing_event = _stm_profiling_event;
+    return true;
+}
+
+static bool close_timing_log(void)
+{
+    if (stmcb_timing_event == &_stm_profiling_event) {
+        stmcb_timing_event = NULL;
+        fclose(profiling_file);
+        profiling_file = NULL;
+        return true;
+    }
+    return false;
+}
+
+static void prof_forksupport_prepare(void)
+{
+    if (profiling_file != NULL)
+        fflush(profiling_file);
+}
+
+static void prof_forksupport_child(void)
+{
+    if (close_timing_log() && profiling_basefn != NULL) {
+        char filename[1024];
+        snprintf(filename, sizeof(filename),
+                 "%s.fork%ld", profiling_basefn, (long)getpid());
+        open_timing_log(filename);
+    }
+}
+
+int stm_set_timing_log(const char *profiling_file_name, int fork_mode,
+                       stm_expand_marker_fn expand_marker)
+{
+    close_timing_log();
+    free(profiling_basefn);
+    profiling_basefn = NULL;
+
+    if (profiling_file_name == NULL)
+        return 0;
+
+    if (!expand_marker)
+        expand_marker = default_expand_marker;
+    profiling_expand_marker = expand_marker;
+
+    static bool fork_support_ready = false;
+    if (!fork_support_ready) {
+        int res = pthread_atfork(prof_forksupport_prepare,
+                                 NULL, prof_forksupport_child);
+        if (res != 0)
+            stm_fatalerror("pthread_atfork() failed: %m");
+        fork_support_ready = true;
+    }
+
+    if (!open_timing_log(profiling_file_name))
+        return -1;
+
+    if (fork_mode != 0)
+        profiling_basefn = strdup(profiling_file_name);
+    return 0;
+}
diff --git a/c8/stm/setup.c b/c8/stm/setup.c
--- a/c8/stm/setup.c
+++ b/c8/stm/setup.c
@@ -221,6 +221,8 @@
     return (pthread_t *)(tl->creating_pthread);
 }
 
+static int thread_local_counters = 0;
+
 void stm_register_thread_local(stm_thread_local_t *tl)
 {
     int num;
@@ -242,6 +244,7 @@
        numbers automatically. */
     tl->associated_segment_num = -1;
     tl->last_associated_segment_num = num + 1;
+    tl->thread_local_counter = ++thread_local_counters;
     *_get_cpth(tl) = pthread_self();
     _init_shadow_stack(tl);
     set_gs_register(get_segment_base(num + 1));
diff --git a/c8/stm/sync.c b/c8/stm/sync.c
--- a/c8/stm/sync.c
+++ b/c8/stm/sync.c
@@ -139,10 +139,13 @@
     }
     /* No segment available.  Wait until release_thread_segment()
        signals that one segment has been freed. */
+    timing_event(tl, STM_WAIT_FREE_SEGMENT);
     cond_wait(C_SEGMENT_FREE);
+    timing_event(tl, STM_WAIT_DONE);
 
     /* Return false to the caller, which will call us again */
     return false;
+
  got_num:
     OPT_ASSERT(num >= 0 && num < NB_SEGMENTS-1);
     sync_ctl.in_use1[num+1] = 1;
@@ -296,10 +299,12 @@
 #ifdef STM_TESTS
         abort_with_mutex();
 #endif
+        timing_event(STM_SEGMENT->running_thread, STM_WAIT_SYNC_PAUSE);
         cond_signal(C_AT_SAFE_POINT);
         STM_PSEGMENT->safe_point = SP_WAIT_FOR_C_REQUEST_REMOVED;
         cond_wait(C_REQUEST_REMOVED);
         STM_PSEGMENT->safe_point = SP_RUNNING;
+        timing_event(STM_SEGMENT->running_thread, STM_WAIT_DONE);
     }
 }
 
diff --git a/c8/stm/sync.h b/c8/stm/sync.h
--- a/c8/stm/sync.h
+++ b/c8/stm/sync.h
@@ -33,3 +33,5 @@
 
 static bool pause_signalled, globally_unique_transaction;
 static void enter_safe_point_if_requested(void);
+
+#define safe_point_requested()  (STM_SEGMENT->nursery_end != NURSERY_END)
diff --git a/c8/stmgc.c b/c8/stmgc.c
--- a/c8/stmgc.c
+++ b/c8/stmgc.c
@@ -14,6 +14,7 @@
 #include "stm/gcpage.h"
 #include "stm/extra.h"
 #include "stm/fprintcolor.h"
+#include "stm/marker.h"
 #include "stm/rewind_setjmp.h"
 #include "stm/finalizer.h"
 
@@ -34,5 +35,7 @@
 #include "stm/core.c"
 #include "stm/extra.c"
 #include "stm/fprintcolor.c"
+#include "stm/marker.c"
+#include "stm/prof.c"
 #include "stm/rewind_setjmp.c"
 #include "stm/finalizer.c"
diff --git a/c8/stmgc.h b/c8/stmgc.h
--- a/c8/stmgc.h
+++ b/c8/stmgc.h
@@ -68,6 +68,7 @@
     /* the next fields are handled internally by the library */
     int associated_segment_num;
     int last_associated_segment_num;
+    int thread_local_counter;
     struct stm_thread_local_s *prev, *next;
     void *creating_pthread[2];
 } stm_thread_local_t;
@@ -342,6 +343,77 @@
 void stm_resume_all_other_threads(void);
 
 
+/* Profiling events.  In the comments: content of the markers, if any */
+enum stm_event_e {
+    /* always STM_TRANSACTION_START followed later by one of COMMIT or ABORT */
+    STM_TRANSACTION_START,
+    STM_TRANSACTION_COMMIT,
+    STM_TRANSACTION_ABORT,
+
+    /* write-read contention: a "marker" is included in the PYPYSTM file
+       saying where the write was done.  Followed by STM_TRANSACTION_ABORT. */
+    STM_CONTENTION_WRITE_READ,
+
+    /* inevitable contention: all threads that try to become inevitable
+       have a STM_BECOME_INEVITABLE event with a position marker.  Then,
+       if it waits it gets a STM_WAIT_OTHER_INEVITABLE.  It is possible
+       that a thread gets STM_BECOME_INEVITABLE followed by
+       STM_TRANSACTION_ABORT if it fails to become inevitable. */
+    STM_BECOME_INEVITABLE,
+
+    /* always one STM_WAIT_xxx followed later by STM_WAIT_DONE */
+    STM_WAIT_FREE_SEGMENT,
+    STM_WAIT_SYNC_PAUSE,
+    STM_WAIT_OTHER_INEVITABLE,
+    STM_WAIT_DONE,
+
+    /* start and end of GC cycles */
+    STM_GC_MINOR_START,
+    STM_GC_MINOR_DONE,
+    STM_GC_MAJOR_START,
+    STM_GC_MAJOR_DONE,
+
+    _STM_EVENT_N
+};
+
+#define STM_EVENT_NAMES                         \
+    "transaction start",                        \
+    "transaction commit",                       \
+    "transaction abort",                        \
+    "contention write read",                    \
+    "wait free segment",                        \
+    "wait other inevitable",                    \
+    "wait done",                                \
+    "gc minor start",                           \
+    "gc minor done",                            \
+    "gc major start",                           \
+    "gc major done"
+
+/* The markers pushed in the shadowstack are an odd number followed by a
+   regular object pointer. */
+typedef struct {
+    uintptr_t odd_number;
+    object_t *object;
+} stm_loc_marker_t;
+extern void (*stmcb_timing_event)(stm_thread_local_t *tl, /* the local thread */
+                                  enum stm_event_e event,
+                                  stm_loc_marker_t *marker);
+
+/* Calling this sets up a stmcb_timing_event callback that will produce
+   a binary file called 'profiling_file_name'.  Call it with
+   'fork_mode == 0' for only the main process, and with
+   'fork_mode == 1' to also write files called
+   'profiling_file_name.fork<PID>' after a fork().  Call it with NULL to
+   stop profiling.  Returns -1 in case of error (see errno then).
+   The optional 'expand_marker' function pointer is called to expand
+   the marker's odd_number and object into printable data, starting at
+   the given position and with the given maximum length. */
+typedef int (*stm_expand_marker_fn)(char *seg_base, stm_loc_marker_t *marker,
+                                    char *output, int output_size);
+int stm_set_timing_log(const char *profiling_file_name, int fork_mode,
+                       stm_expand_marker_fn expand_marker);
+
+
 /* Convenience macros to push the markers into the shadowstack */
 #define STM_PUSH_MARKER(tl, odd_num, p)   do {  \
     uintptr_t _odd_num = (odd_num);             \
diff --git a/c8/test/support.py b/c8/test/support.py
--- a/c8/test/support.py
+++ b/c8/test/support.py
@@ -124,6 +124,56 @@
 long stm_call_on_abort(stm_thread_local_t *, void *key, void callback(void *));
 long stm_call_on_commit(stm_thread_local_t *, void *key, void callback(void *));
 
+/* Profiling events.  In the comments: content of the markers, if any */
+enum stm_event_e {
+    /* always STM_TRANSACTION_START followed later by one of COMMIT or ABORT */
+    STM_TRANSACTION_START,
+    STM_TRANSACTION_COMMIT,
+    STM_TRANSACTION_ABORT,
+
+    /* write-read contention: a "marker" is included in the PYPYSTM file
+       saying where the write was done.  Followed by STM_TRANSACTION_ABORT. */
+    STM_CONTENTION_WRITE_READ,
+
+    /* inevitable contention: all threads that try to become inevitable
+       have a STM_BECOME_INEVITABLE event with a position marker.  Then,
+       if it waits it gets a STM_WAIT_OTHER_INEVITABLE.  It is possible
+       that a thread gets STM_BECOME_INEVITABLE followed by
+       STM_TRANSACTION_ABORT if it fails to become inevitable. */
+    STM_BECOME_INEVITABLE,
+
+    /* always one STM_WAIT_xxx followed later by STM_WAIT_DONE */
+    STM_WAIT_FREE_SEGMENT,
+    STM_WAIT_SYNC_PAUSE,
+    STM_WAIT_OTHER_INEVITABLE,
+    STM_WAIT_DONE,
+
+    /* start and end of GC cycles */
+    STM_GC_MINOR_START,
+    STM_GC_MINOR_DONE,
+    STM_GC_MAJOR_START,
+    STM_GC_MAJOR_DONE,
+    ...
+};
+
+typedef struct {
+    uintptr_t odd_number;
+    object_t *object;
+} stm_loc_marker_t;
+
+typedef void (*stmcb_timing_event_fn)(stm_thread_local_t *tl,
+                                      enum stm_event_e event,
+                                      stm_loc_marker_t *markers);
+stmcb_timing_event_fn stmcb_timing_event;
+
+typedef int (*stm_expand_marker_fn)(char *seg_base, stm_loc_marker_t *marker,
+                                    char *output, int output_size);
+int stm_set_timing_log(const char *profiling_file_name, int fork_mode,
+                       stm_expand_marker_fn expand_marker);
+
+void stm_push_marker(stm_thread_local_t *, uintptr_t, object_t *);
+void stm_update_marker_num(stm_thread_local_t *, uintptr_t);
+void stm_pop_marker(stm_thread_local_t *);
 
 long _stm_count_modified_old_objects(void);
 long _stm_count_objects_pointing_to_nursery(void);
@@ -147,7 +197,6 @@
 void stm_enable_light_finalizer(object_t *);
 
 void (*stmcb_finalizer)(object_t *);
-
 """)
 
 
@@ -374,6 +423,21 @@
     }
 }
 
+void stm_push_marker(stm_thread_local_t *tl, uintptr_t onum, object_t *ob)
+{
+    STM_PUSH_MARKER(*tl, onum, ob);
+}
+
+void stm_update_marker_num(stm_thread_local_t *tl, uintptr_t onum)
+{
+    STM_UPDATE_MARKER_NUM(*tl, onum);
+}
+
+void stm_pop_marker(stm_thread_local_t *tl)
+{
+    STM_POP_MARKER(*tl);
+}
+
 long current_segment_num(void)
 {
     return STM_SEGMENT->segment_num;
diff --git a/c7/test/test_marker.py b/c8/test/test_marker.py
copy from c7/test/test_marker.py
copy to c8/test/test_marker.py
--- a/c7/test/test_marker.py
+++ b/c8/test/test_marker.py
@@ -7,16 +7,11 @@
     def recording(self, *kinds):
         seen = []
         @ffi.callback("stmcb_timing_event_fn")
-        def timing_event(tl, event, markers):
+        def timing_event(tl, event, marker):
             if len(kinds) > 0 and event not in kinds:
                 return
-            if markers:
-                expanded = []
-                for i in range(2):
-                    expanded.append((markers[i].tl,
-                                     markers[i].segment_base,
-                                     markers[i].odd_number,
-                                     markers[i].object))
+            if marker:
+                expanded = (marker.odd_number, marker.object)
             else:
                 expanded = None
             seen.append((tl, event, expanded))
@@ -24,18 +19,13 @@
         self.timing_event_keepalive = timing_event
         self.seen = seen
 
-    def check_recording(self, i1, o1, i2, o2, extra=None):
+    def check_recording(self, i1, o1, generating_thread_num=1):
         seen = self.seen
-        tl, event, markers = seen[0]
-        assert tl == self.tls[1]
-        segbase = lib._stm_get_segment_base
-        assert markers[0] == (self.tls[1], segbase(2), i1, o1)
-        assert markers[1] == (self.tls[0], segbase(1), i2, o2)
-        if extra is None:
-            assert len(seen) == 1
-        else:
-            assert seen[1] == (self.tls[1], extra, None)
-            assert len(seen) == 2
+        tl, event, marker = seen[0]
+        assert tl == self.tls[generating_thread_num]
+        assert marker == (i1, o1)
+        assert len(seen) == 1
+        del self.seen[:]
 
     def test_marker_odd_simple(self):
         self.start_transaction()
@@ -47,9 +37,7 @@
         assert int(ffi.cast("uintptr_t", x)) == 29
 
     def test_abort_marker_no_shadowstack(self):
-        self.recording(lib.STM_CONTENTION_WRITE_WRITE,
-                       lib.STM_WAIT_CONTENTION,
-                       lib.STM_ABORTING_OTHER_CONTENTION)
+        self.recording(lib.STM_CONTENTION_WRITE_READ)
         p = stm_allocate_old(16)
         #
         self.start_transaction()
@@ -57,9 +45,13 @@
         #
         self.switch(1)
         self.start_transaction()
-        py.test.raises(Conflict, stm_set_char, p, 'B')
+        stm_set_char(p, 'B')
         #
-        self.check_recording(0, ffi.NULL, 0, ffi.NULL)
+        self.switch(0)
+        self.commit_transaction()
+        #
+        py.test.raises(Conflict, self.switch, 1)
+        self.check_recording(0, ffi.NULL)
 
     def test_macros(self):
         self.start_transaction()
@@ -96,8 +88,9 @@
         py.test.raises(EmptyStack, self.pop_root)
 
     def test_double_abort_markers_cb_write_write(self):
-        self.recording(lib.STM_CONTENTION_WRITE_WRITE)
+        self.recording(lib.STM_CONTENTION_WRITE_READ)
         p = stm_allocate_old(16)
+        p2 = stm_allocate_old(16)
         #
         self.start_transaction()
         self.push_root(ffi.cast("object_t *", 19))
@@ -107,30 +100,39 @@
         self.pop_root()
         self.push_root(ffi.cast("object_t *", 17))
         self.push_root(ffi.cast("object_t *", ffi.NULL))
+        stm_set_char(p, 'B')
+        stm_set_char(p2, 'C')
         stm_minor_collect()
         #
         self.switch(1)
         self.start_transaction()
         self.push_root(ffi.cast("object_t *", 21))
         self.push_root(ffi.cast("object_t *", ffi.NULL))
-        py.test.raises(Conflict, stm_set_char, p, 'B')
+        stm_set_char(p, 'B')
         #
-        self.check_recording(21, ffi.NULL, 19, ffi.NULL)
+        self.switch(0)
+        self.commit_transaction()
+        #
+        py.test.raises(Conflict, self.switch, 1)
+        self.check_recording(19, ffi.NULL)
 
-    def test_double_abort_markers_cb_inevitable(self):
-        self.recording(lib.STM_CONTENTION_INEVITABLE)
+    def test_become_inevitable_marker(self):
+        self.recording(lib.STM_BECOME_INEVITABLE)
+        #
+        self.switch(1)
+        self.start_transaction()
+        self.push_root(ffi.cast("object_t *", 19))
+        self.push_root(ffi.cast("object_t *", ffi.NULL))
+        self.become_inevitable()
+        #
+        self.check_recording(19, ffi.NULL)
+
+    def test_abort_markers_cb_inevitable(self):
+        self.recording(lib.STM_BECOME_INEVITABLE)
         #
         self.start_transaction()
-        p = stm_allocate(16)
-        stm_set_char(p, 'A')
-        self.push_root(ffi.cast("object_t *", 19))
-        self.push_root(ffi.cast("object_t *", p))
         self.become_inevitable()
-        self.pop_root()
-        self.pop_root()
-        self.push_root(ffi.cast("object_t *", 17))
-        self.push_root(ffi.cast("object_t *", ffi.NULL))
-        stm_minor_collect()
+        self.check_recording(0, ffi.NULL, generating_thread_num=0)
         #
         self.switch(1)
         self.start_transaction()
@@ -140,34 +142,16 @@
         self.push_root(ffi.cast("object_t *", p))
         py.test.raises(Conflict, self.become_inevitable)
         #
-        self.check_recording(21, p, 19, p)
+        # only during tests does become_inevitable() abort because
+        # another thread is already inevitable; but it should have
+        # recorded the marker first
+        self.check_recording(21, p, generating_thread_num=1)
 
     def test_read_write_contention(self):
         self.recording(lib.STM_CONTENTION_WRITE_READ)
         p = stm_allocate_old(16)
         #
         self.start_transaction()
-        assert stm_get_char(p) == '\x00'
-        #
-        self.switch(1)
-        self.start_transaction()
-        self.push_root(ffi.cast("object_t *", 19))
-        self.push_root(ffi.cast("object_t *", ffi.NULL))
-        stm_set_char(p, 'A')
-        self.pop_root()
-        self.pop_root()
-        self.push_root(ffi.cast("object_t *", 17))
-        self.push_root(ffi.cast("object_t *", ffi.NULL))
-        py.test.raises(Conflict, self.commit_transaction)
-        #
-        self.check_recording(19, ffi.NULL, 0, ffi.NULL)
-
-    def test_double_remote_markers_cb_write_write(self):
-        self.recording(lib.STM_CONTENTION_WRITE_WRITE,
-                       lib.STM_ABORTING_OTHER_CONTENTION)
-        p = stm_allocate_old(16)
-        #
-        self.start_transaction()
         self.push_root(ffi.cast("object_t *", 19))
         self.push_root(ffi.cast("object_t *", ffi.NULL))
         stm_set_char(p, 'A')
@@ -175,49 +159,16 @@
         self.pop_root()
         self.push_root(ffi.cast("object_t *", 17))
         self.push_root(ffi.cast("object_t *", ffi.NULL))
-        tl0 = self.get_stm_thread_local()
         #
         self.switch(1)
         self.start_transaction()
-        self.become_inevitable()
-        self.push_root(ffi.cast("object_t *", 21))
-        self.push_root(ffi.cast("object_t *", ffi.NULL))
-        stm_set_char(p, 'B')    # aborts in #0
-        self.pop_root()
-        self.pop_root()
-        self.push_root(ffi.cast("object_t *", 23))
-        self.push_root(ffi.cast("object_t *", ffi.NULL))
+        assert stm_get_char(p) == '\x00'
         #
-        py.test.raises(Conflict, self.switch, 0)
-        #
-        self.check_recording(21, ffi.NULL, 19, ffi.NULL,
-                             extra=lib.STM_ABORTING_OTHER_CONTENTION)
-
-    def test_double_remote_markers_cb_write_read(self):
-        self.recording(lib.STM_CONTENTION_WRITE_READ,
-                       lib.STM_ABORTING_OTHER_CONTENTION)
-        p = stm_allocate_old(16)
-        #
-        self.start_transaction()
-        assert stm_get_char(p) == '\x00'    # read
-        tl0 = self.get_stm_thread_local()
-        #
-        self.switch(1)
-        self.start_transaction()
-        self.become_inevitable()
-        self.push_root(ffi.cast("object_t *", 21))
-        self.push_root(ffi.cast("object_t *", ffi.NULL))
-        stm_set_char(p, 'B')                # write, will abort #0
-        self.pop_root()
-        self.pop_root()
-        self.push_root(ffi.cast("object_t *", 23))
-        self.push_root(ffi.cast("object_t *", ffi.NULL))
+        self.switch(0)
         self.commit_transaction()
         #
-        py.test.raises(Conflict, self.switch, 0)
-        #
-        self.check_recording(21, ffi.NULL, 0, ffi.NULL,
-                             extra=lib.STM_ABORTING_OTHER_CONTENTION)
+        py.test.raises(Conflict, self.switch, 1)
+        self.check_recording(19, ffi.NULL)
 
     def test_all(self):
         self.recording()     # all events


More information about the pypy-commit mailing list