Example #1
0
        private void CountTracking()
        {
            int reportEvery = 10000;
            Shielded<int> lastReport = new Shielded<int>(0);
            Shielded<DateTime> lastTime = new Shielded<DateTime>(DateTime.UtcNow);

            Shield.Conditional(() => _processed >= lastReport + reportEvery, () =>
            {
                DateTime newNow = DateTime.UtcNow;
                int count = _processed;
                int speed = (count - lastReport) * 1000 / (int)newNow.Subtract(lastTime).TotalMilliseconds;
                lastTime.Assign(newNow);
                lastReport.Modify((ref int n) => n += reportEvery);
                int sc = _subscribeCount;
                int ptc = _processTestCount;
                int pbc = _processBodyCount;
                Shield.SideEffect(() =>
                {
                    Console.WriteLine(
                        "{0} at {1} item/s, stats ( {2}, {3}, {4} )",
                        count, speed, sc, ptc, pbc);
                });
                return true;
            });
        }
Example #2
0
        public void ConditionalTest()
        {
            var x = new Shielded<int>();
            var testCounter = 0;
            var triggerCommits = 0;

            Shield.Conditional(() => {
                Interlocked.Increment(ref testCounter);
                return x > 0 && (x & 1) == 0;
            },
            () => {
                Shield.SideEffect(() =>
                    Interlocked.Increment(ref triggerCommits));
                Assert.IsTrue(x > 0 && (x & 1) == 0);
            });

            const int count = 1000;
            ParallelEnumerable.Repeat(1, count).ForAll(i =>
                Shield.InTransaction(() => x.Modify((ref int n) => n++)));

            // one more, for the first call to Conditional()! btw, if this conditional were to
            // write anywhere, he might conflict, and an interlocked counter would give more due to
            // repetitions. so, this confirms reader progress too.
            Assert.AreEqual(count + 1, testCounter);
            // every change triggers it, but by the time it starts, another transaction might have
            // committed, so this is not a fixed number.
            Assert.Greater(triggerCommits, 0);

            // a conditional which does not depend on any Shielded is not allowed!
            int a = 5;
            Assert.Throws<InvalidOperationException>(() =>
                Shield.Conditional(() => a > 10, () => { }));

            bool firstTime = true;
            var x2 = new Shielded<int>();
            // this one succeeds in registering, but fails as soon as it gets triggered, due to changing it's
            // test's access pattern to an empty set.
            Shield.Conditional(() => {
                if (firstTime)
                {
                    firstTime = false;
                    return x2 == 0;
                }
                else
                    // this is of course invalid, and when reaching here we have not touched any Shielded obj.
                    return true;
            }, () => { });

            try
            {
                // this will trigger the conditional
                Shield.InTransaction(() => x2.Modify((ref int n) => n++));
                Assert.Fail();
            }
            catch (AggregateException aggr)
            {
                Assert.AreEqual(1, aggr.InnerExceptions.Count);
                Assert.AreEqual(typeof(InvalidOperationException), aggr.InnerException.GetType());
            }
        }
Example #3
0
        public void SkewWriteTest()
        {
            var cats             = new Shielded <int>(1);
            var dogs             = new Shielded <int>(1);
            int transactionCount = 0;

            Task.WaitAll(
                Enumerable.Range(1, 2).Select(i => Task.Factory.StartNew(() =>
                                                                         Shield.InTransaction(() =>
            {
                Interlocked.Increment(ref transactionCount);
                if (cats + dogs < 3)
                {
                    Thread.Sleep(200);
                    if (i == 1)
                    {
                        cats.Modify((ref int n) => n++);
                    }
                    else
                    {
                        dogs.Modify((ref int n) => n++);
                    }
                }
            }), TaskCreationOptions.LongRunning)).ToArray());
            Assert.AreEqual(3, cats + dogs);
            Assert.AreEqual(3, transactionCount);
        }
