//
        // Signals a waitable and waits on another as an atomic
        // operation, activating the specified cancellers.
        //

        public static bool SignalAndWait(StWaitable tos, StWaitable tow, StCancelArgs cargs)
        {
            //
            // Create a parker to execute the WaitAny prologue on the
            // *tow* waitable.
            //

            StParker  pk   = new StParker();
            WaitBlock hint = null;
            int       sc   = 0;
            WaitBlock wb   = tow._WaitAnyPrologue(pk, StParkStatus.Success, ref hint, ref sc);

            //
            // Signal the *tos* waitable.
            //

            if (!tos._Release())
            {
                //
                // The signal operation failed. So, try to cancel the parker and,
                // if successful, cancel the acquire attempt; otherwise, wait until
                // the thread is unparked and, then, undo the acquire.
                //

                if (pk.TryCancel())
                {
                    tow._CancelAcquire(wb, hint);
                }
                else
                {
                    pk.Park();
                    tow._UndoAcquire();
                }

                //
                // Report the failure appropriately.
                //

                throw tos._SignalException;
            }

            //
            // Park the current thread, activating the specified cancellers
            // and spinning if appropriate.
            //

            int ws = pk.Park(sc, cargs);

            //
            // If we acquired, execute the WaitOne epilogue and return success.
            //

            if (ws == StParkStatus.Success)
            {
                tow._WaitEpilogue();
                return(true);
            }

            //
            // The acquire operation was cancelled; so, cancel the acquire
            // attempt and report the failure appropriately.
            //

            tow._CancelAcquire(wb, hint);
            StCancelArgs.ThrowIfException(ws);
            return(false);
        }
        internal static int WaitAnyInternal(StWaitable[] ws, WaitHandle[] hs, StCancelArgs cargs)
        {
            int len = ws.Length;

            //
            // First, we scan the *ws* array trying to acquire one of the
            // synchronizers.
            //

            for (int i = 0; i < len; i++)
            {
                if (ws[i]._TryAcquire())
                {
                    return(StParkStatus.Success + i);
                }
            }

            if (cargs.Timeout == 0)
            {
                return(StParkStatus.Timeout);
            }

            //
            // Create a parker and execute the WaitAny prologue on all
            // waitables. We stop executing prologues as soon as we detect
            // that the acquire operation was accomplished.
            //

            StParker pk = hs != null
                        ? new StParker(EventBasedParkSpotFactory.Current.
                                       Create(new WaitAnyBehavior(hs, len)))
                        : new StParker(1);

            WaitBlock[] wbs   = new WaitBlock[len];
            WaitBlock[] hints = new WaitBlock[len];

            int lv  = -1;
            int sc  = 0;
            int gsc = 0;

            for (int i = 0; !pk.IsLocked && i < len; i++)
            {
                StWaitable w = ws[i];

                if ((wbs[i] = w._WaitAnyPrologue(pk, i, ref hints[i], ref sc)) == null)
                {
                    if (pk.TryLock())
                    {
                        pk.UnparkSelf(i);
                    }
                    else
                    {
                        w._UndoAcquire();
                    }
                    break;
                }

                //
                // Adjust the global spin count.
                //

                if (gsc < sc)
                {
                    gsc = sc;
                }
                lv = i;
            }

            int        wst = pk.Park(gsc, cargs);
            StWaitable acq = wst >= StParkStatus.Success && wst < len ? ws[wst] : null;

            //
            // Cancel the acquire attempt on all waitables where we executed
            // the WaitAny prologue, except the one we acquired.
            //

            for (int i = 0; i <= lv; i++)
            {
                StWaitable w = ws[i];
                if (w != acq)
                {
                    w._CancelAcquire(wbs[i], hints[i]);
                }
            }

            if (wst >= StParkStatus.Success && wst < len + (hs != null ? hs.Length : 0))
            {
                if (acq != null)
                {
                    acq._WaitEpilogue();
                }
                return(wst);
            }

            StCancelArgs.ThrowIfException(wst);
            return(StParkStatus.Timeout);
        }
        //
        // Constructor: registers a wait with a Waitable synchronizer.
        //

        internal StRegisteredWait(StWaitable waitObject, WaitOrTimerCallback callback,
                                  object cbState, int timeout, bool executeOnce)
        {
            //
            // Validate the arguments.
            //

            if (timeout == 0)
            {
                throw new ArgumentOutOfRangeException("\"timeout\" can't be zero");
            }
            if (callback == null)
            {
                throw new ArgumentOutOfRangeException("\"callback\" can't be null");
            }
            if ((waitObject is StReentrantFairLock) || (waitObject is StReentrantReadWriteLock))
            {
                throw new InvalidOperationException("can't register waits on reentrant locks");
            }
            if ((waitObject is StNotificationEvent) && !executeOnce)
            {
                throw new InvalidOperationException("Notification event can't register waits"
                                                    + " to execute more than once");
            }

            //
            // Initialize the register wait fields
            //

            waitable         = waitObject;
            cbparker         = new CbParker(UnparkCallback);
            toTimer          = new RawTimer(cbparker);
            this.timeout     = timeout;
            this.executeOnce = executeOnce;
            this.callback    = callback;
            this.cbState     = (cbState != null) ? cbState : this;

            //
            // Execute the WaitAny prologue on the waitable.
            //
            int ignored = 0;

            waitBlock = waitObject._WaitAnyPrologue(cbparker, StParkStatus.Success, ref hint,
                                                    ref ignored);

            //
            // Set the registered wait state to active and enable the
            // unpark callback.
            //

            state = ACTIVE;
            int ws = cbparker.EnableCallback(timeout, toTimer);

            if (ws != StParkStatus.Pending)
            {
                //
                // The acquire operation was already accomplished. To prevent
                // uncontrolled reentrancy, the unpark callback is executed inline.
                //

                UnparkCallback(ws);
            }
        }
        //
        // Executes the unpark callback.
        //

        private void UnparkCallback(int ws)
        {
            Debug.Assert(ws == StParkStatus.Success || ws == StParkStatus.Timeout ||
                         ws == StParkStatus.WaitCancelled);

            //
            // If the registered wait was cancelled, cancel the acquire
            // attempt and return immediately.
            //

            if (ws == StParkStatus.WaitCancelled)
            {
                waitable._CancelAcquire(waitBlock, hint);
                return;
            }

            //
            // Set state to *our busy* grabing the current state, and execute
            // the unpark callback processing.
            //

            StParker myBusy   = new SentinelParker();
            StParker oldState = Interlocked.Exchange <StParker>(ref state, myBusy);

            do
            {
                //
                // If the acquire operation was cancelled, cancel the acquire
                // attempt on the waitable.
                //

                if (ws != StParkStatus.Success)
                {
                    waitable._CancelAcquire(waitBlock, hint);
                }

                //
                // Execute the user callback routine.
                //

                cbtid = Thread.CurrentThread.ManagedThreadId;
                callback(cbState, ws == StParkStatus.Timeout);
                cbtid = 0;

                //
                // If the registered wait was configured to execute once or
                // there is an unregister in progress, set the state to INACTIVE.
                // If a thread is waiting to unregister, unpark it.
                //

                if (executeOnce || !(oldState is SentinelParker))
                {
                    if (!(oldState is SentinelParker))
                    {
                        oldState.Unpark(StParkStatus.Success);
                    }
                    else
                    {
                        state = INACTIVE;
                    }
                    return;
                }

                //
                // We must re-register with the Waitable.
                // So, initialize the parker and execute the WaitAny prologue.
                //

                cbparker.Reset(1);
                int ignored = 0;
                waitBlock = waitable._WaitAnyPrologue(cbparker, StParkStatus.Success, ref hint,
                                                      ref ignored);

                //
                // Enable the unpark callback.
                //

                ws = cbparker.EnableCallback(timeout, toTimer);
                if (ws == StParkStatus.Pending)
                {
                    //
                    // If the *state* field constains still *my busy* set it to ACTIVE.
                    //

                    if (state == myBusy)
                    {
                        Interlocked.CompareExchange <StParker>(ref state, ACTIVE, myBusy);
                    }
                    return;
                }

                //
                // The waitable was already signalled. So, execute the unpark
                // callback inline.
                //
            } while (true);
        }
Beispiel #5
0
        //
        // Executes the prologue of the Waitable.WaitAny method.
        //

        internal override WaitBlock _WaitAnyPrologue(StParker pk, int key,
                                                     ref WaitBlock hint, ref int sc)
        {
            return(tmrEvent._WaitAnyPrologue(pk, key, ref hint, ref sc));
        }