//
        // Waits until acquire the specified number of permits, activating
        // the specified cancellers.
        //

        public bool Wait(int acquireCount, StCancelArgs cargs)
        {
            if (acquireCount <= 0 || acquireCount > maximumCount)
            {
                throw new ArgumentException("acquireCount");
            }

            if (TryAcquireInternal(acquireCount))
            {
                return(true);
            }

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

            var wb = new WaitBlock(WaitType.WaitAny, acquireCount);
            int sc = EnqueueAcquire(wb, acquireCount);

            int ws = wb.parker.Park(sc, cargs);

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

            CancelAcquire(wb);
            StCancelArgs.ThrowIfException(ws);
            return(false);
        }
Пример #2
0
        //
        // Waits until the synchronizer allows the acquire, activating
        // the specified cancellers.
        //

        public bool WaitOne(StCancelArgs cargs)
        {
            if (_TryAcquire())
            {
                return(true);
            }

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

            var       pk   = new StParker();
            WaitBlock hint = null;
            int       sc   = 0;
            WaitBlock wb;

            if ((wb = _WaitAnyPrologue(pk, StParkStatus.Success, ref hint, ref sc)) == null)
            {
                return(true);
            }

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

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

            _CancelAcquire(wb, hint);
            StCancelArgs.ThrowIfException(ws);
            return(false);
        }
        //
        // Exchanges a data item, activating the specified cancellers.
        //

        public bool Exchange(T myData, out T yourData, StCancelArgs cargs)
        {
            if (TryExchange(myData, out yourData))
            {
                return(true);
            }

            var pk = new StParker();
            var wn = new WaitNode(pk, myData);

            if (TryExchange(wn, myData, out yourData))
            {
                return(true);
            }

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

            if (ws == StParkStatus.Success)
            {
                yourData = wn.Channel;
                return(true);
            }

            CancelExchange(wn);
            StCancelArgs.ThrowIfException(ws);
            return(false);
        }
Пример #4
0
        //
        // Tries to take a data item from the queue, activating the
        // specified cancellers.
        //

        public bool TryTake(out T di, StCancelArgs cargs)
        {
            if (TryTake(out di))
            {
                return(true);
            }
            if (cargs.Timeout == 0)
            {
                return(false);
            }
            StParker pk = new StParker();
            WaitNode wn, hint = null;

            if ((wn = TryTakePrologue(pk, StParkStatus.Success, out di, ref hint)) == null)
            {
                return(true);
            }
            int ws = pk.Park(cargs);

            if (ws == StParkStatus.Success)
            {
                TakeEpilogue();
                di = wn.channel;
                return(true);
            }

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

            CancelTakeAttempt(wn, hint);
            StCancelArgs.ThrowIfException(ws);
            return(false);
        }
Пример #5
0
        /*++
         *
         * Generic API
         *
         * --*/

        //
        // Tries to add a data item to the queue, activating the specified
        // cancellers.
        //

        public bool TryAdd(T di, StCancelArgs cargs)
        {
            if (TryAdd(di))
            {
                return(true);
            }
            if (cargs.Timeout == 0)
            {
                return(false);
            }
            WaitNode hint = null;
            WaitNode wn;
            StParker pk = new StParker();

            if ((wn = TryAddPrologue(pk, StParkStatus.Success, di, ref hint)) == null)
            {
                return(true);
            }
            int ws = pk.Park(cargs);

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

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

            CancelAddAttempt(wn, hint);
            StCancelArgs.ThrowIfException(ws);
            return(false);
        }
Пример #6
0
        public static int Sleep(StCancelArgs cargs)
        {
            int ws = new StParker().Park(0, cargs);

            StCancelArgs.ThrowIfException(ws);
            return(ws);
        }
        private bool SlowEnter(StCancelArgs cargs)
        {
            int       lastTime      = cargs.Timeout != Timeout.Infinite ? Environment.TickCount : 0;
            bool      timeRemaining = true;
            WaitBlock wb            = EnqueueWaiter();

            do
            {
                if (TryEnter())
                {
                    Cleanup(wb);
                    return(true);
                }

                if (!timeRemaining)
                {
                    return(false);
                }

                int ws = wb.parker.Park(head.next == wb ? spinCount : 0, cargs);

                if (ws != StParkStatus.Success)
                {
                    StCancelArgs.ThrowIfException(ws);
                    return(false);
                }

                if (TryEnter())
                {
                    Cleanup(wb);
                    return(true);
                }

                //
                // We failed to acquire the lock so we must clear the current
                // thread as the candidate owner. After doing so we must recheck
                // the state of lock. If the wait timed out, we exit the loop
                // before parking again.
                //

                if ((timeRemaining = cargs.AdjustTimeout(ref lastTime)))
                {
                    wb.parker.Reset();
                }

                //
                // Avoid a release-followed-by-acquire hazard.
                //

                Interlocked.Exchange(ref wb.request, ACQUIRE);
            } while (true);
        }
