public void CannotWriteWhileWriting() { using (var x = new ReadWriteLock()) { var ok = true; var doneThread = false; using (x.EnterWrite()) { Assert.IsTrue(x.IsCurrentThreadWriter); var a = new Thread ( () => { IDisposable engagement = null; try { ok &= !x.TryEnterWrite(out engagement); } finally { if (engagement != null) { engagement.Dispose(); } doneThread = true; } } ); a.Start(); a.Join(); } Assert.IsTrue(ok); Assert.IsTrue(doneThread); } }
public void CanKnowIsWriter() { // ReadWriteLock always able to tell if a therad is the writer using (var x = new ReadWriteLock(true)) { Assert.IsFalse(x.HasWriter); using (x.EnterWrite()) { Assert.IsTrue(x.IsCurrentThreadWriter); Assert.IsTrue(x.HasWriter); } // Not Reentrant ReadWriteLock is not using (var y = new ReadWriteLock(false)) { Assert.IsFalse(y.HasWriter); using (y.EnterWrite()) { Assert.IsTrue(y.IsCurrentThreadWriter); Assert.IsTrue(y.HasWriter); } // ReadWriteLock is not reentrant by default using (var z = new ReadWriteLock()) { Assert.IsFalse(z.HasWriter); using (z.EnterWrite()) { Assert.IsTrue(z.IsCurrentThreadWriter); Assert.IsTrue(z.HasWriter); } } } } }
public void ReentryReadToWriteRaceCondition() { using (var w = new ManualResetEvent(false)) { using (var x = new ReadWriteLock(true)) { var enterCount = 0; var doneCount = 0; var errorCount = 0; var successCount = 0; ThreadStart tmp = () => { using (x.EnterRead()) { Interlocked.Increment(ref enterCount); w.WaitOne(); // If a thread is a reader it can become a writer as long as there are no other readers // When we have multiple readers trying to become a writer... try { // write mode is requested and reserved by one thread - others fail using (x.EnterWrite()) { Interlocked.Increment(ref successCount); } } catch (InvalidOperationException) { Interlocked.Increment(ref errorCount); } } Interlocked.Increment(ref doneCount); }; var threads = new Thread[5]; for (var index = 0; index < 5; index++) { threads[index] = new Thread(tmp); } for (var index = 0; index < 5; index++) { threads[index].Start(); } do { Thread.Sleep(10); } while (enterCount < 5); w.Set(); for (var index = 0; index < 5; index++) { threads[index].Join(); } Assert.AreEqual(5, doneCount); Assert.AreEqual(1, successCount); // One succeds - the thread that succeds to reserve write waits for others to leave Assert.AreEqual(4, errorCount); // The others get InvalidOperationException - the threads that fail the reserve fail } // This code results in a dead lock in a not reentrant ReadWriteLock } }
public void CanEnterWrite() { var x = new ReadWriteLock(); using (x.EnterWrite()) { Assert.IsTrue(x.IsCurrentThreadWriter); } }
public void WriteWaitsMultipleReadsToFinish() // TODO: Review { using (var w0 = new ManualResetEvent(false)) { using (var w1 = new ManualResetEvent(false)) { using (var x = new ReadWriteLock()) { var ok = false; int[] z = { 0 }; var threads = new Thread[5]; for (var index = 0; index < 5; index++) { threads[index] = new Thread ( () => { w0.WaitOne(); using (x.EnterRead()) { w1.Set(); Interlocked.Increment(ref z[0]); Thread.Sleep(10); } } ); } var a = new Thread ( () => { w1.WaitOne(); using (x.EnterWrite()) { Assert.IsTrue(x.IsCurrentThreadWriter); ok = Interlocked.Increment(ref z[0]) == 6; } } ); for (var index = 0; index < 5; index++) { threads[index].Start(); } a.Start(); w0.Set(); for (var index = 0; index < 5; index++) { threads[index].Join(); } a.Join(); Assert.IsTrue(ok); Assert.AreEqual(7, Interlocked.Increment(ref z[0])); } } } }
public void CanReentryWriteToRead() { var x = new ReadWriteLock(); using (x.EnterWrite()) { // If a thread is a writer it can be also a reader using (x.EnterRead()) { } } }
public void CannotReentryReadToWriteWhenThereAreMoreReaders() { using (var w1 = new ManualResetEvent(false)) { using (var w2 = new ManualResetEvent(false)) { using (var x = new ReadWriteLock(true)) { var ok = true; var a = new Thread ( () => { using (x.EnterRead()) { w2.Set(); w1.WaitOne(); } } ); a.Start(); w2.WaitOne(); using (x.EnterRead()) { Assert.IsFalse(x.IsCurrentThreadWriter); IDisposable engagement = null; try { ok &= !x.TryEnterWrite(out engagement); } finally { if (engagement != null) { engagement.Dispose(); } } } w1.Set(); a.Join(); using (x.EnterRead()) { using (x.EnterWrite()) { Assert.IsTrue(x.IsCurrentThreadWriter); Assert.IsTrue(ok); } } } // This code results in a dead lock in a not reentrant ReadWriteLock } } }
public void CanReentryReadToWrite() { var x = new ReadWriteLock(true); // This code results in a dead lock in a not reentrant ReadWriteLock using (x.EnterRead()) { Assert.IsFalse(x.IsCurrentThreadWriter); // If a thread is a reader it can become a writer as long as there are no other readers using (x.EnterWrite()) { Assert.IsTrue(x.IsCurrentThreadWriter); } } }
public void WriteWaitsReadToFinish() { using (var w = new ManualResetEvent(false)) { using (var x = new ReadWriteLock()) { int[] z = { 0 }; var ok = true; var a = new Thread ( () => { w.WaitOne(); using (x.EnterWrite()) { ok = ok && Interlocked.Increment(ref z[0]) == 2; } } ); var b = new Thread ( () => { using (x.EnterRead()) { w.Set(); Thread.Sleep(10); ok = ok && Interlocked.Increment(ref z[0]) == 1; } } ); a.Start(); b.Start(); a.Join(); b.Join(); Assert.IsTrue(ok); Assert.AreEqual(3, Interlocked.Increment(ref z[0])); } } }
public void OnlyOneWriterAtTheTime() { using (var w = new ManualResetEvent(false)) { using (var x = new ReadWriteLock()) { int[] z = { 0 }; var ok = true; var threads = new Thread[5]; for (var index = 0; index < 5; index++) { threads[index] = new Thread ( () => { w.WaitOne(); using (x.EnterWrite()) { var got = Interlocked.Increment(ref z[0]); Thread.Sleep(10); ok = ok && Interlocked.Increment(ref z[0]) == got + 1; } } ); } for (var index = 0; index < 5; index++) { threads[index].Start(); } w.Set(); for (var index = 0; index < 5; index++) { threads[index].Join(); } Assert.IsTrue(ok); Assert.AreEqual(11, Interlocked.Increment(ref z[0])); } } }
public void OnlyOneWriterAtTheTimeEx() // TODO: Review - race condition { using (var w = new ManualResetEvent(false)) { using (var x = new ReadWriteLock()) { var doneThread = 0; var ok = true; ThreadStart tmp = () => { w.WaitOne(); IDisposable engagementA = null; try { ok &= !x.TryEnterWrite(out engagementA); } finally { if (engagementA != null) { engagementA.Dispose(); } Interlocked.Increment(ref doneThread); } }; var a = new Thread(tmp); using (x.EnterWrite()) { a.Start(); w.Set(); Thread.Sleep(10); } a.Join(); Assert.IsTrue(ok); Assert.AreEqual(1, doneThread); var b = new Thread(tmp); IDisposable engagementB = null; try { if (x.TryEnterWrite(out engagementB)) { Assert.IsTrue(x.IsCurrentThreadWriter); b.Start(); w.Set(); b.Join(); } else { Assert.Fail(); } } finally { if (engagementB != null) { engagementB.Dispose(); } } Assert.IsTrue(ok); Assert.AreEqual(2, doneThread); } } }
public void WriteWaitsMultipleReadsToFinish() { using (var w0 = new ManualResetEvent(false)) { using (var w1 = new ManualResetEvent(false)) { using (var x = new ReadWriteLock()) { int[] z = { 0, 0 }; var foundReaders = -1; var ok = false; var threads = new Thread[5]; for (var index = 0; index < 5; index++) { threads[index] = new Thread ( () => { w0.WaitOne(); using (x.EnterRead()) { Interlocked.Increment(ref z[1]); w1.Set(); Interlocked.Increment(ref z[0]); Thread.Sleep(10); Interlocked.Decrement(ref z[1]); } } ); } var a = new Thread ( () => { w1.WaitOne(); using (x.EnterWrite()) { foundReaders = Volatile.Read(ref z[1]); ok = x.IsCurrentThreadWriter; Interlocked.Increment(ref z[0]); } } ); for (var index = 0; index < 5; index++) { threads[index].Start(); } a.Start(); w0.Set(); for (var index = 0; index < 5; index++) { threads[index].Join(); } a.Join(); Assert.IsTrue(ok); Assert.AreEqual(0, foundReaders); Assert.AreEqual(7, Interlocked.Increment(ref z[0])); } } } }