Exemple #1
0
        //--- Methods ---

        /// <summary>
        /// Try to add an item to the queue.
        /// </summary>
        /// <param name="item">Item to add to queue.</param>
        /// <returns><see langword="True"/> if the enqueue succeeded.</returns>
        /// <returns>Always returns <see langword="True"/>.</returns>
        public bool TryEnqueue(T item)
        {
            // create new entry to add
            SingleLinkNode <T> newTail = new SingleLinkNode <T>(item);

            // loop until we successful enqueue the new tail node
            while (true)
            {
                // capture the current tail reference and its current Next reference
                SingleLinkNode <T> curTail     = _tail;
                SingleLinkNode <T> curTailNext = curTail.Next;

                // check if the current tail is indeed the last node
                if (curTailNext == null)
                {
                    // update the tail's Next reference to point to the new entry
                    if (SysUtil.CAS(ref _tail.Next, null, newTail))
                    {
                        // NOTE (steveb): there is a race-condition here where we may update the tail to point a non-terminal node; that's ok

                        // update the tail reference to the new entry (may fail)
                        SysUtil.CAS(ref _tail, curTail, newTail);
                        return(true);
                    }
                }
                else
                {
                    // tail reference was not properly updated in an earlier attempt, update it now (see note above)
                    SysUtil.CAS(ref _tail, curTail, curTailNext);
                }
            }
        }
        /// <summary>
        /// Pop an item from the head of the queue.
        /// </summary>
        /// <remarks>
        /// NOTE: TrySteal() can be invoked from any thread.
        /// </remarks>
        /// <param name="item">Head item of the queue when operation is successful.</param>
        /// <returns><see langword="True"/> if operation was successful.</returns>
        public bool TrySteal(out T item)
        {
            // read top
            TopData curTop = _top;

            // read bottom
            BottomData curBottom = _bottom;

            if (IsEmpty(curBottom, curTop, _capacity))
            {
                item = default(T);
                if (ReferenceEquals(curTop, _top))
                {
                    return(false);
                }
                else
                {
                    // NOTE (steveb): this is contentious access case; we currently return 'false' but may want to differentiate in the future
                    return(false);
                }
            }

            // if deque isn't empty, calcuate next top pointer
            TopData newTop;

            if (curTop.Index != 0)
            {
                // stay at current node
                newTop = new TopData(curTop.Tag, curTop.Node, curTop.Index - 1);
            }
            else
            {
                // move to next node and update tag
                newTop = new TopData(curTop.Tag + 1, curTop.Node.Prev, _capacity - 1);
            }

            // read value
            T retVal = curTop.Node.Data[curTop.Index];

            // try updating _top using CAS
            if (SysUtil.CAS(ref _top, curTop, newTop))
            {
                // clear out the entry we read, so the GC can reclaim it
                SysUtil.CAS(ref curTop.Node.Data[curTop.Index], retVal, default(T));

                // free old node
                curTop.Node.Next = null;
                item             = retVal;
                return(true);
            }
            else
            {
                item = default(T);

                // NOTE (steveb): this is contentious access case; we currently return 'false' but may want to differentiate in the future
                return(false);
            }
        }
Exemple #3
0
        //--- Methods ---

        /// <summary>
        /// Try to push a new item on top of the stack.
        /// </summary>
        /// <param name="item">Item to add.</param>
        /// <returns><see langword="True"/> if the push succeeded.</returns>
        public bool TryPush(T item)
        {
            SingleLinkNode <T> newNode = new SingleLinkNode <T>(item);

            do
            {
                newNode.Next = _head;
            } while(!SysUtil.CAS(ref _head, newNode.Next, newNode));
            return(true);
        }
        private bool TryDequeueConsumer(out Action <T> callback)
        {
            // TODO (arnec): should convert return to enum to indicate contention vs. empty queue

            // loop until we successfully dequeue a node or the queue is empty
            while (true)
            {
                // capture the current state of the queue
                SingleLinkNode <object> curHead     = _head;
                SingleLinkNode <object> curHeadNext = curHead.Next;
                SingleLinkNode <object> curTail     = _tail;

                // check if the current head and tail are equal
                if (ReferenceEquals(curHead, curTail))
                {
                    // check if the current head has a non-empty Next reference
                    if (curHeadNext == null)
                    {
                        // unable to find an item in the queue
                        callback = null;
                        return(false);
                    }

                    // tail reference was not properly updated in an earlier attempt, update it now (see note above)
                    SysUtil.CAS(ref _tail, curTail, curHeadNext);
                }
                else if (curHeadNext == null)
                {
                    // head and tail differ, but we have no next, i.e. contention changed the queue before we
                    // captured its state
                    callback = null;
                    return(false);
                }
                else if (curHeadNext.Item is Action <T> )
                {
                    // try to replace the current head with the current head's Next reference
                    if (SysUtil.CAS(ref _head, curHead, curHeadNext))
                    {
                        // we have successfully retrieved the head of the queue
                        callback = (Action <T>)curHeadNext.Item;

                        // clear out the Item field so the GC can reclaim the memory
                        curHeadNext.Item = default(T);
                        return(true);
                    }
                }
                else
                {
                    // head contains an item instead of a callback
                    callback = null;
                    return(false);
                }
            }
        }
