예제 #1
0
        public void CopyTo()
        {
            // Create a new priority queue.
            ConcurrentPriorityQueue <int> queue = new ConcurrentPriorityQueue <int>();

            // Create a new array of size 5.
            PriorityValuePair <int>[] arrayCopy = new PriorityValuePair <int> [5];

            // Enqueue 3 elements into the queue.
            PriorityValuePair <int> elem = new PriorityValuePair <int>(3.0, 6);

            queue.Enqueue(1.0, 2);
            queue.Enqueue(elem);
            queue.Enqueue(2.0, 4);

            // Copy the queue data to the array, starting from index 1 (not 0).
            queue.CopyTo(arrayCopy, 1);

            // Expect the first array index to be unset, but all the rest to be set.
            // Note: The order of elements after the first can't be guaranteed, because the heap
            // implementing the queue internally doesn't store things in an exact linear order,
            // but we can be sure that the elements aren't going to be equal to null because we
            // set them.
            Assert.That(arrayCopy[0], Is.EqualTo(null));
            Assert.That(arrayCopy[1], Is.EqualTo(elem));
            Assert.That(arrayCopy[2], Is.Not.EqualTo(null));
            Assert.That(arrayCopy[3], Is.Not.EqualTo(null));
            Assert.That(arrayCopy[4], Is.EqualTo(null));
        }
예제 #2
0
        Enqueue(double priority, WaitCallback callback, object context = null)
        {
            if (IsStopping)
            {
                // We want to block new tasks from being queued while the task runner is trying to
                // shut down, so that it can successfully stop instead of being bogged down by new
                // tasks forever.
                throw new InvalidOperationException("Cannot enqueue tasks while the task runner is stopping.");
            }

            // Create a new task, wrap it in a PriorityValuePair, and enqueue it.
            Task task = new Task(context, callback);
            PriorityValuePair <Task> elem = new PriorityValuePair <Task>(priority, task);

            __queue.Enqueue(elem);

            // Mark the task as waiting to complete.
            onWaitingTask(elem);

            // Subscribe an event listener to the task's CallbackReturned event that will release
            // the task resources and allow another to run.
            ManualResetEvent resetEvent = new ManualResetEvent(false);

            task.TaskComplete += delegate {
                onCompletedTask(elem);
                resetEvent.Set();
            };

            // Return the ManualResetEvent to the caller so they can be aware of when this task
            // finishes execution.
            return(resetEvent);
        }
예제 #3
0
        public void PopValue()
        {
            // Create a new heap.
            ConcurrentBinaryMinHeap <int> heap = new ConcurrentBinaryMinHeap <int>();

            // Ensure that the heap is empty.
            Assert.That(heap.Count, Is.EqualTo(0));

            // Try to PopPriority() and expect an NullReferenceException to be thrown.
            Assert.Throws <NullReferenceException>(() => {
                heap.PopValue();
            });

            // Ensure that the heap is empty.
            Assert.That(heap.Count, Is.EqualTo(0));

            // Store an element and insert it into the heap.
            PriorityValuePair <int> elem = new PriorityValuePair <int>(1f, 2);

            heap.Push(elem);

            // Ensure that the element was inserted into the heap.
            Assert.That(heap.Peek(), Is.EqualTo(elem));

            // Ensure that the value of the pushed element is returned.
            Assert.That(heap.PopValue(), Is.EqualTo(2));

            // Ensure that the element was removed from the heap.
            Assert.That(heap.Count, Is.EqualTo(0));
        }
예제 #4
0
        public void Remove()
        {
            // Create a new heap.
            ConcurrentBinaryMinHeap <int> heap = new ConcurrentBinaryMinHeap <int>();

            // Create and store a few elements.
            PriorityValuePair <int> elem1 = new PriorityValuePair <int>(1f, 2);
            PriorityValuePair <int> elem2 = new PriorityValuePair <int>(2f, 4);
            PriorityValuePair <int> elem3 = new PriorityValuePair <int>(3f, 6);

            // Expect Remove() to return null for an empty heap.
            Assert.That(heap.Remove(elem1), Is.EqualTo(false));

            // Insert 2 of the elements into the heap.
            heap.Push(elem2);
            heap.Push(elem3);

            // Expect Remove() to return false for elem1, indicating the element was removed
            // (since it doesn't belong to the heap and can't be found). This tests the if-else
            // case for when the provided element isn't found in the heap.
            Assert.That(heap.Remove(elem1), Is.False);

            // Expect Remove() to return true for elem2, indicating the element was removed
            // (since it belongs to the heap and can be found). This tests the if-else case for
            // when Count is 2 or greater.
            Assert.That(heap.Remove(elem2), Is.True);

            // Expect Remove() to return true for elem3, indicating the element was removed
            // (since it belongs to the heap and can be found). This tests the if-else case for
            // when Count equals 1.
            Assert.That(heap.Remove(elem3), Is.True);
        }
