public void HeadTests() { var seq4 = new ShieldedSeq <int>( Enumerable.Range(1, 10).ToArray()); Shield.InTransaction(() => { seq4.Prepend(100); }); Assert.AreEqual(11, seq4.Count); Assert.AreEqual(100, seq4.Head); Assert.AreEqual(100, seq4.First()); Assert.IsTrue(seq4.Any()); Assert.AreEqual(100, Shield.InTransaction(() => seq4.TakeHead())); Assert.AreEqual(10, seq4.Count); Assert.AreEqual(1, seq4.Head); Assert.IsTrue(seq4.Any()); for (int i = 0; i < 10; i++) { Assert.AreEqual(i + 1, seq4.Head); Assert.AreEqual(i + 1, Shield.InTransaction(() => seq4.TakeHead())); } Assert.Throws <InvalidOperationException>(() => Shield.InTransaction(() => { seq4.TakeHead(); })); Assert.Throws <InvalidOperationException>(() => { var x = seq4.Head; }); Assert.IsFalse(seq4.Any()); Assert.AreEqual(0, seq4.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.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 RemoveTest() { var seq = new ShieldedSeq <int>( Enumerable.Range(1, 20).ToArray()); Shield.InTransaction(() => Assert.IsTrue(seq.Remove(5))); Assert.AreEqual(19, seq.Count); Assert.IsTrue(seq.Any()); Assert.AreEqual(1, seq.Head); for (int i = 1; i <= 20; i++) { if (i == 5) { continue; } else { Assert.AreEqual(i, seq[i > 5 ? i - 2 : i - 1]); } } Shield.InTransaction(() => Assert.IsTrue(seq.Remove(1))); Assert.AreEqual(18, seq.Count); Assert.IsTrue(seq.Any()); Assert.AreEqual(2, seq.Head); Shield.InTransaction(() => Assert.IsTrue(seq.Remove(20))); Shield.InTransaction(() => Assert.IsFalse(seq.Remove(30))); Assert.AreEqual(17, seq.Count); Assert.IsTrue(seq.Any()); Assert.AreEqual(2, seq.Head); Assert.AreEqual(19, seq[16]); }
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 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 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 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(); } 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 RemoveTest() { var seq = new ShieldedSeq<int>( Enumerable.Range(1, 20).ToArray()); Shield.InTransaction(() => Assert.IsTrue(seq.Remove(5))); Assert.AreEqual(19, seq.Count); Assert.IsTrue(seq.Any()); Assert.AreEqual(1, seq.Head); for (int i = 1; i <= 20; i++) if (i == 5) continue; else Assert.AreEqual(i, seq[i > 5 ? i - 2 : i - 1]); Shield.InTransaction(() => Assert.IsTrue(seq.Remove(1))); Assert.AreEqual(18, seq.Count); Assert.IsTrue(seq.Any()); Assert.AreEqual(2, seq.Head); Shield.InTransaction(() => Assert.IsTrue(seq.Remove(20))); Shield.InTransaction(() => Assert.IsFalse(seq.Remove(30))); Assert.AreEqual(17, seq.Count); Assert.IsTrue(seq.Any()); Assert.AreEqual(2, seq.Head); Assert.AreEqual(19, seq[16]); }
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 HeadTests() { var seq4 = new ShieldedSeq<int>( Enumerable.Range(1, 10).ToArray()); Shield.InTransaction(() => { seq4.Prepend(100); }); Assert.AreEqual(11, seq4.Count); Assert.AreEqual(100, seq4.Head); Assert.AreEqual(100, seq4.First()); Assert.IsTrue(seq4.Any()); Assert.AreEqual(100, Shield.InTransaction(() => seq4.TakeHead())); Assert.AreEqual(10, seq4.Count); Assert.AreEqual(1, seq4.Head); Assert.IsTrue(seq4.Any()); for (int i = 0; i < 10; i++) { Assert.AreEqual(i + 1, seq4.Head); Assert.AreEqual(i + 1, Shield.InTransaction(() => seq4.TakeHead())); } Assert.Throws<InvalidOperationException>(() => Shield.InTransaction(() => { seq4.TakeHead(); })); Assert.Throws<InvalidOperationException>(() => { var x = seq4.Head; }); Assert.IsFalse(seq4.Any()); Assert.AreEqual(0, seq4.Count); }