[pypy-commit] stmgc default: adapt test_random.py and fix overwriting up-to-date objs during abort

Raemi noreply at buildbot.pypy.org
Wed Sep 10 11:35:53 CEST 2014


Author: Remi Meier <remi.meier at inf.ethz.ch>
Branch: 
Changeset: r1380:a1c63ae8d467
Date: 2014-09-10 11:37 +0200
http://bitbucket.org/pypy/stmgc/changeset/a1c63ae8d467/

Log:	adapt test_random.py and fix overwriting up-to-date objs during
	abort

diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -89,10 +89,11 @@
         if ((uintptr_t)cl == -1) {
             /* there is an inevitable transaction running */
 #if STM_TESTS
+            free(free_if_abort);
             stm_abort_transaction();
 #endif
             cl = prev_cl;
-            usleep(1);          /* XXX */
+            _stm_collectable_safe_point();
             continue;
         }
         prev_cl = cl;
@@ -104,8 +105,23 @@
         while ((obj = cl->written[i])) {
             _update_obj_from(cl->segment_num, obj);
 
-            if (!needs_abort && _stm_was_read(obj)) {
+            if (_stm_was_read(obj)) {
                 needs_abort = true;
+
+                /* 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);
             }
 
             i++;
@@ -453,7 +469,6 @@
         size_t obj_size;
 
         obj_size = stmcb_size_rounded_up(bk_obj);
-        assert(obj_size < 4096); /* XXX */
 
         memcpy(REAL_ADDRESS(pseg->pub.segment_base, obj),
                bk_obj, obj_size);
diff --git a/c8/stm/list.h b/c8/stm/list.h
--- a/c8/stm/list.h
+++ b/c8/stm/list.h
@@ -200,6 +200,8 @@
 #define TREE_LOOP_END_AND_COMPRESS                                       \
                          } if (_deleted_factor > 9) _tree_compress(_tree); }
 #define TREE_LOOP_DELETE(tree, item)  { (tree)->count--; (item)->addr = NULL; _deleted_factor += 6; }
+#define TREE_FIND_DELETE(tree, item)  { (tree)->count--; (item)->addr = NULL; }
+
 
 #define TREE_FIND(tree, addr1, result, goto_not_found)          \
 {                                                               \
diff --git a/c8/test/support.py b/c8/test/support.py
--- a/c8/test/support.py
+++ b/c8/test/support.py
@@ -63,7 +63,7 @@
 void _stm_test_switch_segment(int segnum);
 
 void clear_jmpbuf(stm_thread_local_t *tl);
-long stm_start_transaction(stm_thread_local_t *tl);
+long _check_start_transaction(stm_thread_local_t *tl);
 bool _check_commit_transaction(void);
 bool _check_abort_transaction(void);
 bool _check_become_inevitable(stm_thread_local_t *tl);
@@ -155,6 +155,17 @@
     CHECKED(stm_commit_transaction());
 }
 
+long _check_start_transaction(stm_thread_local_t *tl) {
+   void **jmpbuf = tl->rjthread.jmpbuf;                         \
+    if (__builtin_setjmp(jmpbuf) == 0) { /* returned directly */\
+        stm_start_transaction(tl);                              \
+        clear_jmpbuf(tl);                                       \
+        return 0;                                               \
+    }                                                           \
+    clear_jmpbuf(tl);                                           \
+    return 1;
+}
+
 bool _check_stop_safe_point(void) {
     CHECKED(_stm_stop_safe_point());
 }
@@ -474,8 +485,8 @@
     def start_transaction(self):
         tl = self.tls[self.current_thread]
         assert not lib._stm_in_transaction(tl)
-        res = lib.stm_start_transaction(tl)
-        assert res == 0
+        if lib._check_start_transaction(tl):
+            raise Conflict()
         lib.clear_jmpbuf(tl)
         assert lib._stm_in_transaction(tl)
         #
diff --git a/c8/test/test_random.py b/c8/test/test_random.py
new file mode 100644
--- /dev/null
+++ b/c8/test/test_random.py
@@ -0,0 +1,617 @@
+from support import *
+import sys, random
+import py
+
+
+
+class Exec(object):
+    def __init__(self, test):
+        self.content = {'self': test}
+        self.thread_num = 0
+
+    def do(self, cmd):
+        color = "\033[%dm" % (31 + self.thread_num % 6)
+        print >> sys.stderr, color + cmd + "\033[0m"
+        exec cmd in globals(), self.content
+
+
+def raising_call(conflict, func, *args):
+    arguments = ", ".join(map(str, args))
+    if conflict:
+        return "py.test.raises(Conflict, %s, %s)" % (func, arguments)
+    return "%s(%s)" % (func, arguments)
+
+
+class WriteWriteConflictNotTestable(Exception):
+    # How can I test a write-write conflict between
+    # an inevitable and a normal transaction? The
+    # inevitable transaction would have to wait,
+    # but now for tests we simply abort. Of course
+    # aborting the inevitable transaction is not possible..
+    pass
+
+def contention_management(our_trs, other_trs, wait=False, objs_in_conflict=None):
+    """exact copy of logic in contention.c"""
+    if our_trs.inevitable and wait:
+        # we win but cannot wait in tests...
+        raise WriteWriteConflictNotTestable
+
+
+    if our_trs.start_time >= other_trs.start_time:
+        abort_other = False
+    else:
+        abort_other = True
+
+    if other_trs.check_must_abort():
+        abort_other = True
+    elif our_trs.inevitable:
+        abort_other = True
+    elif other_trs.inevitable:
+        abort_other = False
+
+    if not abort_other:
+        our_trs.set_must_abort(objs_in_conflict)
+    else:
+        other_trs.set_must_abort(objs_in_conflict)
+
+
+class TransactionState(object):
+    """State of a transaction running in a thread,
+    e.g. maintains read/write sets. The state will be
+    discarded on abort or pushed to other threads"""
+
+    def __init__(self, start_time, thread_num=None):
+        self.read_set = set()
+        self.write_set = set()
+        self.values = {}
+        self._must_abort = False
+        self.start_time = start_time
+        self.objs_in_conflict = set()
+        self.inevitable = False
+        self.created_in_this_transaction = set()
+        self.thread_num = thread_num
+
+    def get_old_modified(self):
+        # returns only the ones that are modified and not from
+        # this transaction
+        return self.write_set.difference(self.created_in_this_transaction)
+
+    def set_must_abort(self, objs_in_conflict=None):
+        assert not self.inevitable
+        if objs_in_conflict is not None:
+            self.objs_in_conflict |= objs_in_conflict
+        self._must_abort = True
+        color = "\033[%dm" % (31 + self.thread_num % 6)
+        print >> sys.stderr, color + "# must abort: %r\033[0m" % (objs_in_conflict,)
+
+    def check_must_abort(self):
+        return self._must_abort
+
+    def has_conflict_with(self, committed):
+        return bool(self.read_set & committed.write_set)
+
+    def update_from_committed(self, committed, only_new=False):
+        """returns True if conflict"""
+        if only_new:
+            for w in committed.write_set:
+                self.values[w] = committed.values[w]
+            for w in committed.created_in_this_transaction:
+                self.values[w] = committed.values[w]
+        else:
+            self.values.update(committed.values)
+
+        if self.has_conflict_with(committed):
+            # we are too late
+            self.set_must_abort(objs_in_conflict=self.read_set & committed.write_set)
+        return self.check_must_abort()
+
+    def read_root(self, r):
+        self.read_set.add(r)
+        return self.values[r]
+
+    def add_root(self, r, v, created_in_this_transaction):
+        assert self.values.get(r, None) is None
+        self.values[r] = v
+        if created_in_this_transaction:
+            self.created_in_this_transaction.add(r)
+
+    def write_root(self, r, v):
+        if r not in self.created_in_this_transaction:
+            self.read_set.add(r)
+            self.write_set.add(r)
+        old = self.values.get(r, None)
+        self.values[r] = v
+        return old
+
+
+class ThreadState(object):
+    """Maintains state for one thread. Mostly manages things
+    to be kept between transactions (e.g. saved roots) and
+    handles discarding/reseting states on transaction abort"""
+
+    def __init__(self, num, global_state):
+        self.num = num
+        self.saved_roots = []
+        self.roots_on_stack = 0
+        self.roots_on_transaction_start = 0
+        self.transaction_state = None
+        self.global_state = global_state
+
+    def register_root(self, r):
+        self.saved_roots.append(r)
+        assert len(self.saved_roots) < SHADOWSTACK_LENGTH
+
+    def forget_random_root(self):
+        if self.transaction_state.inevitable:
+            # forget *all* roots
+            self.roots_on_stack = 0
+            self.roots_on_transaction_start = 0
+            res = str(self.saved_roots)
+            del self.saved_roots[:]
+        else:
+            # forget all non-pushed roots for now
+            assert self.roots_on_stack == self.roots_on_transaction_start
+            res = str(self.saved_roots[self.roots_on_stack:])
+            del self.saved_roots[self.roots_on_stack:]
+        return res
+
+    def get_random_root(self):
+        rnd = self.global_state.rnd
+        if self.saved_roots:
+            return rnd.choice([rnd.choice(self.global_state.prebuilt_roots),
+                               rnd.choice(self.saved_roots)])
+        return rnd.choice(self.global_state.prebuilt_roots)
+
+    def push_roots(self, ex):
+        assert self.roots_on_stack == self.roots_on_transaction_start
+        for r in self.saved_roots[self.roots_on_transaction_start:]:
+            ex.do('self.push_root(%s)' % r)
+            self.roots_on_stack += 1
+
+    def pop_roots(self, ex):
+        for r in reversed(self.saved_roots[self.roots_on_transaction_start:]):
+            ex.do('%s = self.pop_root()' % r)
+            ex.do('# 0x%x, size %d' % (
+                int(ffi.cast("uintptr_t", ex.content[r])),
+                stm_get_obj_size(ex.content[r])))
+            self.roots_on_stack -= 1
+        assert self.roots_on_stack == self.roots_on_transaction_start
+
+    def reload_roots(self, ex):
+        assert self.roots_on_stack == self.roots_on_transaction_start
+        to_reload = self.saved_roots[:self.roots_on_stack]
+        if to_reload:
+            ex.do("# reload roots on stack:")
+            for r in reversed(to_reload):
+                ex.do('%s = self.pop_root()' % r)
+            for r in to_reload:
+                ex.do('self.push_root(%s)  # 0x%x, size %d' % (
+                    r, int(ffi.cast("uintptr_t", ex.content[r])),
+                    stm_get_obj_size(ex.content[r])))
+
+    def start_transaction(self, thread_num):
+        assert self.transaction_state is None
+        if self.global_state.is_inevitable_transaction_running():
+            return False
+        start_time = self.global_state.inc_and_get_global_time()
+        trs = TransactionState(start_time, thread_num)
+        trs.update_from_committed(
+            self.global_state.committed_transaction_state)
+        self.transaction_state = trs
+        self.roots_on_transaction_start = self.roots_on_stack
+        return True
+
+    def commit_transaction(self):
+        trs = self.transaction_state
+        gtrs = self.global_state.committed_transaction_state
+        #
+        # check for inevitable transaction, must_abort otherwise
+        self.global_state.check_if_can_become_inevitable(trs)
+        if not trs.check_must_abort():
+            # abort all others in conflict
+            self.global_state.check_for_write_read_conflicts(trs)
+        conflicts = trs.check_must_abort()
+        if not conflicts:
+            # update global committed state w/o conflict
+            assert not gtrs.update_from_committed(trs)
+            self.global_state.push_state_to_other_threads(trs)
+            self.transaction_state = None
+        return conflicts
+
+    def abort_transaction(self):
+        assert self.transaction_state.check_must_abort()
+        assert not self.transaction_state.inevitable
+        self.roots_on_stack = self.roots_on_transaction_start
+        del self.saved_roots[self.roots_on_stack:]
+        self.transaction_state = None
+
+
+class GlobalState(object):
+    """Maintains the global view (in a TransactionState) on
+    objects and threads. It also handles checking for conflicts
+    between threads and pushing state to other threads"""
+
+    def __init__(self, ex, rnd):
+        self.ex = ex
+        self.rnd = rnd
+        self.thread_states = []
+        self.prebuilt_roots = []
+        self.committed_transaction_state = TransactionState(0)
+        self.global_time = 0
+        self.root_numbering = 0
+        self.ref_type_map = {}
+        self.root_sizes = {}
+
+    def get_new_root_name(self, is_ref_type, size):
+        self.root_numbering += 1
+        r = "lp_%s_%d" % ("ref" if is_ref_type else "char", self.root_numbering)
+        self.ref_type_map[r] = is_ref_type
+        self.root_sizes[r] = size
+        return r
+
+    def has_ref_type(self, r):
+        return self.ref_type_map[r]
+
+    def get_root_size(self, r):
+        return self.root_sizes[r]
+
+    def inc_and_get_global_time(self):
+        self.global_time += 1
+        return self.global_time
+
+    def push_state_to_other_threads(self, trs):
+        assert not trs.check_must_abort()
+        for ts in self.thread_states:
+            other_trs = ts.transaction_state
+            if other_trs is None or other_trs is trs:
+                continue
+            other_trs.update_from_committed(trs, only_new=True)
+
+        if trs.check_must_abort():
+            self.ex.do('# conflict while pushing to other threads: %s' %
+                       trs.objs_in_conflict)
+
+    def is_inevitable_transaction_running(self):
+        for ts in self.thread_states:
+            other_trs = ts.transaction_state
+            if (other_trs and other_trs.inevitable):
+                self.ex.do("# there is another inevitable transaction:")
+                return True
+        return False
+
+    def check_if_can_become_inevitable(self, trs):
+        assert not trs.check_must_abort()
+        for ts in self.thread_states:
+            other_trs = ts.transaction_state
+            if (other_trs and trs is not other_trs
+                and other_trs.inevitable):
+                self.ex.do("# there is another inevitable transaction:")
+                trs.set_must_abort()
+                break
+
+    def check_for_write_read_conflicts(self, trs):
+        assert not trs.check_must_abort()
+        for ts in self.thread_states:
+            other_trs = ts.transaction_state
+            if other_trs is None or other_trs is trs:
+                continue
+
+            confl_set = other_trs.read_set & trs.write_set
+            if confl_set:
+                # trs wins!
+                other_trs.set_must_abort(objs_in_conflict=confl_set)
+                assert not trs.check_must_abort()
+
+        assert not trs.check_must_abort()
+
+
+###################################################################
+###################################################################
+######################## STM OPERATIONS ###########################
+###################################################################
+###################################################################
+
+
+def op_start_transaction(ex, global_state, thread_state):
+    if thread_state.start_transaction(ex.thread_num):
+        ex.do('self.start_transaction()')
+        thread_state.reload_roots(ex)
+        #
+        # assert that everything known is old:
+        old_objs = thread_state.saved_roots
+        for o in old_objs:
+            ex.do("assert not is_in_nursery(%s)" % o)
+    else:
+        ex.do('py.test.raises(Conflict, self.start_transaction)')
+
+
+def op_commit_transaction(ex, global_state, thread_state):
+    #
+    # push all new roots
+    ex.do("# push new objs before commit:")
+    thread_state.push_roots(ex)
+    aborts = thread_state.commit_transaction()
+    #
+    if aborts:
+        thread_state.abort_transaction()
+    ex.do(raising_call(aborts, "self.commit_transaction"))
+
+def op_abort_transaction(ex, global_state, thread_state):
+    trs = thread_state.transaction_state
+    if trs.inevitable:
+        return
+    trs.set_must_abort()
+    thread_state.abort_transaction()
+    ex.do('self.abort_transaction()')
+
+def op_become_inevitable(ex, global_state, thread_state):
+    trs = thread_state.transaction_state
+    global_state.check_if_can_become_inevitable(trs)
+
+    thread_state.push_roots(ex)
+    ex.do(raising_call(trs.check_must_abort(),
+                       "self.become_inevitable"))
+    if trs.check_must_abort():
+        thread_state.abort_transaction()
+    else:
+        trs.inevitable = True
+        thread_state.pop_roots(ex)
+        thread_state.reload_roots(ex)
+
+
+def op_allocate(ex, global_state, thread_state):
+    size = global_state.rnd.choice([
+        "16",
+        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(%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)
+
+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)
+    thread_state.push_roots(ex)
+    ex.do('%s = stm_allocate_refs(%s)' % (r, num))
+    ex.do('# 0x%x' % (int(ffi.cast("uintptr_t", ex.content[r]))))
+    thread_state.transaction_state.add_root(r, "ffi.NULL", True)
+
+    thread_state.pop_roots(ex)
+    thread_state.reload_roots(ex)
+    thread_state.register_root(r)
+
+def op_minor_collect(ex, global_state, thread_state):
+    thread_state.push_roots(ex)
+    ex.do('stm_minor_collect()')
+    thread_state.pop_roots(ex)
+    thread_state.reload_roots(ex)
+
+def op_major_collect(ex, global_state, thread_state):
+    thread_state.push_roots(ex)
+    ex.do('stm_major_collect()')
+    thread_state.pop_roots(ex)
+    thread_state.reload_roots(ex)
+
+
+def op_forget_root(ex, global_state, thread_state):
+    r = thread_state.forget_random_root()
+    if thread_state.transaction_state.inevitable:
+        ex.do('# inevitable forget %s' % r)
+    else:
+        ex.do('# forget %s' % r)
+
+def op_write(ex, global_state, thread_state):
+    r = thread_state.get_random_root()
+    trs = thread_state.transaction_state
+    is_ref = global_state.has_ref_type(r)
+    try_cards = global_state.rnd.randrange(1, 100) > 5 and False
+    #
+    # decide on a value to write
+    if is_ref:
+        v = thread_state.get_random_root()
+    else:
+        v = ord(global_state.rnd.choice("abcdefghijklmnop"))
+    assert trs.write_root(r, v) is not None
+    #
+    aborts = trs.check_must_abort()
+    if aborts:
+        thread_state.abort_transaction()
+    offset = global_state.get_root_size(r) + " - 1"
+    if is_ref:
+        ex.do(raising_call(aborts, "stm_set_ref", r, offset, v, try_cards))
+        if not aborts:
+            ex.do(raising_call(False, "stm_set_ref", r, "0", v, try_cards))
+    else:
+        ex.do(raising_call(aborts, "stm_set_char", r, repr(chr(v)), offset, try_cards))
+        if not aborts:
+            ex.do(raising_call(False, "stm_set_char", r, repr(chr(v)), "HDR", try_cards))
+
+def op_read(ex, global_state, thread_state):
+    r = thread_state.get_random_root()
+    trs = thread_state.transaction_state
+    v = trs.read_root(r)
+    #
+    offset = global_state.get_root_size(r) + " - 1"
+    if global_state.has_ref_type(r):
+        if v in thread_state.saved_roots or v in global_state.prebuilt_roots:
+            # v = root known to this transaction; or prebuilt
+            ex.do("assert stm_get_ref(%s, %s) == %s" % (r, offset, v))
+            ex.do("assert stm_get_ref(%s, 0) == %s" % (r, v))
+        elif v != "ffi.NULL":
+            global_trs = global_state.committed_transaction_state
+            if v not in trs.values:
+                # not from this transaction AND not known at the start of this
+                # transaction AND not pushed to us by a commit
+                assert False
+            elif v not in global_trs.values:
+                # created and forgotten earlier in this transaction, we still
+                # know its latest value (v in trs.values)
+                ex.do("# revive %r in this transaction" % v)
+            else:
+                # created in an earlier transaction, now also known here. We
+                # know its value (v in trs.values)
+                ex.do("# register %r in this thread" % v)
+            #
+            ex.do("%s = stm_get_ref(%s, %s)" % (v, r, offset))
+            ex.do("%s = stm_get_ref(%s, 0)" % (v, r))
+            thread_state.register_root(v)
+        else:
+            # v is NULL; we still need to read it (as it should be in the read-set):
+            ex.do("assert stm_get_ref(%s, %s) == %s" % (r,offset,v))
+            ex.do("assert stm_get_ref(%s, 0) == %s" % (r,v))
+    else:
+        ex.do("assert stm_get_char(%s, %s) == %s" % (r, offset, repr(chr(v))))
+        ex.do("assert stm_get_char(%s, HDR) == %s" % (r, repr(chr(v))))
+
+def op_assert_size(ex, global_state, thread_state):
+    r = thread_state.get_random_root()
+    size = global_state.get_root_size(r)
+    if global_state.has_ref_type(r):
+        ex.do("assert stm_get_obj_size(%s) == %s" % (r, size + " * WORD + HDR"))
+    else:
+        ex.do("assert stm_get_obj_size(%s) == %s" % (r, size))
+
+def op_assert_modified(ex, global_state, thread_state):
+    trs = thread_state.transaction_state
+    modified = trs.get_old_modified()
+    ex.do("# modified = %s" % modified)
+    ex.do("modified = modified_old_objects()")
+    # check that all definitely old objs we wrote to and that were saved
+    # are in modified_old_objs()
+    saved = [m for m in modified
+             if m in thread_state.saved_roots or m in global_state.prebuilt_roots]
+    ex.do("assert set([%s]).issubset(set(modified))" % (
+        ", ".join(saved)
+    ))
+
+
+def op_switch_thread(ex, global_state, thread_state, new_thread_state=None):
+    if new_thread_state is None:
+        new_thread_state = global_state.rnd.choice(global_state.thread_states)
+
+    if new_thread_state != thread_state:
+        if thread_state.transaction_state:
+            thread_state.push_roots(ex)
+        ex.do('#')
+        #
+        trs = new_thread_state.transaction_state
+        if trs is not None and not trs.inevitable:
+            if global_state.is_inevitable_transaction_running():
+                trs.set_must_abort()
+        conflicts = trs is not None and trs.check_must_abort()
+        ex.thread_num = new_thread_state.num
+        #
+        ex.do(raising_call(conflicts,
+                           "self.switch", new_thread_state.num))
+        if conflicts:
+            new_thread_state.abort_transaction()
+        elif trs:
+            new_thread_state.pop_roots(ex)
+            new_thread_state.reload_roots(ex)
+
+    return new_thread_state
+
+
+###################################################################
+###################################################################
+####################### TEST GENERATION ###########################
+###################################################################
+###################################################################
+
+
+class TestRandom(BaseTest):
+    NB_THREADS = NB_SEGMENTS
+
+    def test_fixed_16_bytes_objects(self, seed=1010):
+        rnd = random.Random(seed)
+
+        N_OBJECTS = 3
+        N_THREADS = self.NB_THREADS
+        ex = Exec(self)
+        ex.do("################################################################\n"*10)
+        ex.do('# initialization')
+
+        global_state = GlobalState(ex, rnd)
+        for i in range(N_THREADS):
+            global_state.thread_states.append(
+                ThreadState(i, global_state))
+        curr_thread = global_state.thread_states[0]
+
+        for i in range(N_OBJECTS):
+            r = global_state.get_new_root_name(False, "384")
+            ex.do('%s = stm_allocate_old(384)' % r)
+            global_state.committed_transaction_state.add_root(r, 0, False)
+            global_state.prebuilt_roots.append(r)
+
+            r = global_state.get_new_root_name(True, "50")
+            ex.do('%s = stm_allocate_old_refs(50)' % r)
+            global_state.committed_transaction_state.add_root(r, "ffi.NULL", False)
+            global_state.prebuilt_roots.append(r)
+        global_state.committed_transaction_state.write_set = set()
+        global_state.committed_transaction_state.read_set = set()
+
+        # random steps:
+        possible_actions = [
+            op_allocate,
+            op_allocate_ref, op_allocate_ref,
+            op_write, op_write, op_write,
+            op_read, op_read, op_read, op_read, op_read, op_read, op_read, op_read,
+            op_commit_transaction,
+            op_abort_transaction,
+            op_forget_root,
+            op_become_inevitable,
+            op_assert_size,
+            op_assert_modified,
+            op_minor_collect,
+            #op_major_collect,
+        ]
+        for _ in range(200):
+            # make sure we are in a transaction:
+            curr_thread = op_switch_thread(ex, global_state, curr_thread)
+
+            if (global_state.is_inevitable_transaction_running()
+                and curr_thread.transaction_state is None):
+                continue        # don't bother trying to start a transaction
+
+            if curr_thread.transaction_state is None:
+                op_start_transaction(ex, global_state, curr_thread)
+                assert curr_thread.transaction_state is not None
+
+            # do something random
+            action = rnd.choice(possible_actions)
+            action(ex, global_state, curr_thread)
+
+        # to make sure we don't have aborts in the test's teardown method,
+        # we will simply stop all running transactions
+        for ts in global_state.thread_states:
+            if ts.transaction_state is not None:
+                if curr_thread != ts:
+                    ex.do('#')
+                    curr_thread = op_switch_thread(ex, global_state, curr_thread,
+                                                  new_thread_state=ts)
+
+                # could have aborted in the switch() above:
+                if curr_thread.transaction_state:
+                    op_commit_transaction(ex, global_state, curr_thread)
+
+
+
+    def _make_fun(seed):
+        def test_fun(self):
+            self.test_fixed_16_bytes_objects(seed)
+        test_fun.__name__ = 'test_random_%d' % seed
+        return test_fun
+
+    for _seed in range(5000, 5200):
+        _fn = _make_fun(_seed)
+        locals()[_fn.__name__] = _fn


More information about the pypy-commit mailing list