private bool WaitForSignal(int timeoutMs)
        {
            Debug.Assert(timeoutMs > 0 || timeoutMs == -1);

            _onWait();

            while (true)
            {
                if (!WaitCore(timeoutMs))
                {
                    // Unregister the waiter. The wait subsystem used above guarantees that a thread that wakes due to a timeout does
                    // not observe a signal to the object being waited upon.
                    _separated._counts.InterlockedDecrementWaiterCount();
                    return(false);
                }

                // Unregister the waiter if this thread will not be waiting anymore, and try to acquire the semaphore
                Counts counts = _separated._counts;
                while (true)
                {
                    Debug.Assert(counts.WaiterCount != 0);
                    Counts newCounts = counts;
                    if (counts.SignalCount != 0)
                    {
                        newCounts.DecrementSignalCount();
                        newCounts.DecrementWaiterCount();
                    }

                    // This waiter has woken up and this needs to be reflected in the count of waiters signaled to wake
                    if (counts.CountOfWaitersSignaledToWake != 0)
                    {
                        newCounts.DecrementCountOfWaitersSignaledToWake();
                    }

                    Counts countsBeforeUpdate = _separated._counts.InterlockedCompareExchange(newCounts, counts);
                    if (countsBeforeUpdate == counts)
                    {
                        if (counts.SignalCount != 0)
                        {
                            return(true);
                        }
                        break;
                    }

                    counts = countsBeforeUpdate;
                }
            }
        }
        public bool Wait(int timeoutMs, bool spinWait)
        {
            Debug.Assert(timeoutMs >= -1);

            int spinCount = spinWait ? _spinCount : 0;

            // Try to acquire the semaphore or
            // a) register as a spinner if spinCount > 0 and timeoutMs > 0
            // b) register as a waiter if there's already too many spinners or spinCount == 0 and timeoutMs > 0
            // c) bail out if timeoutMs == 0 and return false
            Counts counts = _separated._counts;

            while (true)
            {
                Debug.Assert(counts.SignalCount <= _maximumSignalCount);
                Counts newCounts = counts;
                if (counts.SignalCount != 0)
                {
                    newCounts.DecrementSignalCount();
                }
                else if (timeoutMs != 0)
                {
                    if (spinCount > 0 && newCounts.SpinnerCount < byte.MaxValue)
                    {
                        newCounts.IncrementSpinnerCount();
                    }
                    else
                    {
                        // Maximum number of spinners reached, register as a waiter instead
                        newCounts.IncrementWaiterCount();
                    }
                }

                Counts countsBeforeUpdate = _separated._counts.InterlockedCompareExchange(newCounts, counts);
                if (countsBeforeUpdate == counts)
                {
                    if (counts.SignalCount != 0)
                    {
                        return(true);
                    }
                    if (newCounts.WaiterCount != counts.WaiterCount)
                    {
                        return(WaitForSignal(timeoutMs));
                    }
                    if (timeoutMs == 0)
                    {
                        return(false);
                    }
                    break;
                }

                counts = countsBeforeUpdate;
            }

#if CORECLR && TARGET_UNIX
            // The PAL's wait subsystem is slower, spin more to compensate for the more expensive wait
            spinCount *= 2;
#endif
            int processorCount = Environment.ProcessorCount;
            int spinIndex      = processorCount > 1 ? 0 : SpinSleep0Threshold;
            while (spinIndex < spinCount)
            {
                LowLevelSpinWaiter.Wait(spinIndex, SpinSleep0Threshold, processorCount);
                spinIndex++;

                // Try to acquire the semaphore and unregister as a spinner
                counts = _separated._counts;
                while (counts.SignalCount > 0)
                {
                    Counts newCounts = counts;
                    newCounts.DecrementSignalCount();
                    newCounts.DecrementSpinnerCount();

                    Counts countsBeforeUpdate = _separated._counts.InterlockedCompareExchange(newCounts, counts);
                    if (countsBeforeUpdate == counts)
                    {
                        return(true);
                    }

                    counts = countsBeforeUpdate;
                }
            }

            // Unregister as spinner, and acquire the semaphore or register as a waiter
            counts = _separated._counts;
            while (true)
            {
                Counts newCounts = counts;
                newCounts.DecrementSpinnerCount();
                if (counts.SignalCount != 0)
                {
                    newCounts.DecrementSignalCount();
                }
                else
                {
                    newCounts.IncrementWaiterCount();
                }

                Counts countsBeforeUpdate = _separated._counts.InterlockedCompareExchange(newCounts, counts);
                if (countsBeforeUpdate == counts)
                {
                    return(counts.SignalCount != 0 || WaitForSignal(timeoutMs));
                }

                counts = countsBeforeUpdate;
            }
        }