private async Task RunProgramAsync(string logPath, int numberOfParsers, CancellationTokenSource ctSource) { WriteLog(LogEventLevel.Information, $"Starting to process EQ Log file: {logPath}"); _eqJob = CreateJobProcessor(numberOfParsers); _eqJob.CancelSource = ctSource; // Get started waiting for user input // When the user quits, then cancel our CTS (which will cause our JobTask to be cancelled) var consoleTask = GetConsoleUserInputAsync(ctSource.Token); var ctComplete = consoleTask.ContinueWith(_ => ctSource.Cancel(), TaskContinuationOptions.OnlyOnRanToCompletion); // var ctContinue = consoleTask.ContinueWith(_ => { }); // Empty task to await upon // Start the JobProcessor, which will read from the log file continuously, parse the lines and add them to the EQBattle // When it's done, show the summary var jobTask = _eqJob.StartProcessingJobAsync(logPath, _eqBattle); var jtError = jobTask.ContinueWith(_ => Log.Error(_.Exception, $"JobTask Error"), TaskContinuationOptions.OnlyOnFaulted); // Either the log file wasn't found, or we finished reading the log file. It either case, // we need to cancel the 'consoleTask' so we don't wait for the user when we know we're done. var jtNotCancelled = jobTask.ContinueWith(_ => ctSource.Cancel(), TaskContinuationOptions.NotOnCanceled); // When everything completed successfully (which doesn't reall happen) or cancelled (which is the normal path), show the final status var jtComplete = jobTask.ContinueWith(_ => ShowBattleSummary(), TaskContinuationOptions.NotOnFaulted); ConcurrentBag <Task> tasksToWaitFor = new ConcurrentBag <Task>(); try { // Wait for everything to finish. // await Task.WhenAll(ctComplete, ctContinue, jtError, jtNotCancelled, jtComplete); // await ctContinue; // ctSource.CancelAfter(7*1000); await Task.WhenAll(consoleTask, jobTask); tasksToWaitFor.Add(jtNotCancelled); tasksToWaitFor.Add(jtComplete); } // catch (TaskCanceledException) // { // WriteMessage($"{ex.GetType().Name} - {ex.Message}"); // } catch (OperationCanceledException) { Log.Information("Program OperationCanceledException"); if (jobTask.IsCanceled) { tasksToWaitFor.Add(jtComplete); } } catch (Exception ex) { Log.Warning(ex, $"Program Exception"); WriteMessage($"ERROR: {ex.Message}"); if (jobTask.IsFaulted) { tasksToWaitFor.Add(jtNotCancelled); tasksToWaitFor.Add(jtError); } } finally { Log.Verbose("Program Finally Block"); // await Task.WhenAny(jtNotCancelled, jtComplete); // await Task.WhenAny(jtError, jtComplete); // await jtComplete; await Task.WhenAll(tasksToWaitFor); DumpTaskInfo(consoleTask, "consoleTask"); // DumpTaskInfo(ctComplete, "ctComplete"); // DumpTaskInfo(ctContinue, "ctContinue"); DumpTaskInfo(jobTask, "jobTask"); DumpTaskInfo(jtError, "jtError"); DumpTaskInfo(jtNotCancelled, "jtNotCancelled"); DumpTaskInfo(jtComplete, "jtComplete"); } }