internal void BuildNodeList(int level, DispatcherNode[] nodeList, string[] keyParts) { // find/create subnode nodeList[_Level] = this; DispatcherNode subNode; _ChildIndexLock.Enter(); try { if (!_ChildIndex.TryGetValue(keyParts[_Level], out subNode)) { subNode = new DispatcherNode(_Dispatcher, _Level + 1, this); _ChildIndex.Add(keyParts[_Level], subNode); } } finally { _ChildIndexLock.Leave(); } nodeList[_Level + 1] = subNode; if (level > (_Level + 1)) { subNode.BuildNodeList(level, nodeList, keyParts); } }
// constructors internal DispatcherNode(Dispatcher Dispatcher, int level, DispatcherNode parent) { _Dispatcher = Dispatcher; if (level < 0) { throw new ArgumentException("Invalid", nameof(level)); } _Level = level; if ((level == 0) && (parent != null)) { throw new ArgumentException("Invalid", nameof(parent)); } if ((level > 0) && (parent == null)) { throw new ArgumentNullException(nameof(parent)); } _Parent = parent; }
private void ProcessNode(DispatcherItem completedItem) { long threadsInTree = IncThreadsInTree(); try { if (completedItem != null) { // completed item cleanup DecThreadsInTree(); long debugN = Interlocked.Decrement(ref _ThisNodeRunning); Debug.Assert(debugN == 0); _Parent?.DecChildrenRunning(); completedItem = null; } bool looping = true; while (looping) { looping = false; // ensure that at least one pass through the dequeuing logic occurs _Queue.Locked((queue) => { // we have the queue // we can dispatch the head of the queue if: // a) the item is for this level, AND this tree is not running; // or b) the item is for a lower level, AND this node is not running. if (queue.Count > 0) { DispatcherItem workItem = queue.Peek(); Debug.Assert(workItem.Level >= _Level); if ((workItem.Level == _Level) && (Interlocked.Add(ref _ThisNodeRunning, 0) == 0) && (Interlocked.Add(ref _ChildrenRunning, 0) == 0)) { // item is for this level // dispatch it to the threadpool queue.Dequeue(); long debugN = Interlocked.Increment(ref _ThisNodeRunning); Debug.Assert(debugN == 1); IncThreadsInTree(); ThreadPool.QueueUserWorkItem(CallbackWrapper, workItem); } if ((workItem.Level > _Level) && (Interlocked.Add(ref _ThisNodeRunning, 0) == 0)) { // item is for lower level // enqueue it to the child queue.Dequeue(); Interlocked.Increment(ref _ChildrenRunning); DispatcherNode childNode = workItem.NodeList[_Level + 1]; childNode.Enqueue(workItem); // dequeue next looping = true; } } }); } // while looping } finally { threadsInTree = DecThreadsInTree(); } if ((threadsInTree == 0) && (_Parent != null)) { // we are the last thread in this tree - handoff to parent ThreadPool.QueueUserWorkItem(_Parent.AsyncProcessNode, null); } }