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

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

Powered by Google App Engine
This is Rietveld 408576698