static void Test() { ConcurrentExclusiveSchedulerPair pair = new ConcurrentExclusiveSchedulerPair(); readerAB1 = new ActionBlock<int>((i) => { Console.WriteLine(i + " ReaderAB1 begin handing." + " Excute Time:" + DateTime.Now + " currentThreadId : " + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(500); }, new ExecutionDataflowBlockOptions() {TaskScheduler = pair.ConcurrentScheduler}); readerAB2 = new ActionBlock<int>((i) => { Console.WriteLine(i + " ReaderAB2 begin handing." + " Excute Time:" + DateTime.Now + " currentThreadId : " + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(500); }, new ExecutionDataflowBlockOptions() {TaskScheduler = pair.ConcurrentScheduler}); readerAB3 = new ActionBlock<int>((i) => { Console.WriteLine(i + " ReaderAB3 begin handing." + " Excute Time:" + DateTime.Now + " currentThreadId : " + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(500); }, new ExecutionDataflowBlockOptions() {TaskScheduler = pair.ConcurrentScheduler}); writeAB1 = new ActionBlock<int>((i) => { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(i + " writeAB1 begin handing." + " Excute Time:" + DateTime.Now + " currentThreadId : " + Thread.CurrentThread.ManagedThreadId); Console.ResetColor(); Thread.Sleep(3000); }, new ExecutionDataflowBlockOptions() {TaskScheduler = pair.ExclusiveScheduler}); bb.LinkTo(readerAB1); bb.LinkTo(readerAB2); bb.LinkTo(readerAB3); var task1 = Task.Run(() => { while (true) { bb.Post(1); Thread.Sleep(1000); } }); var task2 = Task.Run(() => { while (true) { writeAB1.Post(1); Thread.Sleep(6000); } }); Task.WaitAll(task1, task2); }
public void BasicConcurrentUsageTest() { schedPair = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 4); factory = new TaskFactory(schedPair.ConcurrentScheduler); bool launched = false; factory.StartNew(() => launched = true); Thread.Sleep(600); Assert.IsTrue(launched); }
public IEnumerable<IDocument> Execute(IReadOnlyList<IDocument> inputs, IExecutionContext context) { IRazorPageFactory pageFactory = new VirtualPathRazorPageFactory(context.InputFolder, context, _basePageType); List<IDocument> validInputs = inputs .Where(x => _ignorePrefix == null || !x.ContainsKey("SourceFileName") || !x.String("SourceFileName").StartsWith(_ignorePrefix)) .ToList(); // Compile the pages in parallel ConcurrentDictionary<IDocument, Tuple<ViewContext, ViewEngineResult>> compilationResults = new ConcurrentDictionary<IDocument, Tuple<ViewContext, ViewEngineResult>>(); Parallel.ForEach(validInputs, x => { context.Trace.Verbose("Compiling Razor for {0}", x.Source); IViewStartProvider viewStartProvider = new ViewStartProvider(pageFactory, _viewStartPath?.Invoke(x)); IRazorViewFactory viewFactory = new RazorViewFactory(viewStartProvider); IRazorViewEngine viewEngine = new RazorViewEngine(pageFactory, viewFactory); ViewContext viewContext = new ViewContext(null, new ViewDataDictionary(), null, x.Metadata, context, viewEngine); ViewEngineResult viewEngineResult; using (Stream stream = x.GetStream()) { viewEngineResult = viewEngine.GetView(viewContext, GetRelativePath(x), stream).EnsureSuccessful(); } compilationResults[x] = new Tuple<ViewContext, ViewEngineResult>(viewContext, viewEngineResult); }); // Now evaluate them in sequence - have to do this because BufferedHtmlContent doesn't appear to work well in multi-threaded parallel execution TaskScheduler exclusiveScheduler = new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler; CancellationToken cancellationToken = new CancellationToken(); return validInputs .Select(x => { using (context.Trace.WithIndent().Verbose("Processing Razor for {0}", x.Source)) { Tuple<ViewContext, ViewEngineResult> compilationResult; if (compilationResults.TryGetValue(x, out compilationResult)) { using (StringWriter writer = new StringWriter()) { compilationResult.Item1.View = compilationResult.Item2.View; compilationResult.Item1.Writer = writer; Task.Factory.StartNew(() => compilationResult.Item2.View.RenderAsync(compilationResult.Item1), cancellationToken, TaskCreationOptions.None, exclusiveScheduler).Unwrap().GetAwaiter().GetResult(); return x.Clone(writer.ToString()); } } context.Trace.Warning("Could not find compilation result for {0}", x.Source); return null; } }); }
public void ExclusiveUsageTest () { schedPair = new ConcurrentExclusiveSchedulerPair (TaskScheduler.Default, 4); factory = new TaskFactory (schedPair.ExclusiveScheduler); int count = 0; ManualResetEventSlim mreFinish = new ManualResetEventSlim (false); ManualResetEventSlim mreStart = new ManualResetEventSlim (false); factory.StartNew (() => { mreStart.Set (); Interlocked.Increment (ref count); mreFinish.Wait (); }); mreStart.Wait (); factory.StartNew (() => Interlocked.Increment (ref count)); Thread.Sleep (100); Assert.AreEqual (1, count); mreFinish.Set (); }
private static async Task Main(string[] args) { var inputBlock = new BroadcastBlock <int>(a => a); var taskScheduler = new System.Threading.Tasks.ConcurrentExclusiveSchedulerPair(); Action <int> actionBlockFunction = (int a) => { var counterValue = GetSharedObjectValue(); //very complexLogic Task.Delay(_random.Next(300)).Wait(); Console.WriteLine($"The counter value I've read was {counterValue} " + $"Now it is {_sharedCounter} " + $"I will set it to {counterValue + 1}"); SetSharedObjectValue(counterValue + 1); }; var incrementingBlock1 = new ActionBlock <int>(actionBlockFunction , new ExecutionDataflowBlockOptions() { TaskScheduler = taskScheduler.ExclusiveScheduler }); var incrementingBlock2 = new ActionBlock <int>(actionBlockFunction , new ExecutionDataflowBlockOptions() { TaskScheduler = taskScheduler.ExclusiveScheduler }); inputBlock.LinkToWithPropagation(incrementingBlock1); inputBlock.LinkToWithPropagation(incrementingBlock2); for (int i = 0; i < 10; i++) { inputBlock.Post(i); } inputBlock.Complete(); await incrementingBlock1.Completion; await incrementingBlock2.Completion; Console.WriteLine($"Current counter value {GetSharedObjectValue()}"); }
public void Run() { var propagateCompletion = new DataflowLinkOptions { PropagateCompletion = true }; var csep = new ConcurrentExclusiveSchedulerPair(); var concurrentOptions = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 2, TaskScheduler = csep.ConcurrentScheduler }; var exclusiveOptions = new ExecutionDataflowBlockOptions { TaskScheduler = csep.ExclusiveScheduler }; var concurrent = new ActionBlock<int>(async i => { Console.WriteLine("Concurent print value: {0}", i); await Task.Delay(500); }, concurrentOptions); var exclusive = new ActionBlock<int>(i => { Console.WriteLine("Exclusive print value: {0}", i); Thread.Sleep(750); }, exclusiveOptions); var broadcaster = new BroadcastBlock<int>(i => i); broadcaster.LinkTo(concurrent, propagateCompletion); broadcaster.LinkTo(exclusive, propagateCompletion, i => i % 2 == 0); Enumerable .Range(1, 10) .ToList() .ForEach(i => broadcaster.Post(i)); broadcaster.Complete(); Task.WaitAll(concurrent.Completion, exclusive.Completion); }
public static void TestConcurrentBlockage(bool useReader) { ConcurrentExclusiveSchedulerPair schedPair = new ConcurrentExclusiveSchedulerPair(); TaskFactory readers = new TaskFactory(schedPair.ConcurrentScheduler); TaskFactory writers = new TaskFactory(schedPair.ExclusiveScheduler); ManualResetEvent blockExclusiveTaskEvent = new ManualResetEvent(false); ManualResetEvent blockMainThreadEvent = new ManualResetEvent(false); ManualResetEvent blockMre = new ManualResetEvent(false); //Schedule a concurrent task and ensure that it is executed, just for fun Task<bool> conTask = readers.StartNew<bool>(() => { new ManualResetEvent(false).WaitOne(10); ; return true; }); conTask.Wait(); Assert.True(conTask.Result, "The concurrenttask when executed successfully should have returned true"); //Now scehdule a exclusive task that is blocked(thereby preventing other concurrent tasks to finish) Task<bool> exclusiveTask = writers.StartNew<bool>(() => { blockMainThreadEvent.Set(); blockExclusiveTaskEvent.WaitOne(); return true; }); //With exclusive task in execution mode, schedule a number of concurrent tasks and ensure they are not executed blockMainThreadEvent.WaitOne(); List<Task> taskList = new List<Task>(); for (int i = 0; i < 20; i++) taskList.Add(readers.StartNew<bool>(() => { blockMre.WaitOne(10); return true; })); foreach (Task task in taskList) { bool wasTaskStarted = (task.Status != TaskStatus.Running) && (task.Status != TaskStatus.RanToCompletion); Assert.True(wasTaskStarted, "Concurrent tasks should not be executed when a exclusive task is getting executed"); } blockExclusiveTaskEvent.Set(); Task.WaitAll(taskList.ToArray()); }
public static void TestIntegration(String apiType, bool useReader) { Debug.WriteLine(string.Format(" Running apiType:{0} useReader:{1}", apiType, useReader)); int taskCount = Environment.ProcessorCount; //To get varying number of tasks as a function of cores ConcurrentExclusiveSchedulerPair schedPair = new ConcurrentExclusiveSchedulerPair(); CountdownEvent cde = new CountdownEvent(taskCount); //Used to track how many tasks were executed Action work = () => { cde.Signal(); }; //Work done by all APIs //Choose the right scheduler to use based on input parameter TaskScheduler scheduler = useReader ? schedPair.ConcurrentScheduler : schedPair.ExclusiveScheduler; SelectAPI2Target(apiType, taskCount, scheduler, work); cde.Wait(); //This will cause the test to block (and timeout) until all tasks are finished }
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 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 void ExclusiveUsageWithConcurrentExecutingTest() { schedPair = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 4); TaskFactory exclFact = new TaskFactory(schedPair.ExclusiveScheduler); TaskFactory concFact = new TaskFactory(schedPair.ConcurrentScheduler); int count = 0; bool started = false; ManualResetEventSlim mreStart = new ManualResetEventSlim(false); ManualResetEventSlim mreFinish = new ManualResetEventSlim(false); concFact.StartNew(() => { started = true; mreStart.Set(); mreFinish.Wait(); started = false; }); mreStart.Wait(); exclFact.StartNew(() => Interlocked.Increment(ref count)); Thread.Sleep(100); Assert.IsTrue(started); Assert.AreEqual(0, count); mreFinish.Set(); }
/// <summary>Initializes the debug view.</summary> /// <param name="pair">The pair being debugged.</param> public DebugView(ConcurrentExclusiveSchedulerPair pair) { Debug.Assert(pair != null, "Need a pair with which to construct the debug view."); m_pair = pair; }
public void ConcurrentUsageTest() { schedPair = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 4); factory = new TaskFactory(schedPair.ConcurrentScheduler); int count = 0; ManualResetEventSlim mreFinish = new ManualResetEventSlim(false); CountdownEvent cntd = new CountdownEvent(2); factory.StartNew(() => { Interlocked.Increment(ref count); cntd.Signal(); mreFinish.Wait(); }); factory.StartNew(() => { Interlocked.Increment(ref count); cntd.Signal(); mreFinish.Wait(); }); cntd.Wait(); Assert.AreEqual(2, count); mreFinish.Set(); }
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 void Synchronous_client_call_from_async_context_using_exclusive_scheduler_is_not_deadlocked() { // Note that this will probably leave a hanging worker thread if failing :/ // Not sure how to clean that up properly, might have to implement a custom TaskScheduler to do that var ccsp = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 1); var tf = new TaskFactory(CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskContinuationOptions.None, ccsp.ExclusiveScheduler); // Should have good time to complete in 10seconds, on any computer var hasCompleted = tf.StartNew(async () => { // Just do some random sync client call Client.Critters.Post(new CritterForm() { Name = "hi" }); await Task.Yield(); Client.Critters.Post(new CritterForm() { Name = "again" }); }).Wait(10000); Assert.That(hasCompleted, Is.True, "Task never completed, which means it's probably deadlocked."); }
/// <summary> /// Initializes a new instance of the <see cref="AsyncReaderWriterLock"/> class. /// </summary> public AsyncReaderWriterLock() { // Built upon ConcurrentExclusiveSchedulerPair is a .Net TPL construct that provides exactly what we need // - reader/writer orchestration ConcurrentExclusiveSchedulerPair = new ConcurrentExclusiveSchedulerPair(); ConcurrentNonExclusiveTaskFactory = new TaskFactory(this.ConcurrentExclusiveSchedulerPair.ConcurrentScheduler); ExclusiveTaskFactory = new TaskFactory(this.ConcurrentExclusiveSchedulerPair.ExclusiveScheduler); }
public InnerTaskScheduler(ConcurrentExclusiveSchedulerPair scheduler, ConcurrentQueue <Task> queue) { this.scheduler = scheduler; this.queue = queue; }
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")); } }
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); }
public void ReaderWriterLockUsage() { // для секций с известной операцией доступа (чтение или запись) можно использовать // ReaderWriterLockSlim (ReaderWriterLock не рекомендуется) ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(); rwLock.EnterReadLock(); // критическая секция с чтением данных rwLock.ExitReadLock(); rwLock.EnterWriteLock(); // критическая секция с изменением данных rwLock.ExitWriteLock(); // также существует класс ConcurrentExclusiveSchedulerPair, // который содержит 2 менеджера заданий // все задания в ExclusiveScheduler выполняются по одному, // если не выполняется ни одно задание в ConcurrentScheduler // все задания в ConcurrentScheduler выполняются одновременно, // если не выполняется ни одно задание в ExclusiveScheduler ConcurrentExclusiveSchedulerPair schedulerPair = new ConcurrentExclusiveSchedulerPair(); TaskFactory exclusiveFactory = new TaskFactory(schedulerPair.ExclusiveScheduler); exclusiveFactory.StartNew(() => Console.WriteLine(DateTime.Now.ToLongTimeString())); // класс, который блокирует поток, пока внутренний счётчик не достигнет нуля CountdownEvent countdownEvent = new CountdownEvent(5); countdownEvent.Wait(); // критическая секция countdownEvent.Signal(2); // блокировка не снята countdownEvent.Signal(3); // блокировка снята // класс Barrier предоставляет возможность совокупности потоков проходить // через определённые шаги исполнения одновременно }
public static void TestSchedulerNesting() { // Create a hierarchical set of scheduler pairs var cespParent = new ConcurrentExclusiveSchedulerPair(); var cespChild1 = new ConcurrentExclusiveSchedulerPair(cespParent.ConcurrentScheduler); var cespChild1Child1 = new ConcurrentExclusiveSchedulerPair(cespChild1.ConcurrentScheduler); var cespChild1Child2 = new ConcurrentExclusiveSchedulerPair(cespChild1.ExclusiveScheduler); var cespChild2 = new ConcurrentExclusiveSchedulerPair(cespParent.ExclusiveScheduler); var cespChild2Child1 = new ConcurrentExclusiveSchedulerPair(cespChild2.ConcurrentScheduler); var cespChild2Child2 = new ConcurrentExclusiveSchedulerPair(cespChild2.ExclusiveScheduler); // these are ordered such that we will complete the child schedulers before we complete their parents. That way // we don't complete a parent that's still in use. var cesps = new[] { cespChild1Child1, cespChild1Child2, cespChild1, cespChild2Child1, cespChild2Child2, cespChild2, cespParent, }; // Get the schedulers from all of the pairs List<TaskScheduler> schedulers = new List<TaskScheduler>(); foreach (var s in cesps) { schedulers.Add(s.ConcurrentScheduler); schedulers.Add(s.ExclusiveScheduler); } // Keep track of all created tasks var tasks = new List<Task>(); // Queue lots of work to each scheduler foreach (var scheduler in schedulers) { // Create a function that schedules and inlines recursively queued tasks Action<int> recursiveWork = null; recursiveWork = depth => { if (depth > 0) { Action work = () => { new ManualResetEvent(false).WaitOne(1); recursiveWork(depth - 1); }; TaskFactory factory = new TaskFactory(scheduler); Debug.WriteLine(string.Format("Start tasks in scheduler {0}", scheduler.Id)); Task t1 = factory.StartNew(work); Task t2 = factory.StartNew(work); Task t3 = factory.StartNew(work); Task.WaitAll(t1, t2, t3); } }; for (int i = 0; i < 2; i++) { tasks.Add(Task.Factory.StartNew(() => recursiveWork(2), CancellationToken.None, TaskCreationOptions.None, scheduler)); } } // Wait for all tasks to complete, then complete the schedulers Task.WaitAll(tasks.ToArray()); foreach (var cesp in cesps) { cesp.Complete(); cesp.Completion.Wait(); } }
public InnerTaskScheduler (ConcurrentExclusiveSchedulerPair scheduler, ConcurrentQueue<Task> queue) { this.scheduler = scheduler; this.queue = queue; }
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"); } }
public SchedulerDebuggerView(ConcurrentExclusiveSchedulerPair owner) { this.owner = owner; }
public SchedulerDebuggerView (ConcurrentExclusiveSchedulerPair owner) { this.owner = owner; }
/// <summary>Initializes the debug view.</summary> /// <param name="pair">The pair being debugged.</param> public DebugView(ConcurrentExclusiveSchedulerPair pair) { Contract.Requires(pair != null, "Need a pair with which to construct the debug view."); m_pair = pair; }
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); }
// Token: 0x06006EED RID: 28397 RVA: 0x0017DDB5 File Offset: 0x0017BFB5 public DebugView(ConcurrentExclusiveSchedulerPair pair) { this.m_pair = pair; }