diff options
| author | Stefan Boberg <[email protected]> | 2026-01-19 13:14:42 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-01-19 13:14:42 +0100 |
| commit | 5081a33dcd19c5d6f9d9cdae9f8745ba786374d4 (patch) | |
| tree | 6733e6f69137afc690a8c6dcdf618e69ce8029c5 /src/zencore/process.cpp | |
| parent | Merge pull request #701 from ue-foundation/lm/mac-daemon-mode (diff) | |
| download | zen-5081a33dcd19c5d6f9d9cdae9f8745ba786374d4.tar.xz zen-5081a33dcd19c5d6f9d9cdae9f8745ba786374d4.zip | |
consul package and basic client added (#716)
* this adds a consul package which can be used to fetch a consul binary
* it also adds a `ConsulProcess` helper which can be used to spawn and manage a consul service instance
* zencore dependencies brought across:
- `except_fmt.h` for easer generation of formatted exception messages
- `process.h/cpp` changes (adds `Kill` operation and process group support on Windows)
- `string.h` changes to allow generic use of `WideToUtf8()`
Diffstat (limited to 'src/zencore/process.cpp')
| -rw-r--r-- | src/zencore/process.cpp | 142 |
1 files changed, 140 insertions, 2 deletions
diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp index 0b25d14f4..56849a10d 100644 --- a/src/zencore/process.cpp +++ b/src/zencore/process.cpp @@ -8,14 +8,19 @@ #include <zencore/scopeguard.h> #include <zencore/string.h> #include <zencore/testing.h> +#include <zencore/timer.h> #include <thread> +ZEN_THIRD_PARTY_INCLUDES_START + #if ZEN_PLATFORM_WINDOWS +# include <zencore/windows.h> + +# include <Psapi.h> # include <shellapi.h> # include <Shlobj.h> # include <TlHelp32.h> -# include <zencore/windows.h> #else # include <fcntl.h> # include <pthread.h> @@ -35,8 +40,8 @@ # include <sys/sysctl.h> #endif -ZEN_THIRD_PARTY_INCLUDES_START #include <fmt/format.h> + ZEN_THIRD_PARTY_INCLUDES_END namespace zen { @@ -215,6 +220,51 @@ ProcessHandle::IsValid() const } bool +ProcessHandle::Kill() +{ + if (!IsRunning()) + { + return true; + } + +#if ZEN_PLATFORM_WINDOWS + SetConsoleCtrlHandler(nullptr, TRUE); // Prevent this process from terminating itself + auto _ = MakeGuard([] { SetConsoleCtrlHandler(nullptr, FALSE); }); + + // Try graceful shutdown first + if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, m_Pid)) + { + // Wait briefly for graceful shutdown + if (WaitForSingleObject(m_ProcessHandle, 5000) == WAIT_OBJECT_0) + { + Reset(); + return true; + } + } + + // Fall back to forceful termination if graceful shutdown failed + if (!TerminateProcess(m_ProcessHandle, 0)) + { + return false; + } +#elif ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC + int Res = kill(pid_t(m_Pid), SIGTERM); + if (Res != 0) + { + int err = errno; + if (err != ESRCH) + { + return false; + } + } +#endif + + Reset(); + + return true; +} + +bool ProcessHandle::Terminate(int ExitCode) { if (!IsRunning()) @@ -449,6 +499,10 @@ CreateProcNormal(const std::filesystem::path& Executable, std::string_view Comma { CreationFlags |= CREATE_NO_WINDOW; } + if (Options.Flags & CreateProcOptions::Flag_Windows_NewProcessGroup) + { + CreationFlags |= CREATE_NEW_PROCESS_GROUP; + } const wchar_t* WorkingDir = nullptr; if (Options.WorkingDirectory != nullptr) @@ -1082,6 +1136,90 @@ FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHand #endif // ZEN_PLATFORM_LINUX } +void +WaitForThreads(uint64_t WaitTimeMs) +{ +#if ZEN_PLATFORM_WINDOWS + auto ThreadSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (ThreadSnapshot == INVALID_HANDLE_VALUE) + { + return; + } + auto _ = MakeGuard([ThreadSnapshot] { CloseHandle(ThreadSnapshot); }); + + const DWORD CurrentProcessId = ::GetCurrentProcessId(); + const DWORD CurrentThreadId = ::GetCurrentThreadId(); + + Stopwatch Timer; + + do + { + THREADENTRY32 ThreadEntry; + ThreadEntry.dwSize = sizeof(THREADENTRY32); + + uint32_t ThreadCount = 0; + if (Thread32First(ThreadSnapshot, &ThreadEntry)) + { + do + { + if (ThreadEntry.th32OwnerProcessID == CurrentProcessId && ThreadEntry.th32ThreadID != CurrentThreadId) + { + ThreadCount++; + } + } while (Thread32Next(ThreadSnapshot, &ThreadEntry)); + } + + if (ThreadCount <= 1) + { + break; + } + + const uint64_t SleepMs = 10; + Sleep(SleepMs); + } while (Timer.GetElapsedTimeMs() < WaitTimeMs); +#else + ZEN_UNUSED(WaitTimeMs); +#endif +} + +void +GetProcessMetrics(ProcessHandle& Handle, ProcessMetrics& OutMetrics) +{ +#if ZEN_PLATFORM_WINDOWS + FILETIME CreationTime; + FILETIME ExitTime; + FILETIME KernelTime; + FILETIME UserTime; + + if (GetProcessTimes(Handle.Handle(), &CreationTime, &ExitTime, &KernelTime, &UserTime)) + { + ULARGE_INTEGER KTime; + KTime.LowPart = KernelTime.dwLowDateTime; + KTime.HighPart = KernelTime.dwHighDateTime; + + ULARGE_INTEGER UTime; + UTime.LowPart = UserTime.dwLowDateTime; + UTime.HighPart = UserTime.dwHighDateTime; + + OutMetrics.KernelTimeMs = KTime.QuadPart / 10000; + OutMetrics.UserTimeMs = UTime.QuadPart / 10000; + } + + PROCESS_MEMORY_COUNTERS MemCounters; + if (GetProcessMemoryInfo(Handle.Handle(), &MemCounters, sizeof(MemCounters))) + { + OutMetrics.WorkingSetSize = MemCounters.WorkingSetSize; + OutMetrics.PeakWorkingSetSize = MemCounters.PeakWorkingSetSize; + OutMetrics.PagefileUsage = MemCounters.PagefileUsage; + OutMetrics.PeakPagefileUsage = MemCounters.PeakPagefileUsage; + } +#else + // TODO: implement for Linux and Mac + ZEN_UNUSED(Handle); + ZEN_UNUSED(OutMetrics); +#endif +} + #if ZEN_WITH_TESTS void |