Пример #8
0
        //
        // Waits until enter the write lock, activating the specified
        // cancellers.
        //

        public bool TryEnterWrite(StCancelArgs cargs)
        {
            //
            // Try to enter the write lock and, if succeed, return success.
            //

            if (TryEnterWriteInternal())
            {
                return(true);
            }

            //
            // Return failure, if a null timeout was specified.
            //

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

            //
            // Create a wait block and insert it in the r/w lock's queue.
            //

            WaitBlock wb = new WaitBlock(WaitType.WaitAny, ENTER_WRITE, StParkStatus.Success);
            int       sc = EnqueueEnterWrite(wb);

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

            int ws = wb.parker.Park(sc, cargs);

            //
            // If we entered the write lock, return success.
            //

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

            //
            // The request was cancelled; so, cancel the enter write lock
            // attempt and report the failure appropriately.
            //

            CancelAcquire(wb);
            StCancelArgs.ThrowIfException(ws);
            return(false);
        }
        public static bool ExchangeAny(StExchanger <T>[] xchgs, T myData, out T yourData, StCancelArgs cargs)
        {
            int len = xchgs.Length;

            for (int i = 0; i < len; i++)
            {
                if (xchgs[i].TryExchange(myData, out yourData))
                {
                    return(true);
                }
            }

            yourData = default(T);

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

            var pk  = new StParker(1);
            var wn  = new WaitNode(pk, myData);
            int lv  = -1;
            int gsc = 0;

            for (int i = 0; !pk.IsLocked && i < len; i++)
            {
                StExchanger <T> xchg = xchgs[i];

                if (xchg.TryExchange(wn, myData, out yourData))
                {
                    break;
                }

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

                int sc = xchg.spinCount;
                if (gsc < sc)
                {
                    gsc = sc;
                }

                lv = i;
            }

            int wst = pk.Park(gsc, cargs);

            for (int i = 0; i <= lv; i++)
            {
                xchgs[i].CancelExchange(wn);
            }

            if (wst == StParkStatus.Success)
            {
                return(true);
            }

            StCancelArgs.ThrowIfException(wst);
            return(false);
        }
Пример #10
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);
        }
Пример #11
0
        //
        // 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);
        }
Пример #12
0
        internal static bool WaitAllInternal(StWaitable[] ws, WaitHandle[] hs, StCancelArgs cargs)
        {
            if (ws == null)
            {
                throw new ArgumentNullException("ws");
            }

            int nevts;
            int len = ws.Length;

            StWaitable[] sws = new StWaitable[len];
            WaitHandle[] shs = null;

            int waitHint = SortAndCheckAllowAcquire(ws, sws, out nevts);

            if (waitHint < 0)
            {
                throw new ArgumentException("There are duplicate waitables", "ws");
            }

            if (hs != null)
            {
                shs = Sort(hs);
                if (shs == null)
                {
                    throw new ArgumentException("There are duplicate wait handles", "hs");
                }
            }

            if (waitHint != 0 && shs != null && !WaitHandle.WaitAll(shs, 0))
            {
                waitHint = 0;
            }

            //
            // Return success if all synchronizers are notification events and are set.
            //

            if (waitHint != 0 && nevts == 0)
            {
                return(true);
            }

            if (waitHint == 0 && cargs.Timeout == 0)
            {
                return(false);
            }

            //
            // If a timeout was specified, get the current time in order
            // to adjust the timeout value later, if we re-wait.
            //

            int lastTime = (cargs.Timeout != Timeout.Infinite) ? Environment.TickCount : 0;

            WaitBlock[] wbs   = null;
            WaitBlock[] hints = null;
            do
            {
                if (waitHint == 0)
                {
                    //
                    // Create the wait block arrays if this is the first time
                    // that we execute the acquire-all prologue.
                    //

                    if (wbs == null)
                    {
                        wbs   = new WaitBlock[len];
                        hints = new WaitBlock[len];
                    }

                    //
                    // Create a parker for cooperative release, specifying as many
                    // releasers as the number of waitables. The parker because is
                    // not reused because other threads may have references to it.
                    //

                    StParker pk = shs != null
                                ? new StParker(len, EventBasedParkSpotFactory.Current.
                                               Create(new WaitAllBehavior(shs)))
                                : new StParker(len);

                    int gsc = 1;
                    int sc  = 0;
                    for (int i = 0; i < len; i++)
                    {
                        if ((wbs[i] = sws[i]._WaitAllPrologue(pk, ref hints[i], ref sc)) == null)
                        {
                            if (pk.TryLock())
                            {
                                pk.UnparkSelf(StParkStatus.StateChange);
                            }
                        }
                        else if (gsc != 0)
                        {
                            if (sc == 0)
                            {
                                gsc = 0;
                            }
                            else if (sc > gsc)
                            {
                                gsc = sc;
                            }
                        }
                    }

                    int wst = pk.Park(gsc, cargs);

                    //
                    // If the wait was cancelled due to timeout, alert or interrupt,
                    // cancel the acquire attempt on all waitables where we actually
                    // inserted wait blocks.
                    //

                    if (wst != StParkStatus.StateChange)
                    {
                        for (int i = 0; i < len; i++)
                        {
                            WaitBlock wb = wbs[i];
                            if (wb != null)
                            {
                                sws[i]._CancelAcquire(wb, hints[i]);
                            }
                        }

                        StCancelArgs.ThrowIfException(wst);
                        return(false);
                    }
                }

                //
                // All waitables where we inserted wait blocks seem to allow
                // an immediate acquire operation; so, try to acquire all of
                // them that are not notification events.
                //

                int idx;
                for (idx = 0; idx < nevts; idx++)
                {
                    if (!sws[idx]._TryAcquire())
                    {
                        break;
                    }
                }

                //
                // If all synchronizers were acquired, return success.
                //

                if (idx == nevts)
                {
                    return(true);
                }

                //
                // We failed to acquire all waitables, so undo the acquires
                // that we did above.
                //

                while (--idx >= 0)
                {
                    sws[idx]._UndoAcquire();
                }

                if (shs != null)
                {
                    for (int i = 0; i < shs.Length; i++)
                    {
                        shs[i].UndoAcquire();
                    }
                }

                //
                // If a timeout was specified, adjust the timeout value
                // that will be used on the next wait.
                //

                if (!cargs.AdjustTimeout(ref lastTime))
                {
                    return(false);
                }

                waitHint = 0;
            } while (true);
        }
Пример #13
0
        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);
        }
