Esempio n. 1
0
        public void DegeneratingCommuteTest()
        {
            var a = new Shielded <int>();
            int numInc = 100000;
            int transactionCount = 0, commuteCount = 0;

            ParallelEnumerable.Repeat(1, numInc / 2).ForAll(i => Shield.InTransaction(() => {
                Interlocked.Increment(ref transactionCount);
                a.Commute((ref int n) => {
                    Interlocked.Increment(ref commuteCount);
                    n++;
                });
                a.Commute((ref int n) => {
                    Interlocked.Increment(ref commuteCount);
                    n++;
                });
                // so, we cause it to degenerate. there was a subtle bug in enlisting which
                // would allow a degenerated commute to execute before checking the lock!
                int x = a;
            }));
            Assert.AreEqual(numInc, a);
            // degenerated commutes conflict, which means transaction will repeat. conflict
            // may be detected before the commute lambda actually gets to execute, so the
            // trans count can be greater than commute count.
            Assert.GreaterOrEqual(transactionCount, commuteCount / 2);
            if (commuteCount == numInc)
            {
                Assert.Inconclusive();
            }
        }
Esempio n. 2
0
        public void SideEffectsInCommutes()
        {
            // due to running in an isolated context, side-effects from commutes get
            // separately tracked, but must eventually execute just like any other
            // SideEffect. this basically tests a part of TransactionItems.UnionWith().
            var    x        = new Shielded <int>();
            bool   didItRun = false;
            Thread t        = null;

            Shield.InTransaction(() =>
                                 x.Commute((ref int _) => {
                OneTimeConflict(ref t, x);
                Shield.SideEffect(() => {
                    Assert.IsFalse(didItRun);
                    didItRun = true;
                });
            }));
            Assert.IsTrue(didItRun);

            didItRun = false;
            t        = null;
            Shield.InTransaction(() =>
                                 x.Commute((ref int _) => {
                OneTimeConflict(ref t, x);
                Shield.SyncSideEffect(() => {
                    Assert.IsFalse(didItRun);
                    didItRun = true;
                });
            }));
            Assert.IsTrue(didItRun);
        }
Esempio n. 3
0
        public void CommuteInACommute()
        {
            var a = new Shielded <int>();
            var b = new Shielded <int>();

            Assert.Throws <InvalidOperationException>(() =>
                                                      // there is only one _blockEnlist, and if it is "reused" by an inner call, it
                                                      // would get set to null when the inner commute ends. this would allow later code
                                                      // in the outer commute to violate the access restriction.
                                                      Shield.InTransaction(() =>
                                                                           a.Commute((ref int aRef) => {
                a.Commute((ref int aRef2) => aRef2++);
                b.Value = 1;
            })));
        }
Esempio n. 4
0
        public void CommuteInACommute()
        {
            var a = new Shielded<int>();
            var b = new Shielded<int>();

            Assert.Throws<InvalidOperationException>(() =>
                // there is only one _blockEnlist, and if it is "reused" by an inner call, it
                // would get set to null when the inner commute ends. this would allow later code
                // in the outer commute to violate the access restriction.
                Shield.InTransaction(() =>
                    a.Commute((ref int aRef) => {
                        a.Commute((ref int aRef2) => aRef2++);
                        b.Value = 1;
                    })));
        }
Esempio n. 5
0
        public void SyncSideEffectReadsCommute()
        {
            // in a SyncSideEffect you can read from a commuted field, but your ReadStamp has been set
            // back to its original value, which is possibly lower than that field's last written
            // value from some other, concurrent transaction. fields used to check for this, and roll back
            // the transaction after it was already checked!
            var a = new Shielded <int>();

            int lastReadValue = 0;

            Shield.InTransaction(() => {
                a.Commute((ref int x) => x++);

                var otherThread = new Thread(() => Shield.InTransaction(() =>
                {
                    a.Value = 10;
                }));
                otherThread.Start();
                otherThread.Join();

                Shield.SideEffect(null, () => Assert.Fail("Invalid rollback occurred."));
                Shield.SyncSideEffect(() =>
                {
                    // here, the field's last written value has version 1, and our ReadStamp is 0.
                    // however, the commute subtransaction had ReadStamp 1, and it checked out, so,
                    // this should not be a problem.
                    lastReadValue = a.Value;
                });
            });
            Assert.AreEqual(11, lastReadValue);
        }
Esempio n. 6
0
        public void EventTest()
        {
            var a          = new Shielded <int>(1);
            var eventCount = new Shielded <int>();
            EventHandler <EventArgs> ev =
                (sender, arg) => eventCount.Commute((ref int e) => e++);

            Assert.Throws <InvalidOperationException>(() =>
                                                      a.Changed.Subscribe(ev));

            Shield.InTransaction(() =>
            {
                a.Changed.Subscribe(ev);

                var t = new Thread(() =>
                                   Shield.InTransaction(() =>
                                                        a.Modify((ref int x) => x++)));
                t.Start();
                t.Join();

                var t2 = new Thread(() =>
                                    Shield.InTransaction(() =>
                                                         a.Modify((ref int x) => x++)));
                t2.Start();
                t2.Join();
            });
            Assert.AreEqual(0, eventCount);

            Shield.InTransaction(() => {
                a.Modify((ref int x) => x++);
            });
            Assert.AreEqual(1, eventCount);

            Thread tUnsub = null;

            Shield.InTransaction(() => {
                a.Changed.Unsubscribe(ev);
                a.Modify((ref int x) => x++);

                if (tUnsub == null)
                {
                    tUnsub = new Thread(() =>
                    {
                        Shield.InTransaction(() => {
                            a.Modify((ref int x) => x++);
                            a.Modify((ref int x) => x++);
                        });
                    });
                    tUnsub.Start();
                    tUnsub.Join();
                }
            });
            // the other thread must still see the subscription...
            Assert.AreEqual(3, eventCount);

            Shield.InTransaction(() => a.Modify((ref int x) => x++));
            Assert.AreEqual(3, eventCount);
        }
