Example #1
0
    def testCommutes(self):
        # Make sure multiple transactions that occur simultaneously each commute the same ref
        # Hard to check for this behaving properly---it should have fewer retries than if each 
        # transaction did an alter, but if transactions commit at the same time one might have to retry
        # anyway. The difference is usually an order of magnitude, so this test is pretty safe

        self.numruns = AtomicInteger()
        self.numalterruns = AtomicInteger()
        numthreads = 20

        def incr(curval):
            return curval + 1

        def adder(curval, extraval):
            return curval + extraval

        def t1():
            self.numruns.getAndIncrement()
            self.refA.commute(incr, [])

        def t2():
            # self.d("Thread %s (%s): ALTER BEING RUN, total retry num: %s" % (current_thread().ident, id(current_thread()), self.numalterruns.getAndIncrement()))
            self.numalterruns.getAndIncrement()
            self.refB.alter(adder, [100])

        self.refA = Ref(0, None)
        self.refB = Ref(0, None)
        for i in range(numthreads):
            self.runTransactionInThread(t1)

        self.join_all()

        for i in range(numthreads):
            self.runTransactionInThread(t2)

        self.join_all()

        self.d("Commute took %s runs and counter is %s" % (self.numruns.get(), self.refA.deref()))
        self.d("Alter took %s runs and counter is %s" % (self.numalterruns.get(), self.refB.deref()))

        self.assertEqual(self.refA.deref(), numthreads)
        self.assertEqual(self.refB.deref(), 2000)
        self.assertTrue(self.numalterruns.get() >= self.numruns.get())
