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

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

Issue 23717038: Simplify process exit-code handling on Posix, take 2. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rename and comment the code. Created 7 years, 3 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_android.cc ('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 "platform/globals.h" 5 #include "platform/globals.h"
6 #if defined(TARGET_OS_LINUX) 6 #if defined(TARGET_OS_LINUX)
7 7
8 #include "bin/process.h" 8 #include "bin/process.h"
9 9
10 #include <errno.h> // NOLINT 10 #include <errno.h> // NOLINT
11 #include <fcntl.h> // NOLINT 11 #include <fcntl.h> // NOLINT
12 #include <poll.h> // NOLINT 12 #include <poll.h> // NOLINT
13 #include <signal.h> // NOLINT
14 #include <stdio.h> // NOLINT 13 #include <stdio.h> // NOLINT
15 #include <stdlib.h> // NOLINT 14 #include <stdlib.h> // NOLINT
16 #include <string.h> // NOLINT 15 #include <string.h> // NOLINT
17 #include <sys/wait.h> // NOLINT 16 #include <sys/wait.h> // NOLINT
18 #include <unistd.h> // NOLINT 17 #include <unistd.h> // NOLINT
19 18
20 #include "bin/fdutils.h" 19 #include "bin/fdutils.h"
21 #include "bin/log.h" 20 #include "bin/log.h"
22 #include "bin/thread.h" 21 #include "bin/thread.h"
23 22
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 // Mutex protecting all accesses to the linked list of active 103 // Mutex protecting all accesses to the linked list of active
105 // processes. 104 // processes.
106 static dart::Mutex* mutex_; 105 static dart::Mutex* mutex_;
107 }; 106 };
108 107
109 108
110 ProcessInfo* ProcessInfoList::active_processes_ = NULL; 109 ProcessInfo* ProcessInfoList::active_processes_ = NULL;
111 dart::Mutex* ProcessInfoList::mutex_ = new dart::Mutex(); 110 dart::Mutex* ProcessInfoList::mutex_ = new dart::Mutex();
112 111
113 112
114 // The exit code handler sets up a separate thread which is signalled 113 // The exit code handler sets up a separate thread which waits for child
115 // on SIGCHLD. That separate thread can then get the exit code from 114 // processes to die. That separate thread can then get the exit code from
Søren Gjesse 2013/09/11 08:23:48 s/die/terminate
Anders Johnsen 2013/09/11 11:54:52 Done.
116 // processes that have exited and communicate it to Dart through the 115 // processes that have exited and communicate it to Dart through the
117 // event loop. 116 // event loop.
118 class ExitCodeHandler { 117 class ExitCodeHandler {
119 public: 118 public:
120 // Ensure that the ExitCodeHandler has been initialized. 119 // Ensure that the ExitCodeHandler has been initialized.
121 static bool EnsureInitialized() { 120 static bool EnsureInitialized() {
Søren Gjesse 2013/09/11 08:23:48 No need for a return value as it always returns tr
Anders Johnsen 2013/09/11 11:54:52 Done.
122 // Multiple isolates could be starting processes at the same 121 // Multiple isolates could be starting processes at the same
123 // time. Make sure that only one of them initializes the 122 // time. Make sure that only one ExitCodeHandler thread exists.
124 // ExitCodeHandler.
125 MutexLocker locker(mutex_); 123 MutexLocker locker(mutex_);
126 if (initialized_) { 124 if (running_) {
127 return true; 125 return true;
128 } 126 }
129 127
130 // Allocate a pipe that the signal handler can write a byte to and 128 // Start thread that handles process exits when waitpid returns.
Søren Gjesse 2013/09/11 08:23:48 s/waitpid/wait
Anders Johnsen 2013/09/11 11:54:52 Done.
131 // that the exit handler thread can poll. 129 int result = dart::Thread::Start(ExitCodeHandlerEntry, 0);
132 int result = TEMP_FAILURE_RETRY(pipe(sig_chld_fds_));
133 if (result < 0) {
134 return false;
135 }
136 FDUtils::SetCloseOnExec(sig_chld_fds_[0]);
137 FDUtils::SetCloseOnExec(sig_chld_fds_[1]);
138
139 // Start thread that polls the pipe and handles process exits when
140 // data is received on the pipe.
141 result = dart::Thread::Start(ExitCodeHandlerEntry, sig_chld_fds_[0]);
142 if (result != 0) { 130 if (result != 0) {
143 FATAL1("Failed to start exit code handler worker thread %d", result); 131 FATAL1("Failed to start exit code handler worker thread %d", result);
144 } 132 }
145 133
146 // Mark write end non-blocking. 134 // Thread started and the ExitCodeHandler is running.
147 FDUtils::SetNonBlocking(sig_chld_fds_[1]); 135 running_ = true;
148
149 // Thread started and the ExitCodeHandler is initialized.
150 initialized_ = true;
151 return true; 136 return true;
152 } 137 }
153 138
154 // Get the write end of the pipe.
155 static int WakeUpFd() {
156 return sig_chld_fds_[1];
157 }
158
159 static void TerminateExitCodeThread() { 139 static void TerminateExitCodeThread() {
160 MutexLocker locker(mutex_); 140 MutexLocker locker(mutex_);
161 if (!initialized_) { 141 if (!running_) {
162 return; 142 return;
163 } 143 }
164 144
165 uint8_t data = kThreadTerminateByte; 145 thread_terminate_monitor_->Enter();
166 ssize_t result = 146
167 TEMP_FAILURE_RETRY(write(ExitCodeHandler::WakeUpFd(), &data, 1)); 147 // Set terminate_done_ to false, so we can use it as a guard for our
168 if (result < 1) { 148 // monitor (note that this is done before setting running_ to false).
169 perror("Failed to write to wake-up fd to terminate exit code thread"); 149 terminate_done_ = false;
150 running_ = false;
151
152 // Fork to wake up waitpid.
153 if (TEMP_FAILURE_RETRY(fork()) == 0) {
154 exit(0);
170 } 155 }
171 156
172 { 157 while (!terminate_done_) {
173 MonitorLocker terminate_locker(thread_terminate_monitor_); 158 thread_terminate_monitor_->Wait(dart::Monitor::kNoTimeout);
174 while (!thread_terminated_) {
175 terminate_locker.Wait();
176 }
177 } 159 }
178 } 160 thread_terminate_monitor_->Exit();
179
180 static void ExitCodeThreadTerminated() {
181 MonitorLocker locker(thread_terminate_monitor_);
182 thread_terminated_ = true;
183 locker.Notify();
184 } 161 }
185 162
186 private: 163 private:
187 static const uint8_t kThreadTerminateByte = 1;
188
189 // GetProcessExitCodes is called on a separate thread when a SIGCHLD
190 // signal is received to retrieve the exit codes and post them to
191 // dart.
192 static void GetProcessExitCodes() {
193 pid_t pid = 0;
194 int status = 0;
195 while ((pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG))) > 0) {
196 int exit_code = 0;
197 int negative = 0;
198 if (WIFEXITED(status)) {
199 exit_code = WEXITSTATUS(status);
200 }
201 if (WIFSIGNALED(status)) {
202 exit_code = WTERMSIG(status);
203 negative = 1;
204 }
205 intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid);
206 if (exit_code_fd != 0) {
207 int message[2] = { exit_code, negative };
208 ssize_t result =
209 FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
210 // If the process has been closed, the read end of the exit
211 // pipe has been closed. It is therefore not a problem that
212 // write fails with a broken pipe error. Other errors should
213 // not happen.
214 if (result != -1 && result != sizeof(message)) {
215 FATAL("Failed to write entire process exit message");
216 } else if (result == -1 && errno != EPIPE) {
217 FATAL1("Failed to write exit code: %d", errno);
218 }
219 ProcessInfoList::RemoveProcess(pid);
220 }
221 }
222 }
223
224
225 // Entry point for the separate exit code handler thread started by 164 // Entry point for the separate exit code handler thread started by
226 // the ExitCodeHandler. 165 // the ExitCodeHandler.
227 static void ExitCodeHandlerEntry(uword param) { 166 static void ExitCodeHandlerEntry(uword param) {
228 struct pollfd pollfds; 167 pid_t pid = 0;
229 pollfds.fd = param; 168 int status = 0;
230 pollfds.events = POLLIN; 169 while (running_) {
Søren Gjesse 2013/09/11 08:23:48 We should not check running_ without holding the l
Anders Johnsen 2013/09/11 11:54:52 Done.
231 while (true) { 170 if ((pid = TEMP_FAILURE_RETRY(wait(&status))) > 0) {
232 int result = TEMP_FAILURE_RETRY(poll(&pollfds, 1, -1)); 171 int exit_code = 0;
233 if (result == -1) { 172 int negative = 0;
234 ASSERT(EAGAIN == EWOULDBLOCK); 173 if (WIFEXITED(status)) {
235 if (errno != EWOULDBLOCK) { 174 exit_code = WEXITSTATUS(status);
236 perror("ExitCodeHandler poll failed");
237 } 175 }
238 } else { 176 if (WIFSIGNALED(status)) {
239 // Read the byte from the wake-up fd. 177 exit_code = WTERMSIG(status);
240 ASSERT(result = 1); 178 negative = 1;
241 intptr_t data = 0;
242 ssize_t read_bytes = FDUtils::ReadFromBlocking(pollfds.fd, &data, 1);
243 if (read_bytes < 1) {
244 perror("Failed to read from wake-up fd in exit-code handler");
245 } 179 }
246 if (data == ExitCodeHandler::kThreadTerminateByte) { 180 intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid);
247 ExitCodeThreadTerminated(); 181 if (exit_code_fd != 0) {
182 int message[2] = { exit_code, negative };
183 ssize_t result =
184 FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
185 // If the process has been closed, the read end of the exit
186 // pipe has been closed. It is therefore not a problem that
187 // write fails with a broken pipe error. Other errors should
188 // not happen.
189 if (result != -1 && result != sizeof(message)) {
190 FATAL("Failed to write entire process exit message");
191 } else if (result == -1 && errno != EPIPE) {
192 FATAL1("Failed to write exit code: %d", errno);
193 }
194 ProcessInfoList::RemoveProcess(pid);
195 }
196 } else if (errno == ECHILD) {
197 if (mutex_->TryLock()) {
198 // We got the lock. This means that we are not in the process of
199 // shutting down (and not will since we have the lock). We can thus do
200 // a quick exit by setting running_ to false, unlock and return (exit
201 // the thread).
202 running_ = false;
203 mutex_->Unlock();
248 return; 204 return;
249 } 205 }
250 // Get the exit code from all processes that have died. 206 // If we didn't get the lock, we are trying to terminate. Continue here
251 GetProcessExitCodes(); 207 // as that will ensure we reach our monitor notify.
252 } 208 }
253 } 209 }
210 thread_terminate_monitor_->Enter();
211 terminate_done_ = true;
212 thread_terminate_monitor_->Notify();
213 thread_terminate_monitor_->Exit();
254 } 214 }
255 215
256 static dart::Mutex* mutex_; 216 static dart::Mutex* mutex_;
Søren Gjesse 2013/09/11 08:23:48 Can't we just get away with one monitor protecting
Anders Johnsen 2013/09/11 11:54:52 Rewrote to only have one monitor in total.
257 static bool initialized_; 217 static bool running_;
258 static int sig_chld_fds_[2]; 218 static bool terminate_done_;
259 static bool thread_terminated_;
260 static dart::Monitor* thread_terminate_monitor_; 219 static dart::Monitor* thread_terminate_monitor_;
261 }; 220 };
262 221
263 222
264 dart::Mutex* ExitCodeHandler::mutex_ = new dart::Mutex(); 223 dart::Mutex* ExitCodeHandler::mutex_ = new dart::Mutex();
265 bool ExitCodeHandler::initialized_ = false; 224 bool ExitCodeHandler::running_ = false;
266 int ExitCodeHandler::sig_chld_fds_[2] = { 0, 0 }; 225 bool ExitCodeHandler::terminate_done_ = false;
267 bool ExitCodeHandler::thread_terminated_ = false;
268 dart::Monitor* ExitCodeHandler::thread_terminate_monitor_ = new dart::Monitor(); 226 dart::Monitor* ExitCodeHandler::thread_terminate_monitor_ = new dart::Monitor();
269 227
270 228
271 static void SetChildOsErrorMessage(char** os_error_message) { 229 static void SetChildOsErrorMessage(char** os_error_message) {
272 const int kBufferSize = 1024; 230 const int kBufferSize = 1024;
273 char error_buf[kBufferSize]; 231 char error_buf[kBufferSize];
274 *os_error_message = strdup(strerror_r(errno, error_buf, kBufferSize)); 232 *os_error_message = strdup(strerror_r(errno, error_buf, kBufferSize));
275 } 233 }
276 234
277 235
278 static void SigChldHandler(int process_signal, siginfo_t* siginfo, void* tmp) {
279 // Save errno so it can be restored at the end.
280 int entry_errno = errno;
281 // Signal the exit code handler where the actual processing takes
282 // place.
283 ssize_t result =
284 TEMP_FAILURE_RETRY(write(ExitCodeHandler::WakeUpFd(), "", 1));
285 if (result < 1) {
286 perror("Failed to write to wake-up fd in SIGCHLD handler");
287 }
288 // Restore errno.
289 errno = entry_errno;
290 }
291
292
293 static void ReportChildError(int exec_control_fd) { 236 static void ReportChildError(int exec_control_fd) {
294 // 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
295 // the OS error message to the exec control pipe and exit. 238 // the OS error message to the exec control pipe and exit.
296 int child_errno = errno; 239 int child_errno = errno;
297 const int kBufferSize = 1024; 240 const int kBufferSize = 1024;
298 char error_buf[kBufferSize]; 241 char error_buf[kBufferSize];
299 char* os_error_message = strerror_r(errno, error_buf, kBufferSize); 242 char* os_error_message = strerror_r(errno, error_buf, kBufferSize);
300 ASSERT(sizeof(child_errno) == sizeof(errno)); 243 ASSERT(sizeof(child_errno) == sizeof(errno));
301 int bytes_written = 244 int bytes_written =
302 FDUtils::WriteToBlocking( 245 FDUtils::WriteToBlocking(
(...skipping 19 matching lines...) Expand all
322 intptr_t* id, 265 intptr_t* id,
323 intptr_t* exit_event, 266 intptr_t* exit_event,
324 char** os_error_message) { 267 char** os_error_message) {
325 pid_t pid; 268 pid_t pid;
326 int read_in[2]; // Pipe for stdout to child process. 269 int read_in[2]; // Pipe for stdout to child process.
327 int read_err[2]; // Pipe for stderr to child process. 270 int read_err[2]; // Pipe for stderr to child process.
328 int write_out[2]; // Pipe for stdin to child process. 271 int write_out[2]; // Pipe for stdin to child process.
329 int exec_control[2]; // Pipe to get the result from exec. 272 int exec_control[2]; // Pipe to get the result from exec.
330 int result; 273 int result;
331 274
332 bool initialized = ExitCodeHandler::EnsureInitialized();
333 if (!initialized) {
334 SetChildOsErrorMessage(os_error_message);
335 Log::PrintErr("Error initializing exit code handler: %s\n",
336 *os_error_message);
337 return errno;
338 }
339
340 result = TEMP_FAILURE_RETRY(pipe(read_in)); 275 result = TEMP_FAILURE_RETRY(pipe(read_in));
341 if (result < 0) { 276 if (result < 0) {
342 SetChildOsErrorMessage(os_error_message); 277 SetChildOsErrorMessage(os_error_message);
343 Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); 278 Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
344 return errno; 279 return errno;
345 } 280 }
346 FDUtils::SetCloseOnExec(read_in[0]); 281 FDUtils::SetCloseOnExec(read_in[0]);
347 282
348 result = TEMP_FAILURE_RETRY(pipe(read_err)); 283 result = TEMP_FAILURE_RETRY(pipe(read_err));
349 if (result < 0) { 284 if (result < 0) {
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
405 340
406 char** program_environment = NULL; 341 char** program_environment = NULL;
407 if (environment != NULL) { 342 if (environment != NULL) {
408 program_environment = new char*[environment_length + 1]; 343 program_environment = new char*[environment_length + 1];
409 for (int i = 0; i < environment_length; i++) { 344 for (int i = 0; i < environment_length; i++) {
410 program_environment[i] = environment[i]; 345 program_environment[i] = environment[i];
411 } 346 }
412 program_environment[environment_length] = NULL; 347 program_environment[environment_length] = NULL;
413 } 348 }
414 349
415 struct sigaction act;
416 bzero(&act, sizeof(act));
417 act.sa_sigaction = SigChldHandler;
418 act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
419 if (sigaction(SIGCHLD, &act, 0) != 0) {
420 perror("Process start: setting signal handler failed");
421 }
422 pid = TEMP_FAILURE_RETRY(fork()); 350 pid = TEMP_FAILURE_RETRY(fork());
423 if (pid < 0) { 351 if (pid < 0) {
424 SetChildOsErrorMessage(os_error_message); 352 SetChildOsErrorMessage(os_error_message);
425 delete[] program_arguments; 353 delete[] program_arguments;
426 TEMP_FAILURE_RETRY(close(read_in[0])); 354 TEMP_FAILURE_RETRY(close(read_in[0]));
427 TEMP_FAILURE_RETRY(close(read_in[1])); 355 TEMP_FAILURE_RETRY(close(read_in[1]));
428 TEMP_FAILURE_RETRY(close(read_err[0])); 356 TEMP_FAILURE_RETRY(close(read_err[0]));
429 TEMP_FAILURE_RETRY(close(read_err[1])); 357 TEMP_FAILURE_RETRY(close(read_err[1]));
430 TEMP_FAILURE_RETRY(close(write_out[0])); 358 TEMP_FAILURE_RETRY(close(write_out[0]));
431 TEMP_FAILURE_RETRY(close(write_out[1])); 359 TEMP_FAILURE_RETRY(close(write_out[1]));
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
555 FDUtils::SetNonBlocking(read_in[0]); 483 FDUtils::SetNonBlocking(read_in[0]);
556 *in = read_in[0]; 484 *in = read_in[0];
557 TEMP_FAILURE_RETRY(close(read_in[1])); 485 TEMP_FAILURE_RETRY(close(read_in[1]));
558 FDUtils::SetNonBlocking(write_out[1]); 486 FDUtils::SetNonBlocking(write_out[1]);
559 *out = write_out[1]; 487 *out = write_out[1];
560 TEMP_FAILURE_RETRY(close(write_out[0])); 488 TEMP_FAILURE_RETRY(close(write_out[0]));
561 FDUtils::SetNonBlocking(read_err[0]); 489 FDUtils::SetNonBlocking(read_err[0]);
562 *err = read_err[0]; 490 *err = read_err[0];
563 TEMP_FAILURE_RETRY(close(read_err[1])); 491 TEMP_FAILURE_RETRY(close(read_err[1]));
564 492
493 // Be sure to listen for exit-codes, now we have a child-process.
494 bool initialized = ExitCodeHandler::EnsureInitialized();
495 if (!initialized) {
496 SetChildOsErrorMessage(os_error_message);
497 Log::PrintErr("Error initializing exit code handler: %s\n",
498 *os_error_message);
499 return errno;
500 }
501
565 *id = pid; 502 *id = pid;
566 return 0; 503 return 0;
567 } 504 }
568 505
569 506
570 class BufferList: public BufferListBase { 507 class BufferList: public BufferListBase {
571 public: 508 public:
572 bool Read(int fd, intptr_t available) { 509 bool Read(int fd, intptr_t available) {
573 // Read all available bytes. 510 // Read all available bytes.
574 while (available > 0) { 511 while (available > 0) {
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
695 632
696 633
697 intptr_t Process::CurrentProcessId() { 634 intptr_t Process::CurrentProcessId() {
698 return static_cast<intptr_t>(getpid()); 635 return static_cast<intptr_t>(getpid());
699 } 636 }
700 637
701 } // namespace bin 638 } // namespace bin
702 } // namespace dart 639 } // namespace dart
703 640
704 #endif // defined(TARGET_OS_LINUX) 641 #endif // defined(TARGET_OS_LINUX)
OLDNEW
« no previous file with comments | « runtime/bin/process_android.cc ('k') | runtime/bin/process_macos.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698