Example #4
0
        public void SideEffectTest()
        {
            var x = new Shielded <DateTime>(DateTime.UtcNow);

            try
            {
                Shield.InTransaction(() =>
                {
                    Shield.SideEffect(() => {
                        Assert.Fail("Suicide transaction has committed.");
                    },
                                      () => {
                        throw new IgnoreMe();
                    });
                    // in case Assign() becomes commutative, we use Modify() to ensure conflict.
                    x.Modify((ref DateTime d) => d = DateTime.UtcNow);
                    var t = new Thread(() =>
                                       Shield.InTransaction(() =>
                                                            x.Modify((ref DateTime d) => d = DateTime.UtcNow)));
                    t.Start();
                    t.Join();
                });
                Assert.Fail("Suicide transaction did not throw.");
            }
            catch (AggregateException aggr)
            {
                Assert.AreEqual(1, aggr.InnerExceptions.Count);
                Assert.AreEqual(typeof(IgnoreMe), aggr.InnerException.GetType());
            }

            bool commitFx = false;

            Shield.InTransaction(() => {
                Shield.SideEffect(() => {
                    Assert.IsFalse(commitFx);
                    commitFx = true;
                });
            });
            Assert.IsTrue(commitFx);

            bool outOfTransFx = false, outOfTransOnRollback = false;

            Shield.SideEffect(() => outOfTransFx = true, () => outOfTransOnRollback = true);
            Assert.IsTrue(outOfTransFx);
            Assert.IsFalse(outOfTransOnRollback);
        }
Example #5
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);
        }
Example #6
0
        public void TransactionSafetyTest()
        {
            Shielded <int> a = new Shielded <int>(5);

            Assert.AreEqual(5, a);

            Assert.Throws <InvalidOperationException>(() =>
                                                      a.Modify((ref int n) => n = 10));

            Assert.IsFalse(Shield.IsInTransaction);
            Shield.InTransaction(() =>
            {
                a.Modify((ref int n) => n = 20);
                // the TPL sometimes executes tasks on the same thread.
                int x1 = 0;
                var t  = new Thread(() =>
                {
                    Assert.IsFalse(Shield.IsInTransaction);
                    x1 = a;
                });
                t.Start();
                t.Join();

                Assert.IsTrue(Shield.IsInTransaction);
                Assert.AreEqual(5, x1);
                Assert.AreEqual(20, a);
            });
            Assert.IsFalse(Shield.IsInTransaction);

            int x2 = 0;
            var t2 = new Thread(() =>
            {
                Assert.IsFalse(Shield.IsInTransaction);
                x2 = a;
            });

            t2.Start();
            t2.Join();
            Assert.AreEqual(20, x2);
            Assert.AreEqual(20, a);
        }
Example #7
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);
        }
Example #8
0
 public void BasicTest()
 {
     var a = new Shielded<int>(10);
     Shield.InTransaction(() => {
         a.Value = 20;
         Assert.AreEqual(20, a);
         Shield.ReadOldState(() => {
             Assert.AreEqual(10, a);
             a.Value = 30;
             Assert.AreEqual(10, a);
             a.Modify((ref int x) =>
                 Assert.AreEqual(30, x));
         });
         Assert.AreEqual(30, a.Value);
     });
     Assert.AreEqual(30, a.Value);
 }
Example #9
0
        public void BasicTest()
        {
            var a = new Shielded <int>(10);

            Shield.InTransaction(() => {
                a.Value = 20;
                Assert.AreEqual(20, a);
                Shield.ReadOldState(() => {
                    Assert.AreEqual(10, a);
                    a.Value = 30;
                    Assert.AreEqual(10, a);
                    a.Modify((ref int x) =>
                             Assert.AreEqual(30, x));
                });
                Assert.AreEqual(30, a.Value);
            });
            Assert.AreEqual(30, a.Value);
        }
Example #10
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);
        }
Example #11
0
        public void TransactionSafetyTest()
        {
            Shielded<int> a = new Shielded<int>(5);
            Assert.AreEqual(5, a);

            try
            {
                a.Modify((ref int n) => n = 10);
                Assert.Fail();
            }
            catch (InvalidOperationException) {}

            Assert.IsFalse(Shield.IsInTransaction);
            Shield.InTransaction(() =>
            {
                a.Modify((ref int n) => n = 20);
                // the TPL sometimes executes tasks on the same thread.
                int x1 = 0;
                var t = new Thread(() =>
                {
                    Assert.IsFalse(Shield.IsInTransaction);
                    x1 = a;
                });
                t.Start();
                t.Join();

                Assert.IsTrue(Shield.IsInTransaction);
                Assert.AreEqual(5, x1);
                Assert.AreEqual(20, a);
            });
            Assert.IsFalse(Shield.IsInTransaction);

            int x2 = 0;
            var t2 = new Thread(() =>
            {
                Assert.IsFalse(Shield.IsInTransaction);
                x2 = a;
            });
            t2.Start();
            t2.Join();
            Assert.AreEqual(20, x2);
            Assert.AreEqual(20, a);
        }
