/// <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();
                }
            }