Exemple #5
0
        /// <summary>
        /// Try to pop an item from the top of the stack
        /// </summary>
        /// <param name="item">Storage location for the item to be removed.</param>
        /// <returns><see langword="True"/> if the pop succeeded.</returns>
        public bool TryPop(out T item)
        {
            SingleLinkNode <T> node;

            do
            {
                node = _head;
                if (node == null)
                {
                    item = default(T);
                    return(false);
                }
            } while(!SysUtil.CAS(ref _head, node, node.Next));
            item = node.Item;
            return(true);
        }
Exemple #6
0
        /// <summary>
        /// Get delegate for enqueuing messages asynchronously to named queue.
        /// </summary>
        /// <param name="queueName">Queue name.</param>
        /// <returns>Delegate for enqueuing message asynchronously.</returns>
        public Action <string> GetEnqueueMessageCallback(SqsQueueName queueName)
        {
repeat:
            SqsQueueDelayedSendClient queue;
            var directory = _directory;

            if (!directory.TryGetValue(queueName.Value, out queue))
            {
                var newDirectory = new Dictionary <string, SqsQueueDelayedSendClient>(directory);
                newDirectory[queueName.Value] = queue = new SqsQueueDelayedSendClient(_client, queueName, _timerFactory);
                if (!SysUtil.CAS(ref _directory, directory, newDirectory))
                {
                    goto repeat;
                }
            }
            return(queue.EnqueueMessage);
        }
        //--- Class Methods ---

        /// <summary>
        /// Add a named callback to the clock.
        /// </summary>
        /// <param name="name">Unique key for the callback.</param>
        /// <param name="callback">Callback action.</param>
        public static void AddCallback(string name, ClockCallback callback)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name", "name cannot be null or empty");
            }
            if (callback == null)
            {
                throw new ArgumentNullException("callback");
            }

            // add callback
#if LOCKFREEE
            var newNode = new SingleLinkNode <NamedClockCallback>(new NamedClockCallback(name, callback));
            do
            {
                newNode.Next = _head;
            } while(!SysUtil.CAS(ref _head, newNode.Next, newNode));
#else
            lock (_syncRoot) {
                int index;

                // check if there is an empty slot in the callbacks array
                for (index = 0; index < _callbacks.Length; ++index)
                {
                    if (_callbacks[index].Value == null)
                    {
                        _callbacks[index] = new NamedClockCallback(name, callback);
                        return;
                    }
                }

                // make room to add a new thread by doubling the array size and copying over the existing entries
                var newArray = new NamedClockCallback[2 * _callbacks.Length];
                Array.Copy(_callbacks, newArray, _callbacks.Length);

                // assign new thread
                newArray[index] = new NamedClockCallback(name, callback);

                // update instance field
                _callbacks = newArray;
            }
