internal override WaitNode TryTakePrologue(StParker pk, int key, out T di, ref WaitNode hint)
        {
            var localQueue = Current;

            if (TryTake(out di, localQueue))
            {
                if (pk.TryLock())
                {
                    pk.UnparkSelf(key);
                }
                else
                {
                    localQueue.TryAdd(di);
                }
                return(null);
            }

            var wn = new WaitNode(pk, key);

            waitQueue.Enqueue(wn);

            if (!pk.IsLocked && TryTake(out di, localQueue))
            {
                if (pk.TryLock())
                {
                    waitQueue.Unlink(wn, hint);
                    pk.UnparkSelf(key);
                    return(null);
                }

                localQueue.TryAdd(di);
            }

            return(wn);
        }
Beispiel #2
0
        //
        // Executes the prologue of the TryAdd operation.
        //

        internal override WaitNode TryAddPrologue(StParker pk, int key, T di, ref WaitNode ignored)
        {
            if (pk.TryLock())
            {
                pk.UnparkSelf(key);
            }
            AddWorker(di);
            return(null);
        }
Beispiel #3
0
        //
        // Adds a raw timer to the timer list.
        //

        private static void SetRawTimerWorker(RawTimer timer, int delay)
        {
            //
            // Set the next fire time.
            //

            timer.fireTime = baseTime + delay;

            //
            // Find the insertion position for the new timer.
            //

            RawTimer t = timerListHead.next;

            while (t != timerListHead)
            {
                if (delay < t.delay)
                {
                    break;
                }
                delay -= t.delay;
                t      = t.next;
            }

            //
            // Insert the new timer in the list, adjust the next timer and
            // redefine the delay sentinel.
            //

            timer.delay = delay;
            InsertTailTimerList(t, timer);
            if (t != timerListHead)
            {
                t.delay -= delay;
            }

            //
            // If the timer was inserted at front of the timer list we need
            // to wake the timer thread if it is blocked on its parker.
            //

            bool wake = (timerListHead.next == timer && parker.TryLock());

            //
            // Release the timer list lock and unpark the timer thread, if needed.
            //

            _lock.Exit();
            if (wake)
            {
                parker.Unpark(StParkStatus.Success);
            }
        }
Beispiel #4
0
        //
        // Executes the prologue of the TryAdd operation.
        //

        internal override WaitNode TryAddPrologue(StParker pk, int key, T di, ref WaitNode hint) {
            if (TryReserveSlot()) {
                if (pk.TryLock()) {
                    AddWorker(di);
                    pk.UnparkSelf(key);
                } else {
                    FreeSlot();
                }
                return null;
            }

            //
            // ...
            //

            WaitNode wn;
            hint = waitQueue.Enqueue(wn = new WaitNode(pk, key, di));

            //
            // As a slot could have been free after the check done
            // above, but before we insert the wait block in the wait queue,
            // we must retry to reserve a free slot.
            //

            if (TryReserveSlot()) {
                if (pk.TryLock()) {
                    AddWorker(di);
                    pk.UnparkSelf(key);
                } else {
                    waitQueue.Unlink(wn, hint);
                    FreeSlot();
                }
                return null;
            }
            return wn;
        }
        //
        // Frees the lock and selects a candidate owner from the queue
        // of waiting threads.
        //

        public void Exit()
        {
            WaitBlock wb     = head;
            bool      unpark = false;
            StParker  pk     = null;

            while ((wb = wb.next) != null && wb.request != CANDIDATE)
            {
                pk = wb.parker;
                if (pk.TryLock())
                {
                    wb.request = CANDIDATE;
                    unpark     = true;
                    break;
                }
            }

            state = FREE;

            if (unpark)
            {
                pk.Unpark(StParkStatus.Success);
            }
        }
        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);
        }
        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);
        }