Пример #14
0
            //
            // Sends a message, activating the specified cancellers.
            //

            internal override bool Send(ReqType type, T request, out R response,
                                        StCancelArgs cargs)
            {
                SendWaitNode <T, R> wn = null;
                bool enableCancel      = true;

                do
                {
                    WaitNode t;
                    if ((t = top) == null || t.type != ReqType.Receive)
                    {
                        //
                        // The stack is empty or the first wait node belongs
                        // to a sender thread.
                        //

                        if (wn == null)
                        {
                            if (cargs.Timeout == 0)
                            {
                                response = default(R);
                                return(false);
                            }
                            wn = new SendWaitNode <T, R>(type, request);
                        }
                        wn.next = t;
                        if (Interlocked.CompareExchange <WaitNode>(ref top, wn, t) == t)
                        {
                            break;
                        }
                    }
                    else
                    {
                        //
                        // The top wait node belongs to a receiver thread;
                        // so, try to pop it from the stack.
                        //

                        if (Interlocked.CompareExchange <WaitNode>(ref top, t.next, t) == t)
                        {
                            //
                            // Try to lock the associated parker and, if succeed, initiate
                            // the rendezvous with its owner thread.
                            //

                            if (t.TryLock())
                            {
                                RecvWaitNode <T, R> rwn = (RecvWaitNode <T, R>)t;
                                rwn.request = request;
                                if (type == ReqType.SendOnly)
                                {
                                    rwn.sender = null;
                                    rwn.Unpark(StParkStatus.Success);
                                    response = default(R);
                                    return(true);
                                }
                                if (wn == null)
                                {
                                    wn = new SendWaitNode <T, R>(ReqType.SendWaitReply);
                                }

                                rwn.sender = wn;
                                wn.SelfCancel();
                                rwn.Unpark(StParkStatus.Success);
                                enableCancel = false;
                                break;
                            }
                        }
                    }
                } while (true);

                //
                // Park the current thread, activating the specified cancellers,
                // but only if the rendezvous wasn't initiated.
                //

                int ws;

                if (enableCancel)
                {
                    ws = wn.Park(cargs);
                }
                else
                {
                    wn.Park();
                    ws = StParkStatus.Success;
                }

                //
                // If succeed, retrive the response and return success.
                //

                if (ws == StParkStatus.Success)
                {
                    response = wn.response;
                    return(true);
                }

                //
                // The send was cancelled; so, unlink the wait node from the wait
                // queue and report the failure appropriately.
                //

                Unlink(wn);
                response = default(R);
                StCancelArgs.ThrowIfException(ws);
                return(false);
            }
Пример #15
0
        //
        // Tries to acquire a busy lock, activating the specified cancellers.
        //

        private bool SlowEnter(StCancelArgs cargs)
        {
            //
            // If a timeout was specified, get a time reference in order
            // to adjust the timeout value if the thread need to re-wait.
            //

            int       lastTime = (cargs.Timeout != Timeout.Infinite) ? Environment.TickCount : 0;
            WaitBlock wb       = null;

            do
            {
                //
                // First, try to acquire the lock spinning for the configured
                // number of cycles, but only if the wait queue is empty.
                //

                int sc = spinCount;
                do
                {
                    if (state == FREE &&
                        Interlocked.CompareExchange(ref state, BUSY, FREE) == FREE)
                    {
                        return(true);
                    }
                    if (top != null || sc-- <= 0)
                    {
                        break;
                    }
                    Platform.SpinWait(1);
                } while (true);

                //
                // The lock is busy; so, create a wait block or reset the
                // one previously created and insert it in the wait queue.
                //

                if (wb == null)
                {
                    wb = new WaitBlock(ACQUIRE);
                }
                else
                {
                    wb.parker.Reset();
                }
                do
                {
                    WaitBlock t;
                    wb.next = (t = top);
                    if (Interlocked.CompareExchange <WaitBlock>(ref top, wb, t) == t)
                    {
                        break;
                    }
                } while (true);

                //
                // Since that the lock can become free after we inserted
                // the wait block, we must retry to acquire the lock, if it
                // seems free.
                //

                if (TryEnter())
                {
                    return(true);
                }

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

                int ws = wb.parker.Park(cargs);

                //
                // If the acquire attempt was cancelled; so, report the
                // failure appropriately.
                //

                if (ws != StParkStatus.Success)
                {
                    StCancelArgs.ThrowIfException(ws);
                    return(false);
                }

                //
                // Before adjust the timeout value, try to acquire the lock.
                //

                if (TryEnter())
                {
                    return(true);
                }

                //
                // If a timeout was specified, adjust its value taking into
                // account the elapsed time.
                //

                if (!cargs.AdjustTimeout(ref lastTime))
                {
                    return(false);
                }
            } while (true);
        }
Пример #16
0
            //
            // Receives a message, activating the specified cancellers.
            //

            internal override bool Receive(out T request, out StRendezvousToken token,
                                           StCancelArgs cargs)
            {
                RecvWaitNode <T, R> wn = null;

                do
                {
                    WaitNode t = top;
                    if (t == null || t.type == ReqType.Receive)
                    {
                        //
                        // The stack is empty or the wait note at the top of the
                        // stack belongs to a receiver thread. In this case, we must
                        // create a wait node and push it onto the stack.
                        //

                        if (wn == null)
                        {
                            if (cargs.Timeout == 0)
                            {
                                request = default(T);
                                token   = new StRendezvousToken(null);
                                return(false);
                            }
                            wn = new RecvWaitNode <T, R>();
                        }
                        wn.next = t;
                        if (Interlocked.CompareExchange <WaitNode>(ref top, wn, t) == t)
                        {
                            break;
                        }
                    }
                    else
                    {
                        //
                        // The top wait node belongs to a sender thread; so, try to
                        // pop it from the stack.
                        //

                        if (Interlocked.CompareExchange <WaitNode>(ref top, t.next, t) == t)
                        {
                            //
                            // Try to lock the associated parker and, if succeed, initiate
                            // the rendezvous with its owner thread.
                            //

                            if (t.TryLock())
                            {
                                SendWaitNode <T, R> swn = (SendWaitNode <T, R>)t;
                                request = swn.request;
                                if (swn.type == ReqType.SendOnly)
                                {
                                    token = new StRendezvousToken(null);
                                    swn.Unpark(StParkStatus.Success);
                                }
                                else
                                {
                                    token = new StRendezvousToken(swn);
                                }
                                return(true);
                            }
                        }
                    }
                } while (true);

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

                int ws = wn.Park(cargs);

                //
                // If succeed, retrive the request from the wait node, build a
                // rendezvous token and return success.
                //

                if (ws == StParkStatus.Success)
                {
                    request = wn.request;
                    token   = new StRendezvousToken(wn.sender);
                    return(true);
                }

                //
                // The receive was cancelled; so, unlink the wait node from
                // the wait queue and return the failure approriately.
                //

                Unlink(wn);
                request = default(T);
                token   = new StRendezvousToken(null);
                StCancelArgs.ThrowIfException(ws);
                return(false);
            }
