public static void TestConcurrentExclusiveChain(bool syncContinuations) { var scheduler = new TrackingTaskScheduler(Environment.ProcessorCount); var cesp = new ConcurrentExclusiveSchedulerPair(scheduler); // continuations { var starter = new Task(() => { }); var t = starter; for (int i = 0; i < 10; i++) { t = t.ContinueWith(delegate { }, CancellationToken.None, syncContinuations ? TaskContinuationOptions.ExecuteSynchronously : TaskContinuationOptions.None, cesp.ConcurrentScheduler); t = t.ContinueWith(delegate { }, CancellationToken.None, syncContinuations ? TaskContinuationOptions.ExecuteSynchronously : TaskContinuationOptions.None, cesp.ExclusiveScheduler); } starter.Start(cesp.ExclusiveScheduler); t.Wait(); } // parent/child { var errorString = "hello faulty world"; var root = Task.Factory.StartNew(() => { Task.Factory.StartNew(() => { Task.Factory.StartNew(() => { Task.Factory.StartNew(() => { Task.Factory.StartNew(() => { Task.Factory.StartNew(() => { Task.Factory.StartNew(() => { throw new InvalidOperationException(errorString); }, CancellationToken.None, TaskCreationOptions.AttachedToParent, cesp.ExclusiveScheduler).Wait(); }, CancellationToken.None, TaskCreationOptions.AttachedToParent, cesp.ExclusiveScheduler); }, CancellationToken.None, TaskCreationOptions.AttachedToParent, cesp.ConcurrentScheduler); }, CancellationToken.None, TaskCreationOptions.AttachedToParent, cesp.ExclusiveScheduler); }, CancellationToken.None, TaskCreationOptions.AttachedToParent, cesp.ConcurrentScheduler); }, CancellationToken.None, TaskCreationOptions.AttachedToParent, cesp.ExclusiveScheduler); }, CancellationToken.None, TaskCreationOptions.None, cesp.ConcurrentScheduler); ((IAsyncResult)root).AsyncWaitHandle.WaitOne(); Assert.True(root.IsFaulted, "Root should have been faulted by child's error"); var ae = root.Exception.Flatten(); Assert.True(ae.InnerException is InvalidOperationException && ae.InnerException.Message == errorString, "Child's exception should have propagated to the root."); } }
public static void TestLowerConcurrencyLevel() { //a custom scheduler with maxConcurrencyLevel of one int customSchedulerConcurrency = 1; TrackingTaskScheduler scheduler = new TrackingTaskScheduler(customSchedulerConcurrency); // specify a maxConcurrencyLevel > TaskScheduler's maxconcurrencyLevel to ensure the pair takes the min of the two ConcurrentExclusiveSchedulerPair schedPair = new ConcurrentExclusiveSchedulerPair(scheduler, int.MaxValue); Assert.Equal(scheduler.MaximumConcurrencyLevel, schedPair.ConcurrentScheduler.MaximumConcurrencyLevel); //Now schedule a reader task that would block and verify that more reader tasks scheduled are not executed //(as long as the first task is blocked) TaskFactory readers = new TaskFactory(schedPair.ConcurrentScheduler); ManualResetEvent blockReaderTaskEvent = new ManualResetEvent(false); ManualResetEvent blockMainThreadEvent = new ManualResetEvent(false); //Add a reader tasks that would block readers.StartNew(() => { blockMainThreadEvent.Set(); blockReaderTaskEvent.WaitOne(); }); blockMainThreadEvent.WaitOne(); // wait for the blockedTask to start execution //Now add more reader tasks int maxConcurrentTasks = Environment.ProcessorCount; List <Task> taskList = new List <Task>(); for (int i = 0; i < maxConcurrentTasks; i++) { taskList.Add(readers.StartNew(() => { })); //schedule some dummy reader tasks } foreach (Task task in taskList) { bool wasTaskStarted = (task.Status != TaskStatus.Running) && (task.Status != TaskStatus.RanToCompletion); Assert.True(wasTaskStarted, string.Format("Additional reader tasks should not start when scheduler concurrency is {0} and a reader task is blocked", customSchedulerConcurrency)); } //finally unblock the blocjedTask and wait for all of the tasks, to ensure they all executed properly blockReaderTaskEvent.Set(); Task.WaitAll(taskList.ToArray()); }
public static void TestMaxItemsPerTask(int maxConcurrency, int maxItemsPerTask, bool completeBeforeTaskWait) { //Create a custom TaskScheduler with specified max concurrency (TrackingTaskScheduler is defined in Common\tools\CommonUtils\TPLTestSchedulers.cs) TrackingTaskScheduler scheduler = new TrackingTaskScheduler(maxConcurrency); //We need to use the custom scheduler to achieve the results. As a by-product, we test to ensure custom schedulers are supported ConcurrentExclusiveSchedulerPair schedPair = new ConcurrentExclusiveSchedulerPair(scheduler, maxConcurrency, maxItemsPerTask); TaskFactory readers = new TaskFactory(schedPair.ConcurrentScheduler); //get reader and writer schedulers TaskFactory writers = new TaskFactory(schedPair.ExclusiveScheduler); //These are threadlocals to ensure that no concurrency side effects occur ThreadLocal <int> itemsExecutedCount = new ThreadLocal <int>(); //Track the items executed by CEScheduler Task ThreadLocal <int> schedulerIDInsideTask = new ThreadLocal <int>(); //Used to store the Scheduler ID observed by a Task Executed by CEScheduler Task //Work done by both reader and writer tasks Action work = () => { //Get the id of the parent Task (which is the task created by the scheduler). Each task run by the scheduler task should //see the same SchedulerID value since they are run on the same thread int id = ((TrackingTaskScheduler)scheduler).SchedulerID.Value; if (id == schedulerIDInsideTask.Value) { //since ids match, this is one more Task being executed by the CEScheduler Task itemsExecutedCount.Value = ++itemsExecutedCount.Value; //This does not need to be thread safe since we are looking to ensure that only n number of tasks were executed and not the order //in which they were executed. Also asserting inside the thread is fine since we just want the test to be marked as failure Assert.True(itemsExecutedCount.Value <= maxItemsPerTask, string.Format("itemsExecutedCount={0} cant be greater than maxValue={1}. Parent TaskID={2}", itemsExecutedCount, maxItemsPerTask, id)); } else { //Since ids don't match, this is the first Task being executed in the CEScheduler Task schedulerIDInsideTask.Value = id; //cache the scheduler ID seen by the thread, so other tasks running in same thread can see this itemsExecutedCount.Value = 1; } //Give enough time for a Task to stay around, so that other tasks will be executed by the same CEScheduler Task //or else the CESchedulerTask will die and each Task might get executed by a different CEScheduler Task. This does not affect the //verifications, but its increases the chance of finding a bug if the maxItemPerTask is not respected new ManualResetEvent(false).WaitOne(1); }; List <Task> taskList = new List <Task>(); int maxConcurrentTasks = maxConcurrency * maxItemsPerTask * 5; int maxExclusiveTasks = maxConcurrency * maxItemsPerTask * 2; // Schedule Tasks in both concurrent and exclusive mode for (int i = 0; i < maxConcurrentTasks; i++) { taskList.Add(readers.StartNew(work)); } for (int i = 0; i < maxExclusiveTasks; i++) { taskList.Add(writers.StartNew(work)); } if (completeBeforeTaskWait) { schedPair.Complete(); schedPair.Completion.Wait(); Assert.True(taskList.TrueForAll(t => t.IsCompleted), "All tasks should have completed for scheduler to complete"); } //finally wait for all of the tasks, to ensure they all executed properly Task.WaitAll(taskList.ToArray()); if (!completeBeforeTaskWait) { schedPair.Complete(); schedPair.Completion.Wait(); Assert.True(taskList.TrueForAll(t => t.IsCompleted), "All tasks should have completed for scheduler to complete"); } }
public static void TestLowerConcurrencyLevel() { //a custom scheduler with maxConcurrencyLevel of one int customSchedulerConcurrency = 1; TrackingTaskScheduler scheduler = new TrackingTaskScheduler(customSchedulerConcurrency); // specify a maxConcurrencyLevel > TaskScheduler's maxconcurrencyLevel to ensure the pair takes the min of the two ConcurrentExclusiveSchedulerPair schedPair = new ConcurrentExclusiveSchedulerPair(scheduler, Int32.MaxValue); Assert.Equal(scheduler.MaximumConcurrencyLevel, schedPair.ConcurrentScheduler.MaximumConcurrencyLevel); //Now schedule a reader task that would block and verify that more reader tasks scheduled are not executed //(as long as the first task is blocked) TaskFactory readers = new TaskFactory(schedPair.ConcurrentScheduler); ManualResetEvent blockReaderTaskEvent = new ManualResetEvent(false); ManualResetEvent blockMainThreadEvent = new ManualResetEvent(false); //Add a reader tasks that would block readers.StartNew(() => { blockMainThreadEvent.Set(); blockReaderTaskEvent.WaitOne(); }); blockMainThreadEvent.WaitOne(); // wait for the blockedTask to start execution //Now add more reader tasks int maxConcurrentTasks = Environment.ProcessorCount; List<Task> taskList = new List<Task>(); for (int i = 0; i < maxConcurrentTasks; i++) taskList.Add(readers.StartNew(() => { })); //schedule some dummy reader tasks foreach (Task task in taskList) { bool wasTaskStarted = (task.Status != TaskStatus.Running) && (task.Status != TaskStatus.RanToCompletion); Assert.True(wasTaskStarted, string.Format("Additional reader tasks should not start when scheduler concurrency is {0} and a reader task is blocked", customSchedulerConcurrency)); } //finally unblock the blocjedTask and wait for all of the tasks, to ensure they all executed properly blockReaderTaskEvent.Set(); Task.WaitAll(taskList.ToArray()); }
public static void TestMaxItemsPerTask(int maxConcurrency, int maxItemsPerTask, bool completeBeforeTaskWait) { //Create a custom TaskScheduler with specified max concurrency (TrackingTaskScheduler is defined in Common\tools\CommonUtils\TPLTestSchedulers.cs) TrackingTaskScheduler scheduler = new TrackingTaskScheduler(maxConcurrency); //We need to use the custom scheduler to achieve the results. As a by-product, we test to ensure custom schedulers are supported ConcurrentExclusiveSchedulerPair schedPair = new ConcurrentExclusiveSchedulerPair(scheduler, maxConcurrency, maxItemsPerTask); TaskFactory readers = new TaskFactory(schedPair.ConcurrentScheduler); //get reader and writer schedulers TaskFactory writers = new TaskFactory(schedPair.ExclusiveScheduler); //These are threadlocals to ensure that no concurrency side effects occur ThreadLocal<int> itemsExecutedCount = new ThreadLocal<int>(); //Track the items executed by CEScheduler Task ThreadLocal<int> schedulerIDInsideTask = new ThreadLocal<int>(); //Used to store the Scheduler ID observed by a Task Executed by CEScheduler Task //Work done by both reader and writer tasks Action work = () => { //Get the id of the parent Task (which is the task created by the scheduler). Each task run by the scheduler task should //see the same SchedulerID value since they are run on the same thread int id = ((TrackingTaskScheduler)scheduler).SchedulerID.Value; if (id == schedulerIDInsideTask.Value) { //since ids match, this is one more Task being executed by the CEScheduler Task itemsExecutedCount.Value = ++itemsExecutedCount.Value; //This does not need to be thread safe since we are looking to ensure that only n number of tasks were executed and not the order //in which they were executed. Also asserting inside the thread is fine since we just want the test to be marked as failure Assert.True(itemsExecutedCount.Value <= maxItemsPerTask, string.Format("itemsExecutedCount={0} cant be greater than maxValue={1}. Parent TaskID={2}", itemsExecutedCount, maxItemsPerTask, id)); } else { //Since ids dont match, this is the first Task being executed in the CEScheduler Task schedulerIDInsideTask.Value = id; //cache the scheduler ID seen by the thread, so other tasks running in same thread can see this itemsExecutedCount.Value = 1; } //Give enough time for a Task to stay around, so that other tasks will be executed by the same CEScheduler Task //or else the CESchedulerTask will die and each Task might get executed by a different CEScheduler Task. This does not affect the //verifications, but its increases the chance of finding a bug if the maxItemPerTask is not respected new ManualResetEvent(false).WaitOne(20); }; List<Task> taskList = new List<Task>(); int maxConcurrentTasks = maxConcurrency * maxItemsPerTask * 5; int maxExclusiveTasks = maxConcurrency * maxItemsPerTask * 2; // Schedule Tasks in both concurrent and exclusive mode for (int i = 0; i < maxConcurrentTasks; i++) taskList.Add(readers.StartNew(work)); for (int i = 0; i < maxExclusiveTasks; i++) taskList.Add(writers.StartNew(work)); if (completeBeforeTaskWait) { schedPair.Complete(); schedPair.Completion.Wait(); Assert.True(taskList.TrueForAll(t => t.IsCompleted), "All tasks should have completed for scheduler to complete"); } //finally wait for all of the tasks, to ensure they all executed properly Task.WaitAll(taskList.ToArray()); if (!completeBeforeTaskWait) { schedPair.Complete(); schedPair.Completion.Wait(); Assert.True(taskList.TrueForAll(t => t.IsCompleted), "All tasks should have completed for scheduler to complete"); } }