Esempio n. 1
0
        public static void TaskContinuation()
        {
            int taskCount = Environment.ProcessorCount;
            int maxDOP = Int32.MaxValue;
            int maxNumberExecutionsPerTask = 1;
            int data = 0;

            Task[] allTasks = new Task[taskCount + 1];

            CancellationTokenSource[] cts = new CancellationTokenSource[taskCount + 1];
            for (int i = 0; i <= taskCount; i++)
            {
                cts[i] = new CancellationTokenSource();
            }

            CancellationTokenSource cts2 = new CancellationTokenSource();
            ConcurrentExclusiveSchedulerPair scheduler = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, maxDOP, maxNumberExecutionsPerTask);
            for (int i = 0; i <= taskCount; i++)
            {
                int j = i;
                allTasks[i] = new Task(() =>
                {
                    new TaskFactory(TaskScheduler.Current).StartNew(() => { }).
                    ContinueWith((task, o) =>
                    {
                        int d = (int)o;
                        Interlocked.Add(ref data, d);
                    }, j).
                    ContinueWith((task, o) =>
                    {
                        int d = (int)o;
                        Interlocked.Add(ref data, d);
                        cts[d].Cancel();
                        if (d <= taskCount)
                        {
                            throw new OperationCanceledException(cts[d].Token);
                        }
                        return "Done";
                    }, j, cts[j].Token).
                    ContinueWith((task, o) =>
                        {
                            int d = (int)o;
                            Interlocked.Add(ref data, d);
                        }, j, CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled, TaskScheduler.Default).Wait(Int32.MaxValue - 1, cts2.Token);
                });

                allTasks[i].Start(scheduler.ConcurrentScheduler);
            }

            Task.WaitAll(allTasks, int.MaxValue - 1, CancellationToken.None);
            Debug.WriteLine("Tasks ended: result {0}", data);
            Task completion = scheduler.Completion;
            scheduler.Complete();
            completion.Wait();

            int expectedResult = 3 * taskCount * (taskCount + 1) / 2;
            Assert.Equal(expectedResult, data);

            Assert.NotEqual(TaskScheduler.Default.Id, scheduler.ConcurrentScheduler.Id);
            Assert.NotEqual(TaskScheduler.Default.Id, scheduler.ExclusiveScheduler.Id);
        }
Esempio n. 2
0
        public static void TestCreationOptions(String ctorType)
        {
            ConcurrentExclusiveSchedulerPair schedPair = null;
            //Need to define the default values since these values are passed to the verification methods
            TaskScheduler scheduler = TaskScheduler.Default;
            int maxConcurrentLevel = Environment.ProcessorCount;

            //Based on input args, use one of the ctor overloads
            switch (ctorType.ToLower())
            {
                case "default":
                    schedPair = new ConcurrentExclusiveSchedulerPair();
                    break;
                case "scheduler":
                    schedPair = new ConcurrentExclusiveSchedulerPair(scheduler);
                    break;
                case "maxconcurrent":
                    maxConcurrentLevel = 2;
                    schedPair = new ConcurrentExclusiveSchedulerPair(scheduler, maxConcurrentLevel);
                    break;
                case "all":
                    maxConcurrentLevel = Int32.MaxValue;
                    schedPair = new ConcurrentExclusiveSchedulerPair(scheduler, -1/*MaxConcurrentLevel*/, -1/*MaxItemsPerTask*/); //-1 gets converted to Int32.MaxValue
                    break;
                default:
                    throw new NotImplementedException(String.Format("The option specified {0} to create the ConcurrentExclusiveSchedulerPair is invalid", ctorType));
            }

            //Create the factories that use the exclusive scheduler and the concurrent scheduler. We test to ensure
            //that the ConcurrentExclusiveSchedulerPair created are valid by scheduling work on them.
            TaskFactory writers = new TaskFactory(schedPair.ExclusiveScheduler);
            TaskFactory readers = new TaskFactory(schedPair.ConcurrentScheduler);

            List<Task> taskList = new List<Task>(); //Store all tasks created, to enable wait until all of them are finished

            // Schedule some dummy work that should be run with as much parallelism as possible
            for (int i = 0; i < 50; i++)
            {
                //In the current design, when there are no more tasks to execute, the Task used by concurrentexclusive scheduler dies
                //by sleeping we simulate some non trival work that takes time and causes the concurrentexclusive scheduler Task 
                //to stay around for addition work.
                taskList.Add(readers.StartNew(() => { new ManualResetEvent(false).WaitOne(10); }));
            }
            // Schedule work where each item must be run when no other items are running
            for (int i = 0; i < 10; i++) taskList.Add(writers.StartNew(() => { new ManualResetEvent(false).WaitOne(5); }));

            //Wait on the tasks to finish to ensure that the ConcurrentExclusiveSchedulerPair created can schedule and execute tasks without issues
            foreach (var item in taskList)
            {
                item.Wait();
            }

            //verify that maxconcurrency was respected.
            if (ctorType == "maxconcurrent")
            {
                Assert.Equal(maxConcurrentLevel, schedPair.ConcurrentScheduler.MaximumConcurrencyLevel);
            }
            Assert.Equal(1, schedPair.ExclusiveScheduler.MaximumConcurrencyLevel);

            //verify that the schedulers have not completed
            Assert.False(schedPair.Completion.IsCompleted, "The schedulers should not have completed as a completion request was not issued.");

            //complete the scheduler and make sure it shuts down successfully
            schedPair.Complete();
            schedPair.Completion.Wait();

            //make sure no additional work may be scheduled
            foreach (var schedPairScheduler in new TaskScheduler[] { schedPair.ConcurrentScheduler, schedPair.ExclusiveScheduler })
            {
                Exception caughtException = null;
                try
                {
                    Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, schedPairScheduler);
                }
                catch (Exception exc)
                {
                    caughtException = exc;
                }
                Assert.True(
                    caughtException is TaskSchedulerException && caughtException.InnerException is InvalidOperationException,
                    "Queueing after completion should fail");
            }
        }
