public void RemoveTest() { var dict = new ShieldedDict <string, object>(new KeyValuePair <string, object>[] { new KeyValuePair <string, object>("key a", null), new KeyValuePair <string, object>("key b", null), new KeyValuePair <string, object>("key c", null), }); Assert.Throws <InvalidOperationException>(() => dict.Remove("key a")); Assert.Throws <InvalidOperationException>(() => ((ICollection <KeyValuePair <string, object> >)dict).Remove( new KeyValuePair <string, object>("key a", null))); Shield.InTransaction(() => { dict.Remove("key a"); Assert.AreEqual(2, dict.Count); Assert.IsFalse(dict.ContainsKey("key a")); }); Assert.AreEqual(2, dict.Count); Assert.IsFalse(dict.ContainsKey("key a")); Shield.InTransaction(() => { ((ICollection <KeyValuePair <string, object> >)dict).Remove( new KeyValuePair <string, object>("key b", null)); Assert.AreEqual(1, dict.Count); Assert.IsFalse(dict.ContainsKey("key b")); }); Assert.AreEqual(1, dict.Count); Assert.IsFalse(dict.ContainsKey("key b")); // ToList() avoids the need for a transaction, usually needed for enumerating collections. Assert.AreEqual("key c", dict.ToList().Single().Key); }
public void ContainsTest() { var dict = new ShieldedDict <string, object>(new KeyValuePair <string, object>[] { new KeyValuePair <string, object>("key a", null), new KeyValuePair <string, object>("key b", null), new KeyValuePair <string, object>("key c", null), }); Assert.IsTrue(dict.ContainsKey("key a")); Assert.IsTrue(((ICollection <KeyValuePair <string, object> >)dict).Contains( new KeyValuePair <string, object>("key a", null))); Assert.IsFalse(dict.ContainsKey("not me")); Assert.IsFalse(((ICollection <KeyValuePair <string, object> >)dict).Contains( new KeyValuePair <string, object>("not me", null))); Shield.InTransaction(() => { dict.Add("new key", null); dict.Remove("key a"); Assert.IsFalse(dict.ContainsKey("key a")); Assert.IsFalse(((ICollection <KeyValuePair <string, object> >)dict).Contains( new KeyValuePair <string, object>("key a", null))); Assert.IsTrue(dict.ContainsKey("new key")); Assert.IsTrue(((ICollection <KeyValuePair <string, object> >)dict).Contains( new KeyValuePair <string, object>("new key", null))); }); }
public void BasicTest() { var dict = new ShieldedDict<int, object>(); Assert.Throws<InvalidOperationException>(() => dict[1] = new object()); Shield.InTransaction(() => { dict[2] = new object(); // the TPL sometimes executes tasks on the same thread. object x1 = null; var t = new Thread(() => { Assert.IsFalse(Shield.IsInTransaction); Assert.IsFalse(dict.ContainsKey(2)); }); t.Start(); t.Join(); Assert.IsNotNull(dict[2]); }); object x2 = null; var t2 = new Thread(() => { x2 = dict[2]; }); t2.Start(); t2.Join(); Assert.IsNotNull(x2); Assert.IsNotNull(dict[2]); }
public void EqualityComparerTest() { var dict = new ShieldedDict <int, object>(new OddsAndEvens()); int retryCount = 0; Thread oneTime = null; Shield.InTransaction(() => { retryCount++; dict[1] = new object(); dict[2] = new object(); if (oneTime == null) { oneTime = new Thread(() => { Assert.IsFalse(Shield.IsInTransaction); Assert.IsFalse(dict.ContainsKey(2)); Assert.IsFalse(dict.ContainsKey(4)); Shield.InTransaction(() => dict[4] = new object()); }); oneTime.Start(); oneTime.Join(); } Assert.IsNotNull(dict[2]); Assert.IsNotNull(dict[4]); Assert.AreSame(dict[2], dict[4]); Assert.AreNotSame(dict[1], dict[2]); }); Assert.AreEqual(2, retryCount); object x2 = null; var t2 = new Thread(() => { x2 = dict[2]; Assert.AreSame(dict[2], dict[4]); Assert.AreNotSame(dict[1], dict[2]); }); t2.Start(); t2.Join(); Assert.IsNotNull(x2); Assert.IsNotNull(dict[2]); Assert.AreSame(dict[2], dict[4]); Assert.AreNotSame(dict[1], dict[2]); }
public void SimpleRace() { var dict = new ShieldedDict <int, int>(); ParallelEnumerable.Range(0, 100000) .ForAll(i => Shield.InTransaction( () => dict[i % 100] = dict.ContainsKey(i % 100) ? dict[i % 100] + 1 : 1)); Shield.InTransaction(() => { for (int i = 0; i < 100; i++) { Assert.AreEqual(1000, dict[i]); } }); }
public void CopyToTest() { var dict = new ShieldedDict <int, object>( Enumerable.Range(1, 1000).Select(i => new KeyValuePair <int, object>(i, new object())).ToArray()); Assert.AreEqual(1000, dict.Count); var array = new KeyValuePair <int, object> [1100]; // this works out of transaction (and consequently, so do ToArray and ToList), by opening // a transaction itself. that could not be done when you do a foreach. ((ICollection <KeyValuePair <int, object> >)dict).CopyTo(array, 100); var keys = new HashSet <int>(); foreach (var kvp in array.Skip(100)) { Assert.IsTrue(dict.ContainsKey(kvp.Key)); Assert.IsTrue(keys.Add(kvp.Key)); Assert.AreEqual(dict[kvp.Key], kvp.Value); } }
public void BasicTest() { var dict = new ShieldedDict <int, object>(); Assert.Throws <InvalidOperationException>(() => dict[1] = new object()); bool detectable = false; using (Shield.WhenCommitting(dict, _ => detectable = true)) Shield.InTransaction(() => dict[1] = new object()); Assert.IsTrue(detectable); Shield.InTransaction(() => { dict[2] = new object(); // the TPL sometimes executes tasks on the same thread. var t = new Thread(() => { Assert.IsFalse(Shield.IsInTransaction); Assert.IsFalse(dict.ContainsKey(2)); }); t.Start(); t.Join(); Assert.IsNotNull(dict[2]); }); object x2 = null; var t2 = new Thread(() => { x2 = dict[2]; }); t2.Start(); t2.Join(); Assert.IsNotNull(x2); Assert.IsNotNull(dict[2]); }
private static void DictionaryTest() { ShieldedDict<int, Shielded<int>> dict = new ShieldedDict<int, Shielded<int>>(); var randomizr = new Random(); foreach (var _ in Enumerable.Repeat(0, 10)) { 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.Read : null; Thread.Sleep(1); if (v == null) dict[rnd] = new Shielded<int>(1); else if (v.Read == -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.Read : null; Thread.Sleep(1); if (v == null) dict[rnd] = new Shielded<int>(-1); else if (v.Read == 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); Console.WriteLine(" {0} ms with {1} iterations and sum {2}.", time, transactionCounter, sum); } }
public void SimpleRace() { var dict = new ShieldedDict<int, int>(); ParallelEnumerable.Range(0, 100000) .ForAll(i => Shield.InTransaction( () => dict[i % 100] = dict.ContainsKey(i % 100) ? dict[i % 100] + 1 : 1)); Shield.InTransaction(() => { for (int i = 0; i < 100; i++) Assert.AreEqual(1000, dict[i]); }); }
public void RemoveTest() { var dict = new ShieldedDict<string, object>(new KeyValuePair<string, object>[] { new KeyValuePair<string, object>("key a", null), new KeyValuePair<string, object>("key b", null), new KeyValuePair<string, object>("key c", null), }); Assert.Throws<InvalidOperationException>(() => dict.Remove("key a")); Assert.Throws<InvalidOperationException>(() => ((ICollection<KeyValuePair<string, object>>)dict).Remove( new KeyValuePair<string, object>("key a", null))); Shield.InTransaction(() => { dict.Remove("key a"); Assert.AreEqual(2, dict.Count); Assert.IsFalse(dict.ContainsKey("key a")); }); Assert.AreEqual(2, dict.Count); Assert.IsFalse(dict.ContainsKey("key a")); Shield.InTransaction(() => { ((ICollection<KeyValuePair<string, object>>)dict).Remove( new KeyValuePair<string, object>("key b", null)); Assert.AreEqual(1, dict.Count); Assert.IsFalse(dict.ContainsKey("key b")); }); Assert.AreEqual(1, dict.Count); Assert.IsFalse(dict.ContainsKey("key b")); // ToList() avoids the need for a transaction, usually needed for enumerating collections. Assert.AreEqual("key c", dict.ToList().Single().Key); }
public void CopyToTest() { var dict = new ShieldedDict<int, object>( Enumerable.Range(1, 1000).Select(i => new KeyValuePair<int, object>(i, new object())).ToArray()); Assert.AreEqual(1000, dict.Count); var array = new KeyValuePair<int, object>[1100]; // this works out of transaction (and consequently, so do ToArray and ToList), by opening // a transaction itself. that could not be done when you do a foreach. ((ICollection<KeyValuePair<int, object>>)dict).CopyTo(array, 100); var keys = new HashSet<int>(); foreach (var kvp in array.Skip(100)) { Assert.IsTrue(dict.ContainsKey(kvp.Key)); Assert.IsTrue(keys.Add(kvp.Key)); Assert.AreEqual(dict[kvp.Key], kvp.Value); } }
public void ContainsTest() { var dict = new ShieldedDict<string, object>(new KeyValuePair<string, object>[] { new KeyValuePair<string, object>("key a", null), new KeyValuePair<string, object>("key b", null), new KeyValuePair<string, object>("key c", null), }); Assert.IsTrue(dict.ContainsKey("key a")); Assert.IsTrue(((ICollection<KeyValuePair<string, object>>)dict).Contains( new KeyValuePair<string, object>("key a", null))); Assert.IsFalse(dict.ContainsKey("not me")); Assert.IsFalse(((ICollection<KeyValuePair<string, object>>)dict).Contains( new KeyValuePair<string, object>("not me", null))); Shield.InTransaction(() => { dict.Add("new key", null); dict.Remove("key a"); Assert.IsFalse(dict.ContainsKey("key a")); Assert.IsFalse(((ICollection<KeyValuePair<string, object>>)dict).Contains( new KeyValuePair<string, object>("key a", null))); Assert.IsTrue(dict.ContainsKey("new key")); Assert.IsTrue(((ICollection<KeyValuePair<string, object>>)dict).Contains( new KeyValuePair<string, object>("new key", null))); }); }
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<InvalidOperationException>(() => Shield.InTransaction(() => { x.Value = 1; var obj = d[2]; })); } // new read not allowed using (Shield.WhenCommitting(fs => { var obj = d[2]; })) { Assert.Throws<InvalidOperationException>(() => Shield.InTransaction(() => { x.Value = 1; var obj = d[1]; })); } // new write not allowed using (Shield.WhenCommitting(fs => d[2] = new object())) { Assert.Throws<InvalidOperationException>(() => 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<InvalidOperationException>(() => Shield.InTransaction(() => { d[1] = new object(); var obj = d[2]; })); } using (Shield.WhenCommitting(fs => { var obj = d[2]; })) { Assert.Throws<InvalidOperationException>(() => Shield.InTransaction(() => { d[1] = new object(); })); } using (Shield.WhenCommitting(fs => d[2] = new object())) { Assert.Throws<InvalidOperationException>(() => 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 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(); }); } }