예제 #5
0
        public void Pop()
        {
            // Create a new heap.
            ConcurrentBinaryMinHeap <int> heap = new ConcurrentBinaryMinHeap <int>();

            // Ensure that the heap is empty.
            Assert.That(heap.Count, Is.EqualTo(0));

            // Expect Pop() to return null for an empty heap.
            Assert.That(heap.Pop(), Is.EqualTo(null));

            // Ensure that the heap is empty.
            Assert.That(heap.Count, Is.EqualTo(0));

            // Ensure that the heap is empty.
            Assert.That(heap.Count, Is.EqualTo(0));

            // Store an element and insert it into the heap.
            PriorityValuePair <int> elem = new PriorityValuePair <int>(1f, 2);

            heap.Push(elem);

            // Ensure that the element was inserted into the heap.
            Assert.That(heap.Count, Is.EqualTo(1));
            Assert.That(heap.Peek(), Is.EqualTo(elem));

            // Ensure that the returned element points to the same object we stored earlier.
            Assert.That(heap.Pop(), Is.EqualTo(elem));

            // Ensure that the element was removed from the heap.
            Assert.That(heap.Count, Is.EqualTo(0));
        }
예제 #6
0
        public void Peek()
        {
            // Create a new heap.
            ConcurrentBinaryMinHeap <int> heap = new ConcurrentBinaryMinHeap <int>();

            // Ensure that the heap is empty.
            Assert.That(heap.Count, Is.EqualTo(0));

            // Expect Peek() to return null for an empty heap.
            Assert.That(heap.Peek(), Is.EqualTo(null));

            // Ensure that the heap is empty.
            Assert.That(heap.Count, Is.EqualTo(0));

            // Store an element and insert it into the heap.
            PriorityValuePair <int> elem1 = new PriorityValuePair <int>(1f, 2);

            heap.Push(elem1);

            // Ensure that the element was inserted into the heap as the root element.
            Assert.That(heap.Count, Is.EqualTo(1));
            Assert.That(heap.Peek(), Is.EqualTo(elem1));

            // Ensure that the element was not removed from the heap.
            Assert.That(heap.Count, Is.EqualTo(1));

            // Insert another element with higher priority than the last.
            PriorityValuePair <int> elem2 = new PriorityValuePair <int>(2f, 4);

            heap.Push(elem2);

            // Ensure that Peak() returns the new root element.
            Assert.That(heap.Peek(), Is.EqualTo(elem2));
        }
예제 #7
0
        public void CopyTo()
        {
            // Create a new heap.
            ConcurrentBinaryMinHeap <int> heap = new ConcurrentBinaryMinHeap <int>();

            // Create a new array of size 5.
            PriorityValuePair <int>[] arrayCopy = new PriorityValuePair <int> [5];

            // Push 3 elements onto the queue.
            PriorityValuePair <int> elem = new PriorityValuePair <int>(3f, 6);

            heap.Push(1f, 2);
            heap.Push(elem);
            heap.Push(2f, 4);

            // Copy the heap data to the array, starting from index 1 (not 0).
            heap.CopyTo(arrayCopy, 1);

            // Expect the first array index to be unset, but all the rest to be set.
            // Note: The order of elements after the first can't be guaranteed, because the heap
            // doesn't store things in an exact linear order, but we can be sure that the elements
            // aren't going to be equal to null because we set them.
            Assert.That(arrayCopy[0], Is.EqualTo(null));
            Assert.That(arrayCopy[1], Is.EqualTo(elem));
            Assert.That(arrayCopy[2], Is.Not.EqualTo(null));
            Assert.That(arrayCopy[3], Is.Not.EqualTo(null));
            Assert.That(arrayCopy[4], Is.EqualTo(null));
        }
