| Index: bin/process_win.cc
|
| ===================================================================
|
| --- bin/process_win.cc (revision 380)
|
| +++ bin/process_win.cc (working copy)
|
| @@ -2,10 +2,190 @@
|
| // for details. All rights reserved. Use of this source code is governed by a
|
| // BSD-style license that can be found in the LICENSE file.
|
|
|
| +#include <process.h>
|
| +
|
| #include "bin/builtin.h"
|
| #include "bin/globals.h"
|
| #include "bin/process.h"
|
| +#include "bin/eventhandler.h"
|
|
|
| +static const int kReadHandle = 0;
|
| +static const int kWriteHandle = 1;
|
| +
|
| +class ProcessInfo {
|
| + public:
|
| + ProcessInfo(DWORD process_id, HANDLE process_handle, HANDLE exit_pipe)
|
| + : process_id_(process_id),
|
| + process_handle_(process_handle),
|
| + exit_pipe_(exit_pipe) { }
|
| +
|
| + intptr_t pid() { return process_id_; }
|
| + HANDLE process_handle() { return process_handle_; }
|
| + HANDLE exit_pipe() { return exit_pipe_; }
|
| + ProcessInfo* next() { return next_; }
|
| + void set_next(ProcessInfo* next) { next_ = next; }
|
| +
|
| + private:
|
| + DWORD process_id_; // Process id.
|
| + HANDLE process_handle_; // Process handle.
|
| + HANDLE exit_pipe_; // File descriptor for pipe to report exit code.
|
| + ProcessInfo* next_;
|
| +};
|
| +
|
| +
|
| +ProcessInfo* active_processes = NULL;
|
| +
|
| +
|
| +static void AddProcess(ProcessInfo* process) {
|
| + process->set_next(active_processes);
|
| + active_processes = process;
|
| +}
|
| +
|
| +
|
| +static ProcessInfo* LookupProcess(intptr_t pid) {
|
| + ProcessInfo* current = active_processes;
|
| + while (current != NULL) {
|
| + if (current->pid() == pid) {
|
| + return current;
|
| + }
|
| + current = current->next();
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +
|
| +static void RemoveProcess(intptr_t pid) {
|
| + ProcessInfo* prev = NULL;
|
| + ProcessInfo* current = active_processes;
|
| + while (current != NULL) {
|
| + if (current->pid() == pid) {
|
| + if (prev == NULL) {
|
| + active_processes = current->next();
|
| + } else {
|
| + prev->set_next(current->next());
|
| + }
|
| + delete current;
|
| + return;
|
| + }
|
| + prev = current;
|
| + current = current->next();
|
| + }
|
| +}
|
| +
|
| +
|
| +// Create a pipe for communicating with a new process. The handles array
|
| +// will contain the read and write ends of the pipe. Based on the is_input
|
| +// argument either the read or the write end of the handle will be
|
| +// non-inherited.
|
| +static bool CreateProcessPipe(HANDLE handles[2],
|
| + char* pipe_name,
|
| + bool is_input) {
|
| + SECURITY_ATTRIBUTES security_attributes;
|
| + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
| + security_attributes.bInheritHandle = TRUE;
|
| + security_attributes.lpSecurityDescriptor = NULL;
|
| + if (is_input) {
|
| + handles[kWriteHandle] =
|
| + CreateNamedPipe(pipe_name,
|
| + PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
|
| + PIPE_TYPE_BYTE | PIPE_WAIT,
|
| + 1, // Number of pipes
|
| + 1024, // Out buffer size
|
| + 1024, // In buffer size
|
| + 0, // Timeout in ms
|
| + NULL);
|
| +
|
| + if (handles[kWriteHandle] == INVALID_HANDLE_VALUE) {
|
| + fprintf(stderr, "CreateNamedPipe failed %d\n", GetLastError());
|
| + return false;
|
| + }
|
| +
|
| + handles[kReadHandle] =
|
| + CreateFile(pipe_name,
|
| + GENERIC_READ,
|
| + 0,
|
| + &security_attributes,
|
| + OPEN_EXISTING,
|
| + FILE_READ_ATTRIBUTES | FILE_FLAG_OVERLAPPED,
|
| + NULL);
|
| + if (handles[kReadHandle] == INVALID_HANDLE_VALUE) {
|
| + fprintf(stderr, "CreateFile failed %d\n", GetLastError());
|
| + return false;
|
| + }
|
| + } else {
|
| + handles[kReadHandle] =
|
| + CreateNamedPipe(pipe_name,
|
| + PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
|
| + PIPE_TYPE_BYTE | PIPE_WAIT,
|
| + 1, // Number of pipes
|
| + 1024, // Out buffer size
|
| + 1024, // In buffer size
|
| + 0, // Timeout in ms
|
| + NULL);
|
| +
|
| + if (handles[kReadHandle] == INVALID_HANDLE_VALUE) {
|
| + fprintf(stderr, "CreateNamedPipe failed %d\n", GetLastError());
|
| + return false;
|
| + }
|
| +
|
| + handles[kWriteHandle] =
|
| + CreateFile(pipe_name,
|
| + GENERIC_WRITE,
|
| + 0,
|
| + &security_attributes,
|
| + OPEN_EXISTING,
|
| + FILE_WRITE_ATTRIBUTES | FILE_FLAG_OVERLAPPED,
|
| + NULL);
|
| + if (handles[kWriteHandle] == INVALID_HANDLE_VALUE) {
|
| + fprintf(stderr, "CreateFile failed %d\n", GetLastError());
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +static void CloseProcessPipe(HANDLE handles[2]) {
|
| + for (int i = kReadHandle; i < kWriteHandle; i++) {
|
| + if (handles[i] != INVALID_HANDLE_VALUE) {
|
| + if (!CloseHandle(handles[i])) {
|
| + fprintf(stderr, "CloseHandle failed %d\n", GetLastError());
|
| + }
|
| + handles[i] = INVALID_HANDLE_VALUE;
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +static int SetOsErrorMessage(char* os_error_message,
|
| + int os_error_message_len) {
|
| + int error_code = GetLastError();
|
| + // TODO(sgjesse): Use FormatMessage to get the error message.
|
| + char message[80];
|
| + snprintf(os_error_message, os_error_message_len, "OS Error %d", error_code);
|
| + return error_code;
|
| +}
|
| +
|
| +
|
| +static unsigned int __stdcall TerminationWaitThread(void* args) {
|
| + ProcessInfo* process = reinterpret_cast<ProcessInfo*>(args);
|
| + WaitForSingleObject(process->process_handle(), INFINITE);
|
| + DWORD exit_code;
|
| + BOOL ok = GetExitCodeProcess(process->process_handle(), &exit_code);
|
| + if (!ok) {
|
| + fprintf(stderr, "GetExitCodeProcess failed %d\n", GetLastError());
|
| + }
|
| + intptr_t message[2] = { process->pid(), static_cast<intptr_t>(exit_code) };
|
| + DWORD written;
|
| + ok = WriteFile(
|
| + process->exit_pipe(), message, sizeof(message), &written, NULL);
|
| + if (!ok || written != 8) {
|
| + fprintf(stderr, "FileWrite failed %d\n", GetLastError());
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +
|
| int Process::Start(const char* path,
|
| char* arguments[],
|
| intptr_t arguments_length,
|
| @@ -13,19 +193,66 @@
|
| intptr_t* out,
|
| intptr_t* err,
|
| intptr_t* id,
|
| - intptr_t* exit_event,
|
| + intptr_t* exit_handler,
|
| char* os_error_message,
|
| int os_error_message_len) {
|
| + HANDLE stdin_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
|
| + HANDLE stdout_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
|
| + HANDLE stderr_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
|
| + HANDLE exit_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
|
| + // TODO(sgjesse): Find a better way of generating unique pipe names
|
| + // (e.g. UUID).
|
| + DWORD current_pid = GetCurrentProcessId();
|
| + static int pipe_id = 0;
|
| + char pipe_name[80];
|
| + pipe_id++;
|
| + snprintf(pipe_name, sizeof(pipe_name),
|
| + "\\\\.\\Pipe\\dart%d_%d_1", current_pid, pipe_id);
|
| + if (!CreateProcessPipe(stdin_handles, pipe_name, true)) {
|
| + int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
|
| + CloseProcessPipe(stdin_handles);
|
| + return error_code;
|
| + }
|
| + snprintf(pipe_name, sizeof(pipe_name),
|
| + "\\\\.\\Pipe\\dart%d_%d_2", current_pid, pipe_id);
|
| + if (!CreateProcessPipe(stdout_handles, pipe_name, false)) {
|
| + int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
|
| + CloseProcessPipe(stdin_handles);
|
| + CloseProcessPipe(stdout_handles);
|
| + return error_code;
|
| + }
|
| + snprintf(pipe_name, sizeof(pipe_name),
|
| + "\\\\.\\Pipe\\dart%d_%d_3", current_pid, pipe_id);
|
| + if (!CreateProcessPipe(stderr_handles, pipe_name, false)) {
|
| + int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
|
| + CloseProcessPipe(stdin_handles);
|
| + CloseProcessPipe(stdout_handles);
|
| + CloseProcessPipe(stderr_handles);
|
| + return error_code;
|
| + }
|
| + snprintf(pipe_name, sizeof(pipe_name),
|
| + "\\\\.\\Pipe\\dart%d_%d_4", current_pid, pipe_id);
|
| + if (!CreateProcessPipe(exit_handles, pipe_name, false)) {
|
| + int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
|
| + CloseProcessPipe(stdin_handles);
|
| + CloseProcessPipe(stdout_handles);
|
| + CloseProcessPipe(stderr_handles);
|
| + CloseProcessPipe(exit_handles);
|
| + return error_code;
|
| + }
|
| +
|
| // Setup info structures.
|
| STARTUPINFO startup_info;
|
| - PROCESS_INFORMATION process_info;
|
| ZeroMemory(&startup_info, sizeof(startup_info));
|
| startup_info.cb = sizeof(startup_info);
|
| + startup_info.hStdInput = stdin_handles[kReadHandle];
|
| + startup_info.hStdOutput = stdout_handles[kWriteHandle];
|
| + startup_info.hStdError = stderr_handles[kWriteHandle];
|
| + startup_info.dwFlags |= STARTF_USESTDHANDLES;
|
| +
|
| + PROCESS_INFORMATION process_info;
|
| ZeroMemory(&process_info, sizeof(process_info));
|
|
|
| - // TODO(ager): Once sockets are implemented, use the supplied
|
| - // arguments as in, out and err in the startup info.
|
| -
|
| // Compute command-line length.
|
| int command_line_length = strlen(path);
|
| for (int i = 0; i < arguments_length; i++) {
|
| @@ -36,7 +263,12 @@
|
| command_line_length += 2 + arguments_length + 1;
|
| static const int kMaxCommandLineLength = 32768;
|
| if (command_line_length > kMaxCommandLineLength) {
|
| - return 1;
|
| + int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
|
| + CloseProcessPipe(stdin_handles);
|
| + CloseProcessPipe(stdout_handles);
|
| + CloseProcessPipe(stderr_handles);
|
| + CloseProcessPipe(exit_handles);
|
| + return error_code;
|
| }
|
|
|
| // Put together command-line string.
|
| @@ -59,7 +291,7 @@
|
| command_line,
|
| NULL, // ProcessAttributes
|
| NULL, // ThreadAttributes
|
| - FALSE, // InheritHandles
|
| + TRUE, // InheritHandles
|
| 0, // CreationFlags
|
| NULL, // Environment
|
| NULL, // CurrentDirectory,
|
| @@ -70,25 +302,63 @@
|
| delete[] command_line;
|
|
|
| if (result == 0) {
|
| - return 1;
|
| + int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
|
| + CloseProcessPipe(stdin_handles);
|
| + CloseProcessPipe(stdout_handles);
|
| + CloseProcessPipe(stderr_handles);
|
| + CloseProcessPipe(exit_handles);
|
| + return error_code;
|
| }
|
|
|
| - // Return process handle.
|
| - *id = reinterpret_cast<intptr_t>(process_info.hProcess);
|
| + ProcessInfo* process = new ProcessInfo(process_info.dwProcessId,
|
| + process_info.hProcess,
|
| + exit_handles[kWriteHandle]);
|
| + AddProcess(process);
|
| +
|
| + // TODO(sgjesse): Don't use a separate thread for waiting for each process to
|
| + // terminate.
|
| + uint32_t tid;
|
| + uintptr_t thread_handle =
|
| + _beginthreadex(NULL, 32 * 1024, TerminationWaitThread, process, 0, &tid);
|
| + if (thread_handle == -1) {
|
| + FATAL("Failed to start event handler thread");
|
| + }
|
| +
|
| +
|
| + // Connect the three std streams.
|
| + FileHandle* stdin_handle = new FileHandle(stdin_handles[kWriteHandle]);
|
| + CloseHandle(stdin_handles[kReadHandle]);
|
| + FileHandle* stdout_handle = new FileHandle(stdout_handles[kReadHandle]);
|
| + CloseHandle(stdout_handles[kWriteHandle]);
|
| + FileHandle* stderr_handle = new FileHandle(stderr_handles[kReadHandle]);
|
| + CloseHandle(stderr_handles[kWriteHandle]);
|
| + FileHandle* exit_handle = new FileHandle(exit_handles[kReadHandle]);
|
| + *in = reinterpret_cast<intptr_t>(stdout_handle);
|
| + *out = reinterpret_cast<intptr_t>(stdin_handle);
|
| + *err = reinterpret_cast<intptr_t>(stderr_handle);
|
| + *exit_handler = reinterpret_cast<intptr_t>(exit_handle);
|
| +
|
| + CloseHandle(process_info.hThread);
|
| +
|
| + // Return process id.
|
| + *id = process->pid();
|
| return 0;
|
| }
|
|
|
|
|
| bool Process::Kill(intptr_t id) {
|
| - HANDLE process_handle = reinterpret_cast<HANDLE>(id);
|
| - BOOL result = TerminateProcess(process_handle, -1);
|
| - if (result == 0) {
|
| - return false;
|
| + ProcessInfo* process = LookupProcess(id);
|
| + ASSERT(process != NULL);
|
| + if (process != NULL) {
|
| + BOOL result = TerminateProcess(process->process_handle(), -1);
|
| + if (result == 0) {
|
| + return false;
|
| + }
|
| }
|
| - CloseHandle(process_handle);
|
| return true;
|
| }
|
|
|
|
|
| void Process::Exit(intptr_t id) {
|
| + RemoveProcess(id);
|
| }
|
|
|