/// <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); } } }
//--- 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); } } }
//--- 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 } }