aboutsummaryrefslogtreecommitdiff
path: root/src/zencore/process.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2026-02-28 15:36:50 +0100
committerGitHub Enterprise <[email protected]>2026-02-28 15:36:50 +0100
commitf796ee9e650d5f73844f862ed51a6de6bb33c219 (patch)
tree18f09f4f1567d383058e8648d18dc229b79e08bd /src/zencore/process.cpp
parenttest running / reporting improvements (#797) (diff)
downloadzen-f796ee9e650d5f73844f862ed51a6de6bb33c219.tar.xz
zen-f796ee9e650d5f73844f862ed51a6de6bb33c219.zip
subprocess tracking using Jobs on Windows/hub (#796)HEADmain
This change introduces job object support on Windows to be able to more accurately track and limit resource usage on storage instances created by the hub service. It also ensures that all child instances can be torn down reliably on exit. Also made it so hub tests no longer pop up console windows while running.
Diffstat (limited to 'src/zencore/process.cpp')
-rw-r--r--src/zencore/process.cpp89
1 files changed, 89 insertions, 0 deletions
diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp
index 4a2668912..226a94050 100644
--- a/src/zencore/process.cpp
+++ b/src/zencore/process.cpp
@@ -490,6 +490,8 @@ CreateProcNormal(const std::filesystem::path& Executable, std::string_view Comma
LPSECURITY_ATTRIBUTES ProcessAttributes = nullptr;
LPSECURITY_ATTRIBUTES ThreadAttributes = nullptr;
+ const bool AssignToJob = Options.AssignToJob && Options.AssignToJob->IsValid();
+
DWORD CreationFlags = 0;
if (Options.Flags & CreateProcOptions::Flag_NewConsole)
{
@@ -503,6 +505,10 @@ CreateProcNormal(const std::filesystem::path& Executable, std::string_view Comma
{
CreationFlags |= CREATE_NEW_PROCESS_GROUP;
}
+ if (AssignToJob)
+ {
+ CreationFlags |= CREATE_SUSPENDED;
+ }
const wchar_t* WorkingDir = nullptr;
if (Options.WorkingDirectory != nullptr)
@@ -571,6 +577,15 @@ CreateProcNormal(const std::filesystem::path& Executable, std::string_view Comma
return nullptr;
}
+ if (AssignToJob)
+ {
+ if (!Options.AssignToJob->AssignProcess(ProcessInfo.hProcess))
+ {
+ ZEN_WARN("Failed to assign newly created process to job object");
+ }
+ ResumeThread(ProcessInfo.hThread);
+ }
+
CloseHandle(ProcessInfo.hThread);
return ProcessInfo.hProcess;
}
@@ -644,6 +659,8 @@ CreateProcUnelevated(const std::filesystem::path& Executable, std::string_view C
};
PROCESS_INFORMATION ProcessInfo = {};
+ const bool AssignToJob = Options.AssignToJob && Options.AssignToJob->IsValid();
+
if (Options.Flags & CreateProcOptions::Flag_NewConsole)
{
CreateProcFlags |= CREATE_NEW_CONSOLE;
@@ -652,6 +669,10 @@ CreateProcUnelevated(const std::filesystem::path& Executable, std::string_view C
{
CreateProcFlags |= CREATE_NO_WINDOW;
}
+ if (AssignToJob)
+ {
+ CreateProcFlags |= CREATE_SUSPENDED;
+ }
ExtendableWideStringBuilder<256> CommandLineZ;
CommandLineZ << CommandLine;
@@ -679,6 +700,15 @@ CreateProcUnelevated(const std::filesystem::path& Executable, std::string_view C
return nullptr;
}
+ if (AssignToJob)
+ {
+ if (!Options.AssignToJob->AssignProcess(ProcessInfo.hProcess))
+ {
+ ZEN_WARN("Failed to assign newly created process to job object");
+ }
+ ResumeThread(ProcessInfo.hThread);
+ }
+
CloseHandle(ProcessInfo.hThread);
return ProcessInfo.hProcess;
}
@@ -845,6 +875,65 @@ ProcessMonitor::IsActive() const
//////////////////////////////////////////////////////////////////////////
+#if ZEN_PLATFORM_WINDOWS
+JobObject::JobObject() = default;
+
+JobObject::~JobObject()
+{
+ if (m_JobHandle)
+ {
+ CloseHandle(m_JobHandle);
+ m_JobHandle = nullptr;
+ }
+}
+
+void
+JobObject::Initialize()
+{
+ ZEN_ASSERT(m_JobHandle == nullptr, "JobObject already initialized");
+
+ m_JobHandle = CreateJobObjectW(nullptr, nullptr);
+ if (!m_JobHandle)
+ {
+ ZEN_WARN("Failed to create job object: {}", zen::GetLastError());
+ return;
+ }
+
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION LimitInfo = {};
+ LimitInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+
+ if (!SetInformationJobObject(m_JobHandle, JobObjectExtendedLimitInformation, &LimitInfo, sizeof(LimitInfo)))
+ {
+ ZEN_WARN("Failed to set job object limits: {}", zen::GetLastError());
+ CloseHandle(m_JobHandle);
+ m_JobHandle = nullptr;
+ }
+}
+
+bool
+JobObject::AssignProcess(void* ProcessHandle)
+{
+ ZEN_ASSERT(m_JobHandle != nullptr, "JobObject not initialized");
+ ZEN_ASSERT(ProcessHandle != nullptr, "ProcessHandle is null");
+
+ if (!AssignProcessToJobObject(m_JobHandle, ProcessHandle))
+ {
+ ZEN_WARN("Failed to assign process to job object: {}", zen::GetLastError());
+ return false;
+ }
+
+ return true;
+}
+
+bool
+JobObject::IsValid() const
+{
+ return m_JobHandle != nullptr;
+}
+#endif // ZEN_PLATFORM_WINDOWS
+
+//////////////////////////////////////////////////////////////////////////
+
bool
IsProcessRunning(int pid, std::error_code& OutEc)
{