Esempio n. 3
0
        public static void TestCompletionTask()
        {
            // Completion tasks is valid after initialization
            {
                var cesp = new ConcurrentExclusiveSchedulerPair();
                Assert.True(cesp.Completion != null, "CompletionTask should never be null (after initialization)");
                Assert.True(!cesp.Completion.IsCompleted, "CompletionTask should not have completed");
            }

            // Completion task is valid after complete is called
            {
                var cesp = new ConcurrentExclusiveSchedulerPair();
                cesp.Complete();
                Assert.True(cesp.Completion != null, "CompletionTask should never be null (after complete)");
                cesp.Completion.Wait();
            }

            // Complete method may be called multiple times, and CompletionTask still completes
            {
                var cesp = new ConcurrentExclusiveSchedulerPair();
                for (int i = 0; i < 20; i++) cesp.Complete(); // ensure multiple calls to Complete succeed
                Assert.True(cesp.Completion != null, "CompletionTask should never be null (after multiple completes)");
                cesp.Completion.Wait();
            }

            // Can create a bunch of schedulers, do work on them all, complete them all, and they all complete
            {
                var cesps = new ConcurrentExclusiveSchedulerPair[100];
                for (int i = 0; i < cesps.Length; i++)
                {
                    cesps[i] = new ConcurrentExclusiveSchedulerPair();
                }
                for (int i = 0; i < cesps.Length; i++)
                {
                    Action work = () => new ManualResetEvent(false).WaitOne(2); ;
                    Task.Factory.StartNew(work, CancellationToken.None, TaskCreationOptions.None, cesps[i].ConcurrentScheduler);
                    Task.Factory.StartNew(work, CancellationToken.None, TaskCreationOptions.None, cesps[i].ExclusiveScheduler);
                }
                for (int i = 0; i < cesps.Length; i++)
                {
                    cesps[i].Complete();
                    cesps[i].Completion.Wait();
                }
            }

            // Validate that CESP does not implement IDisposable
            Assert.Equal(null, new ConcurrentExclusiveSchedulerPair() as IDisposable);
        }
