private static async Task Start(CancellationToken abortSignal) { receiveTasks = Enumerable.Range(0, 3).Select(index => DequeueTask(abortSignal)).ToArray(); var apiTasks = new OptimizedTaskQueue <bool>(log); while (!abortSignal.IsCancellationRequested) { // don't inflate the list if it is already large try { await WaitForAndHandleEvent(abortSignal, apiTasks); } catch (Exception e) { log.Error(e, "Error waiting for next event"); } } }
private static async Task WaitForAndHandleEvent(CancellationToken abortSignal, OptimizedTaskQueue <bool> apiTasks) { var watchDogCancelToken = new CancellationTokenSource(); var watchdogCombinedWithAbort = CancellationTokenSource.CreateLinkedTokenSource(abortSignal, watchDogCancelToken.Token); using (watchdogCombinedWithAbort) using (watchDogCancelToken) { var waitAndListenForCancelTask = Task.Delay(20000, watchdogCombinedWithAbort.Token); var allTasks = new List <Task>(); var firstApiTask = apiTasks.Peek; log.Debug($"awaitable is {firstApiTask?.Id}"); if (firstApiTask != null) { allTasks.Add(firstApiTask); } var receiveStart = allTasks.Count; allTasks.AddRange(receiveTasks); var receiveEnd = allTasks.Count; allTasks.Add(waitAndListenForCancelTask); log.Debug($"Waiting for tasks {string.Join(",", allTasks.Select(t => t.Id))}"); // TODO make some fault tolerance on exceptions. Best 2-level, first attempt to recover in the code and second with container recycle var task = await Task.WhenAny(allTasks); log.Debug($"when any returned task #{task.Id}"); var index = allTasks.IndexOf(task); if (index < 0) { throw new Exception("bad index"); } if (index < receiveEnd && index >= receiveStart) { // receiver fired receiveTasks[receiveStart - index] = DequeueTask(abortSignal); watchDogCancelToken.Cancel(); apiTasks.Enqueue(AddSomeApiCallTasks(abortSignal)); } else if (index == 0) { // api fired watchDogCancelToken.Cancel(); apiTasks.Dequeue(firstApiTask); } else { // watchdog timer fired - nothing happened last 20 seconds. return; } } }