18
18
#include < gmock/gmock.h>
19
19
20
20
#include < direct.h>
21
- #include < Windows .h>
21
+ #include < windows .h>
22
22
23
23
#include < chrono>
24
24
#include < csignal>
@@ -46,7 +46,10 @@ PROCESS_INFORMATION create_process(TCHAR * command, const char * path = nullptr)
46
46
nullptr ,
47
47
nullptr ,
48
48
false ,
49
- 0 ,
49
+ // Create process suspended and resume it after adding to the newly created job. Otherwise,
50
+ // there is a potential race condition where newly created process starts a subprocess
51
+ // before we've called AssignProcessToJobObject();
52
+ CREATE_NEW_PROCESS_GROUP | CREATE_SUSPENDED,
50
53
nullptr ,
51
54
path,
52
55
&start_up_info,
@@ -73,8 +76,9 @@ int execute_and_wait_until_completion(const std::string & command, const std::st
73
76
const_char_to_tchar (command.c_str (), command_char);
74
77
75
78
auto process = create_process (command_char, path.c_str ());
76
- DWORD exit_code = 259 ; // 259 is the code one gets if the process is still active.
77
- while (exit_code == 259 ) {
79
+ ResumeThread (process.hThread );
80
+ DWORD exit_code = STILL_ACTIVE;
81
+ while (exit_code == STILL_ACTIVE) {
78
82
EXPECT_TRUE (GetExitCodeProcess (process.hProcess , &exit_code));
79
83
std::this_thread::sleep_for (std::chrono::milliseconds (10 ));
80
84
}
@@ -97,6 +101,7 @@ ProcessHandle start_execution(const std::string & command)
97
101
auto process_info = create_process (command_char);
98
102
99
103
AssignProcessToJobObject (h_job, process_info.hProcess );
104
+ ResumeThread (process_info.hThread );
100
105
Process process;
101
106
process.process_info = process_info;
102
107
process.job_handle = h_job;
@@ -117,12 +122,12 @@ bool wait_until_completion(
117
122
DWORD exit_code = 0 ;
118
123
std::chrono::steady_clock::time_point const start = std::chrono::steady_clock::now ();
119
124
EXPECT_TRUE (GetExitCodeProcess (handle.process_info .hProcess , &exit_code));
120
- // 259 indicates that the process is still active
121
- while (exit_code == 259 && std::chrono::steady_clock::now () - start < timeout) {
125
+ while (exit_code == STILL_ACTIVE && std::chrono::steady_clock::now () - start < timeout) {
122
126
std::this_thread::sleep_for (std::chrono::milliseconds (10 ));
123
127
EXPECT_TRUE (GetExitCodeProcess (handle.process_info .hProcess , &exit_code));
124
128
}
125
- return exit_code != 259 ;
129
+ EXPECT_EQ (exit_code, 0 );
130
+ return exit_code != STILL_ACTIVE;
126
131
}
127
132
128
133
// / @brief Force to stop process with signal if it's currently running
@@ -135,16 +140,37 @@ void stop_execution(
135
140
std::chrono::duration<double > timeout = std::chrono::seconds(10 ))
136
141
{
137
142
// Match the Unix version by allowing for int signum argument - however Windows does not have
138
- // Linux signals in the same way, so there isn't a 1:1 mapping to dispatch e.g. SIGTERM
139
- DWORD exit_code;
143
+ // Linux signals in the same way, so there isn't a 1:1 mapping to dispatch e.g. SIGINT or SIGTERM
144
+ DWORD exit_code = STILL_ACTIVE ;
140
145
EXPECT_TRUE (GetExitCodeProcess (handle.process_info .hProcess , &exit_code));
141
- // 259 indicates that the process is still active: we want to make sure that the process is
142
- // still running properly before killing it.
143
- if (exit_code == 259 ) {
144
- EXPECT_TRUE (GenerateConsoleCtrlEvent (CTRL_C_EVENT, handle.process_info .dwThreadId ));
146
+ // Make sure that the process is still running properly before stopping it.
147
+ if (exit_code == STILL_ACTIVE) {
148
+ switch (signum) {
149
+ // According to the
150
+ // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/signal?view=msvc-170
151
+ // SIGINT and SIGBREAK is not supported for any Win32 application.
152
+ // Need to use native Windows control event instead.
153
+ case SIGINT:
154
+ EXPECT_TRUE (GenerateConsoleCtrlEvent (CTRL_C_EVENT, handle.process_info .dwProcessId ));
155
+ break ;
156
+ case SIGBREAK:
157
+ EXPECT_TRUE (GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, handle.process_info .dwProcessId ));
158
+ break ;
159
+ case SIGTERM:
160
+ // The CTRL_CLOSE_EVENT is analog of the SIGTERM from POSIX. Windows sends CTRL_CLOSE_EVENT
161
+ // to all processes attached to a console when the user closes the console (either by
162
+ // clicking Close on the console window's window menu, or by clicking the End Task
163
+ // button command from Task Manager). Although according to the
164
+ // https://learn.microsoft.com/en-us/windows/console/generateconsolectrlevent the
165
+ // GenerateConsoleCtrlEvent doesn't support sending CTRL_CLOSE_EVENT. There are no way to
166
+ // directly send CTRL_CLOSE_EVENT to the process in the same console application.
167
+ // Therefore, adding SIGTERM to the unsupported events.
168
+ default :
169
+ throw std::runtime_error (" Unsupported signum: " + std::to_string (signum));
170
+ }
145
171
bool process_finished = wait_until_completion (handle, timeout);
146
172
if (!process_finished) {
147
- std::cerr << " Testing process " << handle.process_info .hProcess <<
173
+ std::cerr << " Testing process " << handle.process_info .dwProcessId <<
148
174
" hangout. Killing it with TerminateProcess(..) \n " ;
149
175
EXPECT_TRUE (TerminateProcess (handle.process_info .hProcess , 2 ));
150
176
EXPECT_TRUE (process_finished);
0 commit comments