private void ThreadJob() { // signal that the worker thread has actually started processing the events _operational.Set(); try { const int WAIT_TIMEOUT = 50; // milliseconds // run until Stop() is called while (_running == true) { try { // If there are no tasks to be served, wait for some events to process // Use a timeout to prevent race conditions on the outstanding tasks count // and the actual queue count _doWork.WaitOne(WAIT_TIMEOUT); _logger.Flush(); // Fish from the queue and accumulate, keep track of outstanding tasks to // avoid accumulating too many competing tasks. Note that we are going to schedule // one more tasks than strictly needed, so that we prevent tasks to sit in the queue // because of the race condition on the outstanding task count (_outstandingTasks) // and the tasks actually sitting in the queue. (*) // To prevent this race condition, we will wait with a timeout int count = _dataSource.Count - _outstandingTasks; if (count == 0) { continue; } // check if we have been woken up to actually stop processing EventBatchProcessedEventHandler eventBatchProcessed = null; lock (_syncRoot) { if (_running == false) { return; } // take a snapshot of event handlers to invoke eventBatchProcessed = OnEventsBatchProcessed; } // allocate a container to keep track of tasks for events in the queue var tasks = new List <TaskWrapper>(); // process all messages that have not been processed yet while (--count >= 0) { TaskWrapper <OperationStatus <TQueueItem> > t = null; try { t = _dataSource.TryPop(); } catch { Interlocked.Decrement(ref _outstandingTasks); continue; } // increment outstanding task count Interlocked.Increment(ref _outstandingTasks); t.ContinueWith <TaskWrapper>(popped => { // Decrement the numbers of outstanding tasks. // (*) Note that there is a race condition because at this point in time the tasks // is already out of the queue but we did not decrement the outstanding task count // yet. This race condition may cause tasks to be left sitting in the queue. // To deal with this race condition, we will wait with a timeout Interlocked.Decrement(ref _outstandingTasks); // because the outstanding task counter is incremented before // adding, we should never incur a negative count Debug.Assert(_outstandingTasks >= 0); if (popped?.Result != null && popped.Result.IsSuccess) { return(_dataTarget.SendMessage(popped.Result.Result.GetDeviceId(), popped.Result.Result)); } return(null); }); AddToProcessed(tasks, t); } // alert any client about outstanding message tasks if (eventBatchProcessed != null) { var sh = new SafeAction <List <TaskWrapper> >(allScheduledTasks => eventBatchProcessed(allScheduledTasks), Logger); TaskWrapper.Run(() => sh.SafeInvoke(tasks)); } } catch (StackOverflowException ex) // do not hide stack overflow exceptions { Logger.LogError(_logMessagePrefix + ex.Message); throw; } catch (OutOfMemoryException ex) // do not hide memory exceptions { Logger.LogError(_logMessagePrefix + ex.Message); throw; } catch (Exception ex) // catch all other exceptions { Logger.LogError(_logMessagePrefix + ex.Message); } } } finally { _operational.Set(); } }