Author: Remi Meier <[email protected]>
Branch: use-gcc
Changeset: r1960:10e4734e83c7
Date: 2015-09-16 10:49 +0200
http://bitbucket.org/pypy/stmgc/changeset/10e4734e83c7/

Log:    implement noconflict objects

diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -12,7 +12,7 @@
 {
     assert(undo->type != TYPE_POSITION_MARKER);
     free(undo->backup);
-    assert(undo->backup = (char*)-88);
+    assert(undo->backup = (char*)0xbb);
     increment_total_allocated(-SLICE_SIZE(undo->slice));
 }
 
@@ -308,7 +308,7 @@
     }
 }
 
-static void reset_modified_from_backup_copies(int segment_num);  /* forward */
+static void reset_modified_from_backup_copies(int segment_num, object_t 
*only_obj);  /* forward */
 
 static bool _stm_validate(void)
 {
@@ -409,6 +409,25 @@
                         if (LIKELY(!_stm_was_read(obj)))
                             continue;
 
+                        /* check for NO_CONFLICT flag in seg0. While its data 
may
+                           not be current there, the flag will be there and is
+                           immutable. (we cannot check in my_segnum bc. we may
+                           only have executed stm_read(o) but not touched its 
pages
+                           yet -> they may be NO_ACCESS */
+                        struct object_s *obj0 = (struct object_s 
*)REAL_ADDRESS(get_segment_base(0), obj);
+                        if (obj0->stm_flags & GCFLAG_NO_CONFLICT) {
+                            /* obj is noconflict and therefore shouldn't cause
+                               an abort. However, from now on, we also assume
+                               that an abort would not roll-back to what is in
+                               the backup copy, as we don't trace the bkcpy
+                               during major GCs.
+                               We choose the approach to reset all our changes
+                               to this obj here, so that we can throw away the
+                               backup copy completely: */
+                            reset_modified_from_backup_copies(my_segnum, obj);
+                            continue;
+                        }
+
                         /* conflict! */
                         dprintf(("_stm_validate() failed for obj %p\n", obj));
 
@@ -418,7 +437,7 @@
                            from the old (but unmodified) version to the newer
                            version.
                         */
-                        reset_modified_from_backup_copies(my_segnum);
+                        reset_modified_from_backup_copies(my_segnum, NULL);
                         timing_write_read_contention(cl->written, undo);
                         needs_abort = true;
                         break;
@@ -1399,7 +1418,7 @@
         invoke_general_finalizers(tl);
 }
 
-static void reset_modified_from_backup_copies(int segment_num)
+static void reset_modified_from_backup_copies(int segment_num, object_t 
*only_obj)
 {
 #pragma push_macro("STM_PSEGMENT")
 #pragma push_macro("STM_SEGMENT")
@@ -1415,7 +1434,11 @@
     for (; undo < end; undo++) {
         if (undo->type == TYPE_POSITION_MARKER)
             continue;
+
         object_t *obj = undo->object;
+        if (only_obj != NULL && obj != only_obj)
+            continue;
+
         char *dst = REAL_ADDRESS(pseg->pub.segment_base, obj);
 
         memcpy(dst + SLICE_OFFSET(undo->slice),
@@ -1427,12 +1450,29 @@
                  SLICE_SIZE(undo->slice), undo->backup));
 
         free_bk(undo);
+
+        if (only_obj != NULL) {
+            assert(IMPLY(only_obj != NULL,
+                         (((struct object_s *)dst)->stm_flags
+                          & (GCFLAG_NO_CONFLICT
+                             | GCFLAG_WRITE_BARRIER
+                             | GCFLAG_WB_EXECUTED))
+                         == (GCFLAG_NO_CONFLICT | GCFLAG_WRITE_BARRIER)));
+            /* copy last element over this one */
+            end--;
+            list->count -= 3;
+            if (undo < end)
+                *undo = *end;
+            undo--;  /* next itr */
+        }
     }
 