Пример #17
0
        //
        // Signals the barrier and then waits until the current phase
        // completes, activating the specified cancellers.
        //

        public bool SignalAndWait(StCancelArgs cargs)
        {
            //
            // Get the current phase state.
            //

            PhaseState phs = phState;
            int        s, partners, arrived;

            do
            {
                partners = (s = phs.state) >> PARTNERS_SHIFT;
                arrived  = s & ARRIVED_MASK;
                if (arrived == partners)
                {
                    throw new InvalidOperationException("Barrier partners exceeded");
                }
                if (Interlocked.CompareExchange(ref phs.state, s + 1, s) == s)
                {
                    if (arrived + 1 == partners)
                    {
                        //
                        // This is the last partner thread. So, finish the current
                        // phase and return success.
                        //

                        FinishPhase();
                        return(true);
                    }
                    break;
                }
            } while (true);

            //
            // WaitOne on the phase event, activating the specified cancelers.
            //

            int ws = phs.waitEvent.Wait(cargs);

            //
            // If the event was signalled (i.e., successful synchronization),
            // return appropiately.
            //

            if (ws == StParkStatus.Success)
            {
                if (pphActionEx != null)
                {
                    throw new StBarrierPostPhaseException(pphActionEx);
                }
                return(true);
            }

            //
            // The wait was cancelled. So, try to decrement the counter of
            // arrived partners on our phase, if that is still possible.
            //

            do
            {
                //
                // If our partners of our phase already arrived, we must wait
                // unconditionally on the phase's event, postpone the cancellation
                // and return normally.
                //

                partners = (s = phs.state) >> PARTNERS_SHIFT;
                arrived  = s & ARRIVED_MASK;
                if (arrived == partners)
                {
                    phs.waitEvent.Wait(StCancelArgs.None);
                    StCancelArgs.PostponeCancellation(ws);
                    if (pphActionEx != null)
                    {
                        throw new StBarrierPostPhaseException(pphActionEx);
                    }
                    return(true);
                }

                //
                // Try to decrement the counter of arrived partners of our phase.
                //

                if (Interlocked.CompareExchange(ref phs.state, s - 1, s) == s)
                {
                    //
                    // We get out, so report the failure appropriately.
                    //

                    StCancelArgs.ThrowIfException(ws);
                    return(false);
                }
            } while (true);
        }
Пример #18
0
            //
            // Sends a message, activating the specified cancellers.
            //

            internal override bool Send(ReqType type, T request, out R response,
                                        StCancelArgs cargs)
            {
                SendWaitNode <T, R> wn   = null;
                WaitNode            pred = null;
                bool enableCancel        = true;

                do
                {
                    WaitNode h = head;
                    WaitNode t = tail;
                    WaitNode hn;
                    if ((hn = h.next) == null || hn.type != ReqType.Receive)
                    {
                        //
                        // The wait queue is empty or contains only sender
                        // wait nodes. So, do the consistency checks in order
                        // to insert our wait node.
                        //

                        WaitNode tn;
                        if ((tn = t.next) != null)
                        {
                            AdvanceTail(t, tn);
                            continue;
                        }

                        //
                        // Create a wait node, if we don't have one. However,
                        // if a null timeout was specified, return failure.
                        //

                        if (wn == null)
                        {
                            if (cargs.Timeout == 0)
                            {
                                response = default(R);
                                return(false);
                            }
                            wn = new SendWaitNode <T, R>(type, request);
                        }

                        //
                        // Try to enqueue the wait node.
                        //

                        if (t.CasNext(null, wn))
                        {
                            //
                            // Set the new tail, save the predecessor wait node
                            // and break the loop.
                            //

                            AdvanceTail(t, wn);
                            pred = t;
                            break;
                        }
                    }
                    else
                    {
                        //
                        // It seems that the wait node that is at front of the queue
                        // belongs to a receive thread; so, try to remove it and
                        // to initiate the rendezvous with the underlying thread.
                        //

                        if (AdvanceHead(h, hn))
                        {
                            if (hn.TryLock())
                            {
                                //
                                // We locked the receiver's wait node. So, get
                                // the request message.
                                //

                                RecvWaitNode <T, R> rwn = (RecvWaitNode <T, R>)hn;
                                rwn.request = request;

                                //
                                // If this a send only request, sets the sender to null,
                                // unpark the receiver thread and return success.
                                //

                                if (type == ReqType.SendOnly)
                                {
                                    rwn.sender = null;
                                    rwn.Unpark(StParkStatus.Success);
                                    response = default(R);
                                    return(true);
                                }

                                //
                                // This is a send and reply request. So, create a wait
                                // node in order to park the current thread and pass the
                                // wait node to the receiver through the rendezvous token.
                                //

                                if (wn == null)
                                {
                                    wn = new SendWaitNode <T, R>(ReqType.SendWaitReply);
                                }
                                rwn.sender = wn;
                                wn.SelfCancel();

                                //
                                // Unpark the receiver thread and go to wait until
                                // reply. After the rendezvous is initiated the
                                // cancellers are deactivated.
                                //

                                rwn.Unpark(StParkStatus.Success);
                                enableCancel = false;
                                break;
                            }
                        }
                    }
                } while (true);

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

                int ws;

                if (enableCancel)
                {
                    ws = wn.Park(cargs);
                }
                else
                {
                    wn.Park();
                    ws = StParkStatus.Success;
                }

                //
                // If succeed, retrieve the response from the wait node
                // and return success.
                //

                if (ws == StParkStatus.Success)
                {
                    response = wn.response;
                    return(true);
                }

                //
                // The send was cancelled; so unlink the wait node from
                // the wait queue and report the failure appropriately.
                //

                Unlink(wn, pred);
                response = default(R);
                StCancelArgs.ThrowIfException(ws);
                return(false);
            }
