Esempio n. 1
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);
            }
        }
        //
        // Exits the lock.
        //

        public void Exit()
        {
            //
            // Since that atomic operations on references are more
            // expensive than on integers, we optimize the release when
            // the spin lock's wait queue is empty. However, when the queue
            // seems empty before the lock is released, but it's seen
            // non-empty after the lock is released, our algorithm resorts
            // to two atomic instructions.
            //

            if (top == null)
            {
                Interlocked.Exchange(ref state, FREE);
                if (top == null)
                {
                    return;
                }
            }
            else
            {
                state = FREE;
            }

            //
            // Unpark all waiting threads.
            //
            // NOTE: Since that the spin lock's queue is implemented with
            //       a stack, we build another stack in order to unpark the
            //       waiting thread according to its arrival order.
            //

            StParker p = Interlocked.Exchange <StParker>(ref top, null);
            StParker ws = null, n;

            while (p != null)
            {
                n       = p.pnext;
                p.pnext = ws;
                ws      = p;
                p       = n;
            }
            while (ws != null)
            {
                n = ws.pnext;
                ws.Unpark(StParkStatus.Success);
                ws = n;
            }
        }
        //
        // Tries to unregister the callback. This method is thread-safe.
        //

        public bool Unregister()
        {
            StParker p = parker;

            if (p == null)
            {
                return(false);
            }

            parker = null;

            if (p.TryCancel())
            {
                p.Unpark(StParkStatus.WaitCancelled);
                return(true);
            }
            return(false);
        }
        //
        // 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);
            }
        }
        //
        // Executes the unpark callback.
        //

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

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

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

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

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

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

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

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

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

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

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

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

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

                //
                // Enable the unpark callback.
                //

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

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

                //
                // The take was already accomplished; so, to prevent uncontrolled
                // reentrancy execute the unpark callback inline.
                //
            } while (true);
        }
Esempio n. 6
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);
        }
Esempio n. 7
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);
        }
Esempio n. 8
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);
        }
Esempio n. 9
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);
        }
Esempio n. 10
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);
        }
        //
        // Executes the unpark callback.
        //

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

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

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

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

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

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

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

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

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

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

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

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

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

                //
                // Enable the unpark callback.
                //

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

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

                //
                // The waitable was already signalled. So, execute the unpark
                // callback inline.
                //
            } while (true);
        }
        //
        // 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);
        }
Esempio n. 13
0
        //
        // Executes the timer callback
        //

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

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

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

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

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

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

                tmrEvent.Signal();

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

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

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

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

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

                cbparker.Reset();

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

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

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