Beispiel #8
0
        //
        // Tries to add immediatelly a data item to the queue.
        //

        public override bool TryAdd(T di)
        {
            //
            // If the queue is full, return failure immediatelly.
            //

            if (count == length)
            {
                return(false);
            }

            //
            // The queue seems non-full; so, acquire the queue's lock.
            //

            qlock.Enter();

            //
            // When the queue's buffer has free slots, it can't have threads
            // blocked by the add operation; so, if there are waiters, they
            // were blocked by the take operation.
            //

            if (count < length)
            {
                if (!waitQueue.IsEmpty)
                {
                    //
                    // Try to deliver the data item directly to a waiting thread.
                    //

                    do
                    {
                        WaitNode w  = waitQueue.Dequeue();
                        StParker pk = w.parker;
                        if (pk.TryLock())
                        {
                            //
                            // Release the queue's lock, pass the data item through
                            // the wait node, unpark the waiter thread and return
                            // success.
                            //

                            qlock.Exit();
                            w.channel = di;
                            pk.Unpark(w.waitKey);
                            return(true);
                        }
                    } while (!waitQueue.IsEmpty);
                }

                //
                // There is at least a free slot on the queue; So, copy the
                // data item to the queue's buffer, unlock the queue and
                // return success.
                //

                items[tail] = di;
                if (++tail == length)
                {
                    tail = 0;
                }
                count++;
                qlock.Exit();
                return(true);
            }

            //
            // The queue's buffer is full, so return false.
            //

            qlock.Exit();
            return(false);
        }
Beispiel #9
0
        //
        // Executes the prologue of the BlockingQueue<T>.TryTake method.
        //

        internal override WaitNode TryTakePrologue(StParker pk, int key, out T di, ref WaitNode ignored)
        {
            //
            // Acquire the queue's lock and check if the queue's is empty.
            //

            qlock.Enter();
            if (count != 0)
            {
                //
                // The queue isn't empty; so, ...
                //

                if (!pk.TryLock())
                {
                    qlock.Exit();
                    di = default(T);
                    return(null);
                }
                pk.UnparkSelf(key);

                //
                // Retrieve a data item from the queue.
                //

                di = items[head];
                if (++head == length)
                {
                    head = 0;
                }
                count--;

                //
                // If the wait queue isn't empty, try to use the freed
                // slot to release one of the waiter threads.
                //

                if (!waitQueue.IsEmpty)
                {
                    do
                    {
                        WaitNode w   = waitQueue.Dequeue();
                        StParker pk2 = w.parker;
                        if (pk2.TryLock())
                        {
                            items[tail] = w.channel;
                            if (++tail == length)
                            {
                                tail = 0;
                            }
                            count++;
                            qlock.Exit();
                            pk2.Unpark(w.waitKey);
                            return(null);
                        }
                    } while (!waitQueue.IsEmpty);
                }

                //
                // Release the queue's lock and return null to signal that the
                // take operation was accomplished.
                //

                qlock.Exit();
                return(null);
            }

            //
            // The queue's buffer is empty; so, create ...
            //

            WaitNode wn = new WaitNode(pk, key);

            if (lifoQueue)
            {
                waitQueue.EnqueueHead(wn);
            }
            else
            {
                waitQueue.Enqueue(wn);
            }

            //
            // Release the queue's lock and return the wait node
            // inserted in the wait queue.
            //

            qlock.Exit();
            di = default(T);
            return(wn);
        }
Beispiel #10
0
        //
        // Tries to take immediately a data item from the queue.
        //

        public override bool  TryTake(out T di)
        {
            //
            // If the queue seems empty, return failure.
            //

            if (count == 0)
            {
                di = default(T);
                return(false);
            }

            //
            // The queue seems non-empty; so, acquire the queue's lock.
            //

            qlock.Enter();

            //
            // If the queue is now empty, release the queue's lock
            // and return failure. If it is actually empty, return failure.
            //

            if (count == 0)
            {
                qlock.Exit();
                di = default(T);
                return(false);
            }

            //
            // Retrieve the next data item from the queue's buffer.
            //

            di = items[head];
            if (++head == length)
            {
                head = 0;
            }
            count--;

            //
            // If the wait queue is empty, release the queue's lock and
            // return success.
            //

            if (waitQueue.IsEmpty)
            {
                qlock.Exit();
                return(true);
            }

            //
            // There are threads blocked by the add operation. So, try to use
            // the freed buffer slot to release one of the waiter threads.
            //

            do
            {
                WaitNode w  = waitQueue.Dequeue();
                StParker pk = w.parker;
                if (pk.TryLock())
                {
                    items[tail] = w.channel;
                    if (++tail == length)
                    {
                        tail = 0;
                    }
                    count++;
                    qlock.Exit();
                    pk.Unpark(w.waitKey);
                    return(true);
                }
            } while (!waitQueue.IsEmpty);

            //
            // Release the queue's lock and return success.
            //

            qlock.Exit();
            return(true);
        }
