public void Should_control_executions_per_specification(int maxParallelization, int maxQueuingActions, int totalActions, bool cancelQueuing, bool cancelExecuting, string scenario) { if (totalActions < 0) { throw new ArgumentOutOfRangeException(nameof(totalActions)); } MaxParallelization = maxParallelization; MaxQueuingActions = maxQueuingActions; TotalActions = totalActions; Scenario = $"MaxParallelization {maxParallelization}; MaxQueuing {maxQueuingActions}; TotalActions {totalActions}; CancelQueuing {cancelQueuing}; CancelExecuting {cancelExecuting}: {scenario}"; IBulkheadPolicy bulkhead = GetBulkhead(maxParallelization, maxQueuingActions); using (bulkhead) { BulkheadForStats = bulkhead; // Set up delegates which we can track whether they've started; and control when we allow them to complete (to release their semaphore slot). Actions = new TraceableAction[totalActions]; for (int i = 0; i < totalActions; i++) { Actions[i] = new TraceableAction(i, StatusChangedEvent, TestOutputHelper); } // Throw all the delegates at the bulkhead simultaneously. Tasks = new Task[totalActions]; for (int i = 0; i < totalActions; i++) { Tasks[i] = ExecuteOnBulkhead(bulkhead, Actions[i]); } OutputStatus("Immediately after queueing..."); // Assert the expected distributions of executing, queuing, rejected and completed - when all delegates thrown at bulkhead. ExpectedCompleted = 0; ExpectedCancelled = 0; ExpectedExecuting = Math.Min(totalActions, maxParallelization); ExpectedRejects = Math.Max(0, totalActions - maxParallelization - maxQueuingActions); ExpectedQueuing = Math.Min(maxQueuingActions, Math.Max(0, totalActions - maxParallelization)); ExpectedBulkheadFree = maxParallelization - ExpectedExecuting; ExpectedQueueFree = maxQueuingActions - ExpectedQueuing; try { Within(CohesionTimeLimit, ActualsMatchExpecteds); } finally { OutputStatus("Expected initial state verified..."); } // Complete or cancel delegates one by one, and expect others to take their place (if a slot released and others remain queueing); until all work is done. while (ExpectedExecuting > 0) { if (cancelQueuing) { TestOutputHelper.WriteLine("Cancelling a queueing task..."); Actions.First(a => a.Status == TraceableActionStatus.QueueingForSemaphore).Cancel(); ExpectedCancelled++; ExpectedQueuing--; ExpectedQueueFree++; cancelQueuing = false; } else if (cancelExecuting) { TestOutputHelper.WriteLine("Cancelling an executing task..."); Actions.First(a => a.Status == TraceableActionStatus.Executing).Cancel(); ExpectedCancelled++; if (ExpectedQueuing > 0) { ExpectedQueuing--; ExpectedQueueFree++; } else { ExpectedExecuting--; ExpectedBulkheadFree++; } cancelExecuting = false; } else // Complete an executing delegate. { TestOutputHelper.WriteLine("Completing a task..."); Actions.First(a => a.Status == TraceableActionStatus.Executing).AllowCompletion(); ExpectedCompleted++; if (ExpectedQueuing > 0) { ExpectedQueuing--; ExpectedQueueFree++; } else { ExpectedExecuting--; ExpectedBulkheadFree++; } } try { Within(CohesionTimeLimit, ActualsMatchExpecteds); } finally { OutputStatus("End of next loop iteration..."); } } EnsureNoUnbservedTaskExceptions(); TestOutputHelper.WriteLine("Verifying all tasks completed..."); Within(CohesionTimeLimit, AllTasksCompleted); } }
protected abstract Task ExecuteOnBulkhead(IBulkheadPolicy bulkhead, TraceableAction action);
protected override Task ExecuteOnBulkhead(IBulkheadPolicy bulkhead, TraceableAction action) { return(action.ExecuteOnBulkheadAsync((AsyncBulkheadPolicy)bulkhead)); }
protected override Task ExecuteOnBulkhead(IBulkheadPolicy bulkhead, TraceableAction action) { return(action.ExecuteOnBulkhead <ResultPrimitive>((BulkheadPolicy <ResultPrimitive>)bulkhead)); }
public void Should_be_able_to_use_BulkheadAvailableCount_via_interface() { IBulkheadPolicy bulkhead = Policy.Bulkhead(20, 10); bulkhead.BulkheadAvailableCount.Should().Be(20); }