// [Fact(Skip = "outerloop")] public void RunConcurrentExclusiveSchedulerPairTests() { // Validate invalid arguments { Assert.Throws <ArgumentNullException>(() => new ConcurrentExclusiveSchedulerPair(null)); Assert.Throws <ArgumentOutOfRangeException>(() => new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, -2)); Assert.Throws <ArgumentOutOfRangeException>(() => new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 0)); Assert.Throws <ArgumentOutOfRangeException>(() => new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 1, -2)); Assert.Throws <ArgumentOutOfRangeException>(() => new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 1, 0)); } // Validate completion prevents more tasks { bool localPassed = true; ConcurrentExclusiveSchedulerPair cesp = new ConcurrentExclusiveSchedulerPair(); cesp.Complete(); Assert.Throws <TaskSchedulerException>(() => Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, cesp.ConcurrentScheduler).Wait()); Assert.Throws <TaskSchedulerException>(() => Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, cesp.ExclusiveScheduler).Wait()); Assert.True(localPassed, string.Format("{0}: Completion prevents more tasks", localPassed ? "Success" : "Failure")); } // Validate completion allows existing scheduled tasks to complete { bool localPassed = true; ConcurrentExclusiveSchedulerPair cesp = new ConcurrentExclusiveSchedulerPair(); int tasksToSchedule = 2; int count = 0; var tasks = new Task[tasksToSchedule]; using (var mres = new ManualResetEventSlim()) { for (int i = 0; i < tasksToSchedule; i++) { tasks[i] = Task.Factory.StartNew(() => { mres.Wait(); Interlocked.Increment(ref count); }, CancellationToken.None, TaskCreationOptions.None, cesp.ExclusiveScheduler); } cesp.Complete(); Assert.True(count == 0, "No tasks should have completed yet"); mres.Set(); Task.WaitAll(tasks); Assert.True(count == tasksToSchedule, "All of the tasks should have executed"); cesp.Completion.Wait(); } Assert.True(localPassed, string.Format("{0}: Completion allows existing tasks to complete", localPassed ? "Success" : "Failure")); } // Validate MCL handling { bool localPassed = true; { var cesp = new ConcurrentExclusiveSchedulerPair(new ControllableMclTaskScheduler(4), 8); localPassed &= cesp.ConcurrentScheduler.MaximumConcurrencyLevel == 4; localPassed &= cesp.ExclusiveScheduler.MaximumConcurrencyLevel == 1; } { var cesp = new ConcurrentExclusiveSchedulerPair(new ControllableMclTaskScheduler(8), 4); localPassed &= cesp.ConcurrentScheduler.MaximumConcurrencyLevel == 4; localPassed &= cesp.ExclusiveScheduler.MaximumConcurrencyLevel == 1; } { var cesp = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, -1); localPassed &= cesp.ConcurrentScheduler.MaximumConcurrencyLevel == Int32.MaxValue; localPassed &= cesp.ExclusiveScheduler.MaximumConcurrencyLevel == 1; } { var cesp = new ConcurrentExclusiveSchedulerPair(new ControllableMclTaskScheduler(-2), -1); localPassed &= cesp.ConcurrentScheduler.MaximumConcurrencyLevel == Int32.MaxValue; localPassed &= cesp.ExclusiveScheduler.MaximumConcurrencyLevel == 1; } { var cesp = new ConcurrentExclusiveSchedulerPair(new ControllableMclTaskScheduler(-2), 1); localPassed &= cesp.ConcurrentScheduler.MaximumConcurrencyLevel == 1; localPassed &= cesp.ExclusiveScheduler.MaximumConcurrencyLevel == 1; } Assert.True(localPassed, string.Format("{0}: Max Concurrency Level corner cases handling correctly", localPassed ? "Success" : "Failure")); } // Validate queueing when layering on a faulty scheduler { bool localPassed = true; var ts = new ControllableTaskScheduler(); // NOTE: We're using new pair instances on each iteration to avoid code paths // where a replica task gets queued up in the scheduler. If that happens while the underlying // scheduler is FailQueueing==true, the task used internally by CESP will fault // and will go unobserved. This is by design and we don't want it bringing down the tests. var cesp1 = new ConcurrentExclusiveSchedulerPair(ts); var cesp2 = new ConcurrentExclusiveSchedulerPair(ts); foreach (var cesp in new[] { cesp1, cesp2 }) { var scheduler = cesp == cesp1 ? cesp1.ConcurrentScheduler : cesp2.ExclusiveScheduler; // Queue a task that will cause the CESP to fail queueing to its underlying scheduler ts.FailQueueing = true; Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, scheduler); try { Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, scheduler); localPassed = false; } catch (Exception exc) { Assert.True( exc is TaskSchedulerException && ((TaskSchedulerException)exc).InnerException is InvalidOperationException, "Expected a TaskSchedulerException containing an InvalidOperationException"); } Assert.True(SpinWait.SpinUntil(() => cesp.Completion.IsCompleted, 1), "Excepted CESP to complete in allotted time"); Assert.True(cesp.Completion.Exception != null, "Excepted CESP task to have exceptions"); } Assert.True(localPassed, string.Format("{0}: Test queueing layering on faulty scheduler", localPassed ? "Success" : "Failure")); } // Validate inlining when layering on a faulty scheduler { bool localPassed = true; var ts = new ControllableTaskScheduler(); // NOTE: We're using new pair instances on each iteration to avoid code paths // where a replica task gets queued up in the scheduler. If that happens while the underlying // scheduler is FailQueueing==true, the task used internally by CESP will fault // and will go unobserved. This is by design and we don't want it bringing down the tests. var cesp1 = new ConcurrentExclusiveSchedulerPair(ts); var cesp2 = new ConcurrentExclusiveSchedulerPair(ts); foreach (var cesp in new[] { cesp1, cesp2 }) { var scheduler = cesp == cesp1 ? cesp1.ConcurrentScheduler : cesp2.ExclusiveScheduler; // Inline a task that will cause the CESP to fail queueing to its underlying scheduler ts.FailQueueing = false; Task.Factory.StartNew(() => { ts.FailQueueing = true; Task t = new Task(() => { }); try { t.RunSynchronously(scheduler); localPassed = false; } catch (Exception exc) { Assert.True( exc is TaskSchedulerException && ((TaskSchedulerException)exc).InnerException is TaskSchedulerException, "Excepted a TaskSchedulerException to contain another TaskSchedulerException"); } Assert.True(t.IsCompleted, "Expected the queued task to be completed"); Assert.True(t.Exception != null, "Expected the queued task to be faulted"); }, CancellationToken.None, TaskCreationOptions.None, scheduler).Wait(); ts.FailQueueing = false; Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, scheduler).Wait(); cesp.Complete(); Assert.True(SpinWait.SpinUntil(() => cesp.Completion.IsCompleted, 1), "Expected the CESP to complete in the allotted time"); Assert.True(cesp.Completion.Exception == null, "Expected the task to not be faulted and have no exceptions"); } Assert.True(localPassed, string.Format("{0}: Test inlining layering on faulty scheduler", localPassed ? "Success" : "Failure")); } // Validate tasks on the same scheduler waiting on each other { bool localPassed = true; foreach (var underlyingScheduler in new[] { TaskScheduler.Default, new ControllableTaskScheduler() { FailQueueing = false } }) { var cesp = new ConcurrentExclusiveSchedulerPair(underlyingScheduler); TaskRecursion(2, new ParallelOptions { TaskScheduler = cesp.ExclusiveScheduler }); cesp.Complete(); cesp = new ConcurrentExclusiveSchedulerPair(underlyingScheduler); TaskRecursion(2, new ParallelOptions { TaskScheduler = cesp.ConcurrentScheduler }); cesp.Complete(); } Assert.True(localPassed, string.Format("{0}: Recursively waiting on same scheduler", localPassed ? "Success" : "Failure")); } // Exercise additional inlining code paths { bool localPassed = true; foreach (var underlyingScheduler in new[] { TaskScheduler.Default, new ControllableTaskScheduler() { FailQueueing = false } }) { var cesp = new ConcurrentExclusiveSchedulerPair(underlyingScheduler); Task.Factory.StartNew(() => { }).ContinueWith(_ => { }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, cesp.ExclusiveScheduler).Wait(); Task.Factory.StartNew(() => { }).ContinueWith(_ => { }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, cesp.ConcurrentScheduler).Wait(); var t = new Task(() => { }); t.RunSynchronously(cesp.ConcurrentScheduler); t.Wait(); t = new Task(() => { }); t.RunSynchronously(cesp.ExclusiveScheduler); t.Wait(); Task.Factory.StartNew(() => { new Task(() => { }, TaskCreationOptions.AttachedToParent).RunSynchronously(cesp.ConcurrentScheduler); }, CancellationToken.None, TaskCreationOptions.None, cesp.ConcurrentScheduler).Wait(); Task.Factory.StartNew(() => { new Task(() => { }, TaskCreationOptions.AttachedToParent).RunSynchronously(cesp.ExclusiveScheduler); }, CancellationToken.None, TaskCreationOptions.None, cesp.ExclusiveScheduler).Wait(); } Assert.True(localPassed, string.Format("{0}: Additional inlining code paths", localPassed ? "Success" : "Failure")); } }
public void RunConcurrentExclusiveSchedulerPairTests() { // Validate invalid arguments { Assert.Throws<ArgumentNullException>(() => new ConcurrentExclusiveSchedulerPair(null)); Assert.Throws<ArgumentOutOfRangeException>(() => new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, -2)); Assert.Throws<ArgumentOutOfRangeException>(() => new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 0)); Assert.Throws<ArgumentOutOfRangeException>(() => new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 1, -2)); Assert.Throws<ArgumentOutOfRangeException>(() => new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 1, 0)); } // Validate completion prevents more tasks { bool localPassed = true; ConcurrentExclusiveSchedulerPair cesp = new ConcurrentExclusiveSchedulerPair(); cesp.Complete(); Assert.Throws<TaskSchedulerException>(() => Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, cesp.ConcurrentScheduler).Wait()); Assert.Throws<TaskSchedulerException>(() => Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, cesp.ExclusiveScheduler).Wait()); Assert.True(localPassed, string.Format("{0}: Completion prevents more tasks", localPassed ? "Success" : "Failure")); } // Validate completion allows existing scheduled tasks to complete { bool localPassed = true; ConcurrentExclusiveSchedulerPair cesp = new ConcurrentExclusiveSchedulerPair(); int tasksToSchedule = 2; int count = 0; var tasks = new Task[tasksToSchedule]; using (var mres = new ManualResetEventSlim()) { for (int i = 0; i < tasksToSchedule; i++) { tasks[i] = Task.Factory.StartNew(() => { mres.Wait(); Interlocked.Increment(ref count); }, CancellationToken.None, TaskCreationOptions.None, cesp.ExclusiveScheduler); } cesp.Complete(); Assert.True(count == 0, "No tasks should have completed yet"); mres.Set(); Task.WaitAll(tasks); Assert.True(count == tasksToSchedule, "All of the tasks should have executed"); cesp.Completion.Wait(); } Assert.True(localPassed, string.Format("{0}: Completion allows existing tasks to complete", localPassed ? "Success" : "Failure")); } // Validate MCL handling { bool localPassed = true; { var cesp = new ConcurrentExclusiveSchedulerPair(new ControllableMclTaskScheduler(4), 8); localPassed &= cesp.ConcurrentScheduler.MaximumConcurrencyLevel == 4; localPassed &= cesp.ExclusiveScheduler.MaximumConcurrencyLevel == 1; } { var cesp = new ConcurrentExclusiveSchedulerPair(new ControllableMclTaskScheduler(8), 4); localPassed &= cesp.ConcurrentScheduler.MaximumConcurrencyLevel == 4; localPassed &= cesp.ExclusiveScheduler.MaximumConcurrencyLevel == 1; } { var cesp = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, -1); localPassed &= cesp.ConcurrentScheduler.MaximumConcurrencyLevel == Int32.MaxValue; localPassed &= cesp.ExclusiveScheduler.MaximumConcurrencyLevel == 1; } { var cesp = new ConcurrentExclusiveSchedulerPair(new ControllableMclTaskScheduler(-2), -1); localPassed &= cesp.ConcurrentScheduler.MaximumConcurrencyLevel == Int32.MaxValue; localPassed &= cesp.ExclusiveScheduler.MaximumConcurrencyLevel == 1; } { var cesp = new ConcurrentExclusiveSchedulerPair(new ControllableMclTaskScheduler(-2), 1); localPassed &= cesp.ConcurrentScheduler.MaximumConcurrencyLevel == 1; localPassed &= cesp.ExclusiveScheduler.MaximumConcurrencyLevel == 1; } Assert.True(localPassed, string.Format("{0}: Max Concurrency Level corner cases handling correctly", localPassed ? "Success" : "Failure")); } // Validate queueing when layering on a faulty scheduler { bool localPassed = true; var ts = new ControllableTaskScheduler(); // NOTE: We're using new pair instances on each iteration to avoid code paths // where a replica task gets queued up in the scheduler. If that happens while the underlying // scheduler is FailQueueing==true, the task used internally by CESP will fault // and will go unobserved. This is by design and we don't want it bringing down the tests. var cesp1 = new ConcurrentExclusiveSchedulerPair(ts); var cesp2 = new ConcurrentExclusiveSchedulerPair(ts); foreach (var cesp in new[] { cesp1, cesp2 }) { var scheduler = cesp == cesp1 ? cesp1.ConcurrentScheduler : cesp2.ExclusiveScheduler; // Queue a task that will cause the CESP to fail queueing to its underlying scheduler ts.FailQueueing = true; Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, scheduler); try { Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, scheduler); localPassed = false; } catch (Exception exc) { Assert.True( exc is TaskSchedulerException && ((TaskSchedulerException)exc).InnerException is InvalidOperationException, "Expected a TaskSchedulerException containing an InvalidOperationException"); } Assert.True(SpinWait.SpinUntil(() => cesp.Completion.IsCompleted, 1), "Excepted CESP to complete in allotted time"); Assert.True(cesp.Completion.Exception != null, "Excepted CESP task to have exceptions"); } Assert.True(localPassed, string.Format("{0}: Test queueing layering on faulty scheduler", localPassed ? "Success" : "Failure")); } // Validate inlining when layering on a faulty scheduler { bool localPassed = true; var ts = new ControllableTaskScheduler(); // NOTE: We're using new pair instances on each iteration to avoid code paths // where a replica task gets queued up in the scheduler. If that happens while the underlying // scheduler is FailQueueing==true, the task used internally by CESP will fault // and will go unobserved. This is by design and we don't want it bringing down the tests. var cesp1 = new ConcurrentExclusiveSchedulerPair(ts); var cesp2 = new ConcurrentExclusiveSchedulerPair(ts); foreach (var cesp in new[] { cesp1, cesp2 }) { var scheduler = cesp == cesp1 ? cesp1.ConcurrentScheduler : cesp2.ExclusiveScheduler; // Inline a task that will cause the CESP to fail queueing to its underlying scheduler ts.FailQueueing = false; Task.Factory.StartNew(() => { ts.FailQueueing = true; Task t = new Task(() => { }); try { t.RunSynchronously(scheduler); localPassed = false; } catch (Exception exc) { Assert.True( exc is TaskSchedulerException && ((TaskSchedulerException)exc).InnerException is TaskSchedulerException, "Excepted a TaskSchedulerException to contain another TaskSchedulerException"); } Assert.True(t.IsCompleted, "Expected the queued task to be completed"); Assert.True(t.Exception != null, "Expected the queued task to be faulted"); }, CancellationToken.None, TaskCreationOptions.None, scheduler).Wait(); ts.FailQueueing = false; Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, scheduler).Wait(); cesp.Complete(); Assert.True(SpinWait.SpinUntil(() => cesp.Completion.IsCompleted, 1), "Expected the CESP to complete in the allotted time"); Assert.True(cesp.Completion.Exception == null, "Expected the task to not be faulted and have no exceptions"); } Assert.True(localPassed, string.Format("{0}: Test inlining layering on faulty scheduler", localPassed ? "Success" : "Failure")); } // Validate tasks on the same scheduler waiting on each other { bool localPassed = true; foreach (var underlyingScheduler in new[] { TaskScheduler.Default, new ControllableTaskScheduler() { FailQueueing = false } }) { var cesp = new ConcurrentExclusiveSchedulerPair(underlyingScheduler); TaskRecursion(2, new ParallelOptions { TaskScheduler = cesp.ExclusiveScheduler }); cesp.Complete(); cesp = new ConcurrentExclusiveSchedulerPair(underlyingScheduler); TaskRecursion(2, new ParallelOptions { TaskScheduler = cesp.ConcurrentScheduler }); cesp.Complete(); } Assert.True(localPassed, string.Format("{0}: Recursively waiting on same scheduler", localPassed ? "Success" : "Failure")); } // Exercise additional inlining code paths { bool localPassed = true; foreach (var underlyingScheduler in new[] { TaskScheduler.Default, new ControllableTaskScheduler() { FailQueueing = false } }) { var cesp = new ConcurrentExclusiveSchedulerPair(underlyingScheduler); Task.Factory.StartNew(() => { }).ContinueWith(_ => { }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, cesp.ExclusiveScheduler).Wait(); Task.Factory.StartNew(() => { }).ContinueWith(_ => { }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, cesp.ConcurrentScheduler).Wait(); var t = new Task(() => { }); t.RunSynchronously(cesp.ConcurrentScheduler); t.Wait(); t = new Task(() => { }); t.RunSynchronously(cesp.ExclusiveScheduler); t.Wait(); Task.Factory.StartNew(() => { new Task(() => { }, TaskCreationOptions.AttachedToParent).RunSynchronously(cesp.ConcurrentScheduler); }, CancellationToken.None, TaskCreationOptions.None, cesp.ConcurrentScheduler).Wait(); Task.Factory.StartNew(() => { new Task(() => { }, TaskCreationOptions.AttachedToParent).RunSynchronously(cesp.ExclusiveScheduler); }, CancellationToken.None, TaskCreationOptions.None, cesp.ExclusiveScheduler).Wait(); } Assert.True(localPassed, string.Format("{0}: Additional inlining code paths", localPassed ? "Success" : "Failure")); } }