Beispiel #11
0
        //
        // Executes the prologue of the BlockingQueue<T>.TryAddXxx method.
        //

        internal override WaitNode TryAddPrologue(StParker pk, int key, T di, ref WaitNode ignored)
        {
            //
            // Acquire the queue's lock.
            //

            qlock.Enter();

            //
            // ...
            //

            if (count < length)
            {
                if (!pk.TryLock())
                {
                    qlock.Exit();
                    return(null);
                }
                pk.UnparkSelf(key);

                //
                // If the wait queue isn't empty, try to deliver the data item
                // directly to a waiting thread.
                //

                if (!waitQueue.IsEmpty)
                {
                    do
                    {
                        WaitNode w   = waitQueue.Dequeue();
                        StParker wpk = w.parker;
                        if (wpk.TryLock())
                        {
                            //
                            // Release the queue's lock, pass the data item through
                            // the wait node, unpark the waiter thread and return
                            // success.
                            //

                            qlock.Exit();
                            w.channel = di;
                            wpk.Unpark(w.waitKey);
                            return(null);
                        }
                    } while (!waitQueue.IsEmpty);
                }

                //
                // Add the data item to the non-full queue and return null.
                //

                items[tail] = di;
                if (++tail == length)
                {
                    tail = 0;
                }
                count++;
                qlock.Exit();
                return(null);
            }

            //
            // The queue's buffer is full, so, ...
            //

            WaitNode wn;

            waitQueue.Enqueue(wn = new WaitNode(pk, key, di));

            //
            // Release the queue's lock and return the inserted wait block.
            //

            qlock.Exit();
            return(wn);
        }