Пример #19
0
        //
        // Tries to enter the lock in upgrade read mode, activating
        // the specified cancellers.
        //

        public bool TryEnterUpgradeableRead(StCancelArgs cargs)
        {
            int           tid = Thread.CurrentThread.ManagedThreadId;
            ReaderCounter rc  = rdCounts.Lookup(tid);

            if (!isReentrant)
            {
                if (tid == upgrader)
                {
                    throw new StLockRecursionException("Recursive upgrade not allowed");
                }
                if (tid == writer)
                {
                    throw new StLockRecursionException("Upgrade after write not allowed");
                }
                if (rc != null)
                {
                    throw new StLockRecursionException("Upgrade after read not allowed");
                }
            }
            else
            {
                //
                // If the current thread is the current upgrader, increment the
                // recursive acquisition counter and return.
                //

                if (tid == upgrader)
                {
                    upCount++;
                    return(true);
                }

                //
                // If the current thread is the current writer, it can also
                // becomes the upgrader. If it is also a reader, it will be
                // accounted as reader on the *upgraderIsReader* flag.
                //

                if (tid == writer)
                {
                    upgrader = tid;
                    upCount  = 1;
                    if (rc != null)
                    {
                        upgraderIsReader = true;
                    }
                    return(true);
                }
                if (rc != null)
                {
                    throw new StLockRecursionException("Upgrade after read not allowed");
                }
            }

            //
            // Acquire the spinlock that protects the r/w lock shared state.
            //

            slock.Enter();

            //
            // If the lock isn't in write or upgrade read mode, the
            // current thread becomes the current upgrader. Then, release
            // the spinlock and return success.
            //

            if (writer == UNOWNED && upgrader == UNOWNED)
            {
                upgrader = tid;
                slock.Exit();
                upgraderIsReader = false;
                upCount          = 1;
                return(true);
            }

            //
            // The upgrade read lock can't be acquired immediately.
            // So, if a null timeout was specified, return failure.
            //

            if (cargs.Timeout == 0)
            {
                slock.Exit();
                return(false);
            }

            //
            // Create a wait node and insert it in the upgrader's queue.
            //

            int      sc = (upQueue.IsEmpty && wrQueue.IsEmpty) ? spinCount : 0;
            WaitNode wn;

            upQueue.Enqueue(wn = new WaitNode(tid));

            //
            // Release the spinlock and park the current thread activating
            // the specified cancellers and spinning, if appropriate.
            //

            slock.Exit();
            int ws = wn.Park(sc, cargs);

            //
            // If we acquired the upgrade lock, initialize the recursive
            // acquisition count and the *upgraderIsReader flag and return
            // success.
            //

            if (ws == StParkStatus.Success)
            {
                upCount          = 1;
                upgraderIsReader = false;
                return(true);
            }

            //
            // The acquire attemptwas cancelled. So, ensure that the
            // wait node is unlinked from the wait queue and report
            // the failure appropriately.
            //

            if (wn.next != wn)
            {
                slock.Enter();
                upQueue.Remove(wn);
                slock.Exit();
            }
            StCancelArgs.ThrowIfException(ws);
            return(false);
        }
Пример #20
0
        //
        // Tries to enter the write lock, activating the specified
        // cancellers.
        //

        public bool TryEnterWrite(StCancelArgs cargs)
        {
            int tid = Thread.CurrentThread.ManagedThreadId;

            if (!isReentrant)
            {
                if (tid == writer)
                {
                    throw new StLockRecursionException("Recursive enter write not allowed");
                }
                if (rdCounts.Lookup(tid) != null)
                {
                    throw new StLockRecursionException("Write after read not allowed");
                }
            }
            else
            {
                //
                // If this is a recursive enter, increment the recursive acquisition
                // counter and return success.
                //

                if (tid == writer)
                {
                    wrCount++;
                    return(true);
                }
            }

            //
            // Acquire the spinlock that protects the r/w lock shared state.
            //

            slock.Enter();

            //
            // If the write lock can be entered - this is, there are no lock
            // readers, no lock writer or upgrader or the current thread is the
            // upgrader -, enter the write lock, release the spinlock and
            // return success.
            //

            if (readers == 0 && writer == UNOWNED && (upgrader == tid || upgrader == UNOWNED))
            {
                writer = tid;
                slock.Exit();
                wrCount = 1;
                return(true);
            }

            //
            // If the current thread isn't the current upgrader but is reader,
            // release the spinlock and throw the appropriate exception.
            //

            if (tid != upgrader && rdCounts.Lookup(tid) != null)
            {
                slock.Exit();
                throw new StLockRecursionException("Write after read not allowed");
            }

            //
            // The write lock can't be entered immediately. So, if a null timeout
            // was specified, release the spinlock and return failure.
            //

            if (cargs.Timeout == 0)
            {
                slock.Exit();
                return(false);
            }

            //
            // Create a wait node and insert it in the writers queue.
            // If the current thread isn't the current upgrader, the wait
            // node is inserted in the writer's queue; otherwise, the wait
            // node becomes referenced by the *upgradeToWriteWaiter* field.
            //

            int      sc;
            WaitNode wn = new WaitNode(tid);

            if (tid == upgrader)
            {
                upgToWrWaiter = wn;
                sc            = spinCount;
            }
            else
            {
                sc = wrQueue.IsEmpty ? spinCount : 0;
                wrQueue.Enqueue(wn);
            }

            //
            // Release spin the lock and park the current thread, activating
            // the specified cancellers and spinning, if appropriate.
            //

            slock.Exit();
            int ws = wn.Park(sc, cargs);

            //
            // If the thread entered the write lock, initialize the recursive
            // acquisition counter and return success.
            //

            if (ws == StParkStatus.Success)
            {
                wrCount = 1;
                return(true);
            }

            //
            // The enter attempted was cancelled. So, if the wait node was
            // already remove from the respective queue, report the failure
            // appropriately. Otherwise, unlink the wait node and, if appropriate,
            // taking into account the other waiters.
            //

            if (wn.next == wn)
            {
                goto ReportFailure;
            }
            slock.Enter();
            if (wn.next != wn)
            {
                if (wn == upgToWrWaiter)
                {
                    upgToWrWaiter = null;
                }
                else
                {
                    wrQueue.Remove(wn);

                    //
                    // If the writers queue becomes empty, it is possible that there
                    // is a waiting upgrader or waiting reader threads that can now
                    // proceed.
                    //

                    if (writer == UNOWNED && upgrader == UNOWNED && wrQueue.IsEmpty &&
                        TryWakeupUpgraderAndReaders())
                    {
                        goto ReportFailure;
                    }
                }
            }
            slock.Exit();

ReportFailure:
            StCancelArgs.ThrowIfException(ws);
            return(false);
        }