Esempio n. 4
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");
            }
        }
        public void RunConcurrentExclusiveSchedulerPairRWTests()
        {
            // Validate reader tasks get scheduled concurrently
            {
                bool localPassed = true;
                foreach (var cesp in new[] { new ConcurrentExclusiveSchedulerPair(), new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, -1) })
                {
                    try
                    {
                        int numToSchedule = Environment.ProcessorCount; // doesn't validate much on a single core, but that's ok
                        Barrier b = new Barrier(numToSchedule);
                        var tasks = new Task[numToSchedule];
                        for (int i = 0; i < numToSchedule; i++)
                        {
                            tasks[i] = Task.Factory.StartNew(() =>
                            {
                                localPassed &= b.SignalAndWait(1000);
                            }, CancellationToken.None, TaskCreationOptions.None, cesp.ConcurrentScheduler);
                        }
                        Task.WaitAll(tasks);
                    }
                    finally { cesp.Complete(); }
                }

                Assert.True(localPassed, string.Format("{0}: Test concurrent readers", localPassed ? "Success" : "Failure"));
            }

            // Validate reader tasks don't go above max concurrency level
            {
                bool localPassed = true;
                int mcl = 2;
                var cesp = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, mcl);
                try
                {
                    int numToSchedule = 2;
                    int concurrentTasks = 0;
                    var tasks = new Task[numToSchedule];
                    for (int i = 0; i < numToSchedule; i++)
                    {
                        tasks[i] = Task.Factory.StartNew(() =>
                        {
                            Interlocked.Increment(ref concurrentTasks);
                            Task.Delay(1).Wait();
                            if (concurrentTasks > mcl) localPassed = false;
                            Task.Delay(1).Wait();
                            Interlocked.Decrement(ref concurrentTasks);
                        }, CancellationToken.None, TaskCreationOptions.None, cesp.ConcurrentScheduler);
                    }
                    Task.WaitAll(tasks);
                }
                finally { cesp.Complete(); }
                Assert.True(localPassed, string.Format("{0}: Test concurrent readers stay below maximum", localPassed ? "Success" : "Failure"));
            }

            // Validate writers tasks don't run concurrently with each other
            {
                bool localPassed = true;
                var cesp = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default);
                try
                {
                    int numToSchedule = 2;
                    int concurrentTasks = 0;
                    var tasks = new Task[numToSchedule];
                    for (int i = 0; i < numToSchedule; i++)
                    {
                        tasks[i] = Task.Factory.StartNew(() =>
                        {
                            Interlocked.Increment(ref concurrentTasks);
                            Task.Delay(100).Wait();
                            if (concurrentTasks > 1) localPassed &= false;
                            Task.Delay(100).Wait();
                            Interlocked.Decrement(ref concurrentTasks);
                        }, CancellationToken.None, TaskCreationOptions.None, cesp.ExclusiveScheduler);
                    }
                    Task.WaitAll(tasks);
                }
                finally { cesp.Complete(); }
                Assert.True(localPassed, string.Format("{0}: Test writers don't run concurrently with each other", localPassed ? "Success" : "Failure"));
            }

            // Validate writers tasks don't run concurrently with readers or writers
            {
                bool localPassed = true;
                var cesp = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default);

                int numWritersToSchedule = 2;
                int numReadersToSchedule = 3;
                int runningTasks = 0;

                var readerTasks = new Task[numReadersToSchedule];
                for (int i = 0; i < numReadersToSchedule; i++)
                {
                    readerTasks[i] = Task.Factory.StartNew(() =>
                    {
                        Interlocked.Increment(ref runningTasks);
                        Task.Delay(1).Wait();
                        Interlocked.Decrement(ref runningTasks);
                    }, CancellationToken.None, TaskCreationOptions.None, cesp.ConcurrentScheduler);
                }

                var writerTasks = new Task[numWritersToSchedule];
                for (int i = 0; i < numWritersToSchedule; i++)
                {
                    writerTasks[i] = Task.Factory.StartNew(() =>
                    {
                        Interlocked.Increment(ref runningTasks);
                        Task.Delay(100).Wait();
                        if (runningTasks > 1) localPassed &= false;
                        Task.Delay(100).Wait();
                        Interlocked.Decrement(ref runningTasks);
                    }, CancellationToken.None, TaskCreationOptions.None, cesp.ExclusiveScheduler);
                }

                Task.WaitAll(writerTasks);
                Task.WaitAll(readerTasks);

                // not completing the cesp here, just so that some of our tests don't
                // and so that we don't hide any issues by always completing

                Assert.True(localPassed, string.Format("{0}: Test writers don't run concurrently with anything", 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"));
            }
        }