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

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

Issue 10440044: Process exit code handling reworked for Windows. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Actually unregister wait operation Created 8 years, 6 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 | « no previous file | tests/standalone/io/process_many_script.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 "bin/thread.h"
11 #include "platform/globals.h" 11 #include "platform/globals.h"
12 12
13 static const int kReadHandle = 0; 13 static const int kReadHandle = 0;
14 static const int kWriteHandle = 1; 14 static const int kWriteHandle = 1;
15 15
16 16
17 // ProcessInfo is used to map a process id to the process handle and 17 // ProcessInfo is used to map a process id to the process handle,
18 // the pipe used to communicate the exit code of the process to Dart. 18 // wait handle for registered exit code event and the pipe used to
19 // communicate the exit code of the process to Dart.
19 // ProcessInfo objects are kept in the static singly-linked 20 // ProcessInfo objects are kept in the static singly-linked
20 // ProcessInfoList. 21 // ProcessInfoList.
21 class ProcessInfo { 22 class ProcessInfo {
22 public: 23 public:
23 ProcessInfo(DWORD process_id, HANDLE process_handle, HANDLE exit_pipe) 24 ProcessInfo(DWORD process_id,
25 HANDLE process_handle,
26 HANDLE wait_handle,
27 HANDLE exit_pipe)
24 : process_id_(process_id), 28 : process_id_(process_id),
25 process_handle_(process_handle), 29 process_handle_(process_handle),
30 wait_handle_(wait_handle),
26 exit_pipe_(exit_pipe) { } 31 exit_pipe_(exit_pipe) { }
27 32
28 ~ProcessInfo() { 33 ~ProcessInfo() {
29 BOOL success = CloseHandle(process_handle_); 34 BOOL success = CloseHandle(process_handle_);
30 if (!success) { 35 if (!success) {
31 FATAL("Failed to close process handle"); 36 FATAL("Failed to close process handle");
32 } 37 }
33 success = CloseHandle(exit_pipe_); 38 success = CloseHandle(exit_pipe_);
34 if (!success) { 39 if (!success) {
35 FATAL("Failed to close process exit code pipe"); 40 FATAL("Failed to close process exit code pipe");
36 } 41 }
37 } 42 }
38 43
39 DWORD pid() { return process_id_; } 44 DWORD pid() { return process_id_; }
40 HANDLE process_handle() { return process_handle_; } 45 HANDLE process_handle() { return process_handle_; }
46 HANDLE wait_handle() { return wait_handle_; }
41 HANDLE exit_pipe() { return exit_pipe_; } 47 HANDLE exit_pipe() { return exit_pipe_; }
42 ProcessInfo* next() { return next_; } 48 ProcessInfo* next() { return next_; }
43 void set_next(ProcessInfo* next) { next_ = next; } 49 void set_next(ProcessInfo* next) { next_ = next; }
44 50
45 private: 51 private:
46 DWORD process_id_; // Process id. 52 // Process id.
47 HANDLE process_handle_; // Process handle. 53 DWORD process_id_;
48 HANDLE exit_pipe_; // File descriptor for pipe to report exit code. 54 // Process handle.
55 HANDLE process_handle_;
56 // Wait handle identifying the exit-code wait operation registered
57 // with RegisterWaitForSingleObject.
58 HANDLE wait_handle_;
59 // File descriptor for pipe to report exit code.
60 HANDLE exit_pipe_;
61 // Link to next ProcessInfo object in the singly-linked list.
49 ProcessInfo* next_; 62 ProcessInfo* next_;
50 }; 63 };
51 64
52 65
53 // Singly-linked list of ProcessInfo objects for all active processes 66 // Singly-linked list of ProcessInfo objects for all active processes
54 // started from Dart. 67 // started from Dart.
55 class ProcessInfoList { 68 class ProcessInfoList {
56 public: 69 public:
57 static void AddProcess(DWORD pid, HANDLE handle, HANDLE pipe) { 70 static void AddProcess(DWORD pid, HANDLE handle, HANDLE pipe) {
71 // Create a wait operation for the process handle to extract
72 // the exit code.
73 HANDLE wait_handle = INVALID_HANDLE_VALUE;
74 BOOL success = RegisterWaitForSingleObject(
75 &wait_handle,
76 handle,
77 reinterpret_cast<WAITORTIMERCALLBACK>(ExitCodeCallback),
78 reinterpret_cast<void*>(pid),
79 INFINITE,
80 WT_EXECUTEONLYONCE);
81 if (!success) {
82 FATAL("Failed to register exit code wait operation.");
83 }
84 ProcessInfo* info = new ProcessInfo(pid, handle, wait_handle, pipe);
85 // Now mutate the process list under the mutex.
58 MutexLocker locker(&mutex_); 86 MutexLocker locker(&mutex_);
59 ProcessInfo* info = new ProcessInfo(pid, handle, pipe);
60 info->set_next(active_processes_); 87 info->set_next(active_processes_);
61 active_processes_ = info; 88 active_processes_ = info;
62 ++number_of_processes_;
63 BOOL success = SetEvent(GetProcessAddedEvent());
64 if (!success) {
65 FATAL("Failed to set process added event");
66 }
67 } 89 }
68 90
69 static bool LookupProcess(DWORD pid, HANDLE* handle, HANDLE* pipe) { 91 static bool LookupProcess(DWORD pid,
92 HANDLE* handle,
93 HANDLE* wait_handle,
94 HANDLE* pipe) {
70 MutexLocker locker(&mutex_); 95 MutexLocker locker(&mutex_);
71 ProcessInfo* current = active_processes_; 96 ProcessInfo* current = active_processes_;
72 while (current != NULL) { 97 while (current != NULL) {
73 if (current->pid() == pid) { 98 if (current->pid() == pid) {
74 *handle = current->process_handle(); 99 *handle = current->process_handle();
100 *wait_handle = current->wait_handle();
75 *pipe = current->exit_pipe(); 101 *pipe = current->exit_pipe();
76 return true; 102 return true;
77 } 103 }
78 current = current->next();
79 }
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(); 104 current = current->next();
93 } 105 }
94 return false; 106 return false;
95 } 107 }
96 108
97 static void RemoveProcess(DWORD pid) { 109 static void RemoveProcess(DWORD pid) {
98 MutexLocker locker(&mutex_); 110 MutexLocker locker(&mutex_);
99 ProcessInfo* prev = NULL; 111 ProcessInfo* prev = NULL;
100 ProcessInfo* current = active_processes_; 112 ProcessInfo* current = active_processes_;
101 while (current != NULL) { 113 while (current != NULL) {
102 if (current->pid() == pid) { 114 if (current->pid() == pid) {
103 if (prev == NULL) { 115 if (prev == NULL) {
104 active_processes_ = current->next(); 116 active_processes_ = current->next();
105 } else { 117 } else {
106 prev->set_next(current->next()); 118 prev->set_next(current->next());
107 } 119 }
108 delete current; 120 delete current;
109 --number_of_processes_;
110 return; 121 return;
111 } 122 }
112 prev = current; 123 prev = current;
113 current = current->next(); 124 current = current->next();
114 } 125 }
115 } 126 }
116 127
117 // Extract the process handles from the process list. The handles 128 private:
118 // array argument must have space for MAXIMUM_WAIT_OBJECTS handles. 129 // Callback called when an exit code is available from one of the
119 static DWORD GetHandleArray(HANDLE* handles, intptr_t prefix_size) { 130 // processes in the list.
120 MutexLocker locker(&mutex_); 131 static void ExitCodeCallback(void* data, bool timed_out) {
121 ASSERT(prefix_size >= 0); 132 if (timed_out) return;
122 DWORD number_of_handles = prefix_size + number_of_processes_; 133 DWORD pid = reinterpret_cast<DWORD>(data);
123 if (number_of_handles > MAXIMUM_WAIT_OBJECTS) { 134 HANDLE handle;
124 FATAL1("Only %d processes supported on Windows at this point\n", 135 HANDLE wait_handle;
125 MAXIMUM_WAIT_OBJECTS - prefix_size); 136 HANDLE exit_pipe;
137 bool success = LookupProcess(pid, &handle, &wait_handle, &exit_pipe);
138 if (!success) {
139 FATAL("Failed to lookup process in list of active processes");
126 } 140 }
127 intptr_t i = prefix_size; 141 // Unregister the event in a non-blocking way.
128 ProcessInfo* current = active_processes_; 142 BOOL ok = UnregisterWait(wait_handle);
129 while (current != NULL) { 143 if (!ok && GetLastError() != ERROR_IO_PENDING) {
130 handles[i++] = current->process_handle(); 144 FATAL("Failed unregistering wait operation");
131 current = current->next();
132 } 145 }
133 ASSERT(i == number_of_handles); 146 // Get and report the exit code to Dart.
134 // We have taken a new snapshot of the handles in the list. Reset 147 int exit_code;
135 // the process_added_event so we will get signaled if more 148 ok = GetExitCodeProcess(handle,
136 // processes are added. 149 reinterpret_cast<DWORD*>(&exit_code));
137 BOOL success = ResetEvent(GetProcessAddedEvent()); 150 if (!ok) {
138 if (!success) { 151 FATAL1("GetExitCodeProcess failed %d\n", GetLastError());
139 FATAL("Failed to reset process added event");
140 } 152 }
141 return number_of_handles; 153 int negative = 0;
154 if (exit_code < 0) {
155 exit_code = abs(exit_code);
156 negative = 1;
157 }
158 int message[2] = { exit_code, negative };
159 DWORD written;
160 ok = WriteFile(exit_pipe, message, sizeof(message), &written, NULL);
161 // If the process has been closed, the read end of the exit
162 // pipe has been closed. It is therefore not a problem that
163 // WriteFile fails with a closed pipe error
164 // (ERROR_NO_DATA). Other errors should not happen.
165 if (ok && written != sizeof(message)) {
166 FATAL("Failed to write entire process exit message");
167 } else if (!ok && GetLastError() != ERROR_NO_DATA) {
168 FATAL1("Failed to write exit code: %d", GetLastError());
169 }
170 // Remove the process from the list of active processes.
171 RemoveProcess(pid);
142 } 172 }
143 173
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 174 // Linked list of ProcessInfo objects for all active processes
159 // started from Dart code. 175 // started from Dart code.
160 static ProcessInfo* active_processes_; 176 static ProcessInfo* active_processes_;
161 // Mutex protecting all accesses to the linked list of active 177 // Mutex protecting all accesses to the linked list of active
162 // processes. 178 // processes.
163 static dart::Mutex mutex_; 179 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 }; 180 };
169 181
170 182
171 intptr_t ProcessInfoList::number_of_processes_ = 0;
172 ProcessInfo* ProcessInfoList::active_processes_ = NULL; 183 ProcessInfo* ProcessInfoList::active_processes_ = NULL;
173 dart::Mutex ProcessInfoList::mutex_; 184 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;
284 } else {
285 // The result is the index of the process that was
286 // signalled. Get its exit code and communicate it to Dart.
287 ASSERT(result < number_of_handles);
288 int exit_code;
289 BOOL ok = GetExitCodeProcess(handles[result],
290 reinterpret_cast<DWORD*>(&exit_code));
291 if (!ok) {
292 FATAL1("GetExitCodeProcess failed %d\n", GetLastError());
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_;
338 185
339 186
340 // Types of pipes to create. 187 // Types of pipes to create.
341 enum NamedPipeType { 188 enum NamedPipeType {
342 kInheritRead, 189 kInheritRead,
343 kInheritWrite, 190 kInheritWrite,
344 kInheritNone 191 kInheritNone
345 }; 192 };
346 193
347 194
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
471 const char* working_directory, 318 const char* working_directory,
472 char* environment[], 319 char* environment[],
473 intptr_t environment_length, 320 intptr_t environment_length,
474 intptr_t* in, 321 intptr_t* in,
475 intptr_t* out, 322 intptr_t* out,
476 intptr_t* err, 323 intptr_t* err,
477 intptr_t* id, 324 intptr_t* id,
478 intptr_t* exit_handler, 325 intptr_t* exit_handler,
479 char* os_error_message, 326 char* os_error_message,
480 int os_error_message_len) { 327 int os_error_message_len) {
481 // Ensure that the process exit handler thread has been started.
482 bool initialized = ExitCodeHandler::EnsureInitialized();
483 if (!initialized) {
484 int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
485 fprintf(stderr, "Failed to initialize ExitCodeHandler: %d\n", error_code);
486 return error_code;
487 }
488
489 HANDLE stdin_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; 328 HANDLE stdin_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
490 HANDLE stdout_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; 329 HANDLE stdout_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
491 HANDLE stderr_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; 330 HANDLE stderr_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
492 HANDLE exit_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; 331 HANDLE exit_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
493 332
494 // Generate unique pipe names for the four named pipes needed. 333 // Generate unique pipe names for the four named pipes needed.
495 char pipe_names[4][80]; 334 char pipe_names[4][80];
496 UUID uuid; 335 UUID uuid;
497 RPC_STATUS status = UuidCreateSequential(&uuid); 336 RPC_STATUS status = UuidCreateSequential(&uuid);
498 if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) { 337 if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) {
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after
656 CloseHandle(process_info.hThread); 495 CloseHandle(process_info.hThread);
657 496
658 // Return process id. 497 // Return process id.
659 *id = process_info.dwProcessId; 498 *id = process_info.dwProcessId;
660 return 0; 499 return 0;
661 } 500 }
662 501
663 502
664 bool Process::Kill(intptr_t id) { 503 bool Process::Kill(intptr_t id) {
665 HANDLE process_handle; 504 HANDLE process_handle;
505 HANDLE wait_handle;
666 HANDLE exit_pipe; 506 HANDLE exit_pipe;
667 bool success = 507 bool success = ProcessInfoList::LookupProcess(id,
668 ProcessInfoList::LookupProcess(id, &process_handle, &exit_pipe); 508 &process_handle,
509 &wait_handle,
510 &exit_pipe);
669 ASSERT(success); 511 ASSERT(success);
670 BOOL result = TerminateProcess(process_handle, -1); 512 BOOL result = TerminateProcess(process_handle, -1);
671 if (!result) { 513 if (!result) {
672 return false; 514 return false;
673 } 515 }
674 return true; 516 return true;
675 } 517 }
676 518
677 519
678 void Process::TerminateExitCodeHandler() { 520 void Process::TerminateExitCodeHandler() {
679 ExitCodeHandler::TerminateExitCodeThread(); 521 // Nothing needs to be done on Windows.
680 } 522 }
523
OLDNEW
« no previous file with comments | « no previous file | tests/standalone/io/process_many_script.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698