Example #12
0
 public void SkewWriteTest()
 {
     var cats = new Shielded<int>(1);
     var dogs = new Shielded<int>(1);
     int transactionCount = 0;
     Task.WaitAll(
         Enumerable.Range(1, 2).Select(i => Task.Factory.StartNew(() =>
             Shield.InTransaction(() =>
             {
                 Interlocked.Increment(ref transactionCount);
                 if (cats + dogs < 3)
                 {
                     Thread.Sleep(200);
                     if (i == 1)
                         cats.Modify((ref int n) => n++);
                     else
                         dogs.Modify((ref int n) => n++);
                 }
             }), TaskCreationOptions.LongRunning)).ToArray());
     Assert.AreEqual(3, cats + dogs);
     Assert.AreEqual(3, transactionCount);
 }
Example #13
0
        public void SideEffectTest()
        {
            var x = new Shielded<DateTime>(DateTime.UtcNow);
            try
            {
                Shield.InTransaction(() =>
                {
                    Shield.SideEffect(() => {
                        Assert.Fail("Suicide transaction has committed.");
                    },
                    () => {
                        throw new IgnoreMe();
                    });
                    // in case Assign() becomes commutative, we use Modify() to ensure conflict.
                    x.Modify((ref DateTime d) => d = DateTime.UtcNow);
                    var t = new Thread(() =>
                        Shield.InTransaction(() =>
                            x.Modify((ref DateTime d) => d = DateTime.UtcNow)));
                    t.Start();
                    t.Join();
                });
                Assert.Fail("Suicide transaction did not throw.");
            }
            catch (IgnoreMe) {}

            bool commitFx = false;
            Shield.InTransaction(() => {
                Shield.SideEffect(() => {
                    Assert.IsFalse(commitFx);
                    commitFx = true;
                });
            });
            Assert.IsTrue(commitFx);
        }
Example #14
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);
        }
Example #15
0
        /// <summary>
        /// Creates n events, with three typical offers (1,X,2) for each.
        /// The events get IDs 1-n.
        /// </summary>
        public BetShop(int n)
        {
            List<Shielded<Event>> initialEvents = new List<Shielded<Event>>();

            Shield.InTransaction(() =>
            {
                int eventIdGenerator = 1;
                int offerIdGenerator = 1;
                for (int i = 0; i < n; i++)
                {
                    var newEvent = new Shielded<Event>(new Event()
                    {
                        Id = eventIdGenerator++,
                        HomeTeam = "Home " + i,
                        AwayTeam = "Away " + i
                    });
                    // we have to use Modify, because each offer needs a ref to the shielded
                    // event, which we do not have before that shielded event is constructed. And,
                    // after he is constructed, he can only be changed like this.
                    newEvent.Modify((ref Event e) =>
                        e.BetOffers = new ShieldedSeq<Shielded<BetOffer>>(
                            new Shielded<BetOffer>(new BetOffer()
                            {
                                Id = offerIdGenerator++,
                                Event = newEvent,
                                Pick = "1",
                                Odds = 2m
                            }),
                            new Shielded<BetOffer>(new BetOffer()
                            {
                                Id = offerIdGenerator++,
                                Event = newEvent,
                                Pick = "X",
                                Odds = 4m
                            }),
                            new Shielded<BetOffer>(new BetOffer()
                            {
                                Id = offerIdGenerator++,
                                Event = newEvent,
                                Pick = "2",
                                Odds = 4.5m
                            })));
                    initialEvents.Add(newEvent);
                }
            });

            Events = new ShieldedDict<int, Shielded<Event>>(
                initialEvents.Select(e => new KeyValuePair<int, Shielded<Event>>(e.Read.Id, e)));
        }
