/// <summary>
        /// Try to add a consumer to the queue.
        /// </summary>
        /// <param name="callback">Consumer to add to queue.</param>
        /// <returns>Always returns <see langword="True"/>.</returns>
        public bool TryEnqueue(Action <T> callback)
        {
            if (callback == null)
            {
                throw new ArgumentNullException("callback");
            }
            SingleLinkNode <object> newTail = null;

            while (true)
            {
                // check if we have an item ready
                T item;
                if (TryDequeueItem(out item))
                {
                    callback(item);
                    return(true);
                }

                // try to enqueue the callback
                newTail = newTail ?? new SingleLinkNode <object>(callback);
                if (TryEnqueueConsumer(newTail))
                {
                    return(true);
                }
            }
        }
Beispiel #2
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);
                }
            }
        }
        /// <summary>
        /// Get a string instance from the table for the provided token, if it exists
        /// </summary>
        /// <param name="token">Token to look up or store in the name table</param>
        /// <returns>String instance for token or null, if the token was not found.</returns>
        public override string Get(string token)
        {
            // check for the empty string and always return the built-in constant for it
            if (token.Length == 0)
            {
                return(string.Empty);
            }

            // locate entry based on hashcode of the supplied token
            int hashcode = token.GetAlternativeHashCode();
            int index    = GetIndex(hashcode);
            SingleLinkNode <Entry> current = _buckets[index];

            // check if we're looking for a short string (in that case, we skip the hashcode check)
            if (token.Length < 12)
            {
                // loop over all nodes until we exhaust them or find a match
                for (; current != null; current = current.Next)
                {
                    // do only an ordinal string comparison since the token is short
                    if (CompareStringToString(current.Item.Token, token) == 0)
                    {
                        return(current.Item.Token);
                    }
                }
            }
            else
            {
                // loop over all nodes until we exhaust them or find a match
                for (; current != null; current = current.Next)
                {
                    // do only an ordinal string comparison since the token is short
                    if ((current.Item.HashCode == hashcode) && (CompareStringToString(current.Item.Token, token) == 0))
                    {
                        return(current.Item.Token);
                    }
                }
            }

            // token was not found
            return(null);
        }
        //--- Methods ---

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

            while (true)
            {
                // check if we have a consumer ready
                Action <T> consumer;
                if (TryDequeueConsumer(out consumer))
                {
                    consumer(item);
                    return(true);
                }

                // try to enqueue the item
                newTail = newTail ?? new SingleLinkNode <object>(item);
                if (TryEnqueueItem(newTail))
                {
                    return(true);
                }
            }
        }
        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);
                }
            }
        }
Beispiel #7
0
        //--- Constructors ---

        /// <summary>
        /// Create a new instance of the queue.
        /// </summary>
        public LockFreeQueue()
        {
            _head = new SingleLinkNode <T>();
            _tail = _head;
        }
        //--- Constructors ---

        /// <summary>
        /// Create a new instance of the queue.
        /// </summary>
        public LockFreeItemConsumerQueue()
        {
            _head = new SingleLinkNode <object>();
            _tail = _head;
        }
        /// <summary>
        /// Add a token to the name table and get the common string instance it represents.
        /// </summary>
        /// <remarks>
        /// If the token already exists in the name table, the existing instance is returned.
        /// </remarks>
        /// <param name="token">The token to add.</param>
        /// <returns>String instance for the token.</returns>
        public override string Add(string token)
        {
            // check for the empty string and always return the built-in constant for it
            if (token.Length == 0)
            {
                return(string.Empty);
            }

            // locate entry based on hashcode of the supplied token
            int hashcode = token.GetAlternativeHashCode();
            int index    = GetIndex(hashcode);
            SingleLinkNode <Entry> current = _buckets[index];
            SingleLinkNode <Entry> entry   = null;

            // check if a head node exists for the given hashcode
            if (current == null)
            {
                entry = new SingleLinkNode <Entry>(new Entry(hashcode, token));

                // try to update the head node with the new entry
                current = Interlocked.CompareExchange(ref _buckets[index], entry, null);

                // check if we succeeded, which means the provided token is now the reference token
                if (current == null)
                {
                    return(token);
                }

                // otherwise, continue on since 'current' now has the updated head node
            }

            // loop until we successfully find or append the token
            SingleLinkNode <Entry> previous = null;

            while (true)
            {
                // check if we're looking for a short string (in that case, we skip the hashcode check)
                if (token.Length < 12)
                {
                    // loop over all entries until we exhaust them or find a match
                    for (; current != null; current = current.Next)
                    {
                        // do only an ordinal string comparison since the token is short
                        if (CompareStringToString(current.Item.Token, token) == 0)
                        {
                            return(current.Item.Token);
                        }
                        previous = current;
                    }
                }
                else
                {
                    // loop over all entries until we exhaust them or find a match
                    for (; current != null; current = current.Next)
                    {
                        // do only an ordinal string comparison since the token is short
                        if ((current.Item.HashCode == hashcode) && (CompareStringToString(current.Item.Token, token) == 0))
                        {
                            return(current.Item.Token);
                        }
                        previous = current;
                    }
                }

                // NOTE: it's possible that an earlier attempt already initialized 'entry'
                entry = entry ?? new SingleLinkNode <Entry>(new Entry(hashcode, token));

                // try to update the previous node with the new entry
                current = Interlocked.CompareExchange(ref previous.Next, entry, null);

                // check if we succeeded, which means the provided token is now the reference token
                if (current == null)
                {
                    // provided token was added
                    return(token);
                }

                // otherwise, continue on since 'current' now has the updated next node
            }
        }