Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(233)

Side by Side Diff: runtime/bin/process_win.cc

Issue 9310053: Rework Windows process handling. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix Windows build and add stable binaries. Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « runtime/bin/process_macos.cc ('k') | tools/testing/bin/linux/dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include <process.h> 5 #include <process.h>
6 6
7 #include "bin/builtin.h" 7 #include "bin/builtin.h"
8 #include "bin/process.h" 8 #include "bin/process.h"
9 #include "bin/eventhandler.h" 9 #include "bin/eventhandler.h"
10 #include "bin/thread.h"
10 #include "platform/globals.h" 11 #include "platform/globals.h"
11 12
12 static const int kReadHandle = 0; 13 static const int kReadHandle = 0;
13 static const int kWriteHandle = 1; 14 static const int kWriteHandle = 1;
14 15
16
17 // ProcessInfo is used to map a process id to the process handle and
18 // the pipe used to communicate the exit code of the process to Dart.
19 // ProcessInfo objects are kept in the static singly-linked
20 // ProcessInfoList.
15 class ProcessInfo { 21 class ProcessInfo {
16 public: 22 public:
17 ProcessInfo(DWORD process_id, HANDLE process_handle, HANDLE exit_pipe) 23 ProcessInfo(DWORD process_id, HANDLE process_handle, HANDLE exit_pipe)
18 : process_id_(process_id), 24 : process_id_(process_id),
19 process_handle_(process_handle), 25 process_handle_(process_handle),
20 exit_pipe_(exit_pipe) { } 26 exit_pipe_(exit_pipe) { }
21 27
22 intptr_t pid() { return process_id_; } 28 ~ProcessInfo() {
29 BOOL success = CloseHandle(process_handle_);
30 if (!success) {
31 FATAL("Failed to close process handle");
32 }
33 success = CloseHandle(exit_pipe_);
34 if (!success) {
35 FATAL("Failed to close process exit code pipe");
36 }
37 }
38
39 DWORD pid() { return process_id_; }
23 HANDLE process_handle() { return process_handle_; } 40 HANDLE process_handle() { return process_handle_; }
24 HANDLE exit_pipe() { return exit_pipe_; } 41 HANDLE exit_pipe() { return exit_pipe_; }
25 ProcessInfo* next() { return next_; } 42 ProcessInfo* next() { return next_; }
26 void set_next(ProcessInfo* next) { next_ = next; } 43 void set_next(ProcessInfo* next) { next_ = next; }
27 44
28 private: 45 private:
29 DWORD process_id_; // Process id. 46 DWORD process_id_; // Process id.
30 HANDLE process_handle_; // Process handle. 47 HANDLE process_handle_; // Process handle.
31 HANDLE exit_pipe_; // File descriptor for pipe to report exit code. 48 HANDLE exit_pipe_; // File descriptor for pipe to report exit code.
32 ProcessInfo* next_; 49 ProcessInfo* next_;
33 }; 50 };
34 51
35 52
36 ProcessInfo* active_processes = NULL; 53 // Singly-linked list of ProcessInfo objects for all active processes
37 54 // started from Dart.
38 55 class ProcessInfoList {
39 static void AddProcess(ProcessInfo* process) { 56 public:
40 process->set_next(active_processes); 57 static void AddProcess(DWORD pid, HANDLE handle, HANDLE pipe) {
41 active_processes = process; 58 MutexLocker locker(&mutex_);
42 } 59 ProcessInfo* info = new ProcessInfo(pid, handle, pipe);
43 60 info->set_next(active_processes_);
44 61 active_processes_ = info;
45 static ProcessInfo* LookupProcess(intptr_t pid) { 62 ++number_of_processes_;
46 ProcessInfo* current = active_processes; 63 BOOL success = SetEvent(GetProcessAddedEvent());
47 while (current != NULL) { 64 if (!success) {
48 if (current->pid() == pid) { 65 FATAL("Failed to set process added event");
49 return current; 66 }
50 } 67 }
51 current = current->next(); 68
52 } 69 static bool LookupProcess(DWORD pid, HANDLE* handle, HANDLE* pipe) {
53 return NULL; 70 MutexLocker locker(&mutex_);
54 } 71 ProcessInfo* current = active_processes_;
55 72 while (current != NULL) {
56 73 if (current->pid() == pid) {
57 static void RemoveProcess(intptr_t pid) { 74 *handle = current->process_handle();
58 ProcessInfo* prev = NULL; 75 *pipe = current->exit_pipe();
59 ProcessInfo* current = active_processes; 76 return true;
60 while (current != NULL) { 77 }
61 if (current->pid() == pid) { 78 current = current->next();
62 if (prev == NULL) { 79 }
63 active_processes = current->next(); 80 return false;
81 }
82
83 static bool LookupProcessByHandle(HANDLE handle, DWORD* pid, HANDLE* pipe) {
84 MutexLocker locker(&mutex_);
85 ProcessInfo* current = active_processes_;
86 while (current != NULL) {
87 if (current->process_handle() == handle) {
88 *pid = current->pid();
89 *pipe = current->exit_pipe();
90 return true;
91 }
92 current = current->next();
93 }
94 return false;
95 }
96
97 static void RemoveProcess(DWORD pid) {
98 MutexLocker locker(&mutex_);
99 ProcessInfo* prev = NULL;
100 ProcessInfo* current = active_processes_;
101 while (current != NULL) {
102 if (current->pid() == pid) {
103 if (prev == NULL) {
104 active_processes_ = current->next();
105 } else {
106 prev->set_next(current->next());
107 }
108 delete current;
109 --number_of_processes_;
110 return;
111 }
112 prev = current;
113 current = current->next();
114 }
115 }
116
117 // Extract the process handles from the process list. The handles
118 // array argument must have space for MAXIMUM_WAIT_OBJECTS handles.
119 static DWORD GetHandleArray(HANDLE* handles, intptr_t prefix_size) {
120 MutexLocker locker(&mutex_);
121 ASSERT(prefix_size >= 0);
122 DWORD number_of_handles = prefix_size + number_of_processes_;
123 if (number_of_handles > MAXIMUM_WAIT_OBJECTS) {
124 FATAL1("Only %d processes supported on Windows at this point\n",
125 MAXIMUM_WAIT_OBJECTS - prefix_size);
126 }
127 intptr_t i = prefix_size;
128 ProcessInfo* current = active_processes_;
129 while (current != NULL) {
130 handles[i++] = current->process_handle();
131 current = current->next();
132 }
133 ASSERT(i == number_of_handles);
134 // We have taken a new snapshot of the handles in the list. Reset
135 // the process_added_event so we will get signaled if more
136 // processes are added.
137 BOOL success = ResetEvent(GetProcessAddedEvent());
138 if (!success) {
139 FATAL("Failed to reset process added event");
140 }
141 return number_of_handles;
142 }
143
144 private:
145 friend class ExitCodeHandler;
146 static HANDLE GetProcessAddedEvent() {
147 MutexLocker locker(&process_added_event_mutex_);
148 if (process_added_event_ == INVALID_HANDLE_VALUE) {
149 process_added_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
150 if (process_added_event_ == NULL) {
151 FATAL("Failed to allocate event for signaling addition of processes");
152 }
153 }
154 return process_added_event_;
155 }
156 // Number of processes currently in the list.
157 static intptr_t number_of_processes_;
158 // Linked list of ProcessInfo objects for all active processes
159 // started from Dart code.
160 static ProcessInfo* active_processes_;
161 // Mutex protecting all accesses to the linked list of active
162 // processes.
163 static dart::Mutex mutex_;
164 // Event used to signal that more processes have been added to the
165 // list.
166 static HANDLE process_added_event_;
167 static dart::Mutex process_added_event_mutex_;
168 };
169
170
171 intptr_t ProcessInfoList::number_of_processes_ = 0;
172 ProcessInfo* ProcessInfoList::active_processes_ = NULL;
173 dart::Mutex ProcessInfoList::mutex_;
174 HANDLE ProcessInfoList::process_added_event_ = INVALID_HANDLE_VALUE;
175 dart::Mutex ProcessInfoList::process_added_event_mutex_;
176
177
178 // The exit code handler sets up a separate thread which is waiting
179 // for Dart process termination and process start. When a process
180 // terminates the exit code is extracted and communicated to Dart
181 // through the event loop.
182 class ExitCodeHandler {
183 public:
184 // Ensure that the ExitCodeHandler has been initialized.
185 static bool EnsureInitialized() {
186 // Multiple isolates could be starting processes at the same
187 // time. Make sure that only one of them initializes the
188 // ExitCodeHandler.
189 MutexLocker locker(&mutex_);
190 if (initialized_) {
191 return true;
192 }
193
194 // Allocate an event object to be signaled when the exit code
195 // thread should terminate.
196 terminate_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
197 if (terminate_event_ == NULL) {
198 return false;
199 }
200
201 // Start thread that waits for the process-addition and
202 // thread-termination events as well as all process handles for
203 // all active processes.
204 HANDLE* events = new HANDLE[2];
205 events[0] = ProcessInfoList::GetProcessAddedEvent();
206 events[1] = terminate_event_;
207 int result = dart::Thread::Start(ExitCodeHandlerEntry,
208 reinterpret_cast<uword>(events));
209 if (result != 0) {
210 FATAL1("Failed to start exit code handler thread: %d", result);
211 }
212
213 // Thread started and the ExitCodeHandler is initialized.
214 initialized_ = true;
215 return true;
216 }
217
218 static void TerminateExitCodeThread() {
219 MutexLocker locker(&mutex_);
220 if (!initialized_) {
221 return;
222 }
223
224 BOOL success = SetEvent(terminate_event_);
225 if (!success) {
226 FATAL("Failed to set terminate event for exit code handler shutdown");
227 }
228
229 {
230 MonitorLocker terminate_locker(&thread_terminate_monitor_);
231 while (!thread_terminated_) {
232 terminate_locker.Wait();
233 }
234 }
235 }
236
237 static void ExitCodeThreadTerminated() {
238 MonitorLocker locker(&thread_terminate_monitor_);
239 thread_terminated_ = true;
240 locker.Notify();
241 }
242
243 private:
244 // Entry point for the exit code handler thread started by the
245 // ExitCodeHandler.
246 static void ExitCodeHandlerEntry(uword param) {
247 HANDLE* events = reinterpret_cast<HANDLE*>(param);
248 HANDLE wake_up_event = events[0];
249 HANDLE terminate_event = events[1];
250 delete[] events;
251
252 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
253 handles[0] = wake_up_event;
254 handles[1] = terminate_event;
255
256 while (true) {
257 // Get the list of handles to wait for. Allocate a prefix of two
258 // extra handles for the 'process added' and 'thread
259 // termination' event objects.
260 static const intptr_t kPrefixSize = 2;
261 DWORD number_of_handles =
262 ProcessInfoList::GetHandleArray(handles, kPrefixSize);
263 ASSERT(handles[0] == wake_up_event);
264 ASSERT(handles[1] == terminate_event);
265
266 // Wait for the handles.
267 DWORD result =
268 WaitForMultipleObjects(number_of_handles, handles, FALSE, INFINITE);
269 if (result == WAIT_FAILED) {
270 FATAL("Failed to wait for multiple objects for exit code handling");
271 }
272
273 if (result == 0) {
274 // If the result is 0 the thread woke up because of process
275 // addition. We don't have to do anything we just need to
276 // update the list of handles we are waiting for.
277 } else if (result == 1) {
278 // The termination event was triggered. Free event objects and
279 // exit.
280 CloseHandle(terminate_event_);
281 CloseHandle(wake_up_event);
282 ExitCodeThreadTerminated();
283 return;
64 } else { 284 } else {
65 prev->set_next(current->next()); 285 // The result is the index of the process that was
66 } 286 // signalled. Get its exit code and communicate it to Dart.
67 delete current; 287 ASSERT(result < number_of_handles);
68 return; 288 int exit_code;
69 } 289 BOOL ok = GetExitCodeProcess(handles[result],
70 prev = current; 290 reinterpret_cast<DWORD*>(&exit_code));
71 current = current->next(); 291 if (!ok) {
72 } 292 FATAL1("GetExitCodeProcess failed %d\n", GetLastError());
73 } 293 }
294 int negative = 0;
295 if (exit_code < 0) {
296 exit_code = abs(exit_code);
297 negative = 1;
298 }
299
300 DWORD pid;
301 HANDLE exit_pipe;
302 bool success = ProcessInfoList::LookupProcessByHandle(handles[result],
303 &pid,
304 &exit_pipe);
305 if (!success) {
306 FATAL("Failed to lookup pid and exit pipe from process handle");
307 }
308 int message[2] = { exit_code, negative };
309 DWORD written;
310 ok = WriteFile(exit_pipe, message, sizeof(message), &written, NULL);
311 // If the process has been closed, the read end of the exit
312 // pipe has been closed. It is therefore not a problem that
313 // WriteFile fails with a closed pipe error
314 // (ERROR_NO_DATA). Other errors should not happen.
315 if (ok && written != sizeof(message)) {
316 FATAL("Failed to write entire process exit message");
317 } else if (!ok && GetLastError() != ERROR_NO_DATA) {
318 FATAL1("Failed to write exit code: %d", GetLastError());
319 }
320 ProcessInfoList::RemoveProcess(pid);
321 }
322 }
323 }
324
325 static dart::Mutex mutex_;
326 static bool initialized_;
327 static HANDLE terminate_event_;
328 static bool thread_terminated_;
329 static dart::Monitor thread_terminate_monitor_;
330 };
331
332
333 dart::Mutex ExitCodeHandler::mutex_;
334 bool ExitCodeHandler::initialized_ = false;
335 HANDLE ExitCodeHandler::terminate_event_ = INVALID_HANDLE_VALUE;
336 bool ExitCodeHandler::thread_terminated_ = false;
337 dart::Monitor ExitCodeHandler::thread_terminate_monitor_;
74 338
75 339
76 // Types of pipes to create. 340 // Types of pipes to create.
77 enum NamedPipeType { 341 enum NamedPipeType {
78 kInheritRead, 342 kInheritRead,
79 kInheritWrite, 343 kInheritWrite,
80 kInheritNone 344 kInheritNone
81 }; 345 };
82 346
83 347
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
194 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { 458 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
195 fprintf(stderr, "FormatMessage failed %d\n", GetLastError()); 459 fprintf(stderr, "FormatMessage failed %d\n", GetLastError());
196 } 460 }
197 snprintf(os_error_message, os_error_message_len, "OS Error %d", error_code); 461 snprintf(os_error_message, os_error_message_len, "OS Error %d", error_code);
198 } 462 }
199 os_error_message[os_error_message_len - 1] = '\0'; 463 os_error_message[os_error_message_len - 1] = '\0';
200 return error_code; 464 return error_code;
201 } 465 }
202 466
203 467
204 static unsigned int __stdcall TerminationWaitThread(void* args) {
205 ProcessInfo* process = reinterpret_cast<ProcessInfo*>(args);
206 WaitForSingleObject(process->process_handle(), INFINITE);
207 int exit_code;
208 BOOL ok = GetExitCodeProcess(process->process_handle(),
209 reinterpret_cast<DWORD*>(&exit_code));
210 if (!ok) {
211 fprintf(stderr, "GetExitCodeProcess failed %d\n", GetLastError());
212 }
213 int negative = 0;
214 if (exit_code < 0) {
215 exit_code = abs(exit_code);
216 negative = 1;
217 }
218 int message[3] = { process->pid(), exit_code, negative };
219 DWORD written;
220 ok = WriteFile(
221 process->exit_pipe(), message, sizeof(message), &written, NULL);
222 if (!ok || written != sizeof(message)) {
223 fprintf(stderr, "WriteFile failed %d\n", GetLastError());
224 }
225 return 0;
226 }
227
228
229 int Process::Start(const char* path, 468 int Process::Start(const char* path,
230 char* arguments[], 469 char* arguments[],
231 intptr_t arguments_length, 470 intptr_t arguments_length,
232 const char* working_directory, 471 const char* working_directory,
233 intptr_t* in, 472 intptr_t* in,
234 intptr_t* out, 473 intptr_t* out,
235 intptr_t* err, 474 intptr_t* err,
236 intptr_t* id, 475 intptr_t* id,
237 intptr_t* exit_handler, 476 intptr_t* exit_handler,
238 char* os_error_message, 477 char* os_error_message,
239 int os_error_message_len) { 478 int os_error_message_len) {
479 // Ensure that the process exit handler thread has been started.
480 bool initialized = ExitCodeHandler::EnsureInitialized();
481 if (!initialized) {
482 int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
483 fprintf(stderr, "Failed to initialize ExitCodeHandler: %d\n", error_code);
484 return error_code;
485 }
486
240 HANDLE stdin_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; 487 HANDLE stdin_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
241 HANDLE stdout_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; 488 HANDLE stdout_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
242 HANDLE stderr_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; 489 HANDLE stderr_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
243 HANDLE exit_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; 490 HANDLE exit_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
244 491
245 // Generate unique pipe names for the four named pipes needed. 492 // Generate unique pipe names for the four named pipes needed.
246 char pipe_names[4][80]; 493 char pipe_names[4][80];
247 UUID uuid; 494 UUID uuid;
248 RPC_STATUS status = UuidCreateSequential(&uuid); 495 RPC_STATUS status = UuidCreateSequential(&uuid);
249 if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) { 496 if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) {
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
354 // Deallocate command-line string. 601 // Deallocate command-line string.
355 delete[] command_line; 602 delete[] command_line;
356 603
357 if (result == 0) { 604 if (result == 0) {
358 int error_code = SetOsErrorMessage(os_error_message, os_error_message_len); 605 int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
359 CloseProcessPipes( 606 CloseProcessPipes(
360 stdin_handles, stdout_handles, stderr_handles, exit_handles); 607 stdin_handles, stdout_handles, stderr_handles, exit_handles);
361 return error_code; 608 return error_code;
362 } 609 }
363 610
364 ProcessInfo* process = new ProcessInfo(process_info.dwProcessId, 611 ProcessInfoList::AddProcess(process_info.dwProcessId,
365 process_info.hProcess, 612 process_info.hProcess,
366 exit_handles[kWriteHandle]); 613 exit_handles[kWriteHandle]);
367 AddProcess(process);
368
369 // TODO(sgjesse): Don't use a separate thread for waiting for each process to
370 // terminate.
371 uint32_t tid;
372 uintptr_t thread_handle =
373 _beginthreadex(NULL, 32 * 1024, TerminationWaitThread, process, 0, &tid);
374 if (thread_handle == -1) {
375 FATAL("Failed to start process termination wait thread");
376 }
377 614
378 // Connect the three std streams. 615 // Connect the three std streams.
379 FileHandle* stdin_handle = new FileHandle(stdin_handles[kWriteHandle]); 616 FileHandle* stdin_handle = new FileHandle(stdin_handles[kWriteHandle]);
380 CloseHandle(stdin_handles[kReadHandle]); 617 CloseHandle(stdin_handles[kReadHandle]);
381 FileHandle* stdout_handle = new FileHandle(stdout_handles[kReadHandle]); 618 FileHandle* stdout_handle = new FileHandle(stdout_handles[kReadHandle]);
382 CloseHandle(stdout_handles[kWriteHandle]); 619 CloseHandle(stdout_handles[kWriteHandle]);
383 FileHandle* stderr_handle = new FileHandle(stderr_handles[kReadHandle]); 620 FileHandle* stderr_handle = new FileHandle(stderr_handles[kReadHandle]);
384 CloseHandle(stderr_handles[kWriteHandle]); 621 CloseHandle(stderr_handles[kWriteHandle]);
385 FileHandle* exit_handle = new FileHandle(exit_handles[kReadHandle]); 622 FileHandle* exit_handle = new FileHandle(exit_handles[kReadHandle]);
386 *in = reinterpret_cast<intptr_t>(stdout_handle); 623 *in = reinterpret_cast<intptr_t>(stdout_handle);
387 *out = reinterpret_cast<intptr_t>(stdin_handle); 624 *out = reinterpret_cast<intptr_t>(stdin_handle);
388 *err = reinterpret_cast<intptr_t>(stderr_handle); 625 *err = reinterpret_cast<intptr_t>(stderr_handle);
389 *exit_handler = reinterpret_cast<intptr_t>(exit_handle); 626 *exit_handler = reinterpret_cast<intptr_t>(exit_handle);
390 627
391 CloseHandle(process_info.hThread); 628 CloseHandle(process_info.hThread);
392 629
393 // Return process id. 630 // Return process id.
394 *id = process->pid(); 631 *id = process_info.dwProcessId;
395 return 0; 632 return 0;
396 } 633 }
397 634
398 635
399 bool Process::Kill(intptr_t id) { 636 bool Process::Kill(intptr_t id) {
400 ProcessInfo* process = LookupProcess(id); 637 HANDLE process_handle;
401 ASSERT(process != NULL); 638 HANDLE exit_pipe;
402 if (process != NULL) { 639 bool success =
403 BOOL result = TerminateProcess(process->process_handle(), -1); 640 ProcessInfoList::LookupProcess(id, &process_handle, &exit_pipe);
404 if (result == 0) { 641 ASSERT(success);
405 return false; 642 BOOL result = TerminateProcess(process_handle, -1);
406 } 643 if (!result) {
644 return false;
407 } 645 }
408 return true; 646 return true;
409 } 647 }
410 648
411 649
412 void Process::Exit(intptr_t id) { 650 void Process::TerminateExitCodeHandler() {
413 RemoveProcess(id); 651 ExitCodeHandler::TerminateExitCodeThread();
414 } 652 }
415
416
417 void Process::TerminateExitCodeHandler() {
418 // TODO(ager): Implement.
419 }
OLDNEW
« no previous file with comments | « runtime/bin/process_macos.cc ('k') | tools/testing/bin/linux/dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698