Windows, JNI: add state to WaitableProcess
Move process-management state (e.g. process and
job HANDLEs, PID, exit code) from NativeProcess
into WaitableProcess.
Next step: use WaitableProcess in the test
wrapper.
RELNOTES: none
PiperOrigin-RevId: 243959075
diff --git a/src/main/native/windows/process.cc b/src/main/native/windows/process.cc
index 596ab99..82fec19 100644
--- a/src/main/native/windows/process.cc
+++ b/src/main/native/windows/process.cc
@@ -33,8 +33,6 @@
const std::wstring& argv_rest, void* env,
const std::wstring& wcwd, HANDLE stdin_process,
HANDLE stdout_process, HANDLE stderr_process,
- AutoHandle* out_job, AutoHandle* out_ioport,
- AutoHandle* out_process, DWORD* out_pid,
std::wstring* error) {
std::wstring cwd;
std::wstring error_msg(AsShortPath(wcwd, &cwd));
@@ -60,8 +58,8 @@
commandline.size() + 1);
// MDSN says that the default for job objects is that breakaway is not
// allowed. Thus, we don't need to do any more setup here.
- *out_job = CreateJobObject(NULL, NULL);
- if (!out_job->IsValid()) {
+ job_ = CreateJobObject(NULL, NULL);
+ if (!job_.IsValid()) {
DWORD err_code = GetLastError();
*error = MakeErrorMessage(WSTR(__FILE__), __LINE__,
L"WaitableProcess::Create", argv0, err_code);
@@ -71,7 +69,7 @@
JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = {0};
job_info.BasicLimitInformation.LimitFlags =
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
- if (!SetInformationJobObject(*out_job, JobObjectExtendedLimitInformation,
+ if (!SetInformationJobObject(job_, JobObjectExtendedLimitInformation,
&job_info, sizeof(job_info))) {
DWORD err_code = GetLastError();
*error = MakeErrorMessage(WSTR(__FILE__), __LINE__,
@@ -79,17 +77,17 @@
return false;
}
- *out_ioport = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1);
- if (!out_ioport->IsValid()) {
+ ioport_ = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1);
+ if (!ioport_.IsValid()) {
DWORD err_code = GetLastError();
*error = MakeErrorMessage(WSTR(__FILE__), __LINE__,
L"WaitableProcess::Create", argv0, err_code);
return false;
}
JOBOBJECT_ASSOCIATE_COMPLETION_PORT port;
- port.CompletionKey = *out_job;
- port.CompletionPort = *out_ioport;
- if (!SetInformationJobObject(*out_job,
+ port.CompletionKey = job_;
+ port.CompletionPort = ioport_;
+ if (!SetInformationJobObject(job_,
JobObjectAssociateCompletionPortInformation,
&port, sizeof(port))) {
DWORD err_code = GetLastError();
@@ -147,21 +145,21 @@
return false;
}
- *out_pid = process_info.dwProcessId;
- *out_process = process_info.hProcess;
+ pid_ = process_info.dwProcessId;
+ process_ = process_info.hProcess;
AutoHandle thread(process_info.hThread);
- if (!AssignProcessToJobObject(*out_job, *out_process)) {
+ if (!AssignProcessToJobObject(job_, process_)) {
BOOL is_in_job = false;
- if (IsProcessInJob(*out_process, NULL, &is_in_job) && is_in_job &&
+ if (IsProcessInJob(process_, NULL, &is_in_job) && is_in_job &&
!IsWindows8OrGreater()) {
// Pre-Windows 8 systems don't support nested jobs, and Bazel is already
// in a job. We can't create nested jobs, so just revert to
// TerminateProcess() and hope for the best. In batch mode, the launcher
// puts Bazel in a job so that will take care of cleanup once the
// command finishes.
- *out_job = INVALID_HANDLE_VALUE;
- *out_ioport = INVALID_HANDLE_VALUE;
+ job_ = INVALID_HANDLE_VALUE;
+ ioport_ = INVALID_HANDLE_VALUE;
} else {
DWORD err_code = GetLastError();
*error = MakeErrorMessage(WSTR(__FILE__), __LINE__,
@@ -183,13 +181,10 @@
return true;
}
-int WaitableProcess::WaitFor(int64_t timeout_msec, DWORD pid,
- AutoHandle* in_out_job, AutoHandle* in_out_ioport,
- AutoHandle* in_out_process, DWORD* out_exit_code,
- std::wstring* error) {
+int WaitableProcess::WaitFor(int64_t timeout_msec, std::wstring* error) {
DWORD win32_timeout = timeout_msec < 0 ? INFINITE : timeout_msec;
int result;
- switch (WaitForSingleObject(*in_out_process, win32_timeout)) {
+ switch (WaitForSingleObject(process_, win32_timeout)) {
case WAIT_OBJECT_0:
result = kWaitSuccess;
break;
@@ -202,7 +197,7 @@
default:
DWORD err_code = GetLastError();
*error = MakeErrorMessage(WSTR(__FILE__), __LINE__,
- L"WaitableProcess::WaitFor", ToString(pid),
+ L"WaitableProcess::WaitFor", ToString(pid_),
err_code);
return kWaitError;
}
@@ -210,72 +205,69 @@
// Ensure that the process is really terminated (if WaitForSingleObject
// above timed out, we have to explicitly kill it) and that it doesn't
// leave behind any subprocesses.
- if (!Terminate(*in_out_job, *in_out_process, pid, out_exit_code, error)) {
+ if (!Terminate(error)) {
return kWaitError;
}
- if (in_out_job->IsValid()) {
+ if (job_.IsValid()) {
// Wait for the job object to complete, signalling that all subprocesses
// have exited.
DWORD CompletionCode;
ULONG_PTR CompletionKey;
LPOVERLAPPED Overlapped;
- while (GetQueuedCompletionStatus(*in_out_ioport, &CompletionCode,
+ while (GetQueuedCompletionStatus(ioport_, &CompletionCode,
&CompletionKey, &Overlapped, INFINITE) &&
- !((HANDLE)CompletionKey == (HANDLE)*in_out_job &&
+ !((HANDLE)CompletionKey == (HANDLE)job_ &&
CompletionCode == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO)) {
// Still waiting...
}
- *in_out_job = INVALID_HANDLE_VALUE;
- *in_out_ioport = INVALID_HANDLE_VALUE;
+ job_ = INVALID_HANDLE_VALUE;
+ ioport_ = INVALID_HANDLE_VALUE;
}
// Fetch and store the exit code in case Bazel asks us for it later,
// because we cannot do this anymore after we closed the handle.
- GetExitCode(*in_out_process, pid, out_exit_code, error);
+ GetExitCode(error);
- if (in_out_process->IsValid()) {
- *in_out_process = INVALID_HANDLE_VALUE;
+ if (process_.IsValid()) {
+ process_ = INVALID_HANDLE_VALUE;
}
return result;
}
-int WaitableProcess::GetExitCode(const AutoHandle& process, DWORD pid,
- DWORD* out_exit_code, std::wstring* error) {
- if (*out_exit_code == STILL_ACTIVE) {
- if (!GetExitCodeProcess(process, out_exit_code)) {
+int WaitableProcess::GetExitCode(std::wstring* error) {
+ if (exit_code_ == STILL_ACTIVE) {
+ if (!GetExitCodeProcess(process_, &exit_code_)) {
DWORD err_code = GetLastError();
*error = MakeErrorMessage(WSTR(__FILE__), __LINE__,
- L"WaitableProcess::GetExitCode", ToString(pid),
+ L"WaitableProcess::GetExitCode", ToString(pid_),
err_code);
return -1;
}
}
- return *out_exit_code;
+ return exit_code_;
}
-bool WaitableProcess::Terminate(const AutoHandle& job,
- const AutoHandle& process, DWORD pid,
- DWORD* out_exit_code, std::wstring* error) {
+bool WaitableProcess::Terminate(std::wstring* error) {
static constexpr UINT exit_code = 130; // 128 + SIGINT, like on Linux
- if (job.IsValid()) {
- if (!TerminateJobObject(job, exit_code)) {
+ if (job_.IsValid()) {
+ if (!TerminateJobObject(job_, exit_code)) {
DWORD err_code = GetLastError();
*error = MakeErrorMessage(WSTR(__FILE__), __LINE__,
- L"WaitableProcess::Terminate", ToString(pid),
+ L"WaitableProcess::Terminate", ToString(pid_),
err_code);
return false;
}
- } else if (process.IsValid()) {
- if (!TerminateProcess(process, exit_code)) {
+ } else if (process_.IsValid()) {
+ if (!TerminateProcess(process_, exit_code)) {
DWORD err_code = GetLastError();
std::wstring our_error = MakeErrorMessage(WSTR(__FILE__), __LINE__,
L"WaitableProcess::Terminate",
- ToString(pid), err_code);
+ ToString(pid_), err_code);
// If the process exited, despite TerminateProcess having failed, we're
// still happy and just ignore the error. It might have been a race
@@ -283,7 +275,7 @@
// However, if the process is *still* running at this point (evidenced
// by its exit code still being STILL_ACTIVE) then something went
// really unexpectedly wrong and we should report that error.
- if (GetExitCode(process, pid, out_exit_code, error) == STILL_ACTIVE) {
+ if (GetExitCode(error) == STILL_ACTIVE) {
// Restore the error message from TerminateProcess - it will be much
// more helpful for debugging in case something goes wrong here.
*error = our_error;
@@ -291,10 +283,10 @@
}
}
- if (WaitForSingleObject(process, INFINITE) != WAIT_OBJECT_0) {
+ if (WaitForSingleObject(process_, INFINITE) != WAIT_OBJECT_0) {
DWORD err_code = GetLastError();
*error = MakeErrorMessage(WSTR(__FILE__), __LINE__,
- L"WaitableProcess::Terminate", ToString(pid),
+ L"WaitableProcess::Terminate", ToString(pid_),
err_code);
return false;
}
diff --git a/src/main/native/windows/process.h b/src/main/native/windows/process.h
index 2ae87d2..e750f46 100644
--- a/src/main/native/windows/process.h
+++ b/src/main/native/windows/process.h
@@ -37,22 +37,24 @@
kWaitError = 2,
};
- static bool Create(const std::wstring& argv0, const std::wstring& argv_rest,
- void* env, const std::wstring& wcwd, HANDLE stdin_process,
- HANDLE stdout_process, HANDLE stderr_process,
- AutoHandle* out_job, AutoHandle* out_ioport,
- AutoHandle* out_process, DWORD* out_pid,
- std::wstring* error);
+ WaitableProcess() : pid_(0), exit_code_(STILL_ACTIVE) {}
- static int WaitFor(int64_t timeout_msec, DWORD pid, AutoHandle* in_out_job,
- AutoHandle* in_out_ioport, AutoHandle* in_out_process,
- DWORD* out_exit_code, std::wstring* error);
+ bool Create(const std::wstring& argv0, const std::wstring& argv_rest,
+ void* env, const std::wstring& wcwd, HANDLE stdin_process,
+ HANDLE stdout_process, HANDLE stderr_process,
+ std::wstring* error);
- static int GetExitCode(const AutoHandle& process, DWORD pid,
- DWORD* out_exit_code, std::wstring* error);
+ int WaitFor(int64_t timeout_msec, std::wstring* error);
- static bool Terminate(const AutoHandle& job, const AutoHandle& process,
- DWORD pid, DWORD* out_exit_code, std::wstring* error);
+ int GetExitCode(std::wstring* error);
+
+ bool Terminate(std::wstring* error);
+
+ DWORD GetPid() const { return pid_; }
+
+ private:
+ AutoHandle process_, job_, ioport_;
+ DWORD pid_, exit_code_;
};
} // namespace windows
diff --git a/src/main/native/windows/processes-jni.cc b/src/main/native/windows/processes-jni.cc
index 0ae7144..e3bb480 100644
--- a/src/main/native/windows/processes-jni.cc
+++ b/src/main/native/windows/processes-jni.cc
@@ -150,8 +150,7 @@
class NativeProcess {
public:
- NativeProcess()
- : stdout_(), stderr_(), exit_code_(STILL_ACTIVE), error_(L"") {}
+ NativeProcess() : stdout_(), stderr_(), error_(L"") {}
~NativeProcess() {
stdout_.Close();
@@ -312,11 +311,10 @@
stderr_.SetHandle(pipe_read_h);
stderr_process = pipe_write_h;
}
- return bazel::windows::WaitableProcess::Create(
+ return proc_.Create(
wpath, bazel::windows::GetJavaWstring(env, java_argv_rest),
env_map.ptr(), bazel::windows::GetJavaWpath(env, java_cwd),
- stdin_process, stdout_process, stderr_process, &job_, &ioport_,
- &process_, &pid_, &error_);
+ stdin_process, stdout_process, stderr_process, &error_);
}
void CloseStdin() {
@@ -327,26 +325,22 @@
// Wait for this process to exit (or timeout).
int WaitFor(int64_t timeout_msec) {
- return bazel::windows::WaitableProcess::WaitFor(
- timeout_msec, pid_, &job_, &ioport_, &process_, &exit_code_, &error_);
+ return proc_.WaitFor(timeout_msec, &error_);
}
// Returns the exit code of the process if it has already exited. If the
// process is still running, returns STILL_ACTIVE (= 259).
- int GetExitCode() {
- return bazel::windows::WaitableProcess::GetExitCode(process_, pid_,
- &exit_code_, &error_);
- }
+ int GetExitCode() { return proc_.GetExitCode(&error_); }
- DWORD GetPid() { return pid_; }
+ DWORD GetPid() const { return proc_.GetPid(); }
jint WriteStdin(JNIEnv* env, jbyteArray java_bytes, jint offset,
jint length) {
JavaByteArray bytes(env, java_bytes);
if (offset < 0 || length <= 0 || offset > bytes.size() - length) {
error_ = bazel::windows::MakeErrorMessage(
- WSTR(__FILE__), __LINE__, L"NativeProcess:WriteStdin", ToString(pid_),
- L"Array index out of bounds");
+ WSTR(__FILE__), __LINE__, L"NativeProcess:WriteStdin",
+ ToString(GetPid()), L"Array index out of bounds");
return -1;
}
@@ -357,7 +351,7 @@
DWORD err_code = GetLastError();
error_ = bazel::windows::MakeErrorMessage(WSTR(__FILE__), __LINE__,
L"NativeProcess:WriteStdin",
- ToString(pid_), err_code);
+ ToString(GetPid()), err_code);
return -1;
}
@@ -370,10 +364,7 @@
NativeOutputStream* GetStderrStream() { return &stderr_; }
// Terminates this process (and subprocesses, if job objects are available).
- bool Terminate() {
- return bazel::windows::WaitableProcess::Terminate(job_, process_, pid_,
- &exit_code_, &error_);
- }
+ bool Terminate() { return proc_.Terminate(&error_); }
// Return the last error as a human-readable string and clear it.
jstring GetLastErrorAsString(JNIEnv* env) {
@@ -387,12 +378,8 @@
bazel::windows::AutoHandle stdin_;
NativeOutputStream stdout_;
NativeOutputStream stderr_;
- bazel::windows::AutoHandle process_;
- bazel::windows::AutoHandle job_;
- bazel::windows::AutoHandle ioport_;
- DWORD pid_;
- DWORD exit_code_;
std::wstring error_;
+ bazel::windows::WaitableProcess proc_;
};
// Ensure we can safely cast jlong to void*.