Example #2
0
class TestThreadedTransactions(unittest.TestCase):
    spawned_threads = []

    def setUp(self):
        self.main_thread = current_thread()
        self.first_run = local()
        self.first_run.data = True

    def tearDown(self):
        # Join all if not joined with yet
        self.join_all()

    def d(self, str):
        """
        Debug helper
        """
        with loglock:
            if spew_debug:
                print str

    def runTransactionInThread(self, func, autostart=True, postcommit=None):
        """
        Runs the desired function in a transaction on a secondary thread. Optionally
        allows the caller to start the thread manually, and takes an optional postcommit
        function that is run after the transaction is committed
        """
        def thread_func(transaction_func, postcommit_func):
            self.first_run.data = True
            LockingTransaction.runInTransaction(transaction_func)
            if postcommit_func:
                postcommit_func()

        t = Thread(target=thread_func, args=[func, postcommit])
        if autostart:
            t.start()
        self.spawned_threads.append(t)

        return t

    def join_all(self):
        """
        Joins all spawned threads to make sure they have all finished before continuing
        """
        for thread in self.spawned_threads:
            thread.join()
        self.spawned_threads = []

    def testSimpleConcurrency(self):
        def t1():
            sleep(.1)
            self.ref0.refSet(1)
        def t2():
            self.ref0.refSet(2)

        # Delaying t1 means it should commit after t2
        self.ref0 = Ref(0, None)
        self.runTransactionInThread(t1)
        self.runTransactionInThread(t2)
        self.join_all()
        self.assertEqual(self.ref0.deref(), 1)

    def testFault(self):
        # We want to cause a fault on one ref, that means no committed value yet
        t1wait = Event()
        t1launched = Event()

        def t1():
            # This thread tries to read the value after t2 has written to it, but it starts first
            self.d("* Before wait")
            t1wait.wait()
            val = self.refA.deref()
            self.d("* Derefed, asserting fault")
            # Make sure we only successfully got here w/ 1 fault (deref() triggered a retry the first time around)
            self.assertEqual(self.refA._faults.get(), 1)
            self.assertEqual(self.refA.historyCount(), 1)
            self.assertEqual(val, 6)

            self.d("* Refsetting after fault")
            # When committed, we should create another tval in the history chain
            self.refA.refSet(7)

        def t2():
            t1launched.wait()

            # This thread does the committing after t1 started but before it reads the value of refA
            self.d("** Creating ref")
            self.refA = Ref(5, None)
            self.refA.refSet(6)

        def t2committed():
            self.d("** Notify")
            t1wait.set()

        self.runTransactionInThread(t1)
        self.runTransactionInThread(t2, postcommit=t2committed)

        self.d("Notifying t1")
        t1launched.set()

        self.join_all()

        # The write after the fault should have created a new history chain item
        self.assertEqual(self.refA.historyCount(), 2)
        self.d("Len: %s" % self.refA.historyCount())

    def testBarge(self):
        # Barging happens when one transaction tries to do an in-transaction-write to a ref that has
        # an in-transaction-value from another transaction
        t1wait = Event()
        t2wait = Event()

        self.t1counter = 0
        self.t2counter = 0
        
        def t1():
            # We do the first in-transaction-write
            self.refA.refSet(888)

            # Don't commit yet, we want t2 to run and barge us
            self.d("* Notify")
            t2wait.set()
            self.d("* Wait")
            t1wait.wait()
            self.d("* Done")

            self.t1counter += 1

        def t2():
            # Wait till t1 has done its write
            self.d("** Wait")
            t2wait.wait()

            # Try to write to the ref
            # We should try and succeed to barge them: we were started first
            # and should be long-lived enough
            self.d("** Before barge")
            self.refA.refSet(777)

            self.d("** After barge")
            sleep(.1)
            t1wait.set()

            self.t2counter += 1
            
        self.refA = Ref(999, None)
        th1 = self.runTransactionInThread(t1, autostart=False)
        th2 = self.runTransactionInThread(t2, autostart=False)

        # Start thread 1 first, give the GIL a cycle so it waits for t2wait
        th2.start()
        sleep(.1)
        th1.start()

        # Wait for the test to finish
        self.join_all()

        # T2 should have successfully barged T1, so T1 should have been re-run
        # The final value of the ref should be 888, as T1 ran last
        self.assertEqual(self.t1counter, 2)
        self.assertEqual(self.t2counter, 1)
        self.assertEqual(self.refA.deref(), 888)

        self.d("Final value of ref: %s and t1 ran %s times" % (self.refA.deref(), self.t1counter))

    def testCommutes(self):
        # Make sure multiple transactions that occur simultaneously each commute the same ref
        # Hard to check for this behaving properly---it should have fewer retries than if each 
        # transaction did an alter, but if transactions commit at the same time one might have to retry
        # anyway. The difference is usually an order of magnitude, so this test is pretty safe

        self.numruns = AtomicInteger()
        self.numalterruns = AtomicInteger()
        numthreads = 20

        def incr(curval):
            return curval + 1

        def adder(curval, extraval):
            return curval + extraval

        def t1():
            self.numruns.getAndIncrement()
            self.refA.commute(incr, [])

        def t2():
            # self.d("Thread %s (%s): ALTER BEING RUN, total retry num: %s" % (current_thread().ident, id(current_thread()), self.numalterruns.getAndIncrement()))
            self.numalterruns.getAndIncrement()
            self.refB.alter(adder, [100])

        self.refA = Ref(0, None)
        self.refB = Ref(0, None)
        for i in range(numthreads):
            self.runTransactionInThread(t1)

        self.join_all()

        for i in range(numthreads):
            self.runTransactionInThread(t2)

        self.join_all()

        self.d("Commute took %s runs and counter is %s" % (self.numruns.get(), self.refA.deref()))
        self.d("Alter took %s runs and counter is %s" % (self.numalterruns.get(), self.refB.deref()))

        self.assertEqual(self.refA.deref(), numthreads)
        self.assertEqual(self.refB.deref(), 2000)
        self.assertTrue(self.numalterruns.get() >= self.numruns.get())
Example #3
0
                if x in m:
                    raise InvalidArgumentException("Duplicate key")
                m.impl = m.impl.assoc(x, args[0][x])
            return m
        if fulfillsIndexable(args[0]):
            args = args[0]
    m = EMPTY
    for x in range(0, len(args), 2):
        key = args[x]
        value = args[x + 1]
        m.impl = m.impl.assoc(key, value)
    return m


# need id for print protocol
_id = AtomicInteger()


def nextID():
    return _id.getAndIncrement()


def subvec(v, start, end):
    from clojure.lang.persistentvector import EMPTY as EMPTY_VECTOR
    from clojure.lang.apersistentvector import SubVec
    if end < start or start < 0 or end > len(v):
        raise Exception("Index out of range")
    if start == end:
        return EMPTY_VECTOR
    return SubVec(None, v, start, end)