#endif
        }
        private bool TryEnqueueConsumer(SingleLinkNode <object> newTail)
        {
            // loop until we successful enqueue the new tail node
            while (true)
            {
                // capture the current tail reference and its current Next reference
                SingleLinkNode <object> curHead     = _head;
                SingleLinkNode <object> curTail     = _tail;
                SingleLinkNode <object> curTailNext = curTail.Next;

                // check if the current tail is indeed the last node
                if (curTailNext == null)
                {
                    // ensure if the queue is not empty, that it contains items of the expected type
                    if (!ReferenceEquals(curHead, curTail) && !(curTail.Item is Action <T>))
                    {
                        return(false);
                    }

                    // update the tail's Next reference to point to the new entry
                    if (SysUtil.CAS(ref _tail.Next, null, newTail))
                    {
                        // NOTE (steveb): there is a race-condition here where we may update the tail to point a non-terminal node; that's ok

                        // update the tail reference to the new entry (may fail)
                        SysUtil.CAS(ref _tail, curTail, newTail);
                        return(true);
                    }
                }
                else
                {
                    // tail reference was not properly updated in an earlier attempt, update it now (see note above)
                    SysUtil.CAS(ref _tail, curTail, curTailNext);
                }
            }
        }
        private static void MasterTickThread(object _unused)
        {
            DateTime last = DateTime.UtcNow;

            while (_running)
            {
                // wait until next iteration
                Thread.Sleep(_intervalMilliseconds);

                // get current time and calculate delta
                DateTime now     = DateTime.UtcNow;
                TimeSpan elapsed = now - last;
                last = now;

                // execute all callbacks
#if LOCKFREEE
                SingleLinkNode <NamedClockCallback> previous = null;
                var current = _head;
                while (current != null)
                {
                    var key      = current.Item.Key;
                    var callback = current.Item.Value;
                    if (callback == null)
                    {
                        // remove linked node
                        if (previous == null)
                        {
                            // there might be contention on the head item of the callback list;
                            // hence, we need to do it in a threadsafe fashion
                            SingleLinkNode <NamedClockCallback> head;
                            SingleLinkNode <NamedClockCallback> next;
                            do
                            {
                                head = _head;
                                next = head.Next;
                            } while(!SysUtil.CAS(ref _head, head, next));
                        }
                        else
                        {
                            // other threads don't operate on non-head items of the callback list
                            previous.Next = current.Next;
                        }

                        // clear out the item entirely to indicate we've removed it
                        current.Item = new NamedClockCallback(null, null);
                    }
                    else
                    {
                        try {
                            callback(now, elapsed);
                        } catch (Exception e) {
                            _log.ErrorExceptionMethodCall(e, "GlobalClock callback failed", key);
                        }
                    }
                    previous = current;
                    current  = current.Next;
                }
#else
                lock (_syncRoot) {
                    var callbacks = _callbacks;
                    foreach (NamedClockCallback callback in callbacks)
                    {
                        if (callback.Value != null)
                        {
                            try {
                                callback.Value(now, elapsed);
                            } catch (Exception e) {
                                _log.ErrorExceptionMethodCall(e, "GlobalClock callback failed", callback.Key);
                            }
                        }
                    }
                }
#endif
            }

            // indicate that this thread has exited
            _stopped.Set();
        }
        /// <summary>
        /// Pop an item from the tail of the queue.
        /// </summary>
        /// <remarks>
        /// NOTE: Push() and TryPop() <strong>MUST</strong> be called from the same thread.
        /// </remarks>
        /// <param name="item">Tail item of the queue when operation is successful.</param>
        /// <returns><see langword="True"/> if operation was successful.</returns>
        public bool TryPop(out T item)
        {
            item = default(T);

            // read bottom data
            BottomData curBottom = _bottom;
            BottomData newBottom;

            if (curBottom.Index != (_capacity - 1))
            {
                newBottom = new BottomData(curBottom.Node, curBottom.Index + 1);
            }
            else
            {
                newBottom = new BottomData(curBottom.Node.Next, 0);
            }

            // update bottom
            _bottom = newBottom;

            // read top
            TopData curTop = _top;

            // read data to be popped
            T retVal = newBottom.Node.Data[newBottom.Index];

            // case 1: if _top has crossed _bottom
            if (ReferenceEquals(curBottom.Node, curTop.Node) && (curBottom.Index == curTop.Index))
            {
                // return bottom to its old position
                _bottom = curBottom;
                return(false);
            }

            // case 2: when popping the last entry in the deque (i.e. deque is empty after the update of bottom)
            if (ReferenceEquals(newBottom.Node, curTop.Node) && (newBottom.Index == curTop.Index))
            {
                // try to update _top's tag so no concurrent Steal operation will also pop the same entry
                TopData newTopVal = new TopData(curTop.Tag + 1, curTop.Node, curTop.Index);
                if (SysUtil.CAS(ref _top, curTop, newTopVal))
                {
                    // TODO (steveb): clear out the entry we read, so the GC can reclaim it

                    // free old node if needed
                    if (!ReferenceEquals(curBottom.Node, newBottom.Node))
                    {
                        newBottom.Node.Prev = null;
                    }
                    item = retVal;
                    return(true);
                }
                else
                {
                    // if CAS failed (i.e. a concurrent Steal operation alrady popped that last entry)

                    // return bottom to its old position
                    _bottom = curBottom;
                    return(false);
                }
            }

            // case 3: regular case (i.e. there was a least one entry in the deque _after_ bottom's update)
            // free old node if needed
            if (!ReferenceEquals(curBottom.Node, newBottom.Node))
            {
                newBottom.Node.Prev = null;
            }
            item = retVal;
            return(true);
        }