Example #16
0
        public void Prioritization()
        {
            var x = new Shielded<int>();

            var barrier = new Barrier(2);

            int slowThread1Repeats = 0;
            var slowThread1 = new Thread(() => {
                barrier.SignalAndWait();
                Shield.InTransaction(() => {
                    Interlocked.Increment(ref slowThread1Repeats);
                    int a = x;
                    Thread.Sleep(100);
                    x.Value = a - 1;
                });
            });
            slowThread1.Start();

            IDisposable conditional = null;
            conditional = Shield.Conditional(() => { int i = x; return true; },
                () => {
                    barrier.SignalAndWait();
                    Thread.Yield();
                    conditional.Dispose();
                });

            foreach (int i in Enumerable.Range(1, 1000))
            {
                Shield.InTransaction(() => {
                    x.Modify((ref int a) => a++);
                });
            }
            slowThread1.Join();

            Assert.Greater(slowThread1Repeats, 1);
            Assert.AreEqual(999, x);

            // now, we introduce prioritization.
            // this condition gets triggered before any attempt to write into x
            int ownerThreadId = -1;
            Shield.PreCommit(() => { int a = x; return true; }, () => {
                var threadId = ownerThreadId;
                if (threadId > -1 && threadId != Thread.CurrentThread.ManagedThreadId)
                    // we'll cause lower prio threads to busy wait. we could also
                    // add, e.g., an onRollback SideEffect which would wait for
                    // a certain signal before continuing the next iteration..
                    // (NB that Shield.SideEffect would, of course, have to be called
                    // before calling Rollback.)
                    Shield.Rollback();
            });

            // this will pass due to ownerThreadId == -1
            Shield.InTransaction(() => x.Value = 0);

            int slowThread2Repeats = 0;
            var slowThread2 = new Thread(() => {
                try
                {
                    barrier.SignalAndWait();
                    Interlocked.Exchange(ref ownerThreadId, Thread.CurrentThread.ManagedThreadId);
                    Shield.InTransaction(() => {
                        Interlocked.Increment(ref slowThread2Repeats);
                        int a = x;
                        Thread.Sleep(100);
                        x.Value = a - 1;
                    });
                }
                finally
                {
                    Interlocked.Exchange(ref ownerThreadId, -1);
                }
            });
            slowThread2.Start();

            conditional = Shield.Conditional(() => { int i = x; return true; },
                () => {
                    barrier.SignalAndWait();
                    conditional.Dispose();
                });

            foreach (int i in Enumerable.Range(1, 1000))
            {
                Shield.InTransaction(() => {
                    x.Modify((ref int a) => a++);
                });
            }
            slowThread2.Join();

            Assert.AreEqual(1, slowThread2Repeats);
            Assert.AreEqual(999, x);
        }
Example #17
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));
        }
Example #18
0
        static void ControlledRace()
        {
            var acc1 = new Shielded<Account>(new Account()
            {
                Id = 1,
                Balance = 1000M,
                Transfers = new List<Transfer>()
            });
            var acc2 = new Shielded<Account>(new Account()
            {
                Id = 2,
                Balance = 1000M,
                Transfers = new List<Transfer>()
            });
            int transactionCount = 0;

            mtTest("controlled race", 20, n =>
            {
                if (n % 2 == 0)
                    return Task.Factory.StartNew(() =>
                    {
                        Shield.InTransaction(() =>
                        {
                            Interlocked.Increment(ref transactionCount);
                            Shield.SideEffect(() => Console.WriteLine("Transferred 100.00 .. acc1 -> acc2"),
                                () => Console.WriteLine("Task 1 rollback!"));
                            acc1.Modify((ref Account a) =>
                            {
                                a.Balance = a.Balance - 100M;
                                var list = a.Transfers;
                                Shield.SideEffect(() => list.Add(
                                    new Transfer() { OtherId = acc2.Value.Id, AmountReceived = -100M }));
                            });
                            Thread.Sleep(100);
                            acc2.Modify((ref Account a) =>
                            {
                                a.Balance = a.Balance + 100M;
                                var list = a.Transfers;
                                Shield.SideEffect(() => list.Add(
                                    new Transfer() { OtherId = acc1.Value.Id, AmountReceived = 100M }));
                            });
                        });
                    }, TaskCreationOptions.LongRunning);
                else
                    return Task.Factory.StartNew(() =>
                    {
                        Shield.InTransaction(() =>
                        {
                            Interlocked.Increment(ref transactionCount);
                            Shield.SideEffect(() => Console.WriteLine("Transferred 200.00 .. acc1 <- acc2"),
                                () => Console.WriteLine("Task 2 rollback!"));
                            acc2.Modify((ref Account a) =>
                            {
                                a.Balance = a.Balance - 200M;
                                var list = a.Transfers;
                                Shield.SideEffect(() => list.Add(
                                    new Transfer() { OtherId = acc1.Value.Id, AmountReceived = -200M }));
                            });
                            Thread.Sleep(250);
                            acc1.Modify((ref Account a) =>
                            {
                                a.Balance = a.Balance + 200M;
                                var list = a.Transfers;
                                Shield.SideEffect(() => list.Add(
                                    new Transfer() { OtherId = acc2.Value.Id, AmountReceived = 200M }));
                            });
                        });
                    }, TaskCreationOptions.LongRunning);
            });
            Console.WriteLine("\nCompleted 20 transactions in {0} total attempts.", transactionCount);
            Console.WriteLine("Account 1 balance: {0}", acc1.Value.Balance);
            foreach (var t in acc1.Value.Transfers)
            {
                Console.WriteLine("  {0:####,00}", t.AmountReceived);
            }
            Console.WriteLine("\nAccount 2 balance: {0}", acc2.Value.Balance);
            foreach (var t in acc2.Value.Transfers)
            {
                Console.WriteLine("  {0:####,00}", t.AmountReceived);
            }
        }
