public void NoOdds() { var x = new Shielded<int>(); int preCommitFails = 0; // we will not allow any odd number to be committed into x. Shield.PreCommit(() => (x.Value & 1) == 1, () => { Interlocked.Increment(ref preCommitFails); throw new InvalidOperationException(); }); int transactionCount = 0; Task.WaitAll( Enumerable.Range(1, 100).Select(i => Task.Factory.StartNew(() => { try { Shield.InTransaction(() => { Interlocked.Increment(ref transactionCount); int a = x; Thread.Sleep(5); x.Value = a + i; }); } catch (InvalidOperationException) { } }, TaskCreationOptions.LongRunning)).ToArray()); Assert.AreEqual(50, preCommitFails); Assert.AreEqual(2550, x); // just to confirm validity of test! not really a fail if this fails. Assert.Greater(transactionCount, 100); }
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()); } }
public void Run() { Console.WriteLine("Queue test..."); ProcessorSlot.Set(Environment.ProcessorCount - 1); CountTracking(); Subscribe(); var maxQueueCount = new Shielded<int>(); Shield.Conditional(() => _queue.Count > maxQueueCount, () => { maxQueueCount.Assign(_queue.Count); return true; }); // create ItemCount items and push them in the queue. Stopwatch stopwatch = new Stopwatch(); var items = Enumerable.Range(1, ItemCount).Select( i => new Item() { Id = Guid.NewGuid(), Code = i, Amount = 100m * i }).ToArray(); stopwatch.Start(); for (int i = 0; i < ItemCount / 100; i++) { Shield.InTransaction(() => { for (int j = 0; j < 100; j++) _queue.Append(items[i*100 + j]); }); } Console.WriteLine("..all items added, waiting."); _barrier.SignalAndWait(); var time = stopwatch.ElapsedMilliseconds; Console.WriteLine(" -- completed in {0} ms, with {1} max queue count.", time, maxQueueCount.Read); }
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; }); }
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); }
public void ExceptionWhenTryingToCommit() { var a = new Shielded<int>(); using (Shield.WhenCommitting(a, _ => { throw new CustomException(); })) using (var cont = Shield.RunToCommit(5000, () => a.Value = 5)) { var aggr = Assert.Throws<AggregateException>(() => cont.TryCommit()); Assert.IsInstanceOf<CustomException>(aggr.InnerExceptions.Single()); } }
public void FieldsTest() { var a = new Shielded<int>(); using (var continuation = Shield.RunToCommit(1000, () => { int _ = a.Value; })) { var fields = continuation.Fields; Assert.AreEqual(1, fields.Length); Assert.AreSame(a, fields[0].Field); Assert.IsFalse(fields[0].HasChanges); continuation.InContext(f => Assert.AreSame(fields, f)); } }
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; }))); }
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); }
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); }
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); }
public void BasicRunToCommit() { var a = new Shielded<int>(5); using (var cont = Shield.RunToCommit(5000, () => { if (a == 5) a.Value = 20; })) { int runCount = 0, insideIfCount = 0; var t = new Thread(() => Shield.InTransaction(() => { Interlocked.Increment(ref runCount); if (a == 5) // this will block, and continue after the Commit call below { Interlocked.Increment(ref insideIfCount); a.Value = 10; } })); t.Start(); Thread.Sleep(100); Assert.AreEqual(1, runCount); Assert.AreEqual(0, insideIfCount); cont.InContext(() => Assert.AreEqual(20, a)); Assert.AreEqual(5, a); var t2 = new Thread(cont.Commit); t2.Start(); t2.Join(); t.Join(); Assert.AreEqual(1, runCount); Assert.AreEqual(0, insideIfCount); Assert.AreEqual(20, a); Assert.IsFalse(cont.TryCommit()); Assert.IsFalse(cont.TryRollback()); Assert.IsTrue(cont.Committed); } Assert.AreEqual(20, a); }
public void ShieldedLocalVisibility() { var x = new Shielded<int>(); var local = new ShieldedLocal<int>(); var didItRun = false; using (Shield.WhenCommitting(_ => { didItRun = true; Assert.AreEqual(10, local); })) { Shield.InTransaction(() => { x.Value = 1; local.Value = 10; }); } Assert.IsTrue(didItRun); using (var continuation = Shield.RunToCommit(Timeout.Infinite, () => { local.Value = 20; })) { continuation.InContext(() => Assert.AreEqual(20, local)); } didItRun = false; Shield.InTransaction(() => { local.Value = 30; Shield.SyncSideEffect(() => { didItRun = true; Assert.AreEqual(30, local); }); }); Assert.IsTrue(didItRun); }
public static void DictionaryPoolTest() { int numThreads = Environment.ProcessorCount; int numItems = 1000000; var tree = new ShieldedDictNc<int, int>(); var barrier = new Barrier(numThreads + 1); var counter = 0; int reportEvery = 10000; var lastReport = 0; long time; int x; var y = new Shielded<int>(); _timer = new Stopwatch(); _timer.Start(); time = _timer.ElapsedMilliseconds; foreach (var k in Enumerable.Repeat(1, numItems)) Shield.InTransaction(() => { }); time = _timer.ElapsedMilliseconds - time; Console.WriteLine("Empty 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(); }); } var lastTime = _timer.ElapsedMilliseconds; foreach (var i in Enumerable.Range(0, numItems)) { var index = i; bags[i % numThreads].Add(() => Shield.InTransaction(() => { tree.Add(index, index); Shield.SideEffect(() => { var last = lastReport; var count = Interlocked.Increment(ref counter); var newNow = _timer.ElapsedMilliseconds; if (count > last + reportEvery && Interlocked.CompareExchange(ref lastReport, last + reportEvery, last) == last) { var speed = reportEvery * 1000 / (newNow - lastTime); lastTime = newNow; // risky, but safe ;) Console.Write("\n{0} at {1} item/s", last + reportEvery, speed); } }); })); } lastTime = _timer.ElapsedMilliseconds; for (int i = 0; i < numThreads; i++) threads[i].Start(); barrier.SignalAndWait(); time = _timer.ElapsedMilliseconds; Console.WriteLine("\nTOTAL: {0} ms, at {1} ops/s", time, numItems * 1000 / 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 kvp in tree.OrderBy(kvp => kvp.Key)) x = kvp.Value; }); time = _timer.ElapsedMilliseconds - time; Console.WriteLine("Items read by sorted 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)) { var a = y.Value; } time = _timer.ElapsedMilliseconds - time; Console.WriteLine("One field out-of-tr. reads in {0} ms.", time); time = _timer.ElapsedMilliseconds; foreach (var k in Enumerable.Repeat(1, numItems)) Shield.InTransaction(() => { }); time = _timer.ElapsedMilliseconds - time; Console.WriteLine("Empty transactions in {0} ms.", time); }
/// <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"); }
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)); }
public static void TreeTest() { int numTasks = 100000; var tree = new ShieldedTreeNc<Guid, TreeItem>(); int transactionCount = 0; Shielded<int> lastReport = new Shielded<int>(0); Shielded<int> countComplete = new Shielded<int>(0); if (true) { var treeTime = mtTest("tree", numTasks, i => { return Task.Factory.StartNew(() => { var item1 = new TreeItem(); Shield.InTransaction(() => { //Interlocked.Increment(ref transactionCount); tree.Add(item1.Id, item1); // countComplete.Commute((ref int c) => c++); } ); } ); } ); Guid? previous = null; bool correct = true; Shield.InTransaction(() => { int count = 0; foreach (var item in tree) { count++; if (previous != null && previous.Value.CompareTo(item.Key) > 0) { correct = false; break; } previous = item.Key; } correct = correct && (count == numTasks); } ); Console.WriteLine("\n -- {0} ms with {1} iterations and is {2}.", treeTime, transactionCount, correct ? "correct" : "incorrect"); } if (true) { var dict = new ShieldedDictNc<Guid, TreeItem>(); transactionCount = 0; Shield.InTransaction(() => { countComplete.Value = 0; lastReport.Value = 0; } ); var time = mtTest("dictionary", numTasks, i => { return Task.Factory.StartNew(() => { var item1 = new TreeItem(); Shield.InTransaction(() => { //Interlocked.Increment(ref transactionCount); dict[item1.Id] = item1; // countComplete.Commute((ref int c) => c++); } ); } ); } ); Console.WriteLine("\n -- {0} ms with {1} iterations. Not sorted.", time, transactionCount); } if (true) { ConcurrentDictionary<Guid, TreeItem> dict = new ConcurrentDictionary<Guid, TreeItem>(); var time = mtTest("ConcurrentDictionary", numTasks, i => { return Task.Factory.StartNew(() => { var item1 = new TreeItem(); dict[item1.Id] = item1; } ); } ); Console.WriteLine("\n -- {0} ms with {1} iterations. Not sorted.", time, numTasks); } }
public void DictionaryAccessExpandingTest() { var d = new ShieldedDict <int, object>(); // various combinations - one key is either written or just read, and the // WhenCommitting sub tries to mess with another key, or to promote the // read key. Shield.InTransaction(() => { d[1] = new object(); d[2] = new object(); }); // WhenCommitting does not fire unless at least something changed, so we need this Shielded <int> x = new Shielded <int>(); // reader promotion to writer not allowed using (Shield.WhenCommitting(fs => d[2] = new object())) { Assert.Throws <AggregateException>(() => Shield.InTransaction(() => { x.Value = 1; var obj = d[2]; })); } // new read not allowed using (Shield.WhenCommitting(fs => { var obj = d[2]; })) { Assert.Throws <AggregateException>(() => Shield.InTransaction(() => { x.Value = 1; var obj = d[1]; })); } // new write not allowed using (Shield.WhenCommitting(fs => d[2] = new object())) { Assert.Throws <AggregateException>(() => Shield.InTransaction(() => { x.Value = 1; var obj = d[1]; })); } // same checks, but in situations when we did a write in the dict using (Shield.WhenCommitting(fs => d[2] = new object())) { Assert.Throws <AggregateException>(() => Shield.InTransaction(() => { d[1] = new object(); var obj = d[2]; })); } using (Shield.WhenCommitting(fs => { var obj = d[2]; })) { Assert.Throws <AggregateException>(() => Shield.InTransaction(() => { d[1] = new object(); })); } using (Shield.WhenCommitting(fs => d[2] = new object())) { Assert.Throws <AggregateException>(() => Shield.InTransaction(() => { d[1] = new object(); })); } // removing should likewise be restricted using (Shield.WhenCommitting(fs => { d.Remove(1); Assert.Throws <InvalidOperationException>( () => d.Remove(2)); })) { Shield.InTransaction(() => { d[1] = new object(); }); } // the exception was caught, and the WhenCommiting delegate committed Assert.IsFalse(d.ContainsKey(1)); Shield.InTransaction(() => d[1] = new object()); // finally, something allowed - reading from read or written, and writing into written using (Shield.WhenCommitting(fs => { var obj = d[1]; var obj2 = d[2]; d[2] = new object(); })) { Shield.InTransaction(() => { var obj = d[1]; d[2] = new object(); }); } }
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) {} }); }
public static void TimeTests() { var randomizr = new Random(); int transactionCounter; int sleepTime = 1; int taskCount = 10000; // a little warm up for Shielded var warmUp = new Shielded<int>(); Shield.InTransaction(() => warmUp.Value = warmUp + 1); foreach (var i in Enumerable.Repeat(0, 5)) { var x = new int[100]; transactionCounter = 0; var time = mtTest("dirty write", taskCount, _ => { var rnd = randomizr.Next(100); return Task.Factory.StartNew(() => { Interlocked.Increment(ref transactionCounter); int v = x[rnd]; if (sleepTime > 0) Thread.Sleep(sleepTime); x[rnd] = v + 1; }, sleepTime > 0 ? TaskCreationOptions.LongRunning : TaskCreationOptions.None ); }); var correct = x.Sum() == taskCount; Console.WriteLine(" {0} ms with {1} iterations and is {2}.", time, transactionCounter, correct ? "correct" : "incorrect"); } var lockCount = 100; foreach (var i in Enumerable.Repeat(0, 5)) { var x = new int[100]; transactionCounter = 0; var l = Enumerable.Repeat(0, lockCount).Select(_ => new object()).ToArray(); var time = mtTest(string.Format("{0} lock write", lockCount), taskCount, _ => { var rnd = randomizr.Next(100); return Task.Factory.StartNew(() => { lock (l[rnd % lockCount]) { Interlocked.Increment(ref transactionCounter); int v = x[rnd]; if (sleepTime > 0) Thread.Sleep(sleepTime); x[rnd] = v + 1; } }, sleepTime > 0 ? TaskCreationOptions.LongRunning : TaskCreationOptions.None ); }); var correct = x.Sum() == taskCount; Console.WriteLine(" {0} ms with {1} iterations and is {2}.", time, transactionCounter, correct ? "correct" : "incorrect"); } foreach (var i in Enumerable.Repeat(0, 5)) { var shx = Enumerable.Repeat(0, 100).Select(n => new Shielded<int>(n)).ToArray(); transactionCounter = 0; var time = mtTest("shielded2 write", taskCount, _ => { var rnd = randomizr.Next(100); return Task.Factory.StartNew(() => { Shield.InTransaction(() => { Interlocked.Increment(ref transactionCounter); int v = shx[rnd]; if (sleepTime > 0) Thread.Sleep(sleepTime); shx[rnd].Value = v + 1; }); }, sleepTime > 0 ? TaskCreationOptions.LongRunning : TaskCreationOptions.None ); }); var correct = shx.Sum(s => s.Value) == taskCount; Console.WriteLine(" {0} ms with {1} iterations and is {2}.", time, transactionCounter, correct ? "correct" : "incorrect"); } }
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); }
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); }
private static void DictionaryTest() { var dict = new ShieldedDictNc<int, Shielded<int>>(); var randomizr = new Random(); while (true) { var transactionCounter = 0; var time = mtTest("dictionary", 10000, i => { var rnd = randomizr.Next(10); if (i % 2 == 0) // adder task - 500 of these return Task.Factory.StartNew(() => { Shield.InTransaction(() => { Interlocked.Increment(ref transactionCounter); var v = dict.ContainsKey(rnd) ? dict[rnd] : null; int? num = v != null ? (int?)v.Value : null; Thread.Sleep(1); if (v == null) dict[rnd] = new Shielded<int>(1); else if (v.Value == -1) dict.Remove(rnd); else v.Modify((ref int a) => a = num.Value + 1); } ); }, TaskCreationOptions.LongRunning ); else // subtractor task - 500 of these return Task.Factory.StartNew(() => { Shield.InTransaction(() => { Interlocked.Increment(ref transactionCounter); var v = dict.ContainsKey(rnd) ? dict[rnd] : null; int? num = v != null ? (int?)v.Value : null; Thread.Sleep(1); if (v == null) dict[rnd] = new Shielded<int>(-1); else if (v.Value == 1) dict.Remove(rnd); else v.Modify((ref int a) => a = num.Value - 1); } ); }, TaskCreationOptions.LongRunning ); }); var sum = Enumerable.Range(0, 10).Sum(n => dict.ContainsKey(n) ? dict[n] : 0); var zeroes = Shield.InTransaction(() => dict.Any(kvp => kvp.Value == 0)); Console.WriteLine(" {0} ms with {1} iterations and sum {2}, {3}", time, transactionCounter, sum, zeroes ? "with zeroes!" : "no zeroes."); } }
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; }); }); }
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); }
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]); } }
public void Update() { hudStuff.text = "Energy: " + jugador.getEnergy() + "\n Score: " + jugador.getScore(); hudShadow.text = hudStuff.text; if (jugador.getEnergy() <= 0) { hudStuff.text = "Game Over, \n press return to continue..."; hudShadow.text = hudStuff.text; gameArea.alpha--; projectileContainer.alpha--; entityContainer.alpha--; particleContainer.alpha--; if (Input.GetKeyDown(KeyCode.Return)) { Game.instance.GoToPage(PageType.MainMenuPage); } return; } for (int x = enemies.Count - 1; x >= 0; x--) { enemies[x].Update(); if (enemies[x].getEnergy() < 0) { entityContainer.RemoveChild(enemies[x]); enemies.RemoveAt(x); } } for (int x = projectiles.Count-1; x >= 0; x-- ) { projectiles[x].Update(); for (int w = enemies.Count - 1; w >= 0; w--) { if (projectiles[x].sprite.localRect.CloneAndOffset(projectiles[x].x,projectiles[x].y).CheckIntersect(enemies[w].sprite.localRect.CloneAndOffset(enemies[w].x,enemies[w].y))) { enemies[w].setEnergy(enemies[w].getEnergy()-projectiles[x].getEnergy()); jugador.AddScore(enemies[w].scoreValue); projectiles[x].ttl = -2f; ScreenShake(2, 3); FSoundManager.PlaySound("explosion"); } } if (projectiles[x].ttl < 0) { projectiles[x].DestroyEffect(); projectileContainer.RemoveChild(projectiles[x]); projectiles.RemoveAt(x); } } for (int x = Enemyprojectiles.Count - 1; x >= 0; x--) { Enemyprojectiles[x].Update(); if (Enemyprojectiles[x].sprite.localRect.CloneAndOffset(Enemyprojectiles[x].x, Enemyprojectiles[x].y).CheckIntersect(jugador.sprite.localRect.CloneAndOffset(jugador.Position.x, jugador.Position.y))) { jugador.setEnergy(jugador.getEnergy()-Enemyprojectiles[x].getEnergy()); ScreenShake(3,10); Enemyprojectiles[x].ttl = -1; FSoundManager.PlaySound("explosion"); } if (Enemyprojectiles[x].ttl < 0) { Enemyprojectiles[x].DestroyEffect(); projectileContainer.RemoveChild(Enemyprojectiles[x]); Enemyprojectiles.RemoveAt(x); } } jugador.Update(); if (enemies.Count < minimalEnemies) { GenerateFoe(); } if(jugador.getScore()>0){ if ((jugador.getScore() % 300) == 0) { Shielded shiel = new Shielded(new Vector2(jugador.GetPosition().x + UnityEngine.Random.Range(-Futile.screen.width * 2, Futile.screen.width * 2), Futile.screen.halfHeight - 20)); enemies.Add(shiel); jugador.AddScore(jugador.getScore()+10); } if ((jugador.getScore() % 500) == 0) { minimalEnemies ++ ; } } }
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); }
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); }
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"); }
public static void TreeTest() { int numTasks = 100000; int reportEvery = 1000; ShieldedTree<Guid, TreeItem> tree = new ShieldedTree<Guid, TreeItem>(); int transactionCount = 0; Shielded<int> lastReport = new Shielded<int>(0); Shielded<int> countComplete = new Shielded<int>(0); // Shielded<DateTime> lastTime = new Shielded<DateTime>(DateTime.UtcNow); // // Shield.Conditional(() => countComplete >= lastReport + reportEvery, () => // { // DateTime newNow = DateTime.UtcNow; // int speed = (countComplete - lastReport) * 1000 / (int)newNow.Subtract(lastTime).TotalMilliseconds; // lastTime.Assign(newNow); // lastReport.Modify((ref int n) => n += reportEvery); // int count = countComplete; // Shield.SideEffect(() => // { // Console.Write("\n{0} at {1} item/s", count, speed); // } // ); // return true; // } // ); if (true) { var treeTime = mtTest("tree", numTasks, i => { return Task.Factory.StartNew(() => { var item1 = new TreeItem(); Shield.InTransaction(() => { //Interlocked.Increment(ref transactionCount); tree.Add(item1.Id, item1); // countComplete.Commute((ref int c) => c++); } ); } ); } ); Guid? previous = null; bool correct = true; Shield.InTransaction(() => { int count = 0; foreach (var item in tree) { count++; if (previous != null && previous.Value.CompareTo(item.Key) > 0) { correct = false; break; } previous = item.Key; } correct = correct && (count == numTasks); } ); Console.WriteLine("\n -- {0} ms with {1} iterations and is {2}.", treeTime, transactionCount, correct ? "correct" : "incorrect"); } if (true) { ShieldedDict<Guid, TreeItem> dict = new ShieldedDict<Guid, TreeItem>(); transactionCount = 0; Shield.InTransaction(() => { countComplete.Assign(0); lastReport.Assign(0); } ); var time = mtTest("dictionary", numTasks, i => { return Task.Factory.StartNew(() => { var item1 = new TreeItem(); Shield.InTransaction(() => { //Interlocked.Increment(ref transactionCount); dict[item1.Id] = item1; // countComplete.Commute((ref int c) => c++); } ); } ); } ); Console.WriteLine("\n -- {0} ms with {1} iterations. Not sorted.", time, transactionCount); } if (true) { ConcurrentDictionary<Guid, TreeItem> dict = new ConcurrentDictionary<Guid, TreeItem>(); var time = mtTest("ConcurrentDictionary", numTasks, i => { return Task.Factory.StartNew(() => { var item1 = new TreeItem(); dict[item1.Id] = item1; } ); } ); Console.WriteLine("\n -- {0} ms with {1} iterations. Not sorted.", time, numTasks); } }
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); } }
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()); } }
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); }); }
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); }