-    /* check that all objects have the GCFLAG_WRITE_BARRIER afterwards */
-    check_all_write_barrier_flags(pseg->pub.segment_base, list);
+    if (only_obj == NULL) {
+        /* check that all objects have the GCFLAG_WRITE_BARRIER afterwards */
+        check_all_write_barrier_flags(pseg->pub.segment_base, list);
 
-    list_clear(list);
+        list_clear(list);
+    }
 #pragma pop_macro("STM_SEGMENT")
 #pragma pop_macro("STM_PSEGMENT")
 }
@@ -1468,7 +1508,7 @@
         });
 
     acquire_modification_lock_wr(segment_num);
-    reset_modified_from_backup_copies(segment_num);
+    reset_modified_from_backup_copies(segment_num, NULL);
     release_modification_lock_wr(segment_num);
     _verify_cards_cleared_in_all_lists(pseg);
 
diff --git a/c8/stm/core.h b/c8/stm/core.h
--- a/c8/stm/core.h
+++ b/c8/stm/core.h
@@ -43,6 +43,7 @@
     GCFLAG_CARDS_SET = _STM_GCFLAG_CARDS_SET,
     GCFLAG_VISITED = 0x10,
     GCFLAG_FINALIZATION_ORDERING = 0x20,
+    GCFLAG_NO_CONFLICT = _STM_GCFLAG_NO_CONFLICT,
     /* All remaining bits of the 32-bit 'stm_flags' field are taken by
        the "overflow number".  This is a number that identifies the
        "overflow objects" from the current transaction among all old
@@ -50,7 +51,7 @@
        current transaction that have been flushed out of the nursery,
        which occurs if the same transaction allocates too many objects.
     */
-    GCFLAG_OVERFLOW_NUMBER_bit0 = 0x40   /* must be last */
+    GCFLAG_OVERFLOW_NUMBER_bit0 = 0x80   /* must be last */
 };
 
 #define SYNC_QUEUE_SIZE    31
diff --git a/c8/stmgc.h b/c8/stmgc.h
--- a/c8/stmgc.h
+++ b/c8/stmgc.h
@@ -162,6 +162,7 @@
 #endif
 
 #define _STM_GCFLAG_WRITE_BARRIER      0x01
+#define _STM_GCFLAG_NO_CONFLICT        0x40
 #define _STM_FAST_ALLOC           (66*1024)
 #define _STM_NSE_SIGNAL_ABORT             1
 #define _STM_NSE_SIGNAL_MAX               2
@@ -780,6 +781,24 @@
 void stm_queue_tracefn(stm_queue_t *queue, void trace(object_t **));
 
 