Esempio n. 7
0
        public void PreCommitFirstCommute()
        {
            var a = new Shielded <int>();
            var b = new Shielded <int>();

            using (Shield.PreCommit(() => a == 0 || true, () => b.Commute((ref int n) => n++)))
                Shield.InTransaction(() => a.Value = 1);
            Assert.AreEqual(1, a);
            Assert.AreEqual(1, b);
        }
Esempio n. 8
0
        public void CommuteInvariantProblem()
        {
            // since pre-commits have no limitations on access, they cannot safely
            // execute within the commute sub-transaction. if they would, then this
            // test would fail on the last assertion. instead, pre-commits cause commutes
            // to degenerate.

            var testField        = new Shielded <int>();
            var effectField      = new Shielded <int>();
            var failCommitCount  = new Shielded <int>();
            var failVisibleCount = 0;

            // check if the effect field was written to, that the testField is even.
            Shield.PreCommit(() => effectField > 0, () => {
                if ((testField & 1) == 1)
                {
                    Interlocked.Increment(ref failVisibleCount);
                    // this will always fail to commit, confirming that the transaction
                    // is already bound to fail. but, the failVisibleCount might be >0.
                    failCommitCount.Modify((ref int n) => n++);
                }
            });

            var thread = new Thread(() => {
                // if the testField is even, increment the effectField commutatively.
                foreach (int i in Enumerable.Range(1, 1000))
                {
                    Shield.InTransaction(() => {
                        if ((testField & 1) == 0)
                        {
                            effectField.Commute((ref int n) => n++);
                        }
                    });
                }
            });

            thread.Start();

            foreach (int i in Enumerable.Range(1, 1000))
            {
                Shield.InTransaction(() => {
                    testField.Modify((ref int n) => n++);
                });
            }
            thread.Join();

            Assert.AreEqual(0, failCommitCount);
            Assert.AreEqual(0, failVisibleCount);
        }
Esempio n. 9
0
        public void CommuteInvariantProblem()
        {
            // since pre-commits have no limitations on access, they cannot safely
            // execute within the commute sub-transaction. if they would, then this
            // test would fail on the last assertion. instead, pre-commits cause commutes
            // to degenerate.

            var testField = new Shielded<int>();
            var effectField = new Shielded<int>();
            var failCommitCount = new Shielded<int>();
            var failVisibleCount = 0;

            // check if the effect field was written to, that the testField is even.
            Shield.PreCommit(() => effectField > 0, () => {
                if ((testField & 1) == 1)
                {
                    Interlocked.Increment(ref failVisibleCount);
                    // this will always fail to commit, confirming that the transaction
                    // is already bound to fail. but, the failVisibleCount will be >0.
                    failCommitCount.Modify((ref int n) => n++);
                }
            });

            var thread = new Thread(() => {
                // if the testField is even, increment the effectField commutatively.
                foreach (int i in Enumerable.Range(1, 1000))
                    Shield.InTransaction(() => {
                        if ((testField & 1) == 0)
                        {
                            effectField.Commute((ref int n) => n++);
                        }
                    });
            });
            thread.Start();

            foreach (int i in Enumerable.Range(1, 1000))
                Shield.InTransaction(() => {
                    testField.Modify((ref int n) => n++);
                });
            thread.Join();

            Assert.AreEqual(0, failCommitCount);
            Assert.AreEqual(0, failVisibleCount);
        }
Esempio n. 10
0
        public void BasicCommuteTest()
        {
            var a = new Shielded <int>();

            Shield.InTransaction(() => a.Commute((ref int n) => n++));
            Assert.AreEqual(1, a);

            Shield.InTransaction(() =>
            {
                Assert.AreEqual(1, a);
                a.Commute((ref int n) => n++);
                Assert.AreEqual(2, a);
            });
            Assert.AreEqual(2, a);

            Shield.InTransaction(() =>
            {
                a.Commute((ref int n) => n++);
                Assert.AreEqual(3, a);
            });
            Assert.AreEqual(3, a);

            int transactionCount = 0, commuteCount = 0;

            ParallelEnumerable.Repeat(1, 100).ForAll(i => Shield.InTransaction(() => {
                Interlocked.Increment(ref transactionCount);
                a.Commute((ref int n) => {
                    Interlocked.Increment(ref commuteCount);
                    Thread.Sleep(10); // needs this.. (running on Mono 2.10)
                    n++;
                });
            }));
            Assert.AreEqual(103, a);
            // commutes never conflict (!)
            Assert.AreEqual(100, transactionCount);
            Assert.Greater(commuteCount, 100);

            Shield.InTransaction(() => {
                a.Commute((ref int n) => n -= 3);
                a.Commute((ref int n) => n--);
            });
            Assert.AreEqual(99, a);
        }
