/// <summary> /// Reload this instance. /// </summary> public async Task ReloadAsync(bool http, bool https) { var cfg = ConfigParser.ParseTextFile(m_path); var config = ConfigParser.CreateServerConfig(cfg); config.Storage = m_storage; ((MemoryStorageCreator)m_storage).ExpireCheckInterval = cfg.StorageExpirationCheckIntervalSeconds; // Not a part of .Net Standard or .Net core, but we can call it if we run .Net Framework var createMethod = typeof(System.AppDomain) .GetMethods(BindingFlags.Public | BindingFlags.Static) .Where(x => x.Name == nameof(System.AppDomain.CreateDomain)) .Where(x => x.GetParameters().Length == 5) .Where(x => x.GetParameters().Last().ParameterType == typeof(bool)) .FirstOrDefault(); var domain = (System.AppDomain)createMethod.Invoke(null, new object[] { "CeenRunner-" + Guid.NewGuid().ToString(), null, cfg.Basepath, cfg.Assemblypath, true }); //var domain = System.AppDomain.CreateDomain( // "CeenRunner-" + Guid.NewGuid().ToString(), // null, // cfg.Basepath, // cfg.Assemblypath, // true //); // For debugging, use the same domain //domain = AppDomain.CurrentDomain; var prevdomains = m_handlers.Select(x => x.Wrapper).ToList(); try { await Task.WhenAll(new[] { http?StartRunnerAsync(domain, config, false, cfg.HttpAddress, cfg.HttpPort) : null, https ? StartRunnerAsync(domain, config, true, cfg.HttpsAddress, cfg.HttpsPort) : null }.Where(x => x != null)); } catch { try { System.AppDomain.Unload(domain); } catch { } throw; } var prevdomain = m_appDomain; m_appDomain = domain; await Task.Run(async() => { // Give old domain time to terminate var maxtries = cfg.MaxUnloadWaitSeconds; while (maxtries-- > 0) { if (prevdomains.Select(x => x.ActiveClients).Sum() == 0) { break; } await Task.Delay(1000); } if (prevdomain != null && prevdomain != System.AppDomain.CurrentDomain) { try { System.AppDomain.Unload(prevdomain); } catch { } } }); }
/// <summary> /// Reload this instance. /// </summary> public async Task ReloadAsync(bool http, bool https) { Program.DebugConsoleOutput("Reloading instance"); var cfg = ConfigParser.ParseTextFile(m_path); var config = ConfigParser.CreateServerConfig(cfg); config.Storage = m_storage; ((MemoryStorageCreator)m_storage).ExpireCheckInterval = cfg.StorageExpirationCheckIntervalSeconds; var prevhttp = m_http_runner?.Wrapper; var prevhttps = m_https_runner?.Wrapper; IWrappedRunner new_http_runner = null; IWrappedRunner new_https_runner = null; Program.DebugConsoleOutput("Creating runners"); try { if (http) { new_http_runner = CreateRunner(m_path, false, m_storage, default(System.Threading.CancellationToken)); } if (https) { new_https_runner = CreateRunner(m_path, true, m_storage, default(System.Threading.CancellationToken)); } } catch (Exception ex) { Program.DebugConsoleOutput("Failed to create runners: {0}", ex); // If we fail to start, just kill any of the instances we just started if (new_http_runner != null) { try { new_http_runner.Kill(); } catch { } } if (new_https_runner != null) { try { new_https_runner.Kill(); } catch { } } throw; } Program.DebugConsoleOutput("Created runners, replacing existing runners"); // Set up the new instances m_http_runner = await ReplaceOrRestartAsync(m_http_runner, prevhttp, new_http_runner, cfg.HttpAddress, cfg.HttpPort, false, config); m_https_runner = await ReplaceOrRestartAsync(m_https_runner, prevhttps, new_https_runner, cfg.HttpsAddress, cfg.HttpsPort, true, config); Program.DebugConsoleOutput("Stopping old runners"); if (new_https_runner == null) { if (m_https_runner != null) { m_https_runner.StopAsync(); } } else { if (m_https_runner == null) { m_https_runner = new InstanceRunner(); } m_https_runner.Wrapper = new_https_runner; } // TODO: If the runner is reconfigured, then restart it Program.DebugConsoleOutput("Setting up crash handlers."); // Bind variables var self_http = m_http_runner; var self_https = m_https_runner; // Set up a crash handler to capture crash in log var dummy = self_http?.RunnerTask.ContinueWith(x => { if (!self_http.ShouldStop && InstanceCrashed != null) { InstanceCrashed(cfg.HttpAddress, false, x.IsFaulted ? x.Exception : new Exception("Unexpected stop")); } }); // Set up a crash handler to capture crash in log dummy = self_https?.RunnerTask.ContinueWith(x => { if (!self_https.ShouldStop && InstanceCrashed != null) { InstanceCrashed(cfg.HttpsAddress, true, x.IsFaulted ? x.Exception : new Exception("Unexpected stop")); } }); Program.DebugConsoleOutput("Ensuring that old runners are stopped"); if (prevhttp != null || prevhttps != null) { await Task.Run(async() => { // We need both to stop var t = Task.WhenAll(new[] { prevhttp?.StopAsync(), prevhttps?.StopAsync() }.Where(x => x != null)); // Give old processes time to terminate (if they are handling requests) var maxtries = cfg.MaxUnloadWaitSeconds; while (maxtries-- > 0 && !t.IsCompleted) { await Task.WhenAny(t, Task.Delay(1000)); } // If we failed to stop, request a kill if (!t.IsCompleted) { Program.DebugConsoleOutput("Requesting kill of old runners"); if (prevhttp != null) { prevhttp.Kill(); } if (prevhttps != null) { prevhttps.Kill(); } } }); } Program.DebugConsoleOutput("Reloading completed"); }