Beispiel #12
0
        //
        // Releases the approprite waiters and unlocks the
        // r/w lock's queue.
        //

        private void ReleaseWaitersAndUnlockQueue()
        {
            do
            {
                WaitBlock qh = queue.head;
                WaitBlock w;
                while ((w = qh.next) != null)
                {
                    StParker pk = w.parker;
                    if (w.waitType == WaitType.WaitAny)
                    {
                        int r = (w.request & WaitBlock.MAX_REQUEST);
                        if (r == ENTER_WRITE)
                        {
                            //
                            // The next waiter is a writer, so try to enter the
                            // write lock.
                            //

                            if (!TryEnterWriteQueuedInternal())
                            {
                                break;
                            }

                            //
                            // Try to lock the associated parker and, if succeed, unpark
                            // its owner thread.
                            //

                            if (pk.TryLock() || w.request < 0)
                            {
                                pk.Unpark(w.waitKey);

                                //
                                // Since that no more waiters can be released,
                                // advance the local queue's head and exit the
                                // inner loop.
                                //

                                qh.next = qh;
                                qh      = w;
                                break;
                            }
                            else
                            {
                                //
                                // The acquire attempt was cancelled, so undo the
                                // previous acquire.
                                //

                                UndoEnterWrite();
                            }
                        }
                        else
                        {
                            //
                            // The next waiter is a reader, so try to acquire the
                            // read lock.
                            //

                            if (!TryEnterReadQueuedInternal())
                            {
                                break;
                            }

                            //
                            // Try to lock the associated parker and, if succeed, unpark
                            // its owner thread.
                            //

                            if (pk.TryLock() || w.request < 0)
                            {
                                pk.Unpark(w.waitKey);
                            }
                            else
                            {
                                //
                                // The acquire attempt was cancelled, so undo the
                                // previous acquire.
                                //

                                UndoEnterRead();
                            }
                        }
                    }
                    else
                    {
                        //
                        // WaitOne-all.
                        // If the write lock is free, lock the parker and, if this is
                        // the last cooperative release, unpark its owner thread.
                        //

                        if (state == 0)
                        {
                            if (pk.TryLock())
                            {
                                pk.Unpark(w.waitKey);
                            }
                        }
                        else
                        {
                            //
                            // The write lock is busy, so exit the inner loop.
                            //

                            break;
                        }
                    }

                    //
                    // Advance the local queue's head, marking the wait block
                    // referenced by the previous head as unlinked.
                    //

                    qh.next = qh;
                    qh      = w;
                }

                //
                // It seems that no more waiters can be released; so, set the
                // new queue's head and unlock the queue.
                //

                queue.SetHeadAndUnlock(qh);

                //
                // After release the queue's lock, if it seems that more waiters
                // can released, repeate the release processing.
                //

                if (!IsReleasePending)
                {
                    return;
                }
            } while (true);
        }
        //
        // Only one thread act as a releaser at any given time.
        //

        private void ReleaseWaitersAndUnlockQueue(WaitBlock self)
        {
            do
            {
                WaitBlock qh = queue.head;
                WaitBlock w;

                while (state > 0 && (w = qh.next) != null)
                {
                    StParker pk = w.parker;

                    if (w.waitType == WaitType.WaitAny)
                    {
                        if (!TryAcquireInternalQueued(w.request))
                        {
                            break;
                        }

                        if (pk.TryLock())
                        {
                            if (w == self)
                            {
                                pk.UnparkSelf(w.waitKey);
                            }
                            else
                            {
                                pk.Unpark(w.waitKey);
                            }
                        }
                        else
                        {
                            UndoAcquire(w.request);
                        }
                    }
                    else if (pk.TryLock())
                    {
                        if (w == self)
                        {
                            pk.UnparkSelf(w.waitKey);
                        }
                        else
                        {
                            pk.Unpark(w.waitKey);
                        }
                    }

                    //
                    // Remove the wait block from the semaphore's queue,
                    // marking the previous head as unlinked, and advance
                    // the local queues's head.
                    //

                    qh.next = qh;
                    qh      = w;
                }

                //
                // It seems that no more waiters can be released; so,
                // set the new semaphore queue's head and unlock it.
                //

                queue.SetHeadAndUnlock(qh);

                //
                // If after the semaphore's queue is unlocked, it seems that
                // more waiters can be released, repeat the release processing.
                //

                if (!IsReleasePending)
                {
                    return;
                }
            } while (true);
        }
Beispiel #14
0
        //
        // Executes the prologue of the TryTake operation.
        //

        internal override WaitNode TryTakePrologue(StParker pk, int key, out T di, ref WaitNode hint)
        {
            if (dataQueue.TryDequeue(out di))
            {
                if (pk.TryLock())
                {
                    pk.UnparkSelf(key);
                }
                else
                {
                    //
                    // ...
                    //

                    AddWorker(di);
                }
                return(null);
            }

            //
            // There are no data items available on the queue, ...
            //

            WaitNode wn = new WaitNode(pk, key);

            hint = waitQueue.Enqueue(wn);

            //
            // Since that a data item could have arrived after we check
            // the data queue but before we inserted our wait block in
            // the wait queue, we must retry to dequeue a data item from
            // the data queue if the parker is still unlocked.
            //

            if (!pk.IsLocked && dataQueue.TryDequeue(out di))
            {
                //
                // We got a data item, so try to lock the parker and, if succeed,
                // unlink our wait node from the wait queue and self unpark the
                // current thread.
                //

                if (pk.TryLock())
                {
                    waitQueue.Unlink(wn, hint);
                    pk.UnparkSelf(key);
                    return(null);
                }

                //
                // If the parker is locked, someone else will give as a data
                // item. So, return the data item retrieved from the data queue,
                // undoing the previous take.
                //

                AddWorker(di);
            }

            //
            // Return the wait node inserted in the wait queue.
            //

            return(wn);
        }