Esempio n. 11
0
        public void CommuteTest()
        {
            var a = new Shielded<int>();

            Shield.InTransaction(() => a.Commute((ref int n) => n++));
            Assert.AreEqual(1, a);

            Shield.InTransaction(() =>
            {
                Assert.AreEqual(1, a);
                a.Commute((ref int n) => n++);
                Assert.AreEqual(2, a);
            });
            Assert.AreEqual(2, a);

            Shield.InTransaction(() =>
            {
                a.Commute((ref int n) => n++);
                Assert.AreEqual(3, a);
            });
            Assert.AreEqual(3, a);

            int transactionCount = 0, commuteCount = 0;
            ParallelEnumerable.Repeat(1, 100).ForAll(i => Shield.InTransaction(() => {
                Interlocked.Increment(ref transactionCount);
                a.Commute((ref int n) => {
                    Interlocked.Increment(ref commuteCount);
                    Thread.Sleep(10); // needs this.. (running on Mono 2.10)
                    n++;
                });
            }));
            Assert.AreEqual(103, a);
            // commutes never conflict (!)
            Assert.AreEqual(100, transactionCount);
            Assert.Greater(commuteCount, 100);

            Shield.InTransaction(() => {
                a.Commute((ref int n) => n -= 3);
                a.Commute((ref int n) => n--);
            });
            Assert.AreEqual(99, a);
        }
Esempio n. 12
0
        static void SimpleOps()
        {
            long time;
            _timer = Stopwatch.StartNew();
            var numItems = 1000000;
            var repeatsPerTrans = 50;

            Console.WriteLine(
                "Testing simple ops with {0} iterations, and repeats per trans (N) = {1}",
                numItems, repeatsPerTrans);

            var accessTest = new Shielded<int>();

            Timed("WARM UP", numItems, () => {
                foreach (var k in Enumerable.Repeat(1, numItems))
                    Shield.InTransaction(() => {
                        accessTest.Value = 3;
                        var a = accessTest.Value;
                        accessTest.Modify((ref int n) => n = 5);
                        a = accessTest.Value;
                    });
            });

            var emptyTime = Timed("empty transactions", numItems, () => {
                foreach (var k in Enumerable.Repeat(1, numItems))
                    Shield.InTransaction(() => { });
            });

            var emptyReturningTime = Timed("1 empty transaction w/ result", numItems, () => {
                // this version uses the generic, result-returning InTransaction, which involves creation
                // of a closure, i.e. an allocation.
                foreach (var k in Enumerable.Repeat(1, numItems))
                    Shield.InTransaction(() => 5);
            });

            var outOfTrReadTime = Timed("N out-of-tr. reads", numItems, () => {
                // this version uses the generic, result-returning InTransaction, which involves creation
                // of a closure, i.e. an allocation.
                foreach (var k in Enumerable.Repeat(1, numItems * repeatsPerTrans))
                {
                    var a = accessTest.Value;
                }
            });

            // the purpose here is to get a better picture of the expense of using Shielded. a more
            // complex project would probably, during one transaction, repeatedly access the same
            // field. does this cost much more than a single-access transaction? if it is the same
            // field, then any significant extra expense is unacceptable.

            var oneReadTime = Timed("1-read transactions", numItems, () => {
                foreach (var k in Enumerable.Repeat(1, numItems))
                    Shield.InTransaction(() => {
                        var a = accessTest.Value;
                    });
            });

            var nReadTime = Timed("N-reads transactions", numItems, () => {
                foreach (var k in Enumerable.Repeat(1, numItems))
                    Shield.InTransaction(() => {
                        int a;
                        for (int i = 0; i < repeatsPerTrans; i++)
                            a = accessTest.Value;
                    });
            });

            var oneReadModifyTime = Timed("1-read-1-modify tr.", numItems, () => {
                foreach (var k in Enumerable.Repeat(1, numItems))
                    Shield.InTransaction(() => {
                        var a = accessTest.Value;
                        accessTest.Modify((ref int n) => n = 1);
                    });
            });

            // Assign is no longer commutable, for performance reasons. It is faster,
            // particularly when repeated (almost 10 times), and you can see the difference
            // that not reading the old value does.
            var oneReadAssignTime = Timed("1-read-1-assign tr.", numItems, () => {
                foreach (var k in Enumerable.Repeat(1, numItems))
                    Shield.InTransaction(() => {
                        var a = accessTest.Value;
                        accessTest.Value = 1;
                    });
            });

            var oneModifyTime = Timed("1-modify transactions", numItems, () => {
                foreach (var k in Enumerable.Repeat(1, numItems))
                    Shield.InTransaction(() => accessTest.Modify((ref int n) => n = 1));
            });

            var nModifyTime = Timed("N-modify transactions", numItems, () => {
                foreach (var k in Enumerable.Repeat(1, numItems))
                    Shield.InTransaction(() => {
                        for (int i = 0; i < repeatsPerTrans; i++)
                            accessTest.Modify((ref int n) => n = 1);
                    });
            });

            var accessTest2 = new Shielded<int>();
            var modifyModifyTime = Timed("modify-modify transactions", numItems, () => {
                foreach (var k in Enumerable.Repeat(1, numItems))
                    Shield.InTransaction(() => {
                        accessTest.Modify((ref int n) => n = 1);
                        accessTest2.Modify((ref int n) => n = 2);
                    });
            });

            // here Modify is the first call, making all Reads as fast as can be,
            // reading direct from local storage.
            var oneModifyNReadTime = Timed("1-modify-N-reads tr.", numItems, () => {
                foreach (var k in Enumerable.Repeat(1, numItems))
                    Shield.InTransaction(() => {
                        accessTest.Modify((ref int n) => n = 1);
                        int a;
                        for (int i = 0; i < repeatsPerTrans; i++)
                            a = accessTest.Value;
                    });
            });

            var oneAssignTime = Timed("1-assign transactions", numItems, () => {
                foreach (var k in Enumerable.Repeat(1, numItems))
                    Shield.InTransaction(() => accessTest.Value = 1);
            });

            var nAssignTime = Timed("N-assigns transactions", numItems, () => {
                foreach (var k in Enumerable.Repeat(1, numItems))
                    Shield.InTransaction(() => {
                        for (int i = 0; i < repeatsPerTrans; i++)
                            accessTest.Value = 1;
                    });
            });

            var oneCommuteTime = Timed("1-commute transactions", numItems, () => {
                foreach (var k in Enumerable.Repeat(1, numItems))
                    Shield.InTransaction(() => accessTest.Commute((ref int n) => n = 1));
            });

            var nCommuteTime = Timed("N-commute transactions", numItems, () => {
                foreach (var k in Enumerable.Repeat(1, numItems/10))
                    Shield.InTransaction(() => {
                        for (int i = 0; i < repeatsPerTrans; i++)
                            accessTest.Commute((ref int n) => n = 1);
                    });
            });

            Console.WriteLine("\ncost of empty transaction = {0:0.000} us", emptyTime / (numItems / 1000.0));
            Console.WriteLine("cost of the closure in InTransaction<T> = {0:0.000} us",
                              (emptyReturningTime - emptyTime) / (numItems / 1000.0));
            Console.WriteLine("cost of an out-of-tr. read = {0:0.000} us",
                              outOfTrReadTime * 1000.0 / (numItems * repeatsPerTrans));
            Console.WriteLine("cost of the first read = {0:0.000} us",
                              (oneReadTime - emptyTime) / (numItems / 1000.0));
            Console.WriteLine("cost of an additional read = {0:0.000} us",
                              (nReadTime - oneReadTime) / ((repeatsPerTrans - 1) * numItems / 1000.0));
            Console.WriteLine("cost of Modify after read = {0:0.000} us",
                              (oneReadModifyTime - oneReadTime) / (numItems / 1000.0));
            Console.WriteLine("cost of Assign after read = {0:0.000} us",
                              (oneReadAssignTime - oneReadTime) / (numItems / 1000.0));
            Console.WriteLine("cost of the first Modify = {0:0.000} us",
                              (oneModifyTime - emptyTime) / (numItems / 1000.0));
            Console.WriteLine("cost of an additional Modify = {0:0.000} us",
                              (nModifyTime - oneModifyTime) / ((repeatsPerTrans - 1) * numItems / 1000.0));
            Console.WriteLine("cost of a second, different Modify = {0:0.000} us",
                              (modifyModifyTime - oneModifyTime) / (numItems / 1000.0));
            Console.WriteLine("cost of a read after Modify = {0:0.000} us",
                              (oneModifyNReadTime - oneModifyTime) / (repeatsPerTrans * numItems / 1000.0));
            Console.WriteLine("cost of the first Assign = {0:0.000} us",
                              (oneAssignTime - emptyTime) / (numItems / 1000.0));
            Console.WriteLine("cost of an additional Assign = {0:0.000} us",
                              (nAssignTime - oneAssignTime) / ((repeatsPerTrans - 1) * numItems / 1000.0));
            Console.WriteLine("cost of the first commute = {0:0.000} us",
                              (oneCommuteTime - emptyTime) / (numItems / 1000.0));
            Console.WriteLine("cost of an additional commute = {0:0.000} us",
                              (nCommuteTime*10 - oneCommuteTime) / ((repeatsPerTrans - 1) * numItems / 1000.0));
        }
