public void TwoQueuesTest() { var seq1 = new ShieldedSeq <int>(); var seq2 = new ShieldedSeq <int>(); // conflict should happen only on the accessed sequence, if we're appending to two.. int transactionCount = 0; Thread oneTimer = null; Shield.InTransaction(() => { transactionCount++; seq2.Append(2); seq1.Append(1); if (oneTimer == null) { oneTimer = new Thread(() => Shield.InTransaction(() => { seq2.Append(1); })); oneTimer.Start(); oneTimer.Join(); } var b = seq1.Any(); }); Assert.AreEqual(1, transactionCount); Assert.AreEqual(2, seq2.Count); Assert.IsTrue(seq2.Any()); Assert.AreEqual(1, seq2[0]); Assert.AreEqual(2, seq2[1]); Shield.InTransaction(() => { seq1.Clear(); seq2.Clear(); }); transactionCount = 0; oneTimer = null; Shield.InTransaction(() => { transactionCount++; seq1.Append(1); seq2.Append(2); if (oneTimer == null) { oneTimer = new Thread(() => Shield.InTransaction(() => { seq2.Append(1); })); oneTimer.Start(); oneTimer.Join(); } // the difference is here - seq2: var b = seq2.Any(); }); Assert.AreEqual(2, transactionCount); Assert.AreEqual(2, seq2.Count); Assert.IsTrue(seq2.Any()); Assert.AreEqual(1, seq2[0]); Assert.AreEqual(2, seq2[1]); }
public void TailPassTest() { // pass by the tail - potential commute bug // - when you append(), and then iterate a seq, // will you find the new last item missing? var tailPass = new ShieldedSeq <int>( Enumerable.Range(1, 5).ToArray()); Shield.InTransaction(() => { tailPass.Append(6); int counter = 0; foreach (var i in tailPass) { counter++; } Assert.AreEqual(6, counter); }); Shield.InTransaction(() => { // this causes immediate degeneration of the Append commute. var h = tailPass.Any(); tailPass.Append(7); int counter = 0; foreach (var i in tailPass) { counter++; } Assert.AreEqual(7, counter); }); Shield.InTransaction(() => { tailPass.Append(8); tailPass.Append(9); Assert.AreEqual(1, tailPass.Head); int counter = 0; foreach (var i in tailPass) { Assert.AreEqual(++counter, i); } Assert.AreEqual(9, counter); }); Assert.AreEqual(9, tailPass.Count); }
public void Validation() { var list1 = new ShieldedSeq <int>(Enumerable.Range(1, 100).ToArray()); var list2 = new ShieldedSeq <int>(); int validationFails = 0; Shield.PreCommit(() => list1.Count + list2.Count != 100, () => { Interlocked.Increment(ref validationFails); throw new ValidationException(); }); int transactionCount = 0; Task.WaitAll( Enumerable.Range(1, 100).Select(i => Task.Factory.StartNew(() => { try { Shield.InTransaction(() => { Interlocked.Increment(ref transactionCount); int x = list1.TakeHead(); if (i < 100) { list2.Append(x); } }); Assert.AreNotEqual(100, i); } catch (ValidationException) { Assert.AreEqual(100, i); } }, TaskCreationOptions.LongRunning)).ToArray()); Assert.AreEqual(1, validationFails); Assert.AreEqual(1, list1.Count); Assert.AreEqual(99, list2.Count); }
public void TwoQueuesTest() { var seq1 = new ShieldedSeq<int>(); var seq2 = new ShieldedSeq<int>(); // conflict should happen only on the accessed sequence, if we're appending to two.. int transactionCount = 0; Thread oneTimer = null; Shield.InTransaction(() => { transactionCount++; seq2.Append(2); seq1.Append(1); if (oneTimer == null) { oneTimer = new Thread(() => Shield.InTransaction(() => { seq2.Append(1); })); oneTimer.Start(); oneTimer.Join(); } var b = seq1.HasAny; }); Assert.AreEqual(1, transactionCount); Assert.AreEqual(2, seq2.Count); Assert.IsTrue(seq2.HasAny); Assert.AreEqual(1, seq2[0]); Assert.AreEqual(2, seq2[1]); Shield.InTransaction(() => { seq1.Clear(); seq2.Clear(); }); transactionCount = 0; oneTimer = null; Shield.InTransaction(() => { transactionCount++; seq1.Append(1); seq2.Append(2); if (oneTimer == null) { oneTimer = new Thread(() => Shield.InTransaction(() => { seq2.Append(1); })); oneTimer.Start(); oneTimer.Join(); } var b = seq2.HasAny; }); Assert.AreEqual(2, transactionCount); Assert.AreEqual(2, seq2.Count); Assert.IsTrue(seq2.HasAny); Assert.AreEqual(1, seq2[0]); Assert.AreEqual(2, seq2[1]); }
public void BasicOps() { var seq = new ShieldedSeq<int>( Enumerable.Range(1, 20).ToArray()); Assert.AreEqual(20, seq.Count); Assert.IsTrue(seq.HasAny); Assert.AreEqual(1, seq.Head); for (int i = 0; i < 20; i++) Assert.AreEqual(i + 1, seq [i]); Shield.InTransaction(() => { int i = 1; foreach (int x in seq) { Assert.AreEqual(i, x); i++; } }); ParallelEnumerable.Range(0, 20) .ForAll(n => Shield.InTransaction( () => seq [n] = seq [n] + 20) ); for (int i = 0; i < 20; i++) Assert.AreEqual(i + 21, seq [i]); Shield.InTransaction(() => { seq.Append(0); // test commute Assert.AreEqual(0, seq [20]); } ); Assert.AreEqual(21, seq.Count); var a = Shield.InTransaction(() => seq.TakeHead()); Assert.AreEqual(20, seq.Count); Assert.AreEqual(21, a); for (int i = 0; i < 19; i++) Assert.AreEqual(i + 22, seq [i]); Assert.AreEqual(0, seq [19]); Shield.InTransaction(() => { seq.Prepend(a); seq.RemoveAt(20); } ); Assert.AreEqual(20, seq.Count); for (int i = 0; i < 20; i++) Assert.AreEqual(i + 21, seq [i]); Shield.InTransaction(() => seq.RemoveAll(i => (i & 1) == 1)); Assert.AreEqual(10, seq.Count); for (int i = 0; i < 10; i++) Assert.AreEqual(i * 2 + 22, seq [i]); var seq2 = new ShieldedSeq<int>( Shield.InTransaction(() => seq.ToArray())); Shield.InTransaction(() => seq.RemoveAll(i => true)); Assert.AreEqual(0, seq.Count); Shield.InTransaction(() => seq2.Clear()); Assert.AreEqual(0, seq2.Count); var seq3 = new ShieldedSeq<int>( Enumerable.Range(1, 5).ToArray()); Shield.InTransaction(() => seq3.RemoveAt(0)); Assert.AreEqual(4, seq3.Count); Assert.AreEqual(2, seq3 [0]); Shield.InTransaction(() => seq3.RemoveAt(3)); Assert.AreEqual(3, seq3.Count); Assert.AreEqual(4, seq3 [2]); Shield.InTransaction(() => seq3.Append(100)); Assert.AreEqual(4, seq3.Count); Assert.AreEqual(100, seq3 [3]); Shield.InTransaction(() => seq3.RemoveAll(i => i == 100)); Assert.AreEqual(3, seq3.Count); Assert.AreEqual(4, seq3 [2]); Shield.InTransaction(() => seq3.Append(100)); Assert.AreEqual(4, seq3.Count); Assert.AreEqual(100, seq3 [3]); }
public void TailPassTest() { /////// // pass by the tail - potential commute bug // - when you append(), and then iterate a seq, // will you find the new last item missing? var tailPass = new ShieldedSeq<int>( Enumerable.Range(1, 5).ToArray()); Shield.InTransaction(() => { tailPass.Append(6); int counter = 0; foreach (var i in tailPass) counter++; Assert.AreEqual(6, counter); }); Shield.InTransaction(() => { // this causes immediate degeneration of the Append commute. var h = tailPass.HasAny; tailPass.Append(7); int counter = 0; foreach (var i in tailPass) counter++; Assert.AreEqual(7, counter); }); Shield.InTransaction(() => { tailPass.Append(8); tailPass.Append(9); Assert.AreEqual(1, tailPass.Head); int counter = 0; foreach (var i in tailPass) Assert.AreEqual(++counter, i); Assert.AreEqual(9, counter); }); Assert.AreEqual(9, tailPass.Count); }
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 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 void BasicOps() { var seq = new ShieldedSeq <int>( Enumerable.Range(1, 20).ToArray()); Assert.AreEqual(20, seq.Count); Assert.IsTrue(seq.Any()); Assert.AreEqual(1, seq.Head); for (int i = 0; i < 20; i++) { Assert.AreEqual(i + 1, seq [i]); } Shield.InTransaction(() => { int i = 1; foreach (int x in seq) { Assert.AreEqual(i, x); i++; } }); ParallelEnumerable.Range(0, 20) .ForAll(n => Shield.InTransaction(() => seq [n] = seq [n] + 20) ); for (int i = 0; i < 20; i++) { Assert.AreEqual(i + 21, seq [i]); } Shield.InTransaction(() => { seq.Append(0); // test commute Assert.AreEqual(0, seq [20]); } ); Assert.AreEqual(21, seq.Count); var a = Shield.InTransaction(() => seq.TakeHead()); Assert.AreEqual(20, seq.Count); Assert.AreEqual(21, a); for (int i = 0; i < 19; i++) { Assert.AreEqual(i + 22, seq [i]); } Assert.AreEqual(0, seq [19]); Shield.InTransaction(() => { seq.Prepend(a); seq.RemoveAt(20); } ); Assert.AreEqual(20, seq.Count); for (int i = 0; i < 20; i++) { Assert.AreEqual(i + 21, seq [i]); } Shield.InTransaction(() => seq.RemoveAll(i => (i & 1) == 1)); Assert.AreEqual(10, seq.Count); for (int i = 0; i < 10; i++) { Assert.AreEqual(i * 2 + 22, seq [i]); } var seq2 = new ShieldedSeq <int>( Shield.InTransaction(() => seq.ToArray())); Shield.InTransaction(() => seq.RemoveAll(i => true)); Assert.AreEqual(0, seq.Count); Shield.InTransaction(() => seq2.Clear()); Assert.AreEqual(0, seq2.Count); var seq3 = new ShieldedSeq <int>( Enumerable.Range(1, 5).ToArray()); Shield.InTransaction(() => seq3.RemoveAt(0)); Assert.AreEqual(4, seq3.Count); Assert.AreEqual(2, seq3 [0]); Shield.InTransaction(() => seq3.RemoveAt(3)); Assert.AreEqual(3, seq3.Count); Assert.AreEqual(4, seq3 [2]); Shield.InTransaction(() => seq3.Append(100)); Assert.AreEqual(4, seq3.Count); Assert.AreEqual(100, seq3 [3]); Shield.InTransaction(() => seq3.RemoveAll(i => i == 100)); Assert.AreEqual(3, seq3.Count); Assert.AreEqual(4, seq3 [2]); Shield.InTransaction(() => seq3.Append(100)); Assert.AreEqual(4, seq3.Count); Assert.AreEqual(100, seq3 [3]); }
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 Validation() { var list1 = new ShieldedSeq<int>(Enumerable.Range(1, 100).ToArray()); var list2 = new ShieldedSeq<int>(); int validationFails = 0; Shield.PreCommit(() => list1.Count + list2.Count != 100, () => { Interlocked.Increment(ref validationFails); throw new ValidationException(); }); int transactionCount = 0; Task.WaitAll( Enumerable.Range(1, 100).Select(i => Task.Factory.StartNew(() => { try { Shield.InTransaction(() => { Interlocked.Increment(ref transactionCount); int x = list1.TakeHead(); if (i < 100) list2.Append(x); }); Assert.AreNotEqual(100, i); } catch (ValidationException) { Assert.AreEqual(100, i); } }, TaskCreationOptions.LongRunning)).ToArray()); Assert.AreEqual(1, validationFails); Assert.AreEqual(1, list1.Count); Assert.AreEqual(99, list2.Count); }