/// <summary> /// Handles the specified message. /// </summary> /// <typeparam name="T">the type of the message.</typeparam> /// <param name="workGroup">The work group.</param> /// <param name="message">The message.</param> /// <param name="notifications">The notifications.</param> /// <param name="functionToRun">The function to run.</param> /// <param name="taskFactory">The task factory.</param> /// <returns></returns> public Task HandleAsync <T>(IWorkGroup workGroup, IReceivedMessage <T> message, IWorkerNotification notifications, Action <IReceivedMessage <T>, IWorkerNotification> functionToRun, ITaskFactory taskFactory) where T : class { Guard.NotNull(() => message, message); Guard.NotNull(() => notifications, notifications); Guard.NotNull(() => functionToRun, functionToRun); Guard.NotNull(() => taskFactory, taskFactory); while (true) { //verify that we are not canceling or stopping before trying to queue the item //however, the transport must support rollbacks if (!ShouldHandle(notifications)) { return(null); } if (taskFactory.TryStartNew(state => { WrappedFunction(message, notifications, functionToRun); }, new StateInformation(workGroup), task => { if (task.IsFaulted && task.Exception?.InnerException is OperationCanceledException) { //bubble the cancel exception; the queue will rollback the message if possible throw new OperationCanceledException("user canceled", task.Exception.InnerException); //explicitly throw this } if (task.IsFaulted && task.Exception != null) { //need to throw it throw new DotNetWorkQueueException("Message processing exception", task.Exception.InnerException); } }, out var start).Success()) { try { return(start); } finally { //block here if the scheduler is full try { _waitingOnFreeThreadCounter.Increment(); taskFactory.Scheduler.WaitForFreeThread.Wait(workGroup); } finally { _waitingOnFreeThreadCounter.Decrement(); } } } //block if the scheduler is full try { _waitingOnFreeThreadCounter.Increment(); taskFactory.Scheduler.WaitForFreeThread.Wait(workGroup); } finally { _waitingOnFreeThreadCounter.Decrement(); } } }