Esempio n. 13
0
        public void ComplexCommute()
        {
            // some more complex commute combinations. first, with ShieldedSeq ops.
            var seq = new ShieldedSeq <int>();

            // just a test for proper commute ordering
            Shield.InTransaction(() => {
                seq.Append(1);
                seq.Append(2);
                seq.Append(3);
                seq.Remove(2);
                Assert.IsTrue(seq.Any());
                Assert.AreEqual(2, seq.Count);
                Assert.AreEqual(1, seq[0]);
                Assert.AreEqual(3, seq[1]);
            });

            // a test for commutability of Append()
            Shield.InTransaction(() => { seq.Clear(); });
            int    transactionCount = 0;
            Thread oneTimer         = null;

            Shield.InTransaction(() => {
                transactionCount++;
                seq.Append(1);
                if (oneTimer == null)
                {
                    oneTimer = new Thread(() => Shield.InTransaction(() =>
                    {
                        seq.Append(2);
                    }));
                    oneTimer.Start();
                    oneTimer.Join();
                }
            });
            Assert.AreEqual(1, transactionCount);
            Assert.AreEqual(2, seq.Count);
            // the "subthread" commited the append first, so:
            Assert.AreEqual(2, seq[0]);
            Assert.AreEqual(1, seq[1]);
            Assert.IsTrue(seq.Any());

            // test for a commute degeneration - reading the head of a list causes
            // appends done in the same transaction to stop being commutable. for
            // simplicity - you could continue from the head on to the tail, and it cannot
            // thus be a commute, you read it. it could, of course, be done that it still
            // is a commute, but that would make it pretty complicated.
            Shield.InTransaction(() => { seq.Clear(); seq.Append(1); seq.Append(2); });
            Assert.AreEqual(1, seq[0]);
            Assert.AreEqual(2, seq[1]);
            transactionCount = 0;
            oneTimer         = null;
            Shield.InTransaction(() => {
                transactionCount++;
                Assert.AreEqual(1, seq.TakeHead());
                seq.Append(4);
                if (oneTimer == null)
                {
                    oneTimer = new Thread(() => Shield.InTransaction(() =>
                    {
                        seq.Append(3);
                    }));
                    oneTimer.Start();
                    oneTimer.Join();
                }
            });
            Assert.AreEqual(2, transactionCount);
            Assert.AreEqual(3, seq.Count);
            Assert.IsTrue(seq.Any());
            Assert.AreEqual(2, seq[0]);
            Assert.AreEqual(3, seq[1]);
            Assert.AreEqual(4, seq[2]);

            Shield.InTransaction(() => { seq.Clear(); seq.Append(1); seq.Append(2); });
            Assert.AreEqual(1, seq[0]);
            Assert.AreEqual(2, seq[1]);
            transactionCount = 0;
            oneTimer         = null;
            Shield.InTransaction(() => {
                // if we switch the order, doesn't matter.
                transactionCount++;
                seq.Append(4);
                Assert.AreEqual(1, seq.TakeHead());
                if (oneTimer == null)
                {
                    oneTimer = new Thread(() => Shield.InTransaction(() =>
                    {
                        seq.Append(3);
                    }));
                    oneTimer.Start();
                    oneTimer.Join();
                }
            });
            Assert.AreEqual(2, transactionCount);
            Assert.AreEqual(3, seq.Count);
            Assert.IsTrue(seq.Any());
            Assert.AreEqual(2, seq[0]);
            Assert.AreEqual(3, seq[1]);
            Assert.AreEqual(4, seq[2]);

            // here the removal takes out the last element in the list. this absolutely cannot
            // commute, because it read from the only element's Next field, and the Seq
            // knew that it was the last element. it must conflict.
            Shield.InTransaction(() => { seq.Clear(); seq.Append(1); });
            Assert.AreEqual(1, seq[0]);
            transactionCount = 0;
            oneTimer         = null;
            Shield.InTransaction(() => {
                transactionCount++;
                Assert.AreEqual(1, seq.TakeHead());
                seq.Append(3);
                if (oneTimer == null)
                {
                    oneTimer = new Thread(() => Shield.InTransaction(() =>
                    {
                        seq.Append(2);
                    }));
                    oneTimer.Start();
                    oneTimer.Join();
                }
            });
            Assert.AreEqual(2, transactionCount);
            Assert.AreEqual(2, seq.Count);
            Assert.IsTrue(seq.Any());
            Assert.AreEqual(2, seq[0]);
            Assert.AreEqual(3, seq[1]);


            // it is not allowed to read another Shielded from a Shielded.Commute()!
            // this greatly simplifies things. if you still want to use a value from another
            // Shielded, you must read it in main trans, forcing it's commutes to degenerate.

            var a = new Shielded <int>();
            var b = new Shielded <int>();

            Assert.Throws <InvalidOperationException>(() =>
                                                      Shield.InTransaction(() => {
                a.Commute((ref int n) => n = 1);
                b.Commute((ref int n) => n = a);
            }));

            Assert.Throws <InvalidOperationException>(() =>
                                                      Shield.InTransaction(() => {
                a.Commute((ref int n) => n = 1);
                b.Commute((ref int n) => {
                    n = 1;
                    a.Commute((ref int n2) => n2 = 2);
                });
            }));

            Shield.InTransaction(() => {
                a.Commute((ref int n) => n = 1);
                b.Commute((ref int n) => n = a);
                Assert.Throws <InvalidOperationException>(() => {
                    var x = b.Value;
                });
            });
        }
