/// <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 { }
                }
            });
        }
Exemple #2
0
        /// <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");
        }