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