/// <summary> /// Go through the <see cref="_pendingRemoves"/> array and remove those registered wait handles from the <see cref="_registeredWaits"/> /// and <see cref="_waitHandles"/> arrays, filling the holes along the way. /// </summary> private int ProcessRemovals() { PortableThreadPool threadPoolInstance = ThreadPoolInstance; threadPoolInstance._waitThreadLock.Acquire(); try { Debug.Assert(_numPendingRemoves >= 0); Debug.Assert(_numPendingRemoves <= _pendingRemoves.Length); Debug.Assert(_numUserWaits >= 0); Debug.Assert(_numUserWaits <= _registeredWaits.Length); Debug.Assert(_numPendingRemoves <= _numUserWaits, $"Num removals {_numPendingRemoves} should be less than or equal to num user waits {_numUserWaits}"); if (_numPendingRemoves == 0 || _numUserWaits == 0) { return(_numUserWaits); // return the value taken inside the lock for the caller } int originalNumUserWaits = _numUserWaits; int originalNumPendingRemoves = _numPendingRemoves; // This is O(N^2), but max(N) = 63 and N will usually be very low for (int i = 0; i < _numPendingRemoves; i++) { RegisteredWaitHandle waitHandleToRemove = _pendingRemoves[i] !; int numUserWaits = _numUserWaits; int j = 0; for (; j < numUserWaits && waitHandleToRemove != _registeredWaits[j]; j++) { } Debug.Assert(j < numUserWaits); waitHandleToRemove.OnRemoveWait(); if (j + 1 < numUserWaits) { // Not removing the last element. Due to the possibility of there being duplicate system wait // objects in the wait array, perhaps even with different handle values due to the use of // DuplicateHandle(), don't reorder handles for fairness. When there are duplicate system wait // objects in the wait array and the wait object gets signaled, the system may release the wait in // in deterministic order based on the order in the wait array. Instead, shift the array. int removeAt = j; int count = numUserWaits; Array.Copy(_registeredWaits, removeAt + 1, _registeredWaits, removeAt, count - (removeAt + 1)); _registeredWaits[count - 1] = null !; // Corresponding elements in the wait handles array are shifted up by one removeAt++; count++; Array.Copy(_waitHandles, removeAt + 1, _waitHandles, removeAt, count - (removeAt + 1)); _waitHandles[count - 1] = null !; } else { // Removing the last element _registeredWaits[j] = null !; _waitHandles[j + 1] = null !; } _numUserWaits = numUserWaits - 1; _pendingRemoves[i] = null; waitHandleToRemove.Handle.DangerousRelease(); } _numPendingRemoves = 0; Debug.Assert(originalNumUserWaits - originalNumPendingRemoves == _numUserWaits, $"{originalNumUserWaits} - {originalNumPendingRemoves} == {_numUserWaits}"); return(_numUserWaits); // return the value taken inside the lock for the caller } finally { threadPoolInstance._waitThreadLock.Release(); } }