Esempio n. 14
0
 public void DegeneratingCommuteTest()
 {
     var a = new Shielded<int>();
     int numInc = 100000;
     int transactionCount = 0, commuteCount = 0;
     ParallelEnumerable.Repeat(1, numInc/2).ForAll(i => Shield.InTransaction(() => {
         Interlocked.Increment(ref transactionCount);
         a.Commute((ref int n) => {
             Interlocked.Increment(ref commuteCount);
             n++;
         });
         a.Commute((ref int n) => {
             Interlocked.Increment(ref commuteCount);
             n++;
         });
         // so, we cause it to degenerate. there was a subtle bug in enlisting which
         // would allow a degenerated commute to execute before checking the lock!
         int x = a;
     }));
     Assert.AreEqual(numInc, a);
     // degenerated commutes conflict, which means transaction will repeat. conflict
     // may be detected before the commute lambda actually gets to execute, so the
     // trans count can be greater than commute count.
     Assert.GreaterOrEqual(transactionCount, commuteCount/2);
     // classic, just to confirm there was at least one conflict.
     Assert.Greater(commuteCount, numInc);
 }
Esempio n. 15
0
        public static void SimpleCommuteTest()
        {
            var a = new Shielded<int>();

            Shield.InTransaction(() => a.Commute((ref int n) => n++));
            Console.WriteLine(a);

            Shield.InTransaction(() =>
            {
                Console.WriteLine(a);
                a.Commute((ref int n) => n++);
                Console.WriteLine(a);
            });
            Console.WriteLine(a);

            Shield.InTransaction(() =>
            {
                a.Commute((ref int n) => n++);
                Console.WriteLine(a);
            });
            Console.WriteLine(a);
        }