예제 #8
0
        public void Remove()
        {
            // Create a new priority queue.
            ConcurrentPriorityQueue <int> queue = new ConcurrentPriorityQueue <int>();

            // Create and store a few elements.
            PriorityValuePair <int> elem1 = new PriorityValuePair <int>(1.0, 2);
            PriorityValuePair <int> elem2 = new PriorityValuePair <int>(2.0, 4);
            PriorityValuePair <int> elem3 = new PriorityValuePair <int>(3.0, 6);

            // Expect Remove() to return false for an empty queue.
            Assert.That(queue.Remove(elem1), Is.False);

            // Enqueue 2 of the elements into the heap.
            queue.Enqueue(elem2);
            queue.Enqueue(elem3);

            // Expect Remove() to return false for elem1, indicating the element was removed
            // (since it doesn't belong to the heap and can't be found). This tests the if-else
            // case for when the provided element isn't found in the heap.
            Assert.That(queue.Remove(elem1), Is.False);

            // Expect Remove() to return true for elem2, indicating the element was removed
            // (since it belongs to the heap and can be found). This tests the if-else case for
            // when Count is 2 or greater.
            Assert.That(queue.Remove(elem2), Is.True);

            // Expect Remove() to return true for elem3, indicating the element was removed
            // (since it belongs to the heap and can be found). This tests the if-else case for
            // when Count equals 1.
            Assert.That(queue.Remove(elem3), Is.True);
        }
예제 #9
0
        ThreadProc()
        {
            // Change state to indicate that the task runner is started.
            __state = TaskRunnerState.Running;

            // Emit an event to notify listeners that the task runner started up.
            if (Started != null)
            {
                Started();
            }

            // Continue looping until Stop() is called and all critical tasks are finished.
            while (!IsStopping || IsStopping && HasEnqueuedCriticalTasks)
            {
                // Most of this is only important if something is in the queue.
                if (RunningTasks < ThreadPoolMaxThreads && __queue.Peek() != null)
                {
                    // Execute the task on the thread pool.
                    PriorityValuePair <Task> elem = __queue.Dequeue();
                    Task task = elem.Value;
                    if (task != null)
                    {
                        // We're passing a null context here because the task is packaged with its
                        // own execution context that will be inserted during Task.Run().
                        ThreadPool.QueueUserWorkItem((x) => { task.Run(); }, null);
                    }

                    // Mark the task as starting execution.
                    // Note: This should run even if the task is null above, otherwise the counts
                    // for the number of running/waiting tasks will become skewed.
                    onRunningTask(elem);
                }
                else
                {
                    // Yield the rest of the time slice.
                    Thread.Sleep(1);
                }
            }

            // Wait for all critical tasks to finish running before stopping.
            while (HasWaitingCriticalTasks)
            {
                Thread.Sleep(1);
            }

            // Flag the task runner as stopped.
            __state  = TaskRunnerState.Stopped;
            __thread = null;

            // Emit an event to notify listeners that the task runner stopped.
            if (Stopped != null)
            {
                Stopped();
            }
        }
예제 #10
0
        public void Dequeue()
        {
            // Create a new priority queue.
            ConcurrentPriorityQueue <int> queue = new ConcurrentPriorityQueue <int>();

            // Ensure that the heap is empty.
            Assert.That(queue.Count, Is.EqualTo(0));

            // Expect Dequeue() to return null for an empty heap.
            Assert.That(queue.Dequeue(), Is.EqualTo(null));

            // Ensure that the heap is empty.
            Assert.That(queue.Count, Is.EqualTo(0));

            // Store an element and insert it into the heap.
            PriorityValuePair <int> elem = new PriorityValuePair <int>(1.0, 2);

            queue.Enqueue(elem);

            // Ensure that the element was inserted into the heap.
            Assert.That(queue.Count, Is.EqualTo(1));
            Assert.That(queue.Peek(), Is.EqualTo(elem));

            // Ensure that the PriorityAdjustment was incremented.
            Assert.That(queue.PriorityAdjustment, Is.EqualTo(ConcurrentPriorityQueue <int> .EPSILON));

            // Ensure that the returned element points to the same object we stored earlier.
            Assert.That(queue.Dequeue(), Is.EqualTo(elem));

            // Ensure that the element was removed from the heap.
            Assert.That(queue.Count, Is.EqualTo(0));

            // Ensure that the PriorityAdjustment was reset once the queue became empty.
            Assert.That(queue.PriorityAdjustment, Is.EqualTo(0.0));

            // Enqueue 5 items with the same priority.
            PriorityValuePair <int> elem2 = new PriorityValuePair <int>(2.0, 0);
            PriorityValuePair <int> elem3 = new PriorityValuePair <int>(2.0, 2);
            PriorityValuePair <int> elem4 = new PriorityValuePair <int>(2.0, 4);
            PriorityValuePair <int> elem5 = new PriorityValuePair <int>(2.0, 6);
            PriorityValuePair <int> elem6 = new PriorityValuePair <int>(2.0, 8);

            queue.Enqueue(elem2);
            queue.Enqueue(elem3);
            queue.Enqueue(elem4);
            queue.Enqueue(elem5);
            queue.Enqueue(elem6);

            //// Ensure that Dequeue() returns the items in the order they were enqueued.
            Assert.That(queue.Dequeue(), Is.EqualTo(elem2));
            Assert.That(queue.Dequeue(), Is.EqualTo(elem3));
            Assert.That(queue.Dequeue(), Is.EqualTo(elem4));
            Assert.That(queue.Dequeue(), Is.EqualTo(elem5));
            Assert.That(queue.Dequeue(), Is.EqualTo(elem6));
        }
