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

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

Issue 9293030: Move actual work out of the SIGCHLD signal handler for the dart:io (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix typo 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
« runtime/bin/process_linux.cc ('K') | « runtime/bin/process_linux.cc ('k') | no next file » | 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 "bin/process.h" 5 #include "bin/process.h"
6 6
7 #include <errno.h> 7 #include <errno.h>
8 #include <fcntl.h> 8 #include <fcntl.h>
9 #include <poll.h>
9 #include <signal.h> 10 #include <signal.h>
10 #include <stdio.h> 11 #include <stdio.h>
11 #include <stdlib.h> 12 #include <stdlib.h>
12 #include <string.h> 13 #include <string.h>
13 #include <unistd.h> 14 #include <unistd.h>
14 15
15 #include "bin/fdutils.h" 16 #include "bin/fdutils.h"
17 #include "bin/thread.h"
16 18
17 19
20 // ProcessInfo is used to map a process id to the file descriptor for
21 // the pipe used to communicate the exit code of the process to Dart.
22 // ProcessInfo objects are kept in the static singly-linked
23 // ProcessInfoList.
18 class ProcessInfo { 24 class ProcessInfo {
19 public: 25 public:
20 ProcessInfo(pid_t pid, intptr_t fd) : pid_(pid), fd_(fd) { } 26 ProcessInfo(pid_t pid, intptr_t fd) : pid_(pid), fd_(fd) { }
21
22 pid_t pid() { return pid_; } 27 pid_t pid() { return pid_; }
23 intptr_t fd() { return fd_; } 28 intptr_t fd() { return fd_; }
24 ProcessInfo* next() { return next_; } 29 ProcessInfo* next() { return next_; }
25 void set_next(ProcessInfo* next) { next_ = next; } 30 void set_next(ProcessInfo* info) { next_ = info; }
26 31
27 private: 32 private:
28 pid_t pid_; // Process pid. 33 pid_t pid_;
29 intptr_t fd_; // File descriptor for pipe to report exit code. 34 intptr_t fd_;
30 ProcessInfo* next_; 35 ProcessInfo* next_;
31 }; 36 };
32 37
33 38
34 ProcessInfo* active_processes = NULL; 39 // Singly-linked list of ProcessInfo objects for all active processes
40 // started from Dart.
41 class ProcessInfoList {
42 public:
43 static void AddProcess(pid_t pid, intptr_t fd) {
44 MutexLocker locker(&mutex_);
45 ProcessInfo* info = new ProcessInfo(pid, fd);
46 info->set_next(active_processes_);
47 active_processes_ = info;
48 }
35 49
36 50
37 static void AddProcess(ProcessInfo* process) { 51 static intptr_t LookupProcessExitFd(pid_t pid) {
38 process->set_next(active_processes); 52 MutexLocker locker(&mutex_);
39 active_processes = process; 53 ProcessInfo* current = active_processes_;
40 } 54 while (current != NULL) {
55 if (current->pid() == pid) {
56 return current->fd();
57 }
58 current = current->next();
59 }
60 return 0;
61 }
41 62
42 63
43 static ProcessInfo* LookupProcess(pid_t pid) { 64 static void RemoveProcess(pid_t pid) {
44 ProcessInfo* current = active_processes; 65 MutexLocker locker(&mutex_);
45 while (current != NULL) { 66 ProcessInfo* prev = NULL;
46 if (current->pid() == pid) { 67 ProcessInfo* current = active_processes_;
47 return current; 68 while (current != NULL) {
69 if (current->pid() == pid) {
70 if (prev == NULL) {
71 active_processes_ = current->next();
72 } else {
73 prev->set_next(current->next());
74 }
75 delete current;
76 return;
77 }
78 prev = current;
79 current = current->next();
48 } 80 }
49 current = current->next();
50 } 81 }
51 return NULL; 82
52 } 83 private:
84 // Linked list of ProcessInfo objects for all active processes
85 // started from Dart code.
86 static ProcessInfo* active_processes_;
87 // Mutex protecting all accesses to the linked list of active
88 // processes.
89 static dart::Mutex mutex_;
90 };
53 91
54 92
55 static void RemoveProcess(pid_t pid) { 93 ProcessInfo* ProcessInfoList::active_processes_ = NULL;
56 ProcessInfo* prev = NULL; 94 dart::Mutex ProcessInfoList::mutex_;
57 ProcessInfo* current = active_processes; 95
58 while (current != NULL) { 96
59 if (current->pid() == pid) { 97 // The exit code handler sets up a separate thread which is signalled
60 if (prev == NULL) { 98 // on SIGCHLD. That separate thread can then get the exit code from
61 active_processes = current->next(); 99 // processes that have exited and communicate it to Dart through the
100 // event loop.
101 class ExitCodeHandler {
102 public:
103 // Ensure that the ExitCodeHandler has been initialized.
104 static bool EnsureInitialized() {
105 // Multiple isolates could be starting processes at the same
106 // time. Make sure that only one of them initializes the
107 // ExitCodeHandler.
108 MutexLocker locker(&mutex_);
109 if (initialized_) {
110 return true;
111 }
112
113 // Allocate a pipe that the signal handler can write a byte to and
114 // that the exit handler thread can poll.
115 int result = TEMP_FAILURE_RETRY(pipe(sig_chld_fds_));
116 if (result < 0) {
117 return false;
118 }
119
120 // Start thread that polls the pipe and handles process exits when
121 // data is received on the pipe.
122 new dart::Thread(ExitCodeHandlerEntry, sig_chld_fds_[0]);
123
124 // Mark write end non-blocking.
125 FDUtils::SetNonBlocking(sig_chld_fds_[1]);
126
127 // Thread started and the ExitCodeHandler is initialized.
128 initialized_ = true;
129 return true;
130 }
131
132 // Get the write end of the pipe.
133 static int WakeUpFd() {
134 ASSERT(initialized_);
135 return sig_chld_fds_[1];
136 }
137
138 private:
139 // GetProcessExitCodes is called on a separate thread when a SIGCHLD
140 // signal is received to retrieve the exit codes and post them to
141 // dart.
142 static void GetProcessExitCodes() {
143 pid_t pid = 0;
144 int status = 0;
145 while ((pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG))) > 0) {
146 int exit_code = 0;
147 int negative = 0;
148 if (WIFEXITED(status)) {
149 exit_code = WEXITSTATUS(status);
150 }
151 if (WIFSIGNALED(status)) {
152 exit_code = WTERMSIG(status);
153 negative = 1;
154 }
155 intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid);
156 if (exit_code_fd != 0) {
157 int message[3] = { pid, exit_code, negative };
158 ssize_t result =
159 FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
160 if (result != sizeof(message) && errno != EPIPE) {
161 perror("ExitHandler notification failed");
162 }
163 TEMP_FAILURE_RETRY(close(exit_code_fd));
164 }
165 }
166 }
167
168
169 // Entry point for the separate exit code handler thread started by
170 // the ExitCodeHandler.
171 static void ExitCodeHandlerEntry(uword param) {
172 struct pollfd pollfds;
173 pollfds.fd = param;
174 pollfds.events |= POLLIN;
175 while (true) {
176 int result = TEMP_FAILURE_RETRY(poll(&pollfds, 1, -1));
177 if (result == -1) {
178 ASSERT(EAGAIN == EWOULDBLOCK);
179 if (errno != EWOULDBLOCK) {
180 perror("ExitCodeHandler poll failed");
181 }
62 } else { 182 } else {
63 prev->set_next(current->next()); 183 // Read the byte from the wake-up fd.
184 ASSERT(result = 1);
185 intptr_t data = 0;
186 ssize_t read_bytes = FDUtils::ReadFromBlocking(pollfds.fd, &data, 1);
187 if (read_bytes < 1) {
188 perror("Failed to read from wake-up fd in exit-code handler");
189 }
190 // Get the exit code from all processes that have died.
191 GetProcessExitCodes();
64 } 192 }
65 delete current;
66 return;
67 } 193 }
68 prev = current;
69 current = current->next();
70 } 194 }
71 } 195
196 static dart::Mutex mutex_;
197 static bool initialized_;
198 static int sig_chld_fds_[2];
199 };
200
201
202 dart::Mutex ExitCodeHandler::mutex_;
203 bool ExitCodeHandler::initialized_ = false;
204 int ExitCodeHandler::sig_chld_fds_[2] = { 0, 0 };
72 205
73 206
74 static char* SafeStrNCpy(char* dest, const char* src, size_t n) { 207 static char* SafeStrNCpy(char* dest, const char* src, size_t n) {
75 strncpy(dest, src, n); 208 strncpy(dest, src, n);
76 dest[n - 1] = '\0'; 209 dest[n - 1] = '\0';
77 return dest; 210 return dest;
78 } 211 }
79 212
80 213
81 static void SetChildOsErrorMessage(char* os_error_message, 214 static void SetChildOsErrorMessage(char* os_error_message,
82 int os_error_message_len) { 215 int os_error_message_len) {
83 SafeStrNCpy(os_error_message, strerror(errno), os_error_message_len); 216 SafeStrNCpy(os_error_message, strerror(errno), os_error_message_len);
84 } 217 }
85 218
86 219
87 void ExitHandler(int process_signal, siginfo_t* siginfo, void* tmp) { 220 static void SigChldHandler(int process_signal, siginfo_t* siginfo, void* tmp) {
88 int pid = 0;
89 int status = 0;
90 // Save errno so it can be restored at the end. 221 // Save errno so it can be restored at the end.
91 int entry_errno = errno; 222 int entry_errno = errno;
92 while ((pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG))) > 0) { 223 // Signal the exit code handler where the actual processing takes
93 int exit_code = 0; 224 // place.
94 int negative = 0; 225 ssize_t result =
95 if (WIFEXITED(status)) { 226 TEMP_FAILURE_RETRY(write(ExitCodeHandler::WakeUpFd(), "", 1));
96 exit_code = WEXITSTATUS(status); 227 if (result < 1) {
97 } 228 perror("Failed to write to wake-up fd in SIGCHLD handler");
98 if (WIFSIGNALED(status)) {
99 exit_code = WTERMSIG(status);
100 negative = 1;
101 }
102 // Lookup the process and extract all needed information from
103 // it. The WriteToBlocking call below can cause the deletion of
104 // the process object (because this signal handler can be running
105 // on an arbitrary thread, not just the main thread) so we cannot
106 // touch it after that call.
107 ProcessInfo* process = LookupProcess(pid);
108 intptr_t exit_code_fd = process->fd();
109 if (process != NULL) {
110 int message[3] = { pid, exit_code, negative };
111 intptr_t result =
112 FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
113 if (result != sizeof(message) && errno != EPIPE) {
114 perror("ExitHandler notification failed");
115 }
116 TEMP_FAILURE_RETRY(close(exit_code_fd));
117 }
118 } 229 }
230 // Restore errno.
119 errno = entry_errno; 231 errno = entry_errno;
120 } 232 }
121 233
122 234
123 static void ReportChildError(int exec_control_fd) { 235 static void ReportChildError(int exec_control_fd) {
124 // In the case of failure in the child process write the errno and 236 // In the case of failure in the child process write the errno and
125 // the OS error message to the exec control pipe and exit. 237 // the OS error message to the exec control pipe and exit.
126 int child_errno = errno; 238 int child_errno = errno;
127 char* os_error_message = strerror(errno); 239 char* os_error_message = strerror(errno);
128 ASSERT(sizeof(child_errno) == sizeof(errno)); 240 ASSERT(sizeof(child_errno) == sizeof(errno));
(...skipping 20 matching lines...) Expand all
149 intptr_t* exit_event, 261 intptr_t* exit_event,
150 char* os_error_message, 262 char* os_error_message,
151 int os_error_message_len) { 263 int os_error_message_len) {
152 pid_t pid; 264 pid_t pid;
153 int read_in[2]; // Pipe for stdout to child process. 265 int read_in[2]; // Pipe for stdout to child process.
154 int read_err[2]; // Pipe for stderr to child process. 266 int read_err[2]; // Pipe for stderr to child process.
155 int write_out[2]; // Pipe for stdin to child process. 267 int write_out[2]; // Pipe for stdin to child process.
156 int exec_control[2]; // Pipe to get the result from exec. 268 int exec_control[2]; // Pipe to get the result from exec.
157 int result; 269 int result;
158 270
271 bool initialized = ExitCodeHandler::EnsureInitialized();
272 if (!initialized) {
273 SetChildOsErrorMessage(os_error_message, os_error_message_len);
274 fprintf(stderr,
275 "Error initializing exit code handler: %s\n",
276 os_error_message);
277 return errno;
278 }
279
159 result = TEMP_FAILURE_RETRY(pipe(read_in)); 280 result = TEMP_FAILURE_RETRY(pipe(read_in));
160 if (result < 0) { 281 if (result < 0) {
161 SetChildOsErrorMessage(os_error_message, os_error_message_len); 282 SetChildOsErrorMessage(os_error_message, os_error_message_len);
162 fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); 283 fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message);
163 return errno; 284 return errno;
164 } 285 }
165 286
166 result = TEMP_FAILURE_RETRY(pipe(read_err)); 287 result = TEMP_FAILURE_RETRY(pipe(read_err));
167 if (result < 0) { 288 if (result < 0) {
168 SetChildOsErrorMessage(os_error_message, os_error_message_len); 289 SetChildOsErrorMessage(os_error_message, os_error_message_len);
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
217 338
218 char** program_arguments = new char*[arguments_length + 2]; 339 char** program_arguments = new char*[arguments_length + 2];
219 program_arguments[0] = const_cast<char *>(path); 340 program_arguments[0] = const_cast<char *>(path);
220 for (int i = 0; i < arguments_length; i++) { 341 for (int i = 0; i < arguments_length; i++) {
221 program_arguments[i + 1] = arguments[i]; 342 program_arguments[i + 1] = arguments[i];
222 } 343 }
223 program_arguments[arguments_length + 1] = NULL; 344 program_arguments[arguments_length + 1] = NULL;
224 345
225 struct sigaction act; 346 struct sigaction act;
226 bzero(&act, sizeof(act)); 347 bzero(&act, sizeof(act));
227 act.sa_sigaction = ExitHandler; 348 act.sa_sigaction = SigChldHandler;
228 act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO; 349 act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
229 if (sigaction(SIGCHLD, &act, 0) != 0) { 350 if (sigaction(SIGCHLD, &act, 0) != 0) {
230 perror("Process start: setting signal handler failed"); 351 perror("Process start: setting signal handler failed");
231 } 352 }
232 pid = TEMP_FAILURE_RETRY(fork()); 353 pid = TEMP_FAILURE_RETRY(fork());
233 if (pid < 0) { 354 if (pid < 0) {
234 SetChildOsErrorMessage(os_error_message, os_error_message_len); 355 SetChildOsErrorMessage(os_error_message, os_error_message_len);
235 delete[] program_arguments; 356 delete[] program_arguments;
236 TEMP_FAILURE_RETRY(close(read_in[0])); 357 TEMP_FAILURE_RETRY(close(read_in[0]));
237 TEMP_FAILURE_RETRY(close(read_in[1])); 358 TEMP_FAILURE_RETRY(close(read_in[1]));
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
291 TEMP_FAILURE_RETRY(close(read_in[0])); 412 TEMP_FAILURE_RETRY(close(read_in[0]));
292 TEMP_FAILURE_RETRY(close(read_in[1])); 413 TEMP_FAILURE_RETRY(close(read_in[1]));
293 TEMP_FAILURE_RETRY(close(read_err[0])); 414 TEMP_FAILURE_RETRY(close(read_err[0]));
294 TEMP_FAILURE_RETRY(close(read_err[1])); 415 TEMP_FAILURE_RETRY(close(read_err[1]));
295 TEMP_FAILURE_RETRY(close(write_out[0])); 416 TEMP_FAILURE_RETRY(close(write_out[0]));
296 TEMP_FAILURE_RETRY(close(write_out[1])); 417 TEMP_FAILURE_RETRY(close(write_out[1]));
297 fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); 418 fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message);
298 return errno; 419 return errno;
299 } 420 }
300 421
301 ProcessInfo* process = new ProcessInfo(pid, event_fds[1]); 422 ProcessInfoList::AddProcess(pid, event_fds[1]);
302 AddProcess(process);
303 *exit_event = event_fds[0]; 423 *exit_event = event_fds[0];
304 FDUtils::SetNonBlocking(event_fds[0]); 424 FDUtils::SetNonBlocking(event_fds[0]);
305 425
306 // Notify child process to start. 426 // Notify child process to start.
307 char msg = '1'; 427 char msg = '1';
308 result = FDUtils::WriteToBlocking(read_in[1], &msg, sizeof(msg)); 428 result = FDUtils::WriteToBlocking(read_in[1], &msg, sizeof(msg));
309 if (result != sizeof(msg)) { 429 if (result != sizeof(msg)) {
310 perror("Failed sending notification message"); 430 perror("Failed sending notification message");
311 } 431 }
312 432
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 bool Process::Kill(intptr_t id) { 481 bool Process::Kill(intptr_t id) {
362 int result = TEMP_FAILURE_RETRY(kill(id, SIGKILL)); 482 int result = TEMP_FAILURE_RETRY(kill(id, SIGKILL));
363 if (result == -1) { 483 if (result == -1) {
364 return false; 484 return false;
365 } 485 }
366 return true; 486 return true;
367 } 487 }
368 488
369 489
370 void Process::Exit(intptr_t id) { 490 void Process::Exit(intptr_t id) {
371 RemoveProcess(id); 491 ProcessInfoList::RemoveProcess(id);
372 } 492 }
OLDNEW
« runtime/bin/process_linux.cc ('K') | « runtime/bin/process_linux.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698