Example #19
0
        public static void MultiFieldOps()
        {
            long time;
            _timer = Stopwatch.StartNew();
            var numTrans = 100000;
            var fields = 20;

            Console.WriteLine(
                "Testing multi-field ops with {0} iterations, and nuber of fields (N) = {1}",
                numTrans, fields);

            var accessTest = new Shielded<int>[fields];
            for (int i = 0; i < fields; i++)
                accessTest[i] = new Shielded<int>();
            var dummy = new Shielded<int>();

            time = _timer.ElapsedMilliseconds;
            foreach (var k in Enumerable.Repeat(1, numTrans))
                Shield.InTransaction(() => {
                    dummy.Value = 3;
                    var a = dummy.Value;
                    dummy.Modify((ref int n) => n = 5);
                    a = dummy.Value;
                });
            time = _timer.ElapsedMilliseconds - time;
            Console.WriteLine("WARM UP in {0} ms.", time);

            var results = new long[fields];
            foreach (var i in Enumerable.Range(0, fields))
            {
                time = _timer.ElapsedMilliseconds;
                foreach (var k in Enumerable.Repeat(1, numTrans))
                    Shield.InTransaction(() => {
                        for (int j = 0; j <= i; j++)
                            accessTest[j].Modify((ref int n) => n = 1);
                    });
                results[i] = _timer.ElapsedMilliseconds - time;
                Console.WriteLine("{0} field modifiers in {1} ms.", i + 1, results[i]);
            }
        }
Example #20
0
        public void Prioritization()
        {
            var x = new Shielded <int>();

            var barrier = new Barrier(2);

            // first, the version with no prioritization. the slow thread will repeat.
            int slowThread1Repeats = 0;
            var slowThread1        = new Thread(() => {
                barrier.SignalAndWait();
                Shield.InTransaction(() => {
                    Interlocked.Increment(ref slowThread1Repeats);
                    int a = x;
                    Thread.Sleep(100);
                    x.Value = a - 1;
                });
            });

            slowThread1.Start();

            IDisposable conditional = null;

            conditional = Shield.Conditional(() => { int i = x; return(true); },
                                             () => {
                barrier.SignalAndWait();
                Thread.Yield();
                conditional.Dispose();
            });

            foreach (int i in Enumerable.Range(1, 1000))
            {
                Shield.InTransaction(() => {
                    x.Modify((ref int a) => a++);
                });
            }
            slowThread1.Join();

            Assert.Greater(slowThread1Repeats, 1);
            Assert.AreEqual(999, x);

            // now, we introduce prioritization, using a simple lock
            var lockObj = new object();

            // this condition gets triggered just before any attempt to commit into x
            Shield.PreCommit(() => { int a = x; return(true); }, () => {
                // the simplest way to block low prio writers is just:
                //lock (lockObj) { }
                // but then the actual commit happens outside of the lock and may yet
                // cause a conflict with someone just taking the lock. still, it's safer!
                // and might be good enough for cases where a repetition won't hurt.
                bool taken     = false;
                Action release = () =>
                {
                    if (taken)
                    {
                        Monitor.Exit(lockObj);
                        taken = false;
                    }
                };
                // a bit of extra safety by using sync for the commit case.
                Shield.SyncSideEffect(release);
                Shield.SideEffect(null, release);

                Monitor.Enter(lockObj, ref taken);
            });

            // not yet locked, so this is ok.
            Shield.InTransaction(() => x.Value = 0);

            int slowThread2Repeats = 0;
            var slowThread2        = new Thread(() => {
                barrier.SignalAndWait();
                lock (lockObj)
                {
                    Shield.InTransaction(() =>
                    {
                        Interlocked.Increment(ref slowThread2Repeats);
                        int a = x;
                        Thread.Sleep(100);
                        x.Value = a - 1;
                    });
                }
            });

            slowThread2.Start();

            conditional = Shield.Conditional(() => { int i = x; return(true); }, () => {
                barrier.SignalAndWait();
                conditional.Dispose();
            });

            foreach (int i in Enumerable.Range(1, 1000))
            {
                Shield.InTransaction(() => {
                    x.Modify((ref int a) => a++);
                });
            }
            slowThread2.Join();

            Assert.AreEqual(1, slowThread2Repeats);
            Assert.AreEqual(999, x);
        }