예제 #11
0
        onRunningTask(PriorityValuePair <Task> elem)
        {
            #region Increment counters

            // Based on its priority, increment the appropriate active counter.
            if (elem.Priority >= MinCriticalPriority)
            {
                // Lock the thread -- CRITICAL SECTION BEGIN
                Monitor.Enter(__runningCriticalTasksLock);
                try
                {
                    __runningCriticalTasks++;
                }
                finally
                {
                    Monitor.Exit(__runningCriticalTasksLock);
                    // Unlock the thread -- CRITICAL SECTION END
                }
            }
            else
            {
                // Lock the thread -- CRITICAL SECTION BEGIN
                Monitor.Enter(__runningNoncriticalTasksLock);
                try
                {
                    __runningNoncriticalTasks++;
                }
                finally
                {
                    Monitor.Exit(__runningNoncriticalTasksLock);
                    // Unlock the thread -- CRITICAL SECTION END
                }
            }

            #endregion

            #region Emit events

            // Emit an event to notify listeners that a task has begun executing.
            if (TaskStarted != null)
            {
                TaskStarted();
            }

            // Emit an event to notify listeners that the queue is empty (if so).
            if (QueueEmpty != null && __queue.Peek() != null)
            {
                QueueEmpty();
            }

            #endregion
        }
예제 #12
0
        public void SwapElements()
        {
            // Create a new heap.
            ConcurrentBinaryMinHeap <int> heap = new ConcurrentBinaryMinHeap <int>();

            // Enqueue an element into the queue.
            var elem1 = new PriorityValuePair <int>(2f, 4);

            heap.Push(elem1);

            // Ensure that the element was inserted.
            Assert.That(heap.Count, Is.EqualTo(1));
            Assert.That(heap.Peek(), Is.EqualTo(elem1));

            // Try to HeapSwapElements() while the queue only contains 1 element and expect an
            // InvalidOperationException to be thrown.
            Assert.Throws <InvalidOperationException>(() => {
                heap.SwapElements(0, 1);
            });

            // Enqueue another element with higher priority than the last.
            var elem2 = new PriorityValuePair <int>(1f, 2);

            heap.Push(elem2);

            // Ensure that the element was inserted and that the 1st (higher priority) element is
            // still at the root of the heap.
            Assert.That(heap.Count, Is.EqualTo(2));
            Assert.That(heap.Peek(), Is.EqualTo(elem1));

            // Try to HeapSwapElements() with an invalid index1 and expect an
            // ArgumentOutOfRangeException to be thrown.
            Assert.Throws <ArgumentOutOfRangeException>(() => {
                heap.SwapElements(-1, 1);
            });

            // Try to HeapSwapElements() with an invalid index2 and expect an
            // ArgumentOutOfRangeException to be thrown.
            Assert.Throws <ArgumentOutOfRangeException>(() => {
                heap.SwapElements(0, -1);
            });

            // Actually swap elements now.
            heap.SwapElements(0, 1);

            // Ensure that the elements were swapped.
            Assert.That(heap.Count, Is.EqualTo(2));
            Assert.That(heap.Peek(), Is.EqualTo(elem2));
            Assert.That(heap.Contains(elem1), Is.True);
        }
