/// <summary> /// /// </summary> /// <param name="commandLine"></param> /// <param name="serviceStopToken"></param> /// <exception cref="System.OperationCanceledException">Service stop has been requested.</exception> /// <exception cref="System.AggregateException"></exception> /// <exception cref="System.Exception"></exception> // This function is expected to synchronously start the service and wait for the cancellation token to be signaled, // indicating the service should stop. When that happens, it should throw OperationCanceledException private static void StartServiceAndWaitThrowOpCanceled(CommandLineArgs commandLine, Config config, CancellationToken serviceStopToken) { serviceStopToken.ThrowIfCancellationRequested(); PgMalTrainingDataLoaderFactory trainingDataLoaderFactory = new PgMalTrainingDataLoaderFactory(config.ConnectionStrings.AnimeRecs); (MalTrainingData initialTrainingData, IDictionary <int, IList <int> > initialPrereqs) = LoadInitialData(trainingDataLoaderFactory, serviceStopToken); // TcpRecService constructor does not start the service and does not block on anything. using (TcpRecService recService = new TcpRecService(commandLine.PortNumber, trainingDataLoaderFactory, initialTrainingData, initialPrereqs)) { // Load rec sources into the rec service before listening for connections. LoadRecSources(recService, config, serviceStopToken); // Start listening for connections. Does not block. recService.Start(); Logging.Log.InfoFormat("Started listening on port {0}.", commandLine.PortNumber); // Wait for a service stop request to come in. serviceStopToken.WaitHandle.WaitOne(); // Stop the service, letting connections drain for a few seconds after stopping listening for new connections. const int connectionDrainTimeoutInSeconds = 5; recService.Stop(TimeSpan.FromSeconds(connectionDrainTimeoutInSeconds)); // Throw OperationCanceledException to notify caller that the service was stopped in an orderly manner. throw new OperationCanceledException(serviceStopToken); } }
// Loads all configured rec sources into the rec service in parallel. // Does not return until complete or serviceStopToken is signaled. private static void LoadRecSources(TcpRecService recService, Config config, CancellationToken serviceStopToken) { if (config.RecSources.Count == 0) { Logging.Log.Info("No rec sources configured."); return; } Logging.Log.InfoFormat("Loading {0} rec sources.", config.RecSources.Count); List <ICancellableTask> recSourceLoadTasks = new List <ICancellableTask>(config.RecSources.Count); using (CancellationTokenSource anyTaskFaultedOrCanceled = new CancellationTokenSource()) using (CancellationTokenSource cancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(serviceStopToken, anyTaskFaultedOrCanceled.Token)) { foreach (DTO.LoadRecSourceRequest recSourceConfigX in config.RecSources) { DTO.LoadRecSourceRequest recSourceConfig = recSourceConfigX; // Don't capture the loop variable Task loadRecSourceTask = Task.Factory.StartNew(() => { recService.LoadRecSource(recSourceConfig, cancelTokenSource.Token); }, cancelTokenSource.Token); recSourceLoadTasks.Add(new CancellableTask(loadRecSourceTask, anyTaskFaultedOrCanceled)); } try { AsyncUtils.WhenAllCancelOnFirstExceptionDontWaitForCancellations(recSourceLoadTasks).ConfigureAwait(false).GetAwaiter().GetResult(); } catch (OperationCanceledException) { Logging.Log.Info("Canceled loading rec sources."); throw; } } recService.FinalizeRecSources(serviceStopToken); }
static void Main(string[] args) { System.Threading.Thread.CurrentThread.Name = "Main"; Logging.SetUpLogging(); try { CommandLineArgs commandLine = new CommandLineArgs(args); if (commandLine.ShowHelp) { commandLine.DisplayHelp(Console.Out); return; } ConfigRoot config = null; if (commandLine.ConfigFile != null) { config = ConfigRoot.LoadFromFile(commandLine.ConfigFile); } string connectionString = ConfigurationManager.ConnectionStrings["Postgres"].ToString(); PgMalTrainingDataLoaderFactory trainingDataLoaderFactory = new PgMalTrainingDataLoaderFactory(connectionString); using (TcpRecService recService = new TcpRecService(trainingDataLoaderFactory, commandLine.PortNumber)) { recService.Start(); if (config != null) { using (AnimeRecsClient client = new AnimeRecsClient(commandLine.PortNumber)) { foreach (DTO.LoadRecSourceRequest recSourceToLoad in config.RecSources) { client.LoadRecSource(recSourceToLoad); } if (config.FinalizeAfterLoad) { client.FinalizeRecSources(); } } } #if MONO Logging.Log.InfoFormat("Started listening on port {0}. Press ctrl+c to stop.", commandLine.PortNumber); WaitForUnixStopSignal(); #else Logging.Log.InfoFormat("Started listening on port {0}. Press any key to stop.", commandLine.PortNumber); Console.ReadKey(); #endif Logging.Log.InfoFormat("Got stop signal."); } Logging.Log.InfoFormat("Shutdown complete."); } catch (Exception ex) { Logging.Log.FatalFormat("Fatal error: {0}", ex, ex.Message); Environment.ExitCode = 1; } }