Example #21
0
        public static void TreePoolTest()
        {
            int numThreads = 4;
            int numItems = 200000;
            // for some reason, if this is replaced with ShieldedDict, KeyAlreadyPresent
            // exception is thrown. under one key you can then find an entity which does
            // not have that key. complete mystery.
            var tree = new ShieldedDict<Guid, TreeItem>();
            var barrier = new Barrier(numThreads + 1);
            int reportEvery = 10000;
            Shielded<int> lastReport = new Shielded<int>(0);
            Shielded<DateTime> lastTime = new Shielded<DateTime>(DateTime.UtcNow);

            Shield.Conditional(() => tree.Count >= lastReport + reportEvery, () =>
            {
                DateTime newNow = DateTime.UtcNow;
                int count = tree.Count;
                int speed = (count - lastReport) * 1000 / (int)newNow.Subtract(lastTime).TotalMilliseconds;
                lastTime.Assign(newNow);
                lastReport.Modify((ref int n) => n += reportEvery);
                Shield.SideEffect(() =>
                {
                    Console.Write("\n{0} at {1} item/s", count, speed);
                });
                return true;
            });

            TreeItem x = new TreeItem();

            _timer = new Stopwatch();
            _timer.Start();
            var time = _timer.ElapsedMilliseconds;
            foreach (var k in Enumerable.Repeat(1, numItems))
                Shield.InTransaction(() => x.Id);
            time = _timer.ElapsedMilliseconds - time;
            Console.WriteLine("1 read transactions in {0} ms.", time);

            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)
                    {
                        try
                        {
                            a();
                        }
                        catch
                        {
                            Console.Write(" * ");
                        }
                    }
                    barrier.SignalAndWait();
                });
            }

            foreach (var i in Enumerable.Range(0, numItems))
            {
                var item1 = new TreeItem();
                bags[i % numThreads].Add(() => Shield.InTransaction(() =>
                {
                    tree.Add(item1.Id, item1);
                }));
            }
            for (int i = 0; i < numThreads; i++)
                threads[i].Start();

            barrier.SignalAndWait();
            time = _timer.ElapsedMilliseconds;
            Console.WriteLine(" {0} ms.", time);

            Console.WriteLine("\nReading sequentially...");
            time = _timer.ElapsedMilliseconds;
            var keys = Shield.InTransaction(() => tree.Keys);
            time = _timer.ElapsedMilliseconds - time;
            Console.WriteLine("Keys read in {0} ms.", time);

            time = _timer.ElapsedMilliseconds;
            Shield.InTransaction(() => {
                foreach (var kvp in tree)
                    x = kvp.Value;
            });
            time = _timer.ElapsedMilliseconds - time;
            Console.WriteLine("Items read by enumerator in {0} ms.", time);

            time = _timer.ElapsedMilliseconds;
            Shield.InTransaction(() => {
                foreach (var k in keys)
                    x = tree[k];
            });
            time = _timer.ElapsedMilliseconds - time;
            Console.WriteLine("Items read by key in one trans in {0} ms.", time);

            time = _timer.ElapsedMilliseconds;
            foreach (var k in keys)
                x = tree[k];
            time = _timer.ElapsedMilliseconds - time;
            Console.WriteLine("Items read by key separately in {0} ms.", time);

            time = _timer.ElapsedMilliseconds;
            keys.AsParallel().ForAll(k => x = tree[k]);
            time = _timer.ElapsedMilliseconds - time;
            Console.WriteLine("Items read by key in parallel in {0} ms.", time);

            time = _timer.ElapsedMilliseconds;
            foreach (var k in Enumerable.Repeat(1, numItems))
                Shield.InTransaction(() => x.Id);
            time = _timer.ElapsedMilliseconds - time;
            Console.WriteLine("1 read transactions in {0} ms.", time);
        }
