diff options
Diffstat (limited to 'src/zenutil/zenserverprocess.cpp')
| -rw-r--r-- | src/zenutil/zenserverprocess.cpp | 186 |
1 files changed, 172 insertions, 14 deletions
diff --git a/src/zenutil/zenserverprocess.cpp b/src/zenutil/zenserverprocess.cpp index e0d99c981..2b27b2d8b 100644 --- a/src/zenutil/zenserverprocess.cpp +++ b/src/zenutil/zenserverprocess.cpp @@ -15,6 +15,7 @@ #include <zencore/timer.h> #include <atomic> +#include <string> #include <gsl/gsl-lite.hpp> @@ -764,10 +765,10 @@ ZenServerEnvironment::ZenServerEnvironment(EStorageTag, std::filesystem::path Pr ZenServerEnvironment::ZenServerEnvironment(EHubTag, std::filesystem::path ProgramBaseDir, - std::filesystem::path TestBaseDir, + std::filesystem::path ChildBaseDir, std::string_view ServerClass) { - InitializeForHub(ProgramBaseDir, TestBaseDir, ServerClass); + InitializeForHub(ProgramBaseDir, ChildBaseDir, ServerClass); } ZenServerEnvironment::ZenServerEnvironment(ETestTag, @@ -964,6 +965,7 @@ ZenServerInstance::Shutdown() ZEN_DEBUG("zenserver process {} ({}) exited", m_Name, m_Process.Pid()); int ExitCode = m_Process.GetExitCode(); m_Process.Reset(); + m_ShutdownEvent.reset(); return ExitCode; } @@ -993,6 +995,7 @@ ZenServerInstance::Shutdown() ZEN_DEBUG("zenserver process {} ({}) exited", m_Name, m_Process.Pid()); int ExitCode = m_Process.GetExitCode(); m_Process.Reset(); + m_ShutdownEvent.reset(); return ExitCode; } else if (Ec) @@ -1020,6 +1023,7 @@ ZenServerInstance::Shutdown() int ExitCode = m_Process.GetExitCode(); ZEN_DEBUG("zenserver process {} ({}) exited", m_Name, m_Process.Pid()); m_Process.Reset(); + m_ShutdownEvent.reset(); return ExitCode; } ZEN_DEBUG("Detached from zenserver process {} ({})", m_Name, m_Process.Pid()); @@ -1371,18 +1375,31 @@ ZenServerInstance::OnServerReady() const ZenServerState::ZenServerEntry* Entry = nullptr; - if (m_BasePort) - { - Entry = State.Lookup(m_BasePort); - } - else + // The child process signals its ready event after writing its state entry, but under + // heavy instrumentation (e.g. sanitizers) the shared memory writes may not be immediately + // visible to this process. Retry briefly before giving up. + for (int Attempt = 0; Attempt < 10; ++Attempt) { - State.Snapshot([&](const ZenServerState::ZenServerEntry& InEntry) { - if (InEntry.Pid == (uint32_t)m_Process.Pid()) - { - Entry = &InEntry; - } - }); + if (m_BasePort) + { + Entry = State.Lookup(m_BasePort); + } + else + { + State.Snapshot([&](const ZenServerState::ZenServerEntry& InEntry) { + if (InEntry.Pid == (uint32_t)m_Process.Pid()) + { + Entry = &InEntry; + } + }); + } + + if (Entry) + { + break; + } + + Sleep(100); } if (!Entry) @@ -1419,7 +1436,7 @@ ZenServerInstance::SetDataDir(std::filesystem::path TestDir) } bool -ZenServerInstance::IsRunning() +ZenServerInstance::IsRunning() const { if (!m_Process.IsValid()) { @@ -1428,6 +1445,16 @@ ZenServerInstance::IsRunning() return m_Process.IsRunning(); } +void +ZenServerInstance::ResetDeadProcess() +{ + if (m_Process.IsValid() && !m_Process.IsRunning()) + { + m_Process.Reset(); + m_ShutdownEvent.reset(); + } +} + std::string ZenServerInstance::GetLogOutput() const { @@ -1553,4 +1580,135 @@ ValidateLockFileInfo(const LockFileInfo& Info, std::string& OutReason) return true; } +std::optional<int> +StartupZenServer(LoggerRef LogRef, const StartupZenServerOptions& Options) +{ + auto Log = [&LogRef]() { return LogRef; }; + + // Check if a matching server is already running + { + ZenServerState State; + if (State.InitializeReadOnly()) + { + uint32_t RunningPid = 0; + uint16_t RunningEffectivePort = 0; + State.Snapshot([&, DesiredPort = Options.Port](const ZenServerState::ZenServerEntry& Entry) { + if (RunningPid == 0 && (DesiredPort == 0 || Entry.DesiredListenPort.load() == DesiredPort)) + { + RunningPid = Entry.Pid.load(); + RunningEffectivePort = Entry.EffectiveListenPort.load(); + } + }); + if (RunningPid != 0) + { + ZEN_INFO("Zen server already running at port {}, pid {}", RunningEffectivePort, RunningPid); + return std::nullopt; + } + } + } + + std::filesystem::path ProgramBaseDir = Options.ProgramBaseDir; + if (ProgramBaseDir.empty()) + { + ProgramBaseDir = GetRunningExecutablePath().parent_path(); + } + + ZenServerEnvironment ServerEnvironment; + ServerEnvironment.Initialize(ProgramBaseDir); + ZenServerInstance Server(ServerEnvironment, Options.Mode); + + std::string ServerArguments(Options.ExtraArgs); + if ((Options.Port != 0) && (ServerArguments.find("--port") == std::string::npos)) + { + ServerArguments.append(fmt::format(" --port {}", Options.Port)); + } + Server.SpawnServer(ServerArguments, Options.OpenConsole, /*WaitTimeoutMs*/ 0); + + constexpr int Timeout = 10000; + + if (!Server.WaitUntilReady(Timeout)) + { + ZEN_WARN("{}", Server.GetLogOutput()); + if (Server.IsRunning()) + { + ZEN_WARN("Zen server launch failed (timed out), terminating"); + Server.Terminate(); + return 1; + } + int ExitCode = Server.Shutdown(); + ZEN_WARN("Zen server failed to get to a ready state and exited with return code {}", ExitCode); + return ExitCode != 0 ? ExitCode : 1; + } + + if (Options.ShowLog) + { + ZEN_INFO("{}", Server.GetLogOutput()); + } + return 0; +} + +bool +ShutdownZenServer(LoggerRef LogRef, + ZenServerState& State, + ZenServerState::ZenServerEntry* Entry, + const std::filesystem::path& ProgramBaseDir) +{ + auto Log = [&LogRef]() { return LogRef; }; + int EntryPort = (int)Entry->DesiredListenPort.load(); + const uint32_t ServerProcessPid = Entry->Pid.load(); + try + { + ZenServerEnvironment ServerEnvironment; + ServerEnvironment.Initialize(ProgramBaseDir); + ZenServerInstance Server(ServerEnvironment); + Server.AttachToRunningServer(EntryPort); + + ZEN_INFO("attached to server on port {} (pid {}), requesting shutdown", EntryPort, ServerProcessPid); + + std::error_code Ec; + if (Server.SignalShutdown(Ec) && !Ec) + { + Stopwatch Timer; + while (Timer.GetElapsedTimeMs() < 10000) + { + if (Server.WaitUntilExited(100, Ec) && !Ec) + { + ZEN_INFO("shutdown complete"); + return true; + } + else if (Ec) + { + ZEN_WARN("Waiting for server on port {} (pid {}) failed. Reason: '{}'", EntryPort, ServerProcessPid, Ec.message()); + } + } + } + else if (Ec) + { + ZEN_WARN("Requesting shutdown of server on port {} failed. Reason: '{}'", EntryPort, Ec.message()); + } + } + catch (const std::exception& Ex) + { + ZEN_DEBUG("Exception caught when requesting shutdown: {}", Ex.what()); + } + + ZEN_INFO("Requesting detached shutdown of server on port {}", EntryPort); + Entry->SignalShutdownRequest(); + + Stopwatch Timer; + while (Timer.GetElapsedTimeMs() < 10000) + { + State.Sweep(); + Entry = State.Lookup(EntryPort); + if (Entry == nullptr || Entry->Pid.load() != ServerProcessPid) + { + ZEN_INFO("Shutdown complete"); + return true; + } + Sleep(100); + } + + return false; +} + } // namespace zen |