Пример #1
0
        /// <summary>
        /// Waits for the event to become set. We will spin briefly if the event isn't set
        /// (assuming a non-0 timeout), before falling back to a true wait.
        /// </summary>
        /// <param name="millisecondsTimeout">The maximum amount of time to wait.</param>
        /// <returns>True if the wait succeeded, false if the timeout expired.</returns>
        /// <exception cref="System.ArgumentOutOfRangeException">If the timeout is not within range.</exception>
        public bool Wait(int millisecondsTimeout)
        {
            ThrowIfDisposed();

            if (millisecondsTimeout < -1)
            {
                throw Error.ArgumentOutOfRange("millisecondsTimeout");
            }

            if (!IsSet)
            {
                if (millisecondsTimeout == 0)
                {
                    // For 0-timeouts, we just return immediately.
                    return(false);
                }
                else
                {
#if DEBUG
                    TraceHelpers.TraceInfo("{0} - tid {1}: ManualResetEventSlim::Wait - doing a spin wait for {2} spins",
                                           m_id, Thread.CurrentThread.ManagedThreadId, SpinCount);
#endif

                    // We spin briefly before falling back to allocating and/or waiting on a true event.
                    SpinWait  s  = new SpinWait();
                    Stopwatch sw = null;

                    if (SpinCount > 0)
                    {
                        if (millisecondsTimeout != Timeout.Infinite)
                        {
                            // We will account for time spent spinning, so that we can decrement it from our
                            // timeout.  In most cases the time spent in this section will be negligible.  But
                            // we can't discount the possibility of our thread being switched out for a lengthy
                            // period of time.  The timeout adjustments only take effect when and if we actually
                            // decide to block in the kernel below.
                            sw = Stopwatch.StartNew();
                        }

                        for (int i = 0; i < SpinCount; i++)
                        {
                            s.SpinOnce();

                            // Keep rechecking the state. If we've become signaled while we spun above,
                            // return.  This is done with a volatile read to prevent reordering and hoisting
                            // out of the loop.
                            if (IsSet)
                            {
                                return(true);
                            }
                        }
                    }

                    if (m_eventObj == null)
                    {
                        // Lazily allocate the event. This method internally handles races w/ other threads.
                        LazyInitializeEvent();

                        if (IsSet)
                        {
                            // If it has since become signaled, there's no need to wait. This is strictly
                            // unnecessary, but does avoid a kernel transition.  Since the allocation could
                            // have taken some time, this might be worth the extra check in some cases.
                            return(true);
                        }
                    }

#if DEBUG
                    TraceHelpers.TraceInfo("{0} - tid {1}: ManualResetEventSlim::Wait - entering a real wait",
                                           m_id, Thread.CurrentThread.ManagedThreadId);
#endif

                    // Do our dynamic spin count accounting.  We don't worry about thread safety here.
                    // It's OK for reads and writes to be carried out non-atomically -- we might miss one
                    // here or there, but because it's a 4-byte aligned value we are guaranteed no tears.
                    if (UseDynamicSpinAdjustment)
                    {
                        int spinCount = SpinCount;

                        if (spinCount != 0 && spinCount < MAX_DYNAMIC_SPIN)
                        {
                            // We use an algorithm similar to the OS critical section. If our
                            // spinning didn't work out, we increment the counter so that we increase
                            // the chances of spinning working the next time.
                            m_spinCount = ((spinCount + 1) % int.MaxValue) | USE_DYNAMIC_SPIN_MASK;
                        }
                        else
                        {
                            // If we reached the dynamic spin maximum, and it's still not working, bail.
                            // We don't want to spin any longer since it's not buying us anything (in fact,
                            // it's just wasting time). This forces us to go straight to the wait next time.
                            m_spinCount = 0;
                        }
                    }

                    // If we spun at all, we'll take into account the time elapsed by adjusting
                    // the timeout value before blocking in the kernel.
                    int realMillisecondsTimeout = millisecondsTimeout;
                    if (sw != null)
                    {
                        long elapsedMilliseconds = sw.ElapsedMilliseconds;
                        if (elapsedMilliseconds > int.MaxValue)
                        {
                            return(false);
                        }

                        TraceHelpers.Assert(elapsedMilliseconds >= 0);

                        realMillisecondsTimeout -= (int)elapsedMilliseconds;
                        if (realMillisecondsTimeout <= 0)
                        {
                            return(false);
                        }
                    }

                    return(m_eventObj.WaitOne(realMillisecondsTimeout, false));
                }
            }

            return(true);
        }