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

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

Issue 23536032: Revert "Simplify process exit-code handling on Posix." (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: 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
13 #include <stdio.h> // NOLINT 14 #include <stdio.h> // NOLINT
14 #include <stdlib.h> // NOLINT 15 #include <stdlib.h> // NOLINT
15 #include <string.h> // NOLINT 16 #include <string.h> // NOLINT
16 #include <sys/wait.h> // NOLINT 17 #include <sys/wait.h> // NOLINT
17 #include <unistd.h> // NOLINT 18 #include <unistd.h> // NOLINT
18 19
19 #include "bin/fdutils.h" 20 #include "bin/fdutils.h"
20 #include "bin/log.h" 21 #include "bin/log.h"
21 #include "bin/thread.h" 22 #include "bin/thread.h"
22 23
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
103 // Mutex protecting all accesses to the linked list of active 104 // Mutex protecting all accesses to the linked list of active
104 // processes. 105 // processes.
105 static dart::Mutex* mutex_; 106 static dart::Mutex* mutex_;
106 }; 107 };
107 108
108 109
109 ProcessInfo* ProcessInfoList::active_processes_ = NULL; 110 ProcessInfo* ProcessInfoList::active_processes_ = NULL;
110 dart::Mutex* ProcessInfoList::mutex_ = new dart::Mutex(); 111 dart::Mutex* ProcessInfoList::mutex_ = new dart::Mutex();
111 112
112 113
113 // The exit code handler sets up a separate thread which waits for child 114 // The exit code handler sets up a separate thread which is signalled
114 // processes to die. That separate thread can then get the exit code from 115 // on SIGCHLD. That separate thread can then get the exit code from
115 // processes that have exited and communicate it to Dart through the 116 // processes that have exited and communicate it to Dart through the
116 // event loop. 117 // event loop.
117 class ExitCodeHandler { 118 class ExitCodeHandler {
118 public: 119 public:
119 // Ensure that the ExitCodeHandler has been initialized. 120 // Ensure that the ExitCodeHandler has been initialized.
120 static bool EnsureInitialized() { 121 static bool EnsureInitialized() {
121 // Multiple isolates could be starting processes at the same 122 // Multiple isolates could be starting processes at the same
122 // time. Make sure that only one of them initializes the 123 // time. Make sure that only one of them initializes the
123 // ExitCodeHandler. 124 // ExitCodeHandler.
124 MutexLocker locker(mutex_); 125 MutexLocker locker(mutex_);
125 if (initialized_) { 126 if (initialized_) {
126 return true; 127 return true;
127 } 128 }
128 129
129 // Start thread that handles process exits when waitpid returns. 130 // Allocate a pipe that the signal handler can write a byte to and
130 int result = dart::Thread::Start(ExitCodeHandlerEntry, 0); 131 // that the exit handler thread can poll.
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]);
131 if (result != 0) { 142 if (result != 0) {
132 FATAL1("Failed to start exit code handler worker thread %d", result); 143 FATAL1("Failed to start exit code handler worker thread %d", result);
133 } 144 }
134 145
146 // Mark write end non-blocking.
147 FDUtils::SetNonBlocking(sig_chld_fds_[1]);
148
135 // Thread started and the ExitCodeHandler is initialized. 149 // Thread started and the ExitCodeHandler is initialized.
136 initialized_ = true; 150 initialized_ = true;
137 return true; 151 return true;
138 } 152 }
139 153
154 // Get the write end of the pipe.
155 static int WakeUpFd() {
156 return sig_chld_fds_[1];
157 }
158
140 static void TerminateExitCodeThread() { 159 static void TerminateExitCodeThread() {
141 MutexLocker locker(mutex_); 160 MutexLocker locker(mutex_);
142 if (!initialized_) { 161 if (!initialized_) {
143 return; 162 return;
144 } 163 }
145 164
146 thread_terminate_monitor_->Enter(); 165 uint8_t data = kThreadTerminateByte;
147 166 ssize_t result =
148 terminate_ = true; 167 TEMP_FAILURE_RETRY(write(ExitCodeHandler::WakeUpFd(), &data, 1));
149 // Fork to wake up waitpid. 168 if (result < 1) {
150 if (TEMP_FAILURE_RETRY(fork()) == 0) { 169 perror("Failed to write to wake-up fd to terminate exit code thread");
151 exit(0);
152 } 170 }
153 171
154 thread_terminate_monitor_->Wait(dart::Monitor::kNoTimeout); 172 {
155 thread_terminate_monitor_->Exit(); 173 MonitorLocker terminate_locker(thread_terminate_monitor_);
174 while (!thread_terminated_) {
175 terminate_locker.Wait();
176 }
177 }
178 }
179
180 static void ExitCodeThreadTerminated() {
181 MonitorLocker locker(thread_terminate_monitor_);
182 thread_terminated_ = true;
183 locker.Notify();
156 } 184 }
157 185
158 private: 186 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
159 // Entry point for the separate exit code handler thread started by 225 // Entry point for the separate exit code handler thread started by
160 // the ExitCodeHandler. 226 // the ExitCodeHandler.
161 static void ExitCodeHandlerEntry(uword param) { 227 static void ExitCodeHandlerEntry(uword param) {
162 pid_t pid = 0; 228 struct pollfd pollfds;
163 int status = 0; 229 pollfds.fd = param;
164 while (!terminate_) { 230 pollfds.events = POLLIN;
165 if ((pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, 0))) > 0) { 231 while (true) {
166 int exit_code = 0; 232 int result = TEMP_FAILURE_RETRY(poll(&pollfds, 1, -1));
167 int negative = 0; 233 if (result == -1) {
168 if (WIFEXITED(status)) { 234 ASSERT(EAGAIN == EWOULDBLOCK);
169 exit_code = WEXITSTATUS(status); 235 if (errno != EWOULDBLOCK) {
236 perror("ExitCodeHandler poll failed");
170 } 237 }
171 if (WIFSIGNALED(status)) { 238 } else {
172 exit_code = WTERMSIG(status); 239 // Read the byte from the wake-up fd.
173 negative = 1; 240 ASSERT(result = 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");
174 } 245 }
175 intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid); 246 if (data == ExitCodeHandler::kThreadTerminateByte) {
176 if (exit_code_fd != 0) { 247 ExitCodeThreadTerminated();
177 int message[2] = { exit_code, negative }; 248 return;
178 ssize_t result =
179 FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
180 // If the process has been closed, the read end of the exit
181 // pipe has been closed. It is therefore not a problem that
182 // write fails with a broken pipe error. Other errors should
183 // not happen.
184 if (result != -1 && result != sizeof(message)) {
185 FATAL("Failed to write entire process exit message");
186 } else if (result == -1 && errno != EPIPE) {
187 FATAL1("Failed to write exit code: %d", errno);
188 }
189 ProcessInfoList::RemoveProcess(pid);
190 } 249 }
250 // Get the exit code from all processes that have died.
251 GetProcessExitCodes();
191 } 252 }
192 } 253 }
193 thread_terminate_monitor_->Enter();
194 thread_terminate_monitor_->Notify();
195 thread_terminate_monitor_->Exit();
196 } 254 }
197 255
198 static dart::Mutex* mutex_; 256 static dart::Mutex* mutex_;
199 static bool initialized_; 257 static bool initialized_;
200 static bool terminate_; 258 static int sig_chld_fds_[2];
259 static bool thread_terminated_;
201 static dart::Monitor* thread_terminate_monitor_; 260 static dart::Monitor* thread_terminate_monitor_;
202 }; 261 };
203 262
204 263
205 dart::Mutex* ExitCodeHandler::mutex_ = new dart::Mutex(); 264 dart::Mutex* ExitCodeHandler::mutex_ = new dart::Mutex();
206 bool ExitCodeHandler::initialized_ = false; 265 bool ExitCodeHandler::initialized_ = false;
207 bool ExitCodeHandler::terminate_ = false; 266 int ExitCodeHandler::sig_chld_fds_[2] = { 0, 0 };
267 bool ExitCodeHandler::thread_terminated_ = false;
208 dart::Monitor* ExitCodeHandler::thread_terminate_monitor_ = new dart::Monitor(); 268 dart::Monitor* ExitCodeHandler::thread_terminate_monitor_ = new dart::Monitor();
209 269
210 270
211 static void SetChildOsErrorMessage(char** os_error_message) { 271 static void SetChildOsErrorMessage(char** os_error_message) {
212 const int kBufferSize = 1024; 272 const int kBufferSize = 1024;
213 char error_buf[kBufferSize]; 273 char error_buf[kBufferSize];
214 *os_error_message = strdup(strerror_r(errno, error_buf, kBufferSize)); 274 *os_error_message = strdup(strerror_r(errno, error_buf, kBufferSize));
215 } 275 }
216 276
217 277
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
218 static void ReportChildError(int exec_control_fd) { 293 static void ReportChildError(int exec_control_fd) {
219 // In the case of failure in the child process write the errno and 294 // In the case of failure in the child process write the errno and
220 // the OS error message to the exec control pipe and exit. 295 // the OS error message to the exec control pipe and exit.
221 int child_errno = errno; 296 int child_errno = errno;
222 const int kBufferSize = 1024; 297 const int kBufferSize = 1024;
223 char error_buf[kBufferSize]; 298 char error_buf[kBufferSize];
224 char* os_error_message = strerror_r(errno, error_buf, kBufferSize); 299 char* os_error_message = strerror_r(errno, error_buf, kBufferSize);
225 ASSERT(sizeof(child_errno) == sizeof(errno)); 300 ASSERT(sizeof(child_errno) == sizeof(errno));
226 int bytes_written = 301 int bytes_written =
227 FDUtils::WriteToBlocking( 302 FDUtils::WriteToBlocking(
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
330 405
331 char** program_environment = NULL; 406 char** program_environment = NULL;
332 if (environment != NULL) { 407 if (environment != NULL) {
333 program_environment = new char*[environment_length + 1]; 408 program_environment = new char*[environment_length + 1];
334 for (int i = 0; i < environment_length; i++) { 409 for (int i = 0; i < environment_length; i++) {
335 program_environment[i] = environment[i]; 410 program_environment[i] = environment[i];
336 } 411 }
337 program_environment[environment_length] = NULL; 412 program_environment[environment_length] = NULL;
338 } 413 }
339 414
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 }
340 pid = TEMP_FAILURE_RETRY(fork()); 422 pid = TEMP_FAILURE_RETRY(fork());
341 if (pid < 0) { 423 if (pid < 0) {
342 SetChildOsErrorMessage(os_error_message); 424 SetChildOsErrorMessage(os_error_message);
343 delete[] program_arguments; 425 delete[] program_arguments;
344 TEMP_FAILURE_RETRY(close(read_in[0])); 426 TEMP_FAILURE_RETRY(close(read_in[0]));
345 TEMP_FAILURE_RETRY(close(read_in[1])); 427 TEMP_FAILURE_RETRY(close(read_in[1]));
346 TEMP_FAILURE_RETRY(close(read_err[0])); 428 TEMP_FAILURE_RETRY(close(read_err[0]));
347 TEMP_FAILURE_RETRY(close(read_err[1])); 429 TEMP_FAILURE_RETRY(close(read_err[1]));
348 TEMP_FAILURE_RETRY(close(write_out[0])); 430 TEMP_FAILURE_RETRY(close(write_out[0]));
349 TEMP_FAILURE_RETRY(close(write_out[1])); 431 TEMP_FAILURE_RETRY(close(write_out[1]));
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after
613 695
614 696
615 intptr_t Process::CurrentProcessId() { 697 intptr_t Process::CurrentProcessId() {
616 return static_cast<intptr_t>(getpid()); 698 return static_cast<intptr_t>(getpid());
617 } 699 }
618 700
619 } // namespace bin 701 } // namespace bin
620 } // namespace dart 702 } // namespace dart
621 703
622 #endif // defined(TARGET_OS_LINUX) 704 #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