+
+/* stm_allocate_noconflict() allocates a special kind of object. Validation
+   will never detect conflicts on such an object. However, writes to it can
+   get lost. More precisely: every possible point for validation during a
+   transaction may import a committed version of such objs, thereby resetting
+   it or even contain not-yet-seen values from other (committed) transactions.
+   Hence, changes to such an obj that a transaction commits may or may not
+   propagate to other transactions. */
+__attribute__((always_inline))
+static inline object_t *stm_allocate_noconflict(ssize_t size_rounded_up)
+{
+    object_t *o = stm_allocate(size_rounded_up);
+    o->stm_flags |= _STM_GCFLAG_NO_CONFLICT;
+    return o;
+}
+
+
+
 /* ==================== END ==================== */
 
 extern void (*stmcb_expand_marker)(char *segment_base, uintptr_t odd_number,
diff --git a/c8/test/support.py b/c8/test/support.py
--- a/c8/test/support.py
+++ b/c8/test/support.py
@@ -45,6 +45,7 @@
 object_t *stm_allocate(ssize_t size_rounded_up);
 object_t *stm_allocate_weakref(ssize_t size_rounded_up);
 object_t *stm_allocate_with_finalizer(ssize_t size_rounded_up);
+object_t *stm_allocate_noconflict(ssize_t size_rounded_up);
 
 /*void stm_write_card(); use _checked_stm_write_card() instead */
 
@@ -664,6 +665,18 @@
     lib._set_type_id(o, tid)
     return o
 
+def stm_allocate_noconflict(size):
+    o = lib.stm_allocate_noconflict(size)
+    tid = 42 + size
+    lib._set_type_id(o, tid)
+    return o
+
+def stm_allocate_noconflict_refs(n):
+    o = lib.stm_allocate_noconflict(HDR + n * WORD)
+    tid = 421420 + n
+    lib._set_type_id(o, tid)
+    return o
+
 def stm_allocate_with_finalizer(size):
     o = lib.stm_allocate_with_finalizer(size)
     tid = 42 + size
diff --git a/c8/test/test_noconfl.py b/c8/test/test_noconfl.py
new file mode 100644
--- /dev/null
+++ b/c8/test/test_noconfl.py
@@ -0,0 +1,155 @@
+from support import *
+import py
+
+
+
+class TestNoConflict(BaseTest):
+
+    def test_basic(self):
+        self.start_transaction()
+        o = stm_allocate_noconflict(16)
+        stm_set_char(o, 'x')
+        self.push_root(o)
+        self.commit_transaction()
+        o = self.pop_root()
+        self.push_root(o)
+
+        self.switch(1)
+
+        self.start_transaction()
+        assert stm_get_char(o) == 'x'
+
+    def test_propagate_on_validation(self):
+        self.start_transaction()
+        o = stm_allocate_noconflict(16)
+        self.push_root(o)
+        self.commit_transaction()
+
+        self.start_transaction()
+        o = self.pop_root()
+        self.push_root(o)
+
+        self.switch(1)
+
+        self.start_transaction()
+        assert stm_get_char(o) == '\0'
+
+        self.switch(0)
+        stm_set_char(o, 'a')
+        self.commit_transaction()
+
+        self.switch(1, False)
+        assert stm_get_char(o) == '\0'
+        stm_validate()
+        assert stm_get_char(o) == 'a'
+
+    def test_propagate_on_validation2(self):
+        self.start_transaction()
+        o = stm_allocate_noconflict(16)
+        self.push_root(o)
+        self.commit_transaction()
+
+        self.start_transaction()
+        o = self.pop_root()
+        self.push_root(o)
+
+        self.switch(1)
+
+        self.start_transaction()
+        assert stm_get_char(o) == '\0'
+        stm_set_char(o, 'b') # later lost
+
+        self.switch(0)
+        stm_set_char(o, 'a')
+        self.commit_transaction()
+
+        self.switch(1, False)
+        assert stm_get_char(o) == 'b'
+        stm_validate()
+        assert stm_get_char(o) == 'a'
+
+    def test_abort_doesnt_revert(self):
+        self.start_transaction()
+        o = stm_allocate_noconflict(16)
+        self.push_root(o)
+        self.commit_transaction()
+
+        self.start_transaction()
+        o = self.pop_root()
+        self.push_root(o)
+
+        self.switch(1)
+
+        self.start_transaction()
+        assert stm_get_char(o) == '\0'
+        stm_set_char(o, 'b') # later lost
+
+        self.switch(0)
+        stm_set_char(o, 'a')
+        self.commit_transaction()
+
+        self.switch(1, False)
+        assert stm_get_char(o) == 'b'
+        stm_validate()
+        assert stm_get_char(o) == 'a'
+        # now make sure we never revert back to '\0'
+        # since then we would need to trace backup copies
+        # in the GC to keep refs alive there
+        self.abort_transaction()
+        self.start_transaction()
+        assert stm_get_char(o) == 'a'
+
+
+    def test_huge_obj(self):
+        self.start_transaction()
+        o = stm_allocate_noconflict(1000+20*CARD_SIZE)
+        self.push_root(o)
+        self.commit_transaction()
+        self.start_transaction()
+        o = self.pop_root()
+        self.push_root(o)
+
+        stm_set_char(o, 'x', HDR, True)
+        stm_set_char(o, 'y', 1000, True)
+
+        self.switch(1)
+        self.start_transaction()
+        assert stm_get_char(o, HDR) == '\0'
+        stm_set_char(o, 'b', HDR, False) # later lost
+
+        self.switch(0)
+        self.commit_transaction()
+
+        self.switch(1, False)
+        assert stm_get_char(o, HDR) == 'b'
+        assert stm_get_char(o, 1000) == '\0'
+        stm_validate()
+        assert stm_get_char(o, HDR) == 'x'
+        assert stm_get_char(o, 1000) == 'y'
+        self.abort_transaction()
+        self.start_transaction()
+        assert stm_get_char(o, HDR) == 'x'
+        assert stm_get_char(o, 1000) == 'y'
+
+    def test_only_read(self):
+        self.start_transaction()
+        o = stm_allocate_noconflict(16)
+        self.push_root(o)
+        self.commit_transaction()
+
+        self.start_transaction()
+        o = self.pop_root()
+        self.push_root(o)
+
+        self.switch(1)
+
+        self.start_transaction()
+        stm_read(o) # don't touch the memory
+
+        self.switch(0)
+        stm_set_char(o, 'a')
+        self.commit_transaction()
+
+        self.switch(1, False)
+        stm_validate()
+        assert stm_get_char(o) == 'a'
diff --git a/c8/test/test_random.py b/c8/test/test_random.py
--- a/c8/test/test_random.py
+++ b/c8/test/test_random.py
@@ -243,6 +243,7 @@
         self.root_numbering = 0
         self.ref_type_map = {}
         self.root_sizes = {}
+        self.noconfl_objs = set()
 
     def get_new_root_name(self, is_ref_type, size):
         self.root_numbering += 1
@@ -261,6 +262,12 @@
         self.global_time += 1
         return self.global_time
 
+    def add_noconfl_obj(self, o):
+        self.noconfl_objs.add(o)
+
+    def is_noconfl(self, o):
+        return o in self.noconfl_objs
+
     def push_state_to_other_threads(self, trs):
         assert not trs.check_must_abort()
         for ts in self.thread_states:
@@ -382,6 +389,27 @@
     thread_state.reload_roots(ex)
     thread_state.register_root(r)
 
+def op_allocate_no_confl(ex, global_state, thread_state):
+    # copy&paste of op_allocate...
+    size = global_state.rnd.choice([
+        "16", "48", "288",
+        str(4096+16),
+        str(80*1024+16),
+        #"SOME_MEDIUM_SIZE+16",
+        #"SOME_LARGE_SIZE+16",
+    ])
+    r = global_state.get_new_root_name(False, size)
+    thread_state.push_roots(ex)
+
+    ex.do('%s = stm_allocate_noconflict(%s)' % (r, size))
+    ex.do('# 0x%x' % (int(ffi.cast("uintptr_t", ex.content[r]))))
+    thread_state.transaction_state.add_root(r, 0, True)
+
+    thread_state.pop_roots(ex)
+    thread_state.reload_roots(ex)
+    thread_state.register_root(r)
+    global_state.add_noconfl_obj(r)
+
 def op_allocate_ref(ex, global_state, thread_state):
     num = str(global_state.rnd.randrange(1, 1000))
     r = global_state.get_new_root_name(True, num)
@@ -429,6 +457,10 @@
     trs = thread_state.transaction_state
     is_ref = global_state.has_ref_type(r)
     try_cards = global_state.rnd.randrange(1, 100) > 5 # and False
+    # noconfl:
+    if global_state.is_noconfl(r):
+        ex.do('stm_write(%s) # noconfl' % r)
+        return
     #
     # decide on a value to write
     if is_ref:
@@ -449,6 +481,10 @@
     r = thread_state.get_random_root()
     trs = thread_state.transaction_state
     v = trs.read_root(r)
+    # noconfl:
+    if global_state.is_noconfl(r):
+        ex.do('stm_read(%s) # noconfl' % r)
+        return
     #
     offset = global_state.get_root_size(r) + " - 1"
     if global_state.has_ref_type(r):
@@ -576,6 +612,7 @@
             [op_write,]*70,
             [op_allocate,]*10,
             [op_allocate_ref]*10,
+            [op_allocate_no_confl]*2,
             [op_commit_transaction,]*6,
             [op_abort_transaction,],
             [op_forget_root]*10,
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to