Пример #21
0
        //
        // Tries to enter the read lock, activating the
        // specified cancellers.
        //

        public bool TryEnterRead(StCancelArgs cargs)
        {
            int           tid = Thread.CurrentThread.ManagedThreadId;
            ReaderCounter rc  = rdCounts.Lookup(tid);

            if (!isReentrant)
            {
                if (tid == writer)
                {
                    throw new StLockRecursionException("Read after write not allowed");
                }
                if (rc != null)
                {
                    throw new StLockRecursionException("Recursive read not allowed");
                }
            }
            else
            {
                //
                // If this is a recursive enter, increment the recursive
                // acquisition counter and return.
                //

                if (rc != null)
                {
                    rc.count++;
                    return(true);
                }
            }

            //
            // Acquire the spinlock that protected the r/u/w lock shared state.
            //

            slock.Enter();

            //
            // If the current thread is the upgrader, it can also enter
            // the read lock. So, add an entry to the readers table,
            // release the spinlock and return success.
            //

            if (tid == upgrader)
            {
                rdCounts.Add(tid);
                slock.Exit();
                upgraderIsReader = true;
                return(true);
            }

            //
            // The read lock can be entered, if the r/w lock isn't in write
            // mode and no thread is waiting to enter the writer mode.
            // If these conditions are met, increment the number of lock
            // readers, add an entry to the readers table, release the
            // spinlock and return success.
            //

            if (writer == UNOWNED && wrQueue.IsEmpty && upgToWrWaiter == null)
            {
                readers++;
                rdCounts.Add(tid);
                slock.Exit();
                return(true);
            }

            //
            // If the r/w lock is reentrant and the current thread is the
            // current writer, it can also to become a reader. So, increment
            // the number of lock readers, add an entry to the readers table,
            // release the spinlock and return success.
            //

            if (isReentrant && tid == writer)
            {
                readers++;
                rdCounts.Add(tid);
                slock.Exit();
                return(true);
            }

            //
            // The current thread can't enter the read lock immediately.
            // So, if a null timeout was specified, release the spinlock
            // and return failure.
            //

            if (cargs.Timeout == 0)
            {
                slock.Exit();
                return(false);
            }

            //
            // Create a wait node  and insert it in the readers queue.
            // Compute also the amount of spinning.
            //

            int      sc = rdQueue.IsEmpty ? spinCount : 0;
            WaitNode wn;

            rdQueue.Enqueue(wn = new WaitNode(tid));

            //
            // Release the spinlock and park the current thread, activating
            // the specified cancellers and spinning if appropriate.
            //

            slock.Exit();
            int ws = wn.Park(sc, cargs);

            //
            // If we entered the read lock, return success.
            //

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

            //
            // The enter attempt was cancelled. So, ensure that the wait node
            // is unlinked from the queue and report the failure appropriately.
            //

            if (wn.next != wn)
            {
                slock.Enter();
                rdQueue.Remove(wn);
                slock.Exit();
            }
            StCancelArgs.ThrowIfException(ws);
            return(false);
        }
        //
        // Writes the specified number of data items to the
        // stream blocking queue, activating the specified cancellers.
        //

        public int Write(T[] buffer, int offset, int count, StCancelArgs cargs)
        {
            //
            // If this is a zero length write, return immediately.
            //

            if (count == 0)
            {
                return(0);
            }

            //
            // Initialize the local variables.
            //

            int remaining        = count;
            WaitNodeQueue <T> wl = new WaitNodeQueue <T>();
            int toCopy;

            //
            // Acquire the queue's lock.
            //

            slock.Enter();

            //
            // If the queue's buffer is full, check if the current
            // thread must wait.
            //

            if (available == length)
            {
                goto CheckForWait;
            }

            //
            // If there are waiting readers (i.e., the buffer is empty),
            // transfer data items transferring directly to the waiting
            // readers' buffers.
            //

            if (!waitQueue.IsEmpty)
            {
                do
                {
                    WaitNode <T> rdw = waitQueue.head;

                    //
                    // Compute the number of data items to transfer to the
                    // waiting reader's buffer and perform the transfer.
                    //

                    if ((toCopy = rdw.remaining) > remaining)
                    {
                        toCopy = remaining;
                    }
                    Array.Copy(buffer, offset, rdw.buffer, rdw.offset, toCopy);
                    rdw.remaining -= toCopy;
                    rdw.offset    += toCopy;
                    remaining     -= toCopy;
                    offset        += toCopy;

                    //
                    // If the waiting reader completes its read operation,
                    // remove its wait node from the wait list and try to
                    // lock the associated parker.
                    //

                    if (rdw.remaining == 0)
                    {
                        waitQueue.Dequeue();
                        if (rdw.TryLock() && !rdw.UnparkInProgress(StParkStatus.Success))
                        {
                            wl.Add(rdw);
                        }
                    }
                    else
                    {
                        //
                        // The data items are not enough to satisfy the waiting
                        // reader that is at front of wait queue, so break the loop.
                        //

                        break;
                    }
                } while (remaining != 0 && !waitQueue.IsEmpty);
            }

            //
            // If we have still data items to write and there is free space
            // in the queue's buffer, transfer the appropriate number of data
            // items the queue's buffer.
            //

            if (remaining != 0 && available < length)
            {
                //
                // Compute the number of data items that can be copied
                // to the queue's buffer and perform the transfer.
                //

                if ((toCopy = remaining) > (length - available))
                {
                    toCopy = length - available;
                }
                int t = tail;
                int tillEnd;
                if ((tillEnd = length - t) >= toCopy)
                {
                    Array.Copy(buffer, offset, items, t, toCopy);
                    if ((t += toCopy) >= length)
                    {
                        t = 0;
                    }
                }
                else
                {
                    int fromBegin = toCopy - tillEnd;
                    Array.Copy(buffer, offset, items, t, tillEnd);
                    Array.Copy(buffer, offset + tillEnd, items, 0, fromBegin);
                    t = fromBegin;
                }

                //
                // Update counters and indexes.
                //

                tail       = t;
                available += toCopy;
                remaining -= toCopy;
                offset    += toCopy;
            }

CheckForWait:

            //
            // If there are still data items to write, the current thread must
            // wait if a null timeout wasn't specified.
            //

            WaitNode <T> wn = null;
            bool mustWait;

            if (mustWait = (remaining != 0 && cargs.Timeout != 0))
            {
                waitQueue.Enqueue(wn = new WaitNode <T>(buffer, offset, remaining));
            }

            //
            // Release the queue's lock and unpark the readers threads
            // release above.
            //

            slock.Exit();
            wl.UnparkAll();

            //
            // If the current thread doesn't need to wait, return the
            // number of written data items.
            //

            if (!mustWait)
            {
                return(count - remaining);
            }

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

            int ws = wn.Park(cargs);

            //
            // If the write was completed, return the number of written
            // data items.
            //

            if (ws == StParkStatus.Success)
            {
                return(count);
            }

            //
            // The write was cancelled due to timeout, alert or interrupt.
            // If the wait node is still inserted in the wait queue, acquire
            // the queue's lock and unlink it.
            //

            if (wn.next != wn)
            {
                slock.Enter();
                waitQueue.Remove(wn);
                slock.Exit();
            }

            //
            // If at least a data item was written, ignore the cancellation
            // and return the number of transferred data items.
            //

            int c;

            if ((c = count - wn.remaining) != 0)
            {
                StCancelArgs.PostponeCancellation(ws);
                return(c);
            }

            //
            // No data items were written; so, report the failure appropriately.
            //

            StCancelArgs.ThrowIfException(ws);
            return(0);
        }
        //
        // Waits until read the specified number of data items from
        // the stream queue, activating the specified cancellers.
        //

        public int Read(T[] buffer, int offset, int count, StCancelArgs cargs)
        {
            //
            // If this is a zero length read, return immediately.
            //

            if (count == 0)
            {
                return(0);
            }

            //
            // Initialize the local variables and acquire the queue's lock.
            //

            int remaining        = count;
            WaitNodeQueue <T> wl = new WaitNodeQueue <T>();
            int toCopy;

            slock.Enter();

            //
            // If the queue's buffer is empty, check if the current thread
            // must wait.
            //

            if (available == 0)
            {
                goto CheckForWait;
            }

            //
            // Compute the number of data items that we can read
            // from the queue's buffer and perform the transfer.
            //

            if ((toCopy = remaining) > available)
            {
                toCopy = available;
            }
            int h = head;
            int tillEnd;

            if ((tillEnd = length - h) >= toCopy)
            {
                Array.Copy(items, h, buffer, offset, toCopy);
                if ((h += toCopy) >= length)
                {
                    h = 0;
                }
            }
            else
            {
                int fromBegin = toCopy - tillEnd;
                Array.Copy(items, h, buffer, offset, tillEnd);
                Array.Copy(items, 0, buffer, offset + tillEnd, fromBegin);
                h = fromBegin;
            }

            //
            // Adjust counters and indexes.
            //

            head       = h;
            available -= toCopy;
            remaining -= toCopy;
            offset    += toCopy;

            //
            // If we have still data items to read and there are waiting
            // writers, transfer the data directly from our buffer to the
            // waiting writers' buffers.
            //

            if (remaining != 0 && !waitQueue.IsEmpty)
            {
                do
                {
                    WaitNode <T> wrw = waitQueue.head;

                    //
                    // Compute the number of data items to transfer to the
                    // waiting writer's buffer and perform the transfer.
                    //

                    if ((toCopy = remaining) > wrw.remaining)
                    {
                        toCopy = wrw.remaining;
                    }
                    Array.Copy(wrw.buffer, wrw.offset, buffer, offset, toCopy);
                    wrw.remaining -= toCopy;
                    wrw.offset    += toCopy;
                    remaining     -= toCopy;
                    offset        += toCopy;

                    //
                    // If the write operation was completed, try to release
                    // the writer thread.
                    //

                    if (wrw.remaining == 0)
                    {
                        waitQueue.Dequeue();
                        if (wrw.TryLock() && !wrw.UnparkInProgress(StParkStatus.Success))
                        {
                            wl.Add(wrw);
                        }
                    }
                    else
                    {
                        //
                        // The read is completed, so break the loop.
                        //

                        break;
                    }
                } while (remaining != 0 && !waitQueue.IsEmpty);
            }

            //
            // If there is available space in the queue's buffer and
            // waiting writers, try to fill the queue's buffer with
            // the data of the waiting writers.
            //

            if (available < length && !waitQueue.IsEmpty)
            {
                do
                {
                    WaitNode <T> wrw = waitQueue.head;

                    //
                    // Compute the number of data items to copy from the writer's
                    // buffer to the queue's buffer and perform the transfer.
                    //

                    if ((toCopy = wrw.remaining) > (length - available))
                    {
                        toCopy = length - available;
                    }

                    int t = tail;
                    if ((tillEnd = length - t) >= toCopy)
                    {
                        Array.Copy(wrw.buffer, wrw.offset, items, t, toCopy);
                        if ((t += toCopy) >= length)
                        {
                            t = 0;
                        }
                    }
                    else
                    {
                        int fromBegin = toCopy - tillEnd;
                        Array.Copy(wrw.buffer, wrw.offset, items, t, tillEnd);
                        Array.Copy(wrw.buffer, wrw.offset + tillEnd, items, 0, fromBegin);
                        t = fromBegin;
                    }

                    //
                    // Update counters and indexes.
                    //

                    tail           = t;
                    available     += toCopy;
                    wrw.remaining -= toCopy;
                    wrw.offset    += toCopy;

                    //
                    // If the writer completed its write, release it.
                    //

                    if (wrw.remaining == 0)
                    {
                        waitQueue.Dequeue();
                        if (wrw.TryLock() && !wrw.UnparkInProgress(StParkStatus.Success))
                        {
                            wl.Add(wrw);
                        }
                    }
                    else
                    {
                        //
                        // The queue's buffer is full, so break the loop.
                        //

                        break;
                    }
                } while (available < length && !waitQueue.IsEmpty);
            }

CheckForWait:

            //
            // If the read operation was not completed, the current thread
            // must wait if it didn't read all data items and a null timeout
            // wasn't specified.
            //

            WaitNode <T> wn = null;
            bool mustWait;

            if (mustWait = (remaining != 0 && cargs.Timeout != 0))
            {
                waitQueue.Enqueue(wn = new WaitNode <T>(buffer, offset, remaining));
            }

            //
            // Release the queue's lock and unpark the released waiters.
            //

            slock.Exit();
            wl.UnparkAll();

            //
            // If the read was completed or the thread specified a null
            // timeout, return the number of read data items.
            //

            if (!mustWait)
            {
                return(count - remaining);
            }

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

            int ws = wn.Park(cargs);

            //
            // If succeed, return the number of data items transferred.
            //

            if (ws == StParkStatus.Success)
            {
                return(count);
            }

            //
            // The read was cancelled due to timeout, alert or interrupt.
            // If the wait block is still inserted in the wait queue, acquire
            // the queue's lock and remove it from the queue.
            //

            if (wn.next != wn)
            {
                slock.Enter();
                waitQueue.Remove(wn);
                slock.Exit();
            }
            int c;

            if ((c = count - wn.remaining) != 0)
            {
                StCancelArgs.PostponeCancellation(ws);
                return(c);
            }

            //
            // No data items were transferred, so report the failure
            // appropriately.
            //

            StCancelArgs.ThrowIfException(ws);
            return(0);
        }
