public void TestNestedLockAndWait() { NmsSynchronizationMonitor syncRoot = new NmsSynchronizationMonitor(); ManualResetEvent lockedEvent = new ManualResetEvent(false); EventList evList = new EventList(); var task = Task.Run(() => { Thread.Sleep(5); using (syncRoot.Lock()) { evList.Add("A1"); Thread.Sleep(1); evList.Add("A1"); Thread.Sleep(1); evList.Add("A1"); Thread.Sleep(1); using (syncRoot.Lock()) { evList.Add("A1"); Thread.Sleep(1); evList.Add("A1"); Thread.Sleep(1); evList.Add("A1"); Thread.Sleep(1); lockedEvent.Set(); syncRoot.Wait(); evList.Add("A2"); Thread.Sleep(1); evList.Add("A2"); Thread.Sleep(1); evList.Add("A2"); Thread.Sleep(1); } evList.Add("A2"); Thread.Sleep(1); evList.Add("A2"); Thread.Sleep(1); evList.Add("A2"); Thread.Sleep(1); } }); var taskB = Task.Run(() => { while (!task.IsCompleted) { using (syncRoot.Lock()) { evList.Add("B"); Thread.Sleep(1); } } }); lockedEvent.WaitOne(); Thread.Sleep(10); // to give Task A Time to go to sleep and B to work Task.Run(() => { syncRoot.Pulse(); }); task.Wait(); taskB.Wait(); // Now Asses that block A1 and A2 are not intersected by B, however A1 And A2 should have some B between (during sleep period) var events = evList.list.Select((a, i) => new Tuple <string, int>(a, i)); var a1 = events.Where(a => a.Item1 == "A1").ToList(); var a2 = events.Where(a => a.Item1 == "A2").ToList(); Assert.AreEqual(6, a1.Count()); Assert.AreEqual(5, a1.Last().Item2 - a1.First().Item2); // not intersected by anything Assert.AreEqual(6, a2.Count()); Assert.AreEqual(5, a2.Last().Item2 - a2.First().Item2); // not intersected by anything Assert.Greater(Math.Abs(a1.Last().Item2 - a2.First().Item2), 1, "A1 and A2, should be intersected by B, and not happening one right after another"); }
public void TestConcurrentProducersSyncAndAsync(int sleepTimeMs, int testTimeMs, int minimumOccurences) { EventList evListCommon = new EventList(); NmsSynchronizationMonitor syncRootA = new NmsSynchronizationMonitor(); NmsSynchronizationMonitor syncRootB = new NmsSynchronizationMonitor(); bool runTest = true; // int sleepTimeMs = 1; var task1 = Task.Run(async() => { int counter = 0; while (runTest) { var lockA = (counter % 2 == 0) ? syncRootA.Lock() : await syncRootA.LockAsync(); using (lockA) { await Task.Delay(sleepTimeMs); await Task.Yield(); evListCommon.Add("A1"); await Task.Delay(sleepTimeMs); await Task.Yield(); evListCommon.Add("A1"); await Task.Delay(sleepTimeMs); await Task.Yield(); evListCommon.Add("A1"); await Task.Delay(sleepTimeMs); await Task.Yield(); var lockB = (counter % 2 == 0) ? syncRootB.Lock() : await syncRootB.LockAsync(); using (lockB) { evListCommon.Add("B1"); await Task.Delay(sleepTimeMs); await Task.Yield(); evListCommon.Add("B1"); await Task.Delay(sleepTimeMs); await Task.Yield(); evListCommon.Add("B1"); await Task.Delay(sleepTimeMs); await Task.Yield(); } await Task.Delay(sleepTimeMs); await Task.Yield(); evListCommon.Add("A1"); await Task.Delay(sleepTimeMs); await Task.Yield(); evListCommon.Add("A1"); await Task.Delay(sleepTimeMs); await Task.Yield(); evListCommon.Add("A1"); await Task.Delay(sleepTimeMs); await Task.Yield(); } counter++; } }); var task2 = Task.Run(async() => { int counter = 0; // Also test the reentrancy of lock and mix of async and non async async Task ResourceAccess(string symbol, int level, NmsSynchronizationMonitor syncRoot) { var locked = (counter % 2 == 0) ? syncRoot.Lock() : await syncRoot.LockAsync(); using (locked) { int depth = counter % 4; if (level == depth) { await Task.Delay(sleepTimeMs); await Task.Yield(); evListCommon.Add(symbol); await Task.Delay(sleepTimeMs); await Task.Yield(); evListCommon.Add(symbol); await Task.Delay(sleepTimeMs); await Task.Yield(); evListCommon.Add(symbol); await Task.Delay(sleepTimeMs); await Task.Yield(); } else { await ResourceAccess(symbol, level + 1, syncRoot); } } } while (runTest) { await ResourceAccess("A2", 0, syncRootA); await ResourceAccess("B2", 0, syncRootB); counter++; } }); var task3 = Task.Run(() => { int counter = 0; while (runTest) { var lockA = (counter % 2 == 0) ? syncRootA.Lock() : syncRootA.LockAsync().GetAsyncResult(); using (lockA) { Thread.Sleep(sleepTimeMs); evListCommon.Add("A3"); Thread.Sleep(sleepTimeMs); evListCommon.Add("A3"); Thread.Sleep(sleepTimeMs); evListCommon.Add("A3"); Thread.Sleep(sleepTimeMs); } var lockB = (counter % 2 == 0) ? syncRootB.Lock() : syncRootB.LockAsync().GetAsyncResult(); using (lockB) { evListCommon.Add("B3"); Thread.Sleep(sleepTimeMs); evListCommon.Add("B3"); Thread.Sleep(sleepTimeMs); evListCommon.Add("B3"); Thread.Sleep(sleepTimeMs); } counter++; } }); // Let it run for one sec Thread.Sleep(testTimeMs); runTest = false; task1.Wait(); task2.Wait(); task3.Wait(); var sequenceCommon = evListCommon.ToString(); var sequenceA = evListCommon.ToString("A"); var sequenceB = evListCommon.ToString("B"); // remove B locks interfering with A sequence of rvalidating task1 Enumerable.Range(0, 10).ToList().ForEach((i) => sequenceCommon = sequenceCommon .Replace("A1B2", "A1") .Replace("A1B3", "A1") .Replace("B2A1", "A1") .Replace("B3A1", "A1") ); sequenceCommon = sequenceCommon.Replace("A1A1A1B1B1B1A1A1A1", ""); // The only allowed sequence in common for task1 is nested sequence Assert.IsFalse(sequenceCommon.Contains("A1"), "Sequence should only contain task 1 resource in right order:" + sequenceCommon); Assert.IsFalse(sequenceCommon.Contains("B1"), "Sequence should only contain task 1 resource in right order:" + sequenceCommon); int countA1 = sequenceA.Where(a => a == '1').Count(); int countA2 = sequenceA.Where(a => a == '2').Count(); int countA3 = sequenceA.Where(a => a == '3').Count(); int countB1 = sequenceB.Where(a => a == '1').Count(); int countB2 = sequenceB.Where(a => a == '2').Count(); int countB3 = sequenceB.Where(a => a == '3').Count(); // Assert that all of the threads had their fair share of action Assert.GreaterOrEqual(countA1, minimumOccurences); Assert.GreaterOrEqual(countA2, minimumOccurences); Assert.GreaterOrEqual(countA3, minimumOccurences); Assert.GreaterOrEqual(countB1, minimumOccurences); Assert.GreaterOrEqual(countB2, minimumOccurences); Assert.GreaterOrEqual(countB3, minimumOccurences); sequenceA = sequenceA.Replace("A1A1A1", ""); sequenceA = sequenceA.Replace("A2A2A2", ""); sequenceA = sequenceA.Replace("A3A3A3", ""); sequenceB = sequenceB.Replace("B1B1B1", ""); sequenceB = sequenceB.Replace("B2B2B2", ""); sequenceB = sequenceB.Replace("B3B3B3", ""); Assert.AreEqual(0, sequenceA.Length, "There were illegal sequences of execution for resource A: " + sequenceA); Assert.AreEqual(0, sequenceB.Length, "There were illegal sequences of execution for resource B: " + sequenceB); }