예제 #13
0
        public void Peek()
        {
            // Create a new priority queue.
            ConcurrentPriorityQueue <int> queue = new ConcurrentPriorityQueue <int>();

            // Ensure that the heap is empty.
            Assert.That(queue.Count, Is.EqualTo(0));

            // Expect Peek() to return null for an empty heap.
            Assert.That(queue.Peek(), Is.EqualTo(null));

            // Ensure that the queue is empty.
            Assert.That(queue.Count, Is.EqualTo(0));

            // Store an element and insert it into the queue.
            PriorityValuePair <int> elem1 = new PriorityValuePair <int>(1.0, 2);

            queue.Enqueue(elem1);

            // Ensure that the element was inserted into the queue at the front.
            Assert.That(queue.Count, Is.EqualTo(1));
            Assert.That(queue.Peek(), Is.EqualTo(elem1));

            // Ensure that the element was not removed from the heap.
            Assert.That(queue.Count, Is.EqualTo(1));

            // Insert another element with higher priority than the last.
            PriorityValuePair <int> elem2 = new PriorityValuePair <int>(2.0, 4);

            queue.Enqueue(elem2);

            // Ensure that Peek() returns the new front element.
            Assert.That(queue.Peek(), Is.EqualTo(elem2));

            // Insert another element with the same priority as the last.
            PriorityValuePair <int> elem3 = new PriorityValuePair <int>(2.0, 6);

            queue.Enqueue(elem3);

            // Ensure that Peek() returns still returns the first value with that priority.
            Assert.That(queue.Peek(), Is.EqualTo(elem2));

            // Remove the element from the queue.
            queue.Dequeue();

            // Ensure that Peek() returns now returns the other value with the same priorty.
            Assert.That(queue.Peek(), Is.EqualTo(elem3));
        }
예제 #14
0
        public void Contains()
        {
            // Create a new heap.
            ConcurrentBinaryMinHeap <int> heap = new ConcurrentBinaryMinHeap <int>();

            // Create and store a new element.
            PriorityValuePair <int> elem = new PriorityValuePair <int>(1f, 2);

            // Ensure the queue contains the element.
            Assert.That(heap.Contains(elem), Is.False);

            // Push it onto the heap.
            heap.Push(elem);

            // Ensure the queue now contains the element.
            Assert.That(heap.Contains(elem), Is.True);
        }
예제 #15
0
        public void Contains()
        {
            // Create a new priority queue.
            ConcurrentPriorityQueue <int> queue = new ConcurrentPriorityQueue <int>();

            // Create and store a new element.
            PriorityValuePair <int> elem = new PriorityValuePair <int>(1.0, 2);

            // Ensure the queue contains the element.
            Assert.That(queue.Contains(elem), Is.False);

            // Enqueue it in the queue.
            queue.Enqueue(elem);

            // Ensure the queue now contains the element.
            Assert.That(queue.Contains(elem), Is.True);
        }
예제 #16
0
        onWaitingTask(PriorityValuePair <Task> elem)
        {
            #region Increment counters

            // Based on its priority, increment the appropriate waiting counter.
            if (elem.Priority >= MinCriticalPriority)
            {
                // Lock the thread -- CRITICAL SECTION BEGIN
                Monitor.Enter(__waitingCriticalTasksLock);
                try
                {
                    __waitingCriticalTasks++;
                }
                finally
                {
                    Monitor.Exit(__waitingCriticalTasksLock);
                    // Unlock the thread -- CRITICAL SECTION END
                }
            }
            else
            {
                // Lock the thread -- CRITICAL SECTION BEGIN
                Monitor.Enter(__waitingNoncriticalTasksLock);
                try
                {
                    __waitingNoncriticalTasks++;
                }
                finally
                {
                    Monitor.Exit(__waitingNoncriticalTasksLock);
                    // Unlock the thread -- CRITICAL SECTION END
                }
            }

            #endregion

            #region Emit events

            // Emit an event to notify listeners that a task has been enqueued.
            if (TaskEnqueued != null)
            {
                TaskEnqueued();
            }

            #endregion
        }
예제 #17
0
        public void EnqueueElement()
        {
            // Create a new priority queue.
            ConcurrentPriorityQueue <int> queue = new ConcurrentPriorityQueue <int>();

            // Ensure that the queue is empty.
            Assert.That(queue.Count, Is.EqualTo(0));

            // Store an element and insert it into the queue.
            PriorityValuePair <int> elem = new PriorityValuePair <int>(1.0, 2);

            queue.Enqueue(elem);

            // Ensure that the element was inserted into the queue.
            Assert.That(queue.Peek(), Is.EqualTo(elem));

            // Store another element with higher priority and insert it as well.
            elem = new PriorityValuePair <int>(2.0, 4);
            queue.Enqueue(elem);

            // Ensure that the element was inserted into the queue and is at the front.
            Assert.That(queue.Peek(), Is.EqualTo(elem));
        }