Example #22
0
        public int? BuyTicket(decimal payIn, params Shielded<BetOffer>[] bets)
        {
            var newId = Interlocked.Increment(ref _ticketIdGenerator);
            var newTicket = new Shielded<Ticket>(new Ticket()
            {
                Id = newId,
                PayInAmount = payIn
            });
            return Shield.InTransaction(() =>
            {
                newTicket.Modify((ref Ticket t) =>
                {
                    t.Bets = bets.Select(shBo => new Bet()
                        {
                            Offer = shBo,
                            Odds = shBo.Read.Odds
                        }).ToArray();
                    t.WinAmount = t.PayInAmount *
                        t.Bets.Aggregate(1m, (curr, nextBet) => curr * nextBet.Odds);
                });

                var hash = GetOfferHash(newTicket);
                var totalWin = _sameTicketWins.ContainsKey(hash) ?
                    _sameTicketWins[hash] + newTicket.Read.WinAmount : newTicket.Read.WinAmount;
                if (totalWin > SameTicketWinLimit)
                    return false;

                Tickets[newId] = newTicket;
                _sameTicketWins[hash] = totalWin;
                return true;
            }) ? (int?)newId : null;
        }
Example #23
0
 public static void Take()
 {
     Shield.InTransaction(() => _count.Modify((ref int c) => c--));
 }
Example #24
0
        /// <summary>
        /// Creates a BetShop, and tries to buy a large number of random tickets. Afterwards it
        /// checks that the rule limiting same ticket winnings is not violated.
        /// </summary>
        public static void BetShopTest()
        {
            int numEvents = 100;
            var betShop = new BetShop(numEvents);
            var randomizr = new Random();
            int reportEvery = 1000;
            var lastReport = new Shielded<int>(0);
            var lastTime = new Shielded<DateTime>(DateTime.UtcNow);
            long time;

            using (var reportingCond = 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);
                    });
                }))
            {
                time = mtTest("bet shop w/ " + numEvents, 50000, i =>
                {
                    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);
                    return Task.Factory.StartNew(() => 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);
                    }));
                });
            }
            //}

            var totalCorrect = betShop.VerifyTickets();
            Console.WriteLine(" {0} ms with {1} tickets paid in and is {2}.",
                time, betShop.Tickets.Count, totalCorrect ? "correct" : "incorrect");
        }
Example #25
0
        public void SideEffectTest()
        {
            var x = new Shielded<DateTime>(DateTime.UtcNow);
            try
            {
                Shield.InTransaction(() =>
                {
                    Shield.SideEffect(() => {
                        Assert.Fail("Suicide transaction has committed.");
                    },
                    () => {
                        throw new IgnoreMe();
                    });
                    // in case Assign() becomes commutative, we use Modify() to ensure conflict.
                    x.Modify((ref DateTime d) => d = DateTime.UtcNow);
                    var t = new Thread(() =>
                        Shield.InTransaction(() =>
                            x.Modify((ref DateTime d) => d = DateTime.UtcNow)));
                    t.Start();
                    t.Join();
                });
                Assert.Fail("Suicide transaction did not throw.");
            }
            catch (AggregateException aggr)
            {
                Assert.AreEqual(1, aggr.InnerExceptions.Count);
                Assert.AreEqual(typeof(IgnoreMe), aggr.InnerException.GetType());
            }

            bool commitFx = false;
            Shield.InTransaction(() => {
                Shield.SideEffect(() => {
                    Assert.IsFalse(commitFx);
                    commitFx = true;
                });
            });
            Assert.IsTrue(commitFx);

            bool outOfTransFx = false, outOfTransOnRollback = false;
            Shield.SideEffect(() => outOfTransFx = true, () => outOfTransOnRollback = true);
            Assert.IsTrue(outOfTransFx);
            Assert.IsFalse(outOfTransOnRollback);
        }
Example #26
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");
        }