Esempio n. 16
0
        public void ComplexCommute()
        {
            // some more complex commute combinations. first, with ShieldedSeq ops.
            var seq = new ShieldedSeq<int>();

            Shield.InTransaction(() => {
                // test for potential disorder of the seq commutes.
                seq.Append(1);
                seq.Clear();
                Assert.IsFalse(seq.HasAny);
                Assert.AreEqual(0, seq.Count);
            });

            Shield.InTransaction(() => {
                seq.Append(1);
                seq.Append(2);
                seq.Append(3);
                seq.Remove(2);
                Assert.IsTrue(seq.HasAny);
                Assert.AreEqual(2, seq.Count);
                Assert.AreEqual(1, seq[0]);
                Assert.AreEqual(3, seq[1]);
                seq.Clear();
                Assert.IsFalse(seq.HasAny);
                Assert.AreEqual(0, seq.Count);
            });

            Shield.InTransaction(() => { seq.Append(1); seq.Append(2); });
            Assert.AreEqual(1, seq[0]);
            Assert.AreEqual(2, seq[1]);
            int transactionCount = 0;
            Thread oneTimer = null;
            Shield.InTransaction(() => {
                // here's a weird one - the seq is only partially commuted, due to reading
                // from the head, but it still commutes with a trans that is only appending.
                transactionCount++;
                Assert.AreEqual(1, seq.TakeHead());
                // Count or tail were not read! Clearing can commute with appending.
                seq.Clear();
                if (oneTimer == null)
                {
                    oneTimer = new Thread(() => Shield.InTransaction(() =>
                    {
                        seq.Append(3);
                    }));
                    oneTimer.Start();
                    oneTimer.Join();
                }
            });
            Assert.AreEqual(1, transactionCount);
            Assert.AreEqual(0, seq.Count);
            Assert.IsFalse(seq.HasAny);

            Shield.InTransaction(() => { seq.Append(1); seq.Append(2); });
            Assert.AreEqual(1, seq[0]);
            Assert.AreEqual(2, seq[1]);
            transactionCount = 0;
            oneTimer = null;
            Shield.InTransaction(() => {
                // same as above, but with appending, does not work. reading the _head screws it up,
                // because you could continue from the head to the last item.
                transactionCount++;
                Assert.AreEqual(1, seq.TakeHead());
                seq.Append(4);
                if (oneTimer == null)
                {
                    oneTimer = new Thread(() => Shield.InTransaction(() =>
                    {
                        seq.Append(3);
                    }));
                    oneTimer.Start();
                    oneTimer.Join();
                }
            });
            Assert.AreEqual(2, transactionCount);
            Assert.AreEqual(3, seq.Count);
            Assert.IsTrue(seq.HasAny);
            Assert.AreEqual(2, seq[0]);
            Assert.AreEqual(3, seq[1]);
            Assert.AreEqual(4, seq[2]);

            Shield.InTransaction(() => { seq.Clear(); seq.Append(1); seq.Append(2); });
            Assert.AreEqual(1, seq[0]);
            Assert.AreEqual(2, seq[1]);
            transactionCount = 0;
            oneTimer = null;
            Shield.InTransaction(() => {
                // if we switch the order, doesn't matter.
                transactionCount++;
                seq.Append(4);
                Assert.AreEqual(1, seq.TakeHead());
                if (oneTimer == null)
                {
                    oneTimer = new Thread(() => Shield.InTransaction(() =>
                    {
                        seq.Append(3);
                    }));
                    oneTimer.Start();
                    oneTimer.Join();
                }
            });
            Assert.AreEqual(2, transactionCount);
            Assert.AreEqual(3, seq.Count);
            Assert.IsTrue(seq.HasAny);
            Assert.AreEqual(2, seq[0]);
            Assert.AreEqual(3, seq[1]);
            Assert.AreEqual(4, seq[2]);

            Shield.InTransaction(() => { seq.Clear(); seq.Append(1); });
            Assert.AreEqual(1, seq[0]);
            transactionCount = 0;
            oneTimer = null;
            Shield.InTransaction(() => {
                // here the removal takes out the last element in the list. this cannot
                // commute, because it read from the only element's Next field, and the Seq
                // knew that it was the last element. it must conflict.
                transactionCount++;
                Assert.AreEqual(1, seq.TakeHead());
                seq.Append(3);
                if (oneTimer == null)
                {
                    oneTimer = new Thread(() => Shield.InTransaction(() =>
                    {
                        seq.Append(2);
                    }));
                    oneTimer.Start();
                    oneTimer.Join();
                }
            });
            Assert.AreEqual(2, transactionCount);
            Assert.AreEqual(2, seq.Count);
            Assert.IsTrue(seq.HasAny);
            Assert.AreEqual(2, seq[0]);
            Assert.AreEqual(3, seq[1]);

            // it is not allowed to read another Shielded from a Shielded.Commute()!
            // this greatly simplifies things. if you still want to use a value from another
            // Shielded, you must read it in main trans, forcing it's commutes to degenerate.

            var a = new Shielded<int>();
            var b = new Shielded<int>();
            try
            {
                Shield.InTransaction(() => {
                    a.Commute((ref int n) => n = 1);
                    b.Commute((ref int n) => n = a);
                });
                Assert.Fail();
            }
            catch (InvalidOperationException) {}

            try
            {
                Shield.InTransaction(() => {
                    a.Commute((ref int n) => n = 1);
                    b.Commute((ref int n) => { n = 1; a.Commute((ref int n2) => n2 = 2); });
                });
                Assert.Fail();
            }
            catch (InvalidOperationException) {}

            Shield.InTransaction(() => {
                a.Commute((ref int n) => n = 1);
                b.Commute((ref int n) => n = a);
                try
                {
                    var x = b.Read;
                    Assert.Fail();
                }
                catch (InvalidOperationException) {}
            });
        }
