private void ExitWorkBlocker(WorkBlocker workBlocker) { // ReSharper disable once ConditionIsAlwaysTrueOrFalse Debug.Assert(workBlocker != null, "workBlocker != null"); lock (_workBlockersSyncRoot) { // !!! Everything has changed, after we have captured the lock. !!! //// bool removed = workBlocker.WorkItem.Blockers.Remove(workBlocker); if (!removed) { throw new InvalidOperationException( $"Error exiting work blocker section. Work blocker already released. {CallerInfoToString(workBlocker.DebugInfo, workBlocker.CallerInfo)}"); } removed = _workBlockers.Remove(workBlocker); Debug.Assert(removed, "Double remove error."); // Scheduling next work item after some grace period. if (!_workBlockers.Any() && !IsShutdownStarted) { // Raising event for starting new work item. _noMoreBlockersEvent.Set(); } } }
private WorkBlocker EnterWorkBlocker(ActiveWorkItem workItem, string debugInfo, CallerInfo callerInfo) { // ReSharper disable once ConditionIsAlwaysTrueOrFalse Debug.Assert(workItem != null, "workItem != null"); // ReSharper disable once ConditionIsAlwaysTrueOrFalse Debug.Assert(debugInfo != null, "debugInfo != null"); lock (_workBlockersSyncRoot) { // Validating work item is active. if (!_activeWorkItems.Contains(workItem)) { throw new InvalidOperationException( $"Error entering work blocker section. Work item already completed. {CallerInfoToString(debugInfo, callerInfo)}"); } // !!! Everything has changed, after we have captured the lock. !!! //// // Canceling currently is being created work item. _workItemValidationId++; var workBlocker = new WorkBlocker(false, workItem, debugInfo, callerInfo); // Registering blocker. _workBlockers.Add(workBlocker); workBlocker.WorkItem.Blockers.Add(workBlocker); return(workBlocker); } }
private async Task ExecuteNewWorkItem(long workItemValidationId) { ActiveWorkItem workItem; WorkBlocker workBlocker; lock (_workBlockersSyncRoot) { // !!! Everything has changed in the world, after we are captured the lock. !!! //// _shutdownCancellationToken.ThrowIfCancellationRequested(); // Do nothing, if work item creation has been canceled. if (workItemValidationId != _workItemValidationId) { return; } // Creating and registering a new work item. workItem = new ActiveWorkItem(this); // Creating initial work blocker for the new work item. workBlocker = new WorkBlocker( true, workItem, string.Empty, new CallerInfo("new work item", "initial_blocker", 0)); // Registering blocker. _workBlockers.Add(workBlocker); workItem.Blockers.Add(workBlocker); _activeWorkItems.Add(workItem); } try { // Running new work item. await _workItemAction(workItem, new ActiveWorkerBlockerToken(workBlocker)); } finally { lock (_workBlockersSyncRoot) { bool removed = _activeWorkItems.Remove(workItem); Debug.Assert(removed, "Double remove is wrong"); // When no more work items left and termination requested - setting termination finish cts event. if (IsShutdownStarted && !_activeWorkItems.Any()) { _shutdownCompletedCts.Cancel(); } // It's allowed to not dispose initial blocker. if ((workItem.Blockers.Count == 1) && workItem.Blockers.First().IsInitialBlocker) { var blocker = workItem.Blockers.First(); // Disposing last initial blocker. workItem.Blockers.Remove(blocker); _workBlockers.Remove(blocker); // Scheduling next work item after some grace period. if (!_workBlockers.Any() && !IsShutdownStarted) { // Raising event for starting new work item. _noMoreBlockersEvent.Set(); } } else { // Raising critical errors if some locks was not released when task exited. foreach (WorkBlocker unreleasedBlockers in workItem.Blockers) { Critical.Assert( false, $"Unreleased work blocker section at {CallerInfoToString(unreleasedBlockers.DebugInfo, unreleasedBlockers.CallerInfo)}"); } } } } }