Пример #24
0
            //
            // Receives a message, activativating the specified cancellers.
            //

            internal override bool Receive(out T request, out StRendezvousToken token,
                                           StCancelArgs cargs)
            {
                RecvWaitNode <T, R> wn = null;
                WaitNode            pred;

                do
                {
                    WaitNode h = head;
                    WaitNode t = tail;
                    WaitNode hn;
                    if ((hn = h.next) == null || hn.type == ReqType.Receive)
                    {
                        //
                        // The wait queue is empty or contains only receiver
                        // wait nodes. So, do the consistency checks in order to
                        // insert our wait node.
                        //

                        WaitNode tn;
                        if ((tn = t.next) != null)
                        {
                            AdvanceTail(t, tn);
                            continue;
                        }

                        //
                        // Create a wait node, if we don't have one. However,
                        // return failure if a null timeout was specified.
                        //

                        if (wn == null)
                        {
                            if (cargs.Timeout == 0)
                            {
                                request = default(T);
                                token   = new StRendezvousToken(null);
                                return(false);
                            }
                            wn = new RecvWaitNode <T, R>();
                        }

                        //
                        // Try to enqueue the wait node.
                        //

                        if (t.CasNext(null, wn))
                        {
                            AdvanceTail(t, wn);
                            pred = t;
                            break;
                        }
                    }
                    else
                    {
                        //
                        // There is a sender wait node at the front of the queue.
                        // So, try to remove it to initiate the rendezvous.
                        //

                        if (AdvanceHead(h, hn))
                        {
                            if (hn.TryLock())
                            {
                                SendWaitNode <T, R> swn = (SendWaitNode <T, R>)hn;

                                //
                                // Get the request, then, If this is a send only
                                // request build a null rendezvous token and unpark
                                // the sender thread; otherwise, build a rendezvous token
                                // with the sender wait node.
                                //

                                request = swn.request;
                                if (swn.type == ReqType.SendOnly)
                                {
                                    token = new StRendezvousToken(null);
                                    swn.Unpark(StParkStatus.Success);
                                }
                                else
                                {
                                    token = new StRendezvousToken(swn);
                                }
                                return(true);
                            }
                        }
                    }
                } while (true);

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

                int ws = wn.Park(cargs);

                //
                // If succeed, retrive the request and the rendezvous token
                // from the wait node and return success.
                //

                if (ws == StParkStatus.Success)
                {
                    request = wn.request;
                    token   = new StRendezvousToken(wn.sender);
                    return(true);
                }

                //
                // The receive was cancelled; so, unlink our wait node from
                // the wait queue and report the failure appropriately.
                //

                Unlink(wn, pred);
                request = default(T);
                token   = default(StRendezvousToken);
                StCancelArgs.ThrowIfException(ws);
                return(false);
            }