//
        // Constructor: registers a take with a blocking queue.
        //

        internal StRegisteredTake(StBlockingQueue <T> queue, StTakeCallback <T> callback,
                                  object cbState, int timeout, bool executeOnce)
        {
            //
            // Validate the arguments.
            //

            if (timeout == 0)
            {
                throw new ArgumentOutOfRangeException("\"timeout\" can not be zero");
            }
            if (callback == null)
            {
                throw new ArgumentOutOfRangeException("\"callback\" must be specified");
            }

            //
            // Initialize the registered take fields.
            //

            this.queue = queue;
            cbparker   = new CbParker(UnparkCallback);
            if ((this.timeout = timeout) != Timeout.Infinite)
            {
                toTimer = new RawTimer(cbparker);
            }
            this.executeOnce = executeOnce;
            this.callback    = callback;
            this.cbState     = cbState;

            //
            // Execute the TryTakePrologue prologue on the queue.
            //

            waitNode = queue.TryTakePrologue(cbparker, StParkStatus.Success, out dataItem,
                                             ref hint);

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

            state = ACTIVE;
            int ws;

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

                UnparkCallback(ws);
            }
        }
        //
        // Exchanges a data item asynchronously. The specified callback gets called
        // when the exchange completes and is passed the received data item.
        //
        //  TODO: Prevent unbounded reentrancy (hacked by queueing to the pool).
        //

        public ExchangeRegistration RegisterExchange(T myData, ExchangeCallback <T> callback,
                                                     object state, int timeout)
        {
            if (timeout == 0)
            {
                throw new ArgumentOutOfRangeException("timeout", "The timeout can't be zero");
            }
            if (callback == null)
            {
                throw new ArgumentNullException("callback");
            }

            T yourData;

            if (TryExchange(myData, out yourData))
            {
                ThreadPool.QueueUserWorkItem(_ => callback(state, yourData, false));
                return(new ExchangeRegistration());
            }

            state = state ?? this;
            WaitNode wn       = null;
            var      cbparker = new CbParker(ws => {
                if (ws != StParkStatus.Success && xchgPoint == wn)
                {
                    Interlocked.CompareExchange(ref xchgPoint, null, wn);
                }

                if (ws != StParkStatus.WaitCancelled)
                {
                    callback(state, wn.Channel, ws == StParkStatus.Timeout);
                }
            });

            wn = new WaitNode(cbparker, myData);

            if (TryExchange(wn, myData, out yourData))
            {
                ThreadPool.QueueUserWorkItem(_ => callback(state, yourData, false));
                return(new ExchangeRegistration());
            }

            var timer      = timeout != Timeout.Infinite ? new RawTimer(cbparker) : null;
            int waitStatus = cbparker.EnableCallback(timeout, timer);

            if (waitStatus != StParkStatus.Pending)
            {
                ThreadPool.QueueUserWorkItem(_ => callback(state, wn.Channel, waitStatus == StParkStatus.Timeout));
                return(new ExchangeRegistration());
            }
            return(new ExchangeRegistration(cbparker));
        }
        //
        // Executes the unpark callback.
        //

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

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

            if (ws == StParkStatus.TakeCancelled)
            {
                queue.CancelTakeAttempt(waitNode, hint);
                return;
            }

            //
            // Set state to *our busy*, grabing the current state.
            //

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

            do
            {
                //
                // If the take operation succeeded, execute the take epilogue;
                // otherwise, cancel the take attempt.
                //

                if (ws == StParkStatus.Success)
                {
                    queue.TakeEpilogue();
                }
                else
                {
                    queue.CancelTakeAttempt(waitNode, hint);
                }

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

                cbtid = Thread.CurrentThread.ManagedThreadId;
                callback(cbState, waitNode == null ? dataItem : waitNode.channel,
                         ws == StParkStatus.Timeout);
                cbtid = 0;

                //
                // If the registered take 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 queue.
                // So, initialize the parker and execute the TakeAny prologue.
                //

                cbparker.Reset();
                waitNode = queue.TryTakePrologue(cbparker, StParkStatus.Success, out dataItem,
                                                 ref hint);

                //
                // Enable the unpark callback.
                //

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

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

                //
                // The take was already accomplished; so, to prevent uncontrolled
                // reentrancy execute the unpark callback inline.
                //
            } while (true);
        }
        //
        // 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);
        }
        //
        // 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);
            }
        }
Пример #6
0
        //
        // Executes the timer callback
        //

        internal void TimerCallback(int ws)
        {
            Debug.Assert(ws == StParkStatus.Timeout || ws == StParkStatus.TimerCancelled);

            //
            // If the timer was cancelled, return immediately.
            //

            if (ws == StParkStatus.TimerCancelled)
            {
                return;
            }

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

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

            do
            {
                //
                // Signals the timer's event.
                //

                tmrEvent.Signal();

                //
                // Call the user-defined callback, if specified.
                //

                if (callback != null)
                {
                    cbtid = Thread.CurrentThread.ManagedThreadId;
                    callback(cbState, true);
                    cbtid = 0;
                }

                //
                // If the  timer isn't periodic or if someone is trying to
                // cancel it, process cancellation.
                //

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

                //
                // Initialize the timer's parker.
                //

                cbparker.Reset();

                //
                // Compute the timer delay and enable the unpark callback.
                //

                int timeout;
                if (useDueTime)
                {
                    timeout    = dueTime;
                    useDueTime = false;
                }
                else
                {
                    timeout = period | (1 << 31);
                }
                if ((ws = cbparker.EnableCallback(timeout, timer)) == StParkStatus.Pending)
                {
                    if (state == myBusy)
                    {
                        Interlocked.CompareExchange <StParker>(ref state, ACTIVE, myBusy);
                    }
                    return;
                }

                //
                // The timer already expired. So, execute the timer
                // callback inline.
                //
            } while (true);
        }