public void Decrease_disallowed() { // Arrange var semaphore = new SemaphoreSlimEx(1, 1, 2); // Act var decreased = semaphore.TryDecrease(); // Assert Assert.IsFalse(decreased); Assert.AreEqual(1, semaphore.AvailableSlotsCount); }
public void Increase_allowed() { // Arrange var semaphore = new SemaphoreSlimEx(1, 1, 2); // Act var increased = semaphore.TryIncrease(); // Assert Assert.IsTrue(increased); Assert.AreEqual(2, semaphore.AvailableSlotsCount); }
private async Task ProcessMessages(TimeSpan?visibilityTimeout = null, CancellationToken cancellationToken = default(CancellationToken)) { var runningTasks = new ConcurrentDictionary <Task, Task>(); var semaphore = new SemaphoreSlimEx(_minConcurrentTasks, _minConcurrentTasks, _maxConcurrentTasks); // Define the task pump var pumpTask = Task.Run(async() => { while (!cancellationToken.IsCancellationRequested) { await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); var runningTask = Task.Run(async() => { if (cancellationToken.IsCancellationRequested) { return(false); } CloudQueueMessage message = null; try { message = await _cloudQueue.GetMessageAsync(visibilityTimeout, null, null, cancellationToken); } catch (Exception e) { _logger.ErrorException("An error occured when attempting to get a message from the queue", e); } if (message == null) { try { // The queue is empty OnQueueEmpty?.Invoke(cancellationToken); } #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body catch #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body { // Intentionally left empty. We ignore errors from OnQueueEmpty. } // False indicates that no message was processed return(false); } else { try { // Process the message OnMessage?.Invoke(message, cancellationToken); // Delete the processed message from the queue await _cloudQueue.DeleteMessageAsync(message); } catch (Exception ex) { var isPoison = (message.DequeueCount > _maxDequeueCount); OnError?.Invoke(message, ex, isPoison); if (isPoison) { await _cloudQueue.DeleteMessageAsync(message); } } // True indicates that a message was processed return(true); } }, CancellationToken.None); runningTasks.TryAdd(runningTask, runningTask); runningTask.ContinueWith(async t => { // Decide if we need to scale up or down if (!cancellationToken.IsCancellationRequested) { if (await t) { // The queue is not empty, therefore increase the number of concurrent tasks semaphore.TryIncrease(); } else { // The queue is empty, therefore reduce the number of concurrent tasks semaphore.TryDecrease(); } } // Complete the task semaphore.Release(); Task taskToBeRemoved; runningTasks.TryRemove(t, out taskToBeRemoved); }, TaskContinuationOptions.ExecuteSynchronously) .IgnoreAwait(); } }); // Run the task pump until canceled await pumpTask.UntilCancelled().ConfigureAwait(false); // Task pump has been canceled, wait for the currently running tasks to complete await Task.WhenAll(runningTasks.Values).UntilCancelled().ConfigureAwait(false); }