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");
            }
        }
예제 #4
0
        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.");
            }
        }
예제 #5
0
        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());
        }
예제 #6
0
        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");
            }
        }