Chromium Code Reviews| Index: bin/process_win.cc |
| =================================================================== |
| --- bin/process_win.cc (revision 380) |
| +++ bin/process_win.cc (working copy) |
| @@ -2,10 +2,180 @@ |
| // 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 the communicating with a new process. The handles array |
|
Mads Ager (google)
2011/10/13 07:45:31
for the communicating -> for communicating
Søren Gjesse
2011/10/13 10:36:08
Done.
|
| +// will contain the read and write ends of the pipe. Based on the is_input |
| +// argument (seen from the new process) either the read or the write end of |
|
Mads Ager (google)
2011/10/13 07:45:31
The parenthetical '(seen from the new process)' co
Søren Gjesse
2011/10/13 10:36:08
Done.
|
| +// 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 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 +183,44 @@ |
| 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)) goto cleanup; |
|
Mads Ager (google)
2011/10/13 07:45:31
Let's create a cleanup method and call that and th
Søren Gjesse
2011/10/13 10:36:08
Done.
|
| + snprintf(pipe_name, sizeof(pipe_name), |
| + "\\\\.\\Pipe\\dart%d_%d_2", current_pid, pipe_id); |
| + if (!CreateProcessPipe(stdout_handles, pipe_name, false)) goto cleanup; |
| + snprintf(pipe_name, sizeof(pipe_name), |
| + "\\\\.\\Pipe\\dart%d_%d_3", current_pid, pipe_id); |
| + if (!CreateProcessPipe(stderr_handles, pipe_name, false)) goto cleanup; |
| + snprintf(pipe_name, sizeof(pipe_name), |
| + "\\\\.\\Pipe\\dart%d_%d_4", current_pid, pipe_id); |
| + if (!CreateProcessPipe(exit_handles, pipe_name, false)) goto cleanup; |
| + |
| // 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 +231,7 @@ |
| command_line_length += 2 + arguments_length + 1; |
| static const int kMaxCommandLineLength = 32768; |
| if (command_line_length > kMaxCommandLineLength) { |
| - return 1; |
| + goto cleanup; |
| } |
| // Put together command-line string. |
| @@ -59,7 +254,7 @@ |
| command_line, |
| NULL, // ProcessAttributes |
| NULL, // ThreadAttributes |
| - FALSE, // InheritHandles |
| + TRUE, // InheritHandles |
| 0, // CreationFlags |
| NULL, // Environment |
| NULL, // CurrentDirectory, |
| @@ -70,25 +265,70 @@ |
| delete[] command_line; |
| if (result == 0) { |
| - return 1; |
| + goto cleanup; |
| } |
| - // 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; |
| + |
| + cleanup: |
| + 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); |
| + CloseProcessPipe(stdin_handles); |
| + CloseProcessPipe(stdout_handles); |
| + CloseProcessPipe(stderr_handles); |
| + CloseProcessPipe(exit_handles); |
| + return error_code; |
| } |
| 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); |
| } |