//
        // 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);
            }
        }
        //
        // 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);
        }
示例#3
0
        //
        // Tries to take a data item from the specified subset of queues,
        // defined by offset and count, activating the specified cancellers.
        //

        public static int TryTakeAny(StBlockingQueue <T>[] qs, int offset, int count,
                                     out T di, StCancelArgs cargs)
        {
            int def = 0;
            int len = qs.Length;

            //
            // Try to take a data item immediately from one of the specified queues.
            //

            for (int j = offset, i = 0; i < count; i++)
            {
                StBlockingQueue <T> q = qs[j];
                if (q != null)
                {
                    if (q.TryTake(out di))
                    {
                        return(StParkStatus.Success + j);
                    }
                    def++;
                }
                if (++j >= len)
                {
                    j = 0;
                }
            }

            //
            // If the *qs* array contains only null references, throw the
            // ArgumentException.
            //

            if (def == 0)
            {
                throw new ArgumentException("qs: array contains only null references");
            }

            //
            // None of the specified queues allows an immediate take operation.
            // So, return failure if a null timeout was specified.
            //

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

            //
            // Create a parker and execute the take-any prologue on the
            // queues; the loop is exited when we detect that the take-any
            // operation was satisfied.
            //

            StParker pk = new StParker();

            WaitNode[] wns   = new WaitNode[len];
            WaitNode[] hints = new WaitNode[len];
            int        lv    = -1;

            for (int j = offset, i = 0; !pk.IsLocked && i < count; i++)
            {
                StBlockingQueue <T> q = qs[j];
                if (q != null)
                {
                    if ((wns[j] = q.TryTakePrologue(pk, (StParkStatus.Success + j), out di,
                                                    ref hints[j])) == null)
                    {
                        break;
                    }
                    lv = j;
                }
                if (++j >= len)
                {
                    j = 0;
                }
            }

            //
            // Park the current thread, activating the specified cancellers.
            //

            int ws = pk.Park(cargs);

            //
            // If the take-any operation succeed, compute the index of the
            // queue where we the data item was taken, retrive the data item
            // and execute the take epilogue, if needed.
            //

            int ti = -1;

            if (ws >= StParkStatus.Success)
            {
                ti = ws - StParkStatus.Success;
                if (wns[ti] != null)
                {
                    di = wns[ti].channel;
                    qs[ti].TakeEpilogue();
                }
            }

            //
            // Cancel the take attempt on all queues where we inserted
            // wait blocks, except the one where we retrieved the data item.
            //

            for (int j = offset, i = 0; i < count; i++)
            {
                WaitNode wn;
                if (j != ti && (wn = wns[j]) != null)
                {
                    qs[j].CancelTakeAttempt(wn, hints[j]);
                }
                if (j == lv)
                {
                    break;
                }
                if (++j >= len)
                {
                    j = 0;
                }
            }

            //
            // Return success or failure appropriately.
            //

            if (ti != -1)
            {
                return(ws);
            }
            StCancelArgs.ThrowIfException(ws);
            return(StParkStatus.Timeout);
        }