aboutsummaryrefslogtreecommitdiff
path: root/src/zencore/include/zencore/process.h
blob: e3b7a70d7a09246d779699cbe30a7acd2fb17920 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include <zencore/thread.h>
#include <zencore/zencore.h>

#include <filesystem>

namespace zen {

/** Basic process abstraction
 */
class ProcessHandle
{
public:
	ProcessHandle();

	ProcessHandle(const ProcessHandle&) = delete;
	ProcessHandle& operator=(const ProcessHandle&) = delete;

	~ProcessHandle();

	void					   Initialize(int Pid);
	void					   Initialize(int Pid, std::error_code& OutEc);
	void					   Initialize(void* ProcessHandle);	 /// Initialize with an existing handle - takes ownership of the handle
	[[nodiscard]] bool		   IsRunning() const;
	[[nodiscard]] bool		   IsValid() const;
	bool					   Wait(int TimeoutMs = -1);
	bool					   Wait(int TimeoutMs, std::error_code& OutEc);
	int						   WaitExitCode();
	int						   GetExitCode();
	bool					   Kill();
	bool					   Terminate(int ExitCode);
	void					   Reset();
	[[nodiscard]] inline int   Pid() const { return m_Pid; }
	[[nodiscard]] inline void* Handle() const { return m_ProcessHandle; }

private:
	void* m_ProcessHandle = nullptr;
	int	  m_Pid			  = 0;
#if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
	int m_ExitCode = -1;
#endif
};

/** Basic process creation
 */
struct CreateProcOptions
{
	enum
	{
		Flag_NewConsole = 1 << 0,
		Flag_Elevated	= 1 << 1,
		Flag_Unelevated = 1 << 2,
		Flag_NoConsole	= 1 << 3,
		// This flag creates the new process in a new process group. This is relevant only on Windows, and
		// allows sending ctrl-break events to the new process group without also sending it to the current
		// process.
		Flag_Windows_NewProcessGroup = 1 << 4,
	};

	const std::filesystem::path* WorkingDirectory = nullptr;
	uint32_t					 Flags			  = 0;
	std::filesystem::path		 StdoutFile;
};

#if ZEN_PLATFORM_WINDOWS
using CreateProcResult = void*;	 // handle to the process
#else
using CreateProcResult = int32_t;  // pid
#endif

CreateProcResult CreateProc(const std::filesystem::path& Executable,
							std::string_view			 CommandLine,  // should also include arg[0] (executable name)
							const CreateProcOptions&	 Options = {});

/** Process monitor - monitors a list of running processes via polling

	Intended to be used to monitor a set of "sponsor" processes, where
	we need to determine when none of them remain alive

 */

class ProcessMonitor
{
public:
	ProcessMonitor();
	~ProcessMonitor();

	bool IsRunning();
	void AddPid(int Pid);
	bool IsActive() const;

private:
	using HandleType = void*;

	mutable RwLock			m_Lock;
	std::vector<HandleType> m_ProcessHandles;
};

bool IsProcessRunning(int pid);
bool IsProcessRunning(int pid, std::error_code& OutEc);
int	 GetCurrentProcessId();
int	 GetProcessId(CreateProcResult ProcId);

std::filesystem::path GetProcessExecutablePath(int Pid, std::error_code& OutEc);
std::error_code		  FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHandle, bool IncludeSelf = true);

/** Wait for all threads in the current process to exit (except the calling thread)
 *
 * This is only implemented on Windows currently. The use-case for this is to try
 * and ensure that all threads have exited before exiting main() in order to
 * avoid some issues which can occur if threads are still running during process
 * shutdown especially when the CRT is statically linked into the executable.
 *
 * This is a best-effort function and may return before all threads have exited.
 */
void WaitForThreads(uint64_t WaitTimeMs);

#if ZEN_PLATFORM_LINUX
void IgnoreChildSignals();
#endif

struct ProcessMetrics
{
	uint64_t MemoryBytes		= 0;
	uint64_t KernelTimeMs		= 0;
	uint64_t UserTimeMs			= 0;
	uint64_t WorkingSetSize		= 0;
	uint64_t PeakWorkingSetSize = 0;
	uint64_t PagefileUsage		= 0;
	uint64_t PeakPagefileUsage	= 0;
};

void GetProcessMetrics(ProcessHandle& Handle, ProcessMetrics& OutMetrics);

void process_forcelink();  // internal

}  // namespace zen