1818#include < gmock/gmock.h>
1919
2020#include < direct.h>
21- #include < Windows .h>
21+ #include < windows .h>
2222
2323#include < chrono>
2424#include < csignal>
@@ -46,7 +46,10 @@ PROCESS_INFORMATION create_process(TCHAR * command, const char * path = nullptr)
4646 nullptr ,
4747 nullptr ,
4848 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,
5053 nullptr ,
5154 path,
5255 &start_up_info,
@@ -73,8 +76,9 @@ int execute_and_wait_until_completion(const std::string & command, const std::st
7376 const_char_to_tchar (command.c_str (), command_char);
7477
7578 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) {
7882 EXPECT_TRUE (GetExitCodeProcess (process.hProcess , &exit_code));
7983 std::this_thread::sleep_for (std::chrono::milliseconds (10 ));
8084 }
@@ -97,6 +101,7 @@ ProcessHandle start_execution(const std::string & command)
97101 auto process_info = create_process (command_char);
98102
99103 AssignProcessToJobObject (h_job, process_info.hProcess );
104+ ResumeThread (process_info.hThread );
100105 Process process;
101106 process.process_info = process_info;
102107 process.job_handle = h_job;
@@ -117,12 +122,12 @@ bool wait_until_completion(
117122 DWORD exit_code = 0 ;
118123 std::chrono::steady_clock::time_point const start = std::chrono::steady_clock::now ();
119124 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) {
122126 std::this_thread::sleep_for (std::chrono::milliseconds (10 ));
123127 EXPECT_TRUE (GetExitCodeProcess (handle.process_info .hProcess , &exit_code));
124128 }
125- return exit_code != 259 ;
129+ EXPECT_EQ (exit_code, 0 );
130+ return exit_code != STILL_ACTIVE;
126131}
127132
128133// / @brief Force to stop process with signal if it's currently running
@@ -135,16 +140,37 @@ void stop_execution(
135140 std::chrono::duration<double > timeout = std::chrono::seconds(10 ))
136141{
137142 // 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 ;
140145 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+ }
145171 bool process_finished = wait_until_completion (handle, timeout);
146172 if (!process_finished) {
147- std::cerr << " Testing process " << handle.process_info .hProcess <<
173+ std::cerr << " Testing process " << handle.process_info .dwProcessId <<
148174 " hangout. Killing it with TerminateProcess(..) \n " ;
149175 EXPECT_TRUE (TerminateProcess (handle.process_info .hProcess , 2 ));
150176 EXPECT_TRUE (process_finished);
0 commit comments