Esempio n. 17
0
        public void EventTest()
        {
            var a = new Shielded<int>(1);
            var eventCount = new Shielded<int>();
            EventHandler<EventArgs> ev =
                (sender, arg) => eventCount.Commute((ref int e) => e++);

            try
            {
                a.Changed.Subscribe(ev);
                Assert.Fail();
            }
            catch (InvalidOperationException) {}

            Shield.InTransaction(() =>
            {
                a.Changed.Subscribe(ev);

                var t = new Thread(() =>
                {
                    Shield.InTransaction(() => {
                        a.Modify((ref int x) => x++);
                    });
                });
                t.Start();
                t.Join();

                var t2 = new Thread(() =>
                {
                    Shield.InTransaction(() => {
                        a.Modify((ref int x) => x++);
                    });
                });
                t2.Start();
                t2.Join();
            });
            Assert.AreEqual(0, eventCount);

            Shield.InTransaction(() => {
                a.Modify((ref int x) => x++);
            });
            Assert.AreEqual(1, eventCount);

            Thread tUnsub = null;
            Shield.InTransaction(() => {
                a.Changed.Unsubscribe(ev);
                a.Modify((ref int x) => x++);

                if (tUnsub == null)
                {
                    tUnsub = new Thread(() =>
                    {
                        Shield.InTransaction(() => {
                            a.Modify((ref int x) => x++);
                            a.Modify((ref int x) => x++);
                        });
                    });
                    tUnsub.Start();
                    tUnsub.Join();
                }
            });
            // the other thread must still see the subscription...
            Assert.AreEqual(3, eventCount);

            Shield.InTransaction(() => a.Modify((ref int x) => x++));
            Assert.AreEqual(3, eventCount);
        }
Esempio n. 18
0
        public static void BetShopPoolTest()
        {
            int numThreads = 3;
            int numTickets = 200000;
            int numEvents = 100;
            var barrier = new Barrier(2);
            var betShop = new BetShop(numEvents);
            var randomizr = new Random();

            var bags = new List<Action>[numThreads];
            var threads = new Thread[numThreads];
            for (int i = 0; i < numThreads; i++)
            {
                var bag = bags[i] = new List<Action>();
                threads[i] = new Thread(() => {
                    foreach (var a in bag)
                        a();
                });
            }

            var complete = new Shielded<int>();
            IDisposable completeCond = null;
            completeCond = Shield.Conditional(() => complete == numTickets, () => {
                barrier.SignalAndWait();
                completeCond.Dispose();
            });

            var reportEvery = 10000;
            Shielded<int> lastReport = new Shielded<int>(0);
            Shielded<DateTime> lastTime = new Shielded<DateTime>(DateTime.UtcNow);
            using (Shield.Conditional(
                () => betShop.Tickets.Count >= lastReport + reportEvery,
                () => {
                    DateTime newNow = DateTime.UtcNow;
                    int count = betShop.Tickets.Count;
                    int speed = (count - lastReport) * 1000 / (int)newNow.Subtract(lastTime).TotalMilliseconds;
                    lastTime.Value = newNow;
                    lastReport.Modify((ref int n) => n += reportEvery);
                    Shield.SideEffect(() =>
                            Console.Write("\n{0} at {1} item/s", count, speed));
                }))
            {
                foreach (var i in Enumerable.Range(0, numTickets))
                {
                    decimal payIn = (randomizr.Next(10) + 1m) * 1;
                    int event1Id = randomizr.Next(numEvents) + 1;
                    int event2Id = randomizr.Next(numEvents) + 1;
                    int event3Id = randomizr.Next(numEvents) + 1;
                    int offer1Ind = randomizr.Next(3);
                    int offer2Ind = randomizr.Next(3);
                    int offer3Ind = randomizr.Next(3);
                    bags[i % numThreads].Add(() => Shield.InTransaction(() => {
                        var offer1 = betShop.Events[event1Id].BetOffers[offer1Ind];
                        var offer2 = betShop.Events[event2Id].BetOffers[offer2Ind];
                        var offer3 = betShop.Events[event3Id].BetOffers[offer3Ind];
                        betShop.BuyTicket(payIn, offer1, offer2, offer3);
                        complete.Commute((ref int n) => n++);
                    }));
                }
                _timer = Stopwatch.StartNew();
                for (int i = 0; i < numThreads; i++)
                    threads[i].Start();
                barrier.SignalAndWait();
            }

            var time = _timer.ElapsedMilliseconds;
            var totalCorrect = betShop.VerifyTickets();
            Console.WriteLine(" {0} ms with {1} tickets paid in and is {2}.",
                time, betShop.Tickets.Count, totalCorrect ? "correct" : "incorrect");
        }
