/// <summary> /// Schedules new task. The coalesce decision callback will be called for pending and running tasks /// before returning from this method. /// </summary> /// <param name="action">The task to schedule.</param> /// <param name="arg">The action to schedule. Will be passed as an argument to the coalesce decision callback.</param> /// <returns>The result returned by the task.</returns> public Task <TResult> Enqueue(Func <CancellationToken, Task <TResult> > action, TArg arg) { CoalescingTaskQueueData <TArg, TResult> work; CoalescingTaskQueueData <TArg, TResult> newWork; CoalescingTaskQueueData <TArg, TResult> previousWork; bool cancelRunningTask; QueuedTask <TArg, TResult> newTask = null; do { work = _work; cancelRunningTask = false; newTask?.Dispose(); if (work.PendingTask != null) { // Makes decision on pending task var decision = _decisionCallback(arg, work.PendingTask.Arg, false); if (decision == CoalesceDecision.Join) { return(_work.PendingTask.Task); } } if (work.RunningTask != null && !work.RunningTask.CancellationRequested) { // Makes decision on running task var decision = _decisionCallback(arg, work.RunningTask.Arg, true); if (decision == CoalesceDecision.Join) { return(_work.RunningTask.Task); } // Doesn't cancel running task until successfully scheduling new task, will do that later. cancelRunningTask = decision == CoalesceDecision.Cancel; } newTask = new QueuedTask <TArg, TResult>(action, arg); if (work.RunningTask == null) { // No task is running. Schedule new running task, will start it later. newWork = new CoalescingTaskQueueData <TArg, TResult>( null, newTask); } else { // A task is running. Schedule new pending task. newWork = new CoalescingTaskQueueData <TArg, TResult>( newTask, work.RunningTask); } previousWork = Interlocked.CompareExchange(ref _work, newWork, work); } while (previousWork != work); // Scheduling succeeded // Cancel previous pending task if any var pendingTask = work.PendingTask; if (pendingTask != null) { pendingTask.Cancel(false); pendingTask.Dispose(); } // Cancel running task if requested by decision callback if (cancelRunningTask) { work.RunningTask.Cancel(true); } // Start new task if scheduled as running if (newWork.RunningTask == newTask) { Run(newTask); } return(newTask.Task); }