Example #27
0
        public void ConditionalTest()
        {
            var x              = new Shielded <int>();
            var testCounter    = 0;
            var triggerCommits = 0;

            Shield.Conditional(() => {
                Interlocked.Increment(ref testCounter);
                return(x > 0 && (x & 1) == 0);
            },
                               () => {
                Shield.SideEffect(() =>
                                  Interlocked.Increment(ref triggerCommits));
                Assert.IsTrue(x > 0 && (x & 1) == 0);
            });

            const int count = 1000;

            ParallelEnumerable.Repeat(1, count).ForAll(i =>
                                                       Shield.InTransaction(() => x.Modify((ref int n) => n++)));

            // one more, for the first call to Conditional()! btw, if this conditional were to
            // write anywhere, he might conflict, and an interlocked counter would give more due to
            // repetitions. so, this confirms reader progress too.
            Assert.AreEqual(count + 1, testCounter);
            // every change triggers it, but by the time it starts, another transaction might have
            // committed, so this is not a fixed number.
            Assert.Greater(triggerCommits, 0);


            // a conditional which does not depend on any Shielded is not allowed!
            int a = 5;

            Assert.Throws <InvalidOperationException>(() =>
                                                      Shield.Conditional(() => a > 10, () => { }));

            bool firstTime = true;
            var  x2        = new Shielded <int>();

            // this one succeeds in registering, but fails as soon as it gets triggered, due to changing it's
            // test's access pattern to an empty set.
            Shield.Conditional(() => {
                if (firstTime)
                {
                    firstTime = false;
                    return(x2 == 0);
                }
                else
                {
                    // this is of course invalid, and when reaching here we have not touched any Shielded obj.
                    return(true);
                }
            }, () => { });

            try
            {
                // this will trigger the conditional
                Shield.InTransaction(() => x2.Modify((ref int n) => n++));
                Assert.Fail();
            }
            catch (AggregateException aggr)
            {
                Assert.AreEqual(1, aggr.InnerExceptions.Count);
                Assert.AreEqual(typeof(InvalidOperationException), aggr.InnerException.GetType());
            }
        }
Example #28
0
 static void OneTransaction()
 {
     Shielded<int> sh = new Shielded<int>();
     Shield.InTransaction(() =>
     {
         int x = sh;
         Console.WriteLine("Value: {0}", x);
         sh.Modify((ref int a) => a = x + 1);
         Console.WriteLine("Value after increment: {0}", sh.Value);
     });
 }
Example #29
0
        public void Prioritization()
        {
            var x = new Shielded <int>();

            var barrier = new Barrier(2);

            int slowThread1Repeats = 0;
            var slowThread1        = new Thread(() => {
                barrier.SignalAndWait();
                Shield.InTransaction(() => {
                    Interlocked.Increment(ref slowThread1Repeats);
                    int a = x;
                    Thread.Sleep(100);
                    x.Value = a - 1;
                });
            });

            slowThread1.Start();

            IDisposable conditional = null;

            conditional = Shield.Conditional(() => { int i = x; return(true); },
                                             () => {
                barrier.SignalAndWait();
                Thread.Yield();
                conditional.Dispose();
            });

            foreach (int i in Enumerable.Range(1, 1000))
            {
                Shield.InTransaction(() => {
                    x.Modify((ref int a) => a++);
                });
            }
            slowThread1.Join();

            Assert.Greater(slowThread1Repeats, 1);
            Assert.AreEqual(999, x);

            // now, we introduce prioritization.
            // this condition gets triggered before any attempt to write into x
            int ownerThreadId = -1;

            Shield.PreCommit(() => { int a = x; return(true); }, () => {
                var threadId = ownerThreadId;
                if (threadId > -1 && threadId != Thread.CurrentThread.ManagedThreadId)
                {
                    // we'll cause lower prio threads to busy wait. we could also
                    // add, e.g., an onRollback SideEffect which would wait for
                    // a certain signal before continuing the next iteration..
                    // (NB that Shield.SideEffect would, of course, have to be called
                    // before calling Rollback.)
                    Shield.Rollback();
                }
            });

            // this will pass due to ownerThreadId == -1
            Shield.InTransaction(() => x.Value = 0);

            int slowThread2Repeats = 0;
            var slowThread2        = new Thread(() => {
                try
                {
                    barrier.SignalAndWait();
                    Interlocked.Exchange(ref ownerThreadId, Thread.CurrentThread.ManagedThreadId);
                    Shield.InTransaction(() => {
                        Interlocked.Increment(ref slowThread2Repeats);
                        int a = x;
                        Thread.Sleep(100);
                        x.Value = a - 1;
                    });
                }
                finally
                {
                    Interlocked.Exchange(ref ownerThreadId, -1);
                }
            });

            slowThread2.Start();

            conditional = Shield.Conditional(() => { int i = x; return(true); },
                                             () => {
                barrier.SignalAndWait();
                conditional.Dispose();
            });

            foreach (int i in Enumerable.Range(1, 1000))
            {
                Shield.InTransaction(() => {
                    x.Modify((ref int a) => a++);
                });
            }
            slowThread2.Join();

            Assert.AreEqual(1, slowThread2Repeats);
            Assert.AreEqual(999, x);
        }