Esempio n. 19
0
        public void ComplexCommute()
        {
            // some more complex commute combinations. first, with ShieldedSeq ops.
            var seq = new ShieldedSeq<int>();

            // just a test for proper commute ordering
            Shield.InTransaction(() => {
                seq.Append(1);
                seq.Append(2);
                seq.Append(3);
                seq.Remove(2);
                Assert.IsTrue(seq.Any());
                Assert.AreEqual(2, seq.Count);
                Assert.AreEqual(1, seq[0]);
                Assert.AreEqual(3, seq[1]);
            });

            // a test for commutability of Append()
            Shield.InTransaction(() => { seq.Clear(); });
            int transactionCount = 0;
            Thread oneTimer = null;
            Shield.InTransaction(() => {
                transactionCount++;
                seq.Append(1);
                if (oneTimer == null)
                {
                    oneTimer = new Thread(() => Shield.InTransaction(() =>
                        {
                            seq.Append(2);
                        }));
                    oneTimer.Start();
                    oneTimer.Join();
                }
            });
            Assert.AreEqual(1, transactionCount);
            Assert.AreEqual(2, seq.Count);
            // the "subthread" commited the append first, so:
            Assert.AreEqual(2, seq[0]);
            Assert.AreEqual(1, seq[1]);
            Assert.IsTrue(seq.Any());

            // test for a commute degeneration - reading the head of a list causes
            // appends done in the same transaction to stop being commutable. for
            // simplicity - you could continue from the head on to the tail, and it cannot
            // thus be a commute, you read it. it could, of course, be done that it still
            // is a commute, but that would make it pretty complicated.
            Shield.InTransaction(() => { seq.Clear(); seq.Append(1); seq.Append(2); });
            Assert.AreEqual(1, seq[0]);
            Assert.AreEqual(2, seq[1]);
            transactionCount = 0;
            oneTimer = null;
            Shield.InTransaction(() => {
                transactionCount++;
                Assert.AreEqual(1, seq.TakeHead());
                seq.Append(4);
                if (oneTimer == null)
                {
                    oneTimer = new Thread(() => Shield.InTransaction(() =>
                        {
                            seq.Append(3);
                        }));
                    oneTimer.Start();
                    oneTimer.Join();
                }
            });
            Assert.AreEqual(2, transactionCount);
            Assert.AreEqual(3, seq.Count);
            Assert.IsTrue(seq.Any());
            Assert.AreEqual(2, seq[0]);
            Assert.AreEqual(3, seq[1]);
            Assert.AreEqual(4, seq[2]);

            Shield.InTransaction(() => { seq.Clear(); seq.Append(1); seq.Append(2); });
            Assert.AreEqual(1, seq[0]);
            Assert.AreEqual(2, seq[1]);
            transactionCount = 0;
            oneTimer = null;
            Shield.InTransaction(() => {
                // if we switch the order, doesn't matter.
                transactionCount++;
                seq.Append(4);
                Assert.AreEqual(1, seq.TakeHead());
                if (oneTimer == null)
                {
                    oneTimer = new Thread(() => Shield.InTransaction(() =>
                        {
                            seq.Append(3);
                        }));
                    oneTimer.Start();
                    oneTimer.Join();
                }
            });
            Assert.AreEqual(2, transactionCount);
            Assert.AreEqual(3, seq.Count);
            Assert.IsTrue(seq.Any());
            Assert.AreEqual(2, seq[0]);
            Assert.AreEqual(3, seq[1]);
            Assert.AreEqual(4, seq[2]);

            // here the removal takes out the last element in the list. this absolutely cannot
            // commute, because it read from the only element's Next field, and the Seq
            // knew that it was the last element. it must conflict.
            Shield.InTransaction(() => { seq.Clear(); seq.Append(1); });
            Assert.AreEqual(1, seq[0]);
            transactionCount = 0;
            oneTimer = null;
            Shield.InTransaction(() => {
                transactionCount++;
                Assert.AreEqual(1, seq.TakeHead());
                seq.Append(3);
                if (oneTimer == null)
                {
                    oneTimer = new Thread(() => Shield.InTransaction(() =>
                        {
                            seq.Append(2);
                        }));
                    oneTimer.Start();
                    oneTimer.Join();
                }
            });
            Assert.AreEqual(2, transactionCount);
            Assert.AreEqual(2, seq.Count);
            Assert.IsTrue(seq.Any());
            Assert.AreEqual(2, seq[0]);
            Assert.AreEqual(3, seq[1]);

            // it is not allowed to read another Shielded from a Shielded.Commute()!
            // this greatly simplifies things. if you still want to use a value from another
            // Shielded, you must read it in main trans, forcing it's commutes to degenerate.

            var a = new Shielded<int>();
            var b = new Shielded<int>();
            Assert.Throws<InvalidOperationException>(() =>
                Shield.InTransaction(() => {
                    a.Commute((ref int n) => n = 1);
                    b.Commute((ref int n) => n = a);
                }));

            Assert.Throws<InvalidOperationException>(() =>
                Shield.InTransaction(() => {
                    a.Commute((ref int n) => n = 1);
                    b.Commute((ref int n) => {
                        n = 1;
                        a.Commute((ref int n2) => n2 = 2);
                    });
                }));

            Shield.InTransaction(() => {
                a.Commute((ref int n) => n = 1);
                b.Commute((ref int n) => n = a);
                Assert.Throws<InvalidOperationException>(() => {
                    var x = b.Value;
                });
            });
        }