예제 #18
0
        public void PushElement()
        {
            // Create a new heap.
            ConcurrentBinaryMinHeap <int> heap = new ConcurrentBinaryMinHeap <int>();

            // Ensure that the heap is empty.
            Assert.That(heap.Count, Is.EqualTo(0));

            // Store an element and insert it into the heap.
            PriorityValuePair <int> elem = new PriorityValuePair <int>(1f, 2);

            heap.Push(elem);

            // Ensure that the element was inserted into the heap.
            Assert.That(heap.Peek(), Is.EqualTo(elem));

            // Store another element with higher priority and insert it as well.
            elem = new PriorityValuePair <int>(2f, 4);
            heap.Push(elem);

            // Ensure that the element was inserted into the queue and is at the root.
            Assert.That(heap.Peek(), Is.EqualTo(elem));
        }
예제 #19
0
        static void Main(string[] args)
        {
            ConcurrentPriorityQueue <string> queue = new ConcurrentPriorityQueue <string>();

            queue.Enqueue(1000.0, "This ");
            queue.Enqueue(1000.0, "should ");
            queue.Enqueue(1000.0, "form ");
            queue.Enqueue(1000.0, "a ");
            queue.Enqueue(1000.0, "complete ");
            queue.Enqueue(1000.0, "and ");
            queue.Enqueue(1000.0, "understandable ");
            queue.Enqueue(1000.0, "sentence.");

            int numIterations = queue.Count;

            for (int x = 0; x < numIterations; x++)
            {
                Console.WriteLine("ITERATION " + (x + 1));
                Console.WriteLine("");

                // Print out the current state of the heap
                PriorityValuePair <string>[] heapArray = new PriorityValuePair <string> [queue.Count];
                queue.CopyTo(heapArray, 0);
                for (int i = 0; i < heapArray.Length; i++)
                {
                    Console.WriteLine(heapArray[i].Value + ", " + heapArray[i].Priority);
                }

                // Dequeue the next element
                PriorityValuePair <string> dequeued = queue.Dequeue();
                Console.WriteLine("");
                Console.WriteLine("DEQUEUED: " + dequeued.Value + ", " + dequeued.Priority);
                Console.WriteLine("");
            }

            Console.ReadLine();
        }
예제 #20
0
        onCompletedTask(PriorityValuePair <Task> elem)
        {
            #region Decrement counters

            // Based on its priority, increment the appropriate active counter.
            if (elem.Priority >= MinCriticalPriority)
            {
                // Lock the thread -- CRITICAL SECTION BEGIN
                Monitor.Enter(__runningCriticalTasksLock);
                try
                {
                    __runningCriticalTasks--;
                }
                finally
                {
                    Monitor.Exit(__runningCriticalTasksLock);
                    // Unlock the thread -- CRITICAL SECTION END
                }

                // Lock the thread -- CRITICAL SECTION BEGIN
                Monitor.Enter(__waitingCriticalTasksLock);
                try
                {
                    __waitingCriticalTasks--;
                }
                finally
                {
                    Monitor.Exit(__waitingCriticalTasksLock);
                    // Unlock the thread -- CRITICAL SECTION END
                }
            }
            else
            {
                // Lock the thread -- CRITICAL SECTION BEGIN
                Monitor.Enter(__runningNoncriticalTasksLock);
                try
                {
                    __runningNoncriticalTasks--;
                }
                finally
                {
                    Monitor.Exit(__runningNoncriticalTasksLock);
                    // Unlock the thread -- CRITICAL SECTION END
                }

                // Lock the thread -- CRITICAL SECTION BEGIN
                Monitor.Enter(__waitingNoncriticalTasksLock);
                try
                {
                    __waitingNoncriticalTasks--;
                }
                finally
                {
                    Monitor.Exit(__waitingNoncriticalTasksLock);
                    // Unlock the thread -- CRITICAL SECTION END
                }
            }

            #endregion

            #region Emit events

            // Emit an event to notify listeners that a task has finished executing.
            if (TaskCompleted != null)
            {
                TaskCompleted();
            }

            // Emit an event to notify listeners that all critical tasks have finished
            // executing (if so).
            if (__waitingCriticalTasks <= 0)
            {
                if (CriticalTasksCompleted != null)
                {
                    CriticalTasksCompleted();
                }
            }

            // Emit an event to notify listeners that all tasks have finished executing
            // (if so).
            if (__waitingCriticalTasks <= 0 && __waitingNoncriticalTasks <= 0)
            {
                if (AllTasksCompleted != null)
                {
                    AllTasksCompleted();
                }
            }

            #endregion
        }