Revert "[dartdev] Use VmInteropHandler for invoking sub commands"

This reverts commit 08252fc9e9.

Reason for revert: https://github.com/dart-lang/sdk/issues/59784

Original change's description:
> [dartdev] Use VmInteropHandler for invoking sub commands
>
> Use VmInteropHandler for invoking sub commands instead of running them
> in an isolate. Running sub commands in an isolate causes an increased footprint.
> Changing this to use VmInteropHandler avoids the additional memory footprint.
>
> Commands that need to use an AOT runtime for execution now exec the AOT
> runtime and run the command.
>
> TEST=ci
>
> Change-Id: If7aed1cab2fec9d9940bd562ad5aa9c4e9a6ac7f
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/398604
> Reviewed-by: Ben Konyi <bkonyi@google.com>
> Reviewed-by: Brian Quinlan <bquinlan@google.com>
> Commit-Queue: Siva Annamalai <asiva@google.com>

Change-Id: I82a997d49a7d52e1fdaa7d75f509603ebe5e51dd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/401901
Reviewed-by: Siva Annamalai <asiva@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
This commit is contained in:
Ben Konyi
2024-12-20 11:12:00 -08:00
parent b5d4097654
commit 7783e61a02
11 changed files with 55 additions and 362 deletions
+4 -11
View File
@@ -17,7 +17,6 @@ import '../experiments.dart';
import '../native_assets.dart';
import '../sdk.dart';
import '../utils.dart';
import '../vm_interop_handler.dart';
const int genericErrorExitCode = 255;
const int compileErrorExitCode = 254;
@@ -94,18 +93,17 @@ class CompileJSCommand extends CompileSubcommandCommand {
final args = argResults!;
var snapshot = sdk.dart2jsAotSnapshot;
var runtime = sdk.dartAotRuntime;
var isAOT = true;
if (!Sdk.checkArtifactExists(snapshot, logError: false)) {
// AOT snapshots cannot be generated on IA32, so we need this fallback
// branch until support for IA32 is dropped (https://dartbug.com/49969).
snapshot = sdk.dart2jsSnapshot;
runtime = sdk.dart;
if (!Sdk.checkArtifactExists(snapshot)) {
return genericErrorExitCode;
}
runtime = sdk.dart;
isAOT = false;
}
final dart2jsCommand = [
runtime,
snapshot,
'--libraries-spec=${sdk.librariesJson}',
'--cfe-invocation-modes=compile',
@@ -114,13 +112,8 @@ class CompileJSCommand extends CompileSubcommandCommand {
if (args.rest.isNotEmpty) ...args.rest.sublist(0),
];
try {
VmInteropHandler.run(
runtime,
dart2jsCommand,
packageConfigOverride: null,
isAOT : isAOT,
);
return 0;
final exitCode = await runProcessInheritStdio(dart2jsCommand);
return exitCode;
} catch (e, st) {
log.stderr('Error: JS compilation failed');
log.stderr(e.toString());
+3 -8
View File
@@ -18,9 +18,6 @@ abstract class VmInteropHandler {
///
/// If [markMainIsolateAsSystemIsolate] is given and set to true, the spawned
/// isolate will run with `--mark-main-isolate-as-system-isolate` enabled.
///
/// If [isAOT] is given and set to true, the script is executed by
/// execing the dartaotruntime.
static void run(
String script,
List<String> args, {
@@ -30,12 +27,11 @@ abstract class VmInteropHandler {
//
// See https://github.com/dart-lang/sdk/issues/53576
bool markMainIsolateAsSystemIsolate = false,
bool isAOT = false,
}) {
final port = _port;
if (port == null) return;
final message = <dynamic>[
isAOT ? _kResultRunAOT : _kResultRunJIT,
_kResultRun,
script,
packageConfigOverride,
markMainIsolateAsSystemIsolate,
@@ -55,9 +51,8 @@ abstract class VmInteropHandler {
}
// Note: keep in sync with runtime/bin/dartdev_isolate.h
static const int _kResultRunJIT = 1;
static const int _kResultRunAOT = 2;
static const int _kResultExit = 3;
static const int _kResultRun = 1;
static const int _kResultExit = 2;
static SendPort? _port;
}
+3 -86
View File
@@ -38,7 +38,6 @@ DartDevIsolate::DartDevRunner DartDevIsolate::runner_ =
DartDevIsolate::DartDevRunner();
bool DartDevIsolate::should_run_dart_dev_ = false;
bool DartDevIsolate::print_usage_error_ = false;
CommandLineOptions* DartDevIsolate::vm_options_ = nullptr;
Monitor* DartDevIsolate::DartDevRunner::monitor_ = new Monitor();
DartDevIsolate::DartDev_Result DartDevIsolate::DartDevRunner::result_ =
DartDevIsolate::DartDev_Result_Unknown;
@@ -139,7 +138,7 @@ void DartDevIsolate::DartDevRunner::Run(
Thread::Start("DartDev Runner", RunCallback, reinterpret_cast<uword>(this));
monitor_->WaitMicros(Monitor::kNoTimeout);
if (result_ == DartDevIsolate::DartDev_Result_RunJIT) {
if (result_ == DartDevIsolate::DartDev_Result_Run) {
// Clear the DartDev dart_options and replace them with the processed
// options provided by DartDev.
dart_options_->Reset();
@@ -158,8 +157,8 @@ void DartDevIsolate::DartDevRunner::DartDevResultCallback(
ASSERT(message->type == Dart_CObject_kArray);
int32_t type = GetArrayItem(message, 0)->value.as_int32;
switch (type) {
case DartDevIsolate::DartDev_Result_RunJIT: {
result_ = DartDevIsolate::DartDev_Result_RunJIT;
case DartDevIsolate::DartDev_Result_Run: {
result_ = DartDevIsolate::DartDev_Result_Run;
ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString);
auto item2 = GetArrayItem(message, 2);
@@ -205,86 +204,6 @@ void DartDevIsolate::DartDevRunner::DartDevResultCallback(
}
break;
}
case DartDevIsolate::DartDev_Result_RunAOT: {
result_ = DartDevIsolate::DartDev_Result_RunAOT;
ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString);
auto item2 = GetArrayItem(message, 2);
ASSERT(item2->type == Dart_CObject_kString ||
item2->type == Dart_CObject_kNull);
auto item3 = GetArrayItem(message, 3);
ASSERT(item3->type == Dart_CObject_kBool);
const bool mark_main_isolate_as_system_isolate = item3->value.as_bool;
if (mark_main_isolate_as_system_isolate) {
Options::set_mark_main_isolate_as_system_isolate(true);
}
if (*script_ != nullptr) {
free(*script_);
}
if (*package_config_override_ != nullptr) {
free(*package_config_override_);
*package_config_override_ = nullptr;
}
*script_ = Utils::StrDup(GetArrayItem(message, 1)->value.as_string);
if (item2->type == Dart_CObject_kString) {
*package_config_override_ = Utils::StrDup(item2->value.as_string);
}
intptr_t num_vm_options = 0;
const char** vm_options = nullptr;
ASSERT(GetArrayItem(message, 4)->type == Dart_CObject_kArray);
Dart_CObject* args = GetArrayItem(message, 4);
intptr_t argc = args->value.as_array.length;
Dart_CObject** dart_args = args->value.as_array.values;
if (vm_options_ != nullptr) {
num_vm_options = vm_options_->count();
vm_options = vm_options_->arguments();
}
auto deleter = [](char** args) {
for (intptr_t i = 0; i < argc_; ++i) {
free(args[i]);
}
delete[] args;
};
// Total count of arguments to be passed to the script being execed.
argc_ = argc + num_vm_options + 1;
// Array of arguments to be passed to the script being execed.
argv_ = std::unique_ptr<char*[], void (*)(char**)>(new char*[argc_ + 1],
deleter);
intptr_t idx = 0;
// Copy in name of the script to run (dartaotruntime).
argv_[0] = Utils::StrDup(GetArrayItem(message, 1)->value.as_string);
idx += 1;
// Copy in any vm options that need to be passed to the execed process.
for (intptr_t i = 0; i < num_vm_options; ++i) {
argv_[i + idx] = Utils::StrDup(vm_options[i]);
}
idx += num_vm_options;
// Copy in the dart options that need to be passed to the command.
for (intptr_t i = 0; i < argc; ++i) {
argv_[i + idx] = Utils::StrDup(dart_args[i]->value.as_string);
}
// Null terminate the argv array.
argv_[argc + idx] = nullptr;
// Exec the script to be run and pass the arguments.
char err_msg[256];
err_msg[0] = '\0';
int ret = Process::Exec(nullptr, *script_,
const_cast<const char**>(argv_.get()), argc_,
nullptr, err_msg, sizeof(err_msg));
if (ret != 0) {
ProcessError(err_msg, kErrorExitCode);
}
break;
}
case DartDevIsolate::DartDev_Result_Exit: {
ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kInt32);
int32_t dartdev_exit_code = GetArrayItem(message, 1)->value.as_int32;
@@ -395,9 +314,7 @@ DartDevIsolate::DartDev_Result DartDevIsolate::RunDartDev(
Dart_IsolateGroupCreateCallback create_isolate,
char** packages_file,
char** script,
CommandLineOptions* vm_options,
CommandLineOptions* dart_options) {
vm_options_ = vm_options;
runner_.Run(create_isolate, packages_file, script, dart_options);
return runner_.result();
}
+3 -6
View File
@@ -27,9 +27,8 @@ class DartDevIsolate {
// Note: keep in sync with pkg/dartdev/lib/vm_interop_handler.dart
typedef enum {
DartDev_Result_Unknown = -1,
DartDev_Result_RunJIT = 1,
DartDev_Result_RunAOT = 2,
DartDev_Result_Exit = 3,
DartDev_Result_Run = 1,
DartDev_Result_Exit = 2,
} DartDev_Result;
// Returns true if there does not exist a file at |script_uri| or the URI is
@@ -59,7 +58,6 @@ class DartDevIsolate {
Dart_IsolateGroupCreateCallback create_isolate,
char** packages_file,
char** script,
CommandLineOptions* vm_options,
CommandLineOptions* dart_options);
protected:
@@ -85,11 +83,11 @@ class DartDevIsolate {
static char** package_config_override_;
static std::unique_ptr<char*[], void (*)(char**)> argv_;
static intptr_t argc_;
static Monitor* monitor_;
Dart_IsolateGroupCreateCallback create_isolate_;
CommandLineOptions* dart_options_;
const char* packages_file_;
static Monitor* monitor_;
DISALLOW_ALLOCATION();
};
@@ -100,7 +98,6 @@ class DartDevIsolate {
static DartDevRunner runner_;
static bool should_run_dart_dev_;
static bool print_usage_error_;
static CommandLineOptions* vm_options_;
DISALLOW_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(DartDevIsolate);
+2 -2
View File
@@ -1421,11 +1421,11 @@ void main(int argc, char** argv) {
Options::gen_snapshot_kind() == SnapshotKind::kNone) {
DartDevIsolate::DartDev_Result dartdev_result = DartDevIsolate::RunDartDev(
CreateIsolateGroupAndSetup, &package_config_override, &script_name,
&vm_options, &dart_options);
&dart_options);
ASSERT(dartdev_result != DartDevIsolate::DartDev_Result_Unknown);
ran_dart_dev = true;
should_run_user_program =
(dartdev_result == DartDevIsolate::DartDev_Result_RunJIT);
(dartdev_result == DartDevIsolate::DartDev_Result_Run);
if (should_run_user_program) {
try_load_snapshots_lambda();
}
+2 -2
View File
@@ -114,9 +114,9 @@ void FUNCTION_NAME(Process_Start)(Dart_NativeArguments args) {
const char* path = DartUtils::GetStringValue(path_handle);
Dart_Handle arguments = Dart_GetNativeArgument(args, 3);
intptr_t args_length = 0;
const char** string_args = const_cast<const char**>(
char** string_args =
ExtractCStringList(arguments, status_handle,
"Arguments must be builtin strings", &args_length));
"Arguments must be builtin strings", &args_length);
if (string_args == nullptr) {
Dart_SetBooleanReturnValue(args, false);
return;
+1 -18
View File
@@ -94,7 +94,7 @@ class Process {
// process exit streams.
static int Start(Namespace* namespc,
const char* path,
const char* arguments[],
char* arguments[],
intptr_t arguments_length,
const char* working_directory,
char* environment[],
@@ -114,23 +114,6 @@ class Process {
intptr_t exit_handler,
ProcessResult* result);
// Exec process.
// On systems that support 'exec' it will use it to replace
// the current process image with the image corresponding to 'path'
// On systems that do not support it (Windows) it will start in a
// child process in the same group as the parent so that when the parent
// is killed the child also dies.
// Returns 0 if the process could be execed successfully
// Returns -1 if the exec could not be done successfully and 'errmsg'
// points to the error message
static int Exec(Namespace* namespc,
const char* path,
const char* arguments[],
intptr_t arguments_length,
const char* working_directory,
char* errmsg,
intptr_t errmsg_len);
// Kill a process with a given pid.
static bool Kill(intptr_t id, int signal);
-13
View File
@@ -838,19 +838,6 @@ int Process::Start(Namespace* namespc,
return starter.Start();
}
// The command line dart utility does not run on Fuchsia, this functionality
// is not supported on that platform.
int Process::Exec(Namespace* namespc,
const char* path,
const char** arguments,
const char* working_directory,
char* errmsg,
intptr_t errmsg_len) {
snprintf(errmsg, errmsg_len,
"Process::Exec is not supported on this platform");
return -1;
}
intptr_t Process::SetSignalHandler(intptr_t signal) {
errno = ENOSYS;
return -1;
+29 -66
View File
@@ -246,47 +246,11 @@ int ExitCodeHandler::process_count_ = 0;
bool ExitCodeHandler::terminate_done_ = false;
Monitor* ExitCodeHandler::monitor_ = nullptr;
// Tries to find path relative to the current namespace unless it should be
// searched in the PATH environment variable.
// The path that should be passed to exec is returned in realpath.
// Returns true on success, and false if there was an error that should
// be reported to the parent.
static bool PathInNamespace(char* realpath,
intptr_t realpath_size,
Namespace* namespc,
const char* path) {
// Perform a PATH search if there's no slash in the path.
if (Namespace::IsDefault(namespc) || strchr(path, '/') == nullptr) {
// TODO(zra): If there is a non-default namespace, the entries in PATH
// should be treated as relative to the namespace.
strncpy(realpath, path, realpath_size);
realpath[realpath_size - 1] = '\0';
return true;
}
NamespaceScope ns(namespc, path);
const int fd =
TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_RDONLY | O_CLOEXEC));
if (fd == -1) {
return false;
}
char procpath[PATH_MAX];
snprintf(procpath, PATH_MAX, "/proc/self/fd/%d", fd);
const intptr_t length =
TEMP_FAILURE_RETRY(readlink(procpath, realpath, realpath_size));
if (length < 0) {
FDUtils::SaveErrorAndClose(fd);
return false;
}
realpath[length] = '\0';
FDUtils::SaveErrorAndClose(fd);
return true;
}
class ProcessStarter {
public:
ProcessStarter(Namespace* namespc,
const char* path,
const char* arguments[],
char* arguments[],
intptr_t arguments_length,
const char* working_directory,
char* environment[],
@@ -317,7 +281,7 @@ class ProcessStarter {
exec_control_[0] = -1;
exec_control_[1] = -1;
program_arguments_ = reinterpret_cast<const char**>(Dart_ScopeAllocate(
program_arguments_ = reinterpret_cast<char**>(Dart_ScopeAllocate(
(arguments_length + 2) * sizeof(*program_arguments_)));
program_arguments_[0] = const_cast<char*>(path_);
for (int i = 0; i < arguments_length; i++) {
@@ -483,7 +447,31 @@ class ProcessStarter {
// Returns true on success, and false if there was an error that should
// be reported to the parent.
bool FindPathInNamespace(char* realpath, intptr_t realpath_size) {
return PathInNamespace(realpath, realpath_size, namespc_, path_);
// Perform a PATH search if there's no slash in the path.
if (Namespace::IsDefault(namespc_) || strchr(path_, '/') == nullptr) {
// TODO(zra): If there is a non-default namespace, the entries in PATH
// should be treated as relative to the namespace.
strncpy(realpath, path_, realpath_size);
realpath[realpath_size - 1] = '\0';
return true;
}
NamespaceScope ns(namespc_, path_);
const int fd =
TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_RDONLY | O_CLOEXEC));
if (fd == -1) {
return false;
}
char procpath[PATH_MAX];
snprintf(procpath, PATH_MAX, "/proc/self/fd/%d", fd);
const intptr_t length =
TEMP_FAILURE_RETRY(readlink(procpath, realpath, realpath_size));
if (length < 0) {
FDUtils::SaveErrorAndClose(fd);
return false;
}
realpath[length] = '\0';
FDUtils::SaveErrorAndClose(fd);
return true;
}
void ExecProcess() {
@@ -784,7 +772,7 @@ class ProcessStarter {
int write_out_[2]; // Pipe for stdin to child process.
int exec_control_[2]; // Pipe to get the result from exec.
const char** program_arguments_;
char** program_arguments_;
char** program_environment_;
Namespace* namespc_;
@@ -804,7 +792,7 @@ class ProcessStarter {
int Process::Start(Namespace* namespc,
const char* path,
const char* arguments[],
char* arguments[],
intptr_t arguments_length,
const char* working_directory,
char* environment[],
@@ -926,31 +914,6 @@ bool Process::Wait(intptr_t pid,
return true;
}
int Process::Exec(Namespace* namespc,
const char* path,
const char* arguments[],
intptr_t arguments_length,
const char* working_directory,
char* errmsg,
intptr_t errmsg_len) {
if (working_directory != nullptr &&
!Directory::SetCurrent(namespc, working_directory)) {
Utils::StrError(errno, errmsg, errmsg_len);
return -1;
}
char realpath[PATH_MAX];
if (!PathInNamespace(realpath, PATH_MAX, namespc, path)) {
Utils::StrError(errno, errmsg, errmsg_len);
return -1;
}
// TODO(dart:io) Test for the existence of execveat, and use it instead.
execvp(const_cast<const char*>(realpath),
const_cast<char* const*>(arguments));
Utils::StrError(errno, errmsg, errmsg_len);
return -1;
}
bool Process::Kill(intptr_t id, int signal) {
return (TEMP_FAILURE_RETRY(kill(id, signal)) != -1);
}
+4 -22
View File
@@ -249,7 +249,7 @@ Monitor* ExitCodeHandler::monitor_ = nullptr;
class ProcessStarter {
public:
ProcessStarter(const char* path,
const char* arguments[],
char* arguments[],
intptr_t arguments_length,
const char* working_directory,
char* environment[],
@@ -279,7 +279,7 @@ class ProcessStarter {
exec_control_[0] = -1;
exec_control_[1] = -1;
program_arguments_ = reinterpret_cast<const char**>(Dart_ScopeAllocate(
program_arguments_ = reinterpret_cast<char**>(Dart_ScopeAllocate(
(arguments_length + 2) * sizeof(*program_arguments_)));
program_arguments_[0] = const_cast<char*>(path_);
for (int i = 0; i < arguments_length; i++) {
@@ -740,7 +740,7 @@ class ProcessStarter {
int write_out_[2]; // Pipe for stdin to child process.
int exec_control_[2]; // Pipe to get the result from exec.
const char** program_arguments_;
char** program_arguments_;
char** program_environment_;
const char* path_;
@@ -760,7 +760,7 @@ class ProcessStarter {
int Process::Start(Namespace* namespc,
const char* path,
const char* arguments[],
char* arguments[],
intptr_t arguments_length,
const char* working_directory,
char* environment[],
@@ -898,24 +898,6 @@ bool Process::Wait(intptr_t pid,
#endif // defined(DART_HOST_OS_IOS)
}
int Process::Exec(Namespace* namespc,
const char* path,
const char* arguments[],
intptr_t arguments_length,
const char* working_directory,
char* errmsg,
intptr_t errmsg_len) {
if (working_directory != nullptr &&
TEMP_FAILURE_RETRY(chdir(working_directory)) == -1) {
Utils::StrError(errno, errmsg, errmsg_len);
return -1;
}
execvp(const_cast<const char*>(path), const_cast<char* const*>(arguments));
Utils::StrError(errno, errmsg, errmsg_len);
return -1;
}
static int SignalMap(intptr_t id) {
switch (static_cast<ProcessSignals>(id)) {
case kSighup:
+4 -128
View File
@@ -20,7 +20,6 @@
#include "bin/utils.h"
#include "bin/utils_win.h"
#include "platform/syslog.h"
#include "platform/text_buffer.h"
namespace dart {
namespace bin {
@@ -345,7 +344,7 @@ static int GenerateNames(wchar_t pipe_names[Count][kMaxPipeNameSize]) {
class ProcessStarter {
public:
ProcessStarter(const char* path,
const char* arguments[],
char* arguments[],
intptr_t arguments_length,
const char* working_directory,
char* environment[],
@@ -374,12 +373,11 @@ class ProcessStarter {
stderr_handles_[kWriteHandle] = INVALID_HANDLE_VALUE;
exit_handles_[kReadHandle] = INVALID_HANDLE_VALUE;
exit_handles_[kWriteHandle] = INVALID_HANDLE_VALUE;
child_process_handle_ = INVALID_HANDLE_VALUE;
// Transform input strings to system format.
const wchar_t* system_path = StringUtilsWin::Utf8ToWide(path_);
const wchar_t** system_arguments;
system_arguments = reinterpret_cast<const wchar_t**>(
wchar_t** system_arguments;
system_arguments = reinterpret_cast<wchar_t**>(
Dart_ScopeAllocate(arguments_length * sizeof(*system_arguments)));
for (int i = 0; i < arguments_length; i++) {
system_arguments[i] = StringUtilsWin::Utf8ToWide(arguments[i]);
@@ -564,42 +562,7 @@ class ProcessStarter {
*exit_handler_ = reinterpret_cast<intptr_t>(exit_handle);
}
}
child_process_handle_ = process_info.hProcess;
CloseHandle(process_info.hThread);
// Return process id.
*id_ = process_info.dwProcessId;
return 0;
}
int StartForExec() {
// Setup info
STARTUPINFOEXW startup_info;
ZeroMemory(&startup_info, sizeof(startup_info));
startup_info.StartupInfo.cb = sizeof(startup_info);
ASSERT(mode_ == kInheritStdio);
ASSERT(Process::ModeIsAttached(mode_));
ASSERT(!Process::ModeHasStdio(mode_));
PROCESS_INFORMATION process_info;
ZeroMemory(&process_info, sizeof(process_info));
// Create process.
DWORD creation_flags =
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT;
BOOL result = CreateProcessW(
nullptr, // ApplicationName
command_line_,
nullptr, // ProcessAttributes
nullptr, // ThreadAttributes
TRUE, // InheritHandles
creation_flags, environment_block_, system_working_directory_,
reinterpret_cast<STARTUPINFOW*>(&startup_info), &process_info);
if (result == 0) {
return SetOsErrorMessage(os_error_message_);
}
child_process_handle_ = process_info.hProcess;
CloseHandle(process_info.hThread);
// Return process id.
@@ -664,7 +627,6 @@ class ProcessStarter {
HANDLE stdout_handles_[2];
HANDLE stderr_handles_[2];
HANDLE exit_handles_[2];
HANDLE child_process_handle_;
const wchar_t* system_working_directory_;
wchar_t* command_line_;
@@ -689,7 +651,7 @@ class ProcessStarter {
int Process::Start(Namespace* namespc,
const char* path,
const char* arguments[],
char* arguments[],
intptr_t arguments_length,
const char* working_directory,
char* environment[],
@@ -918,92 +880,6 @@ bool Process::Wait(intptr_t pid,
return true;
}
int Process::Exec(Namespace* namespc,
const char* path,
const char* arguments[],
intptr_t arguments_length,
const char* working_directory,
char* errmsg,
intptr_t errmsg_len) {
// Create a Job object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
HANDLE hjob = CreateJobObject(nullptr, nullptr);
if (hjob == nullptr) {
BufferFormatter f(errmsg, errmsg_len);
f.Printf("Process::Exec - CreateJobObject failed %d\n", GetLastError());
return -1;
}
JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
DWORD qresult;
if (!QueryInformationJobObject(hjob, JobObjectExtendedLimitInformation, &info,
sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION),
&qresult)) {
BufferFormatter f(errmsg, errmsg_len);
f.Printf("Process::Exec - QueryInformationJobObject failed %d\n",
GetLastError());
return -1;
}
info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if (!SetInformationJobObject(hjob, JobObjectExtendedLimitInformation, &info,
sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION))) {
BufferFormatter f(errmsg, errmsg_len);
f.Printf("Process::Exec - SetInformationJobObject failed %d\n",
GetLastError());
return -1;
}
// Put the current process into the job object (there is a race here
// as the process can crash before it is in the Job object, but since
// we haven't spawned any children yet this race is harmless)
if (!AssignProcessToJobObject(hjob, GetCurrentProcess())) {
BufferFormatter f(errmsg, errmsg_len);
f.Printf("Process::Exec - AssignProcessToJobObject failed %d\n",
GetLastError());
return -1;
}
// Spawn the new child process (this child will automatically get
// added to the Job object).
// If the parent process is killed or it crashes the Job object
// will get destroyed and all the child processes will also get killed.
// arguments includes the name of the executable to run which is the same
// as the value passed in 'path', we strip that off when starting the
// process.
intptr_t pid = -1;
char* os_error_message = nullptr; // Scope allocated by Process::Start.
ProcessStarter starter(path, &(arguments[1]), (arguments_length - 1),
working_directory, nullptr, 0, kInheritStdio, nullptr,
nullptr, nullptr, &pid, nullptr, &os_error_message);
int result = starter.StartForExec();
if (result != 0) {
BufferFormatter f(errmsg, errmsg_len);
f.Printf("Process::Exec - %s\n", os_error_message);
return -1;
}
// Now wait for this child process to terminate (normal exit or crash).
HANDLE child_process = starter.child_process_handle_;
ASSERT(child_process != INVALID_HANDLE_VALUE);
DWORD wait_result = WaitForSingleObject(child_process, INFINITE);
if (wait_result != WAIT_OBJECT_0) {
BufferFormatter f(errmsg, errmsg_len);
f.Printf("Process::Exec - WaitForSingleObject failed %d\n", GetLastError());
CloseHandle(child_process);
return -1;
}
int retval;
if (!GetExitCodeProcess(child_process, reinterpret_cast<DWORD*>(&retval))) {
BufferFormatter f(errmsg, errmsg_len);
f.Printf("Process::Exec - GetExitCodeProcess failed %d\n", GetLastError());
CloseHandle(child_process);
return -1;
}
CloseHandle(child_process);
// We exit the process here to simulate the same behaviour as exec on systems
// that support it.
ExitProcess(retval);
return 0;
}
bool Process::Kill(intptr_t id, int signal) {
USE(signal); // signal is not used on Windows.
HANDLE process_handle;