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

Side by Side Diff: base/process_util_posix.cc

Issue 5377001: base: wait for children to terminate. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 10 years 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 | 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) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <dirent.h> 5 #include <dirent.h>
6 #include <errno.h> 6 #include <errno.h>
7 #include <fcntl.h> 7 #include <fcntl.h>
8 #include <signal.h> 8 #include <signal.h>
9 #include <stdlib.h> 9 #include <stdlib.h>
10 #include <sys/resource.h> 10 #include <sys/resource.h>
(...skipping 22 matching lines...) Expand all
33 #include <crt_externs.h> 33 #include <crt_externs.h>
34 #define environ (*_NSGetEnviron()) 34 #define environ (*_NSGetEnviron())
35 #else 35 #else
36 extern char** environ; 36 extern char** environ;
37 #endif 37 #endif
38 38
39 namespace base { 39 namespace base {
40 40
41 namespace { 41 namespace {
42 42
43 int WaitpidWithTimeout(ProcessHandle handle, int64 wait_milliseconds, 43 // WaitpidWithTimeout performs a timed waitpid() call. The arguments and return
44 bool* success) { 44 // value match waitpid(2).
45 pid_t WaitpidWithTimeout(ProcessHandle handle, int* status,
46 int64 wait_milliseconds) {
45 // This POSIX version of this function only guarantees that we wait no less 47 // This POSIX version of this function only guarantees that we wait no less
46 // than |wait_milliseconds| for the process to exit. The child process may 48 // than |wait_milliseconds| for the process to exit. The child process may
47 // exit sometime before the timeout has ended but we may still block for up 49 // exit sometime before the timeout has ended but we may still block for up
48 // to 256 milliseconds after the fact. 50 // to 256 milliseconds after the fact.
49 // 51 //
50 // waitpid() has no direct support on POSIX for specifying a timeout, you can 52 // waitpid() has no direct support on POSIX for specifying a timeout, you can
51 // either ask it to block indefinitely or return immediately (WNOHANG). 53 // either ask it to block indefinitely or return immediately (WNOHANG).
52 // When a child process terminates a SIGCHLD signal is sent to the parent. 54 // When a child process terminates a SIGCHLD signal is sent to the parent.
53 // Catching this signal would involve installing a signal handler which may 55 // Catching this signal would involve installing a signal handler which may
54 // affect other parts of the application and would be difficult to debug. 56 // affect other parts of the application and would be difficult to debug.
55 // 57 //
56 // Our strategy is to call waitpid() once up front to check if the process 58 // Our strategy is to call waitpid() once up front to check if the process
57 // has already exited, otherwise to loop for wait_milliseconds, sleeping for 59 // has already exited, otherwise to loop for wait_milliseconds, sleeping for
58 // at most 256 milliseconds each time using usleep() and then calling 60 // at most 256 milliseconds each time using usleep() and then calling
59 // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and 61 // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and
60 // we double it every 4 sleep cycles. 62 // we double it every 4 sleep cycles.
61 // 63 //
62 // usleep() is speced to exit if a signal is received for which a handler 64 // usleep() is speced to exit if a signal is received for which a handler
63 // has been installed. This means that when a SIGCHLD is sent, it will exit 65 // has been installed. This means that when a SIGCHLD is sent, it will exit
64 // depending on behavior external to this function. 66 // depending on behavior external to this function.
65 // 67 //
66 // This function is used primarily for unit tests, if we want to use it in 68 // This function is used in unit tests and by DidProcessCrash. See the
67 // the application itself it would probably be best to examine other routes. 69 // comments in DidProcessCrash about its use in Chrome itself.
68 int status = -1;
69 pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
70 static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds. 70 static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds.
71 int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds. 71 int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds.
72 int64 double_sleep_time = 0; 72 int double_sleep_time = 0;
73
74 pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG));
75 if (ret_pid != 0)
76 return ret_pid;
73 77
74 // If the process hasn't exited yet, then sleep and try again. 78 // If the process hasn't exited yet, then sleep and try again.
75 Time wakeup_time = Time::Now() + 79 Time wakeup_time = Time::Now() +
76 TimeDelta::FromMilliseconds(wait_milliseconds); 80 TimeDelta::FromMilliseconds(wait_milliseconds);
77 while (ret_pid == 0) { 81 while (ret_pid == 0) {
78 Time now = Time::Now(); 82 Time now = Time::Now();
79 if (now > wakeup_time) 83 if (now > wakeup_time)
80 break; 84 break;
81 // Guaranteed to be non-negative! 85 // Guaranteed to be non-negative!
82 int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds(); 86 int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds();
83 // Sleep for a bit while we wait for the process to finish. 87 // Sleep for a bit while we wait for the process to finish.
84 if (sleep_time_usecs > max_sleep_time_usecs) 88 if (sleep_time_usecs > max_sleep_time_usecs)
85 sleep_time_usecs = max_sleep_time_usecs; 89 sleep_time_usecs = max_sleep_time_usecs;
86 90
87 // usleep() will return 0 and set errno to EINTR on receipt of a signal 91 // usleep() will return 0 and set errno to EINTR on receipt of a signal
88 // such as SIGCHLD. 92 // such as SIGCHLD.
89 usleep(sleep_time_usecs); 93 usleep(sleep_time_usecs);
90 ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); 94 ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG));
91 95
92 if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && 96 if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) &&
93 (double_sleep_time++ % 4 == 0)) { 97 (double_sleep_time++ % 4 == 0)) {
94 max_sleep_time_usecs *= 2; 98 max_sleep_time_usecs *= 2;
95 } 99 }
96 } 100 }
97 101
98 if (success) 102 return ret_pid;
99 *success = (ret_pid != -1);
100
101 return status;
102 } 103 }
103 104
104 void StackDumpSignalHandler(int signal, siginfo_t* info, ucontext_t* context) { 105 void StackDumpSignalHandler(int signal, siginfo_t* info, ucontext_t* context) {
105 LOG(ERROR) << "Received signal " << signal; 106 LOG(ERROR) << "Received signal " << signal;
106 debug::StackTrace().PrintBacktrace(); 107 debug::StackTrace().PrintBacktrace();
107 108
108 // TODO(shess): Port to Linux. 109 // TODO(shess): Port to Linux.
109 #if defined(OS_MACOSX) 110 #if defined(OS_MACOSX)
110 // TODO(shess): Port to 64-bit. 111 // TODO(shess): Port to 64-bit.
111 #if ARCH_CPU_32_BITS 112 #if ARCH_CPU_32_BITS
(...skipping 512 matching lines...) Expand 10 before | Expand all | Expand 10 after
624 625
625 return success; 626 return success;
626 } 627 }
627 628
628 void RaiseProcessToHighPriority() { 629 void RaiseProcessToHighPriority() {
629 // On POSIX, we don't actually do anything here. We could try to nice() or 630 // On POSIX, we don't actually do anything here. We could try to nice() or
630 // setpriority() or sched_getscheduler, but these all require extra rights. 631 // setpriority() or sched_getscheduler, but these all require extra rights.
631 } 632 }
632 633
633 bool DidProcessCrash(bool* child_exited, ProcessHandle handle) { 634 bool DidProcessCrash(bool* child_exited, ProcessHandle handle) {
635 // We call DidProcessCrash when we see an EOF from a socket to that child.
636 // Sadly, the kernel is raceable: it will close a dead process's file
637 // descriptors before marking the process as dead. Therefore it's possible to
638 // see the EOF and still have a subsequent waitpid() return zero. (Had I
639 // known that from the beginning, child processing would have been designed
640 // differently.)
641 //
642 // Everything higher up copes with the case where waitpid fails, but we might
643 // lose some crash notifications. In order to minimise this we waitpid() in a
644 // loop with a timeout to try and catch the exit code. In the typical case,
645 // we should only be blocking for microseconds here.
646 //
647 // It's possible for a bad child to close its socket without exiting. Because
648 // we don't want to hang the browser in this case we have a timeout.
634 int status; 649 int status;
635 const pid_t result = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); 650 pid_t pid = WaitpidWithTimeout(handle, &status, 250 /* milliseconds */);
636 if (result == -1) { 651
637 PLOG(ERROR) << "waitpid(" << handle << ")"; 652 if (pid == 0) {
653 // We waited for the full timeout and the child still isn't dead.
654 LOG(ERROR) << "We janked because we expected child "
655 << handle << " to have exited.";
638 if (child_exited) 656 if (child_exited)
639 *child_exited = false; 657 *child_exited = false;
640 return false; 658 return false;
641 } else if (result == 0) {
642 // the child hasn't exited yet.
643 if (child_exited)
644 *child_exited = false;
645 return false;
646 } 659 }
647 660
648 if (child_exited) 661 if (child_exited)
649 *child_exited = true; 662 *child_exited = true;
650 663
651 if (WIFSIGNALED(status)) { 664 if (WIFSIGNALED(status)) {
652 switch (WTERMSIG(status)) { 665 switch (WTERMSIG(status)) {
653 case SIGSEGV: 666 case SIGSEGV:
654 case SIGILL: 667 case SIGILL:
655 case SIGABRT: 668 case SIGABRT:
(...skipping 22 matching lines...) Expand all
678 return true; 691 return true;
679 } 692 }
680 693
681 // If it didn't exit cleanly, it must have been signaled. 694 // If it didn't exit cleanly, it must have been signaled.
682 DCHECK(WIFSIGNALED(status)); 695 DCHECK(WIFSIGNALED(status));
683 return false; 696 return false;
684 } 697 }
685 698
686 bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code, 699 bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code,
687 int64 timeout_milliseconds) { 700 int64 timeout_milliseconds) {
688 bool waitpid_success = false; 701 int status;
689 int status = WaitpidWithTimeout(handle, timeout_milliseconds, 702 pid_t pid = WaitpidWithTimeout(handle, &status, timeout_milliseconds);
690 &waitpid_success); 703
691 if (status == -1) 704 if (pid <= 0)
692 return false;
693 if (!waitpid_success)
694 return false; 705 return false;
695 if (!WIFEXITED(status)) 706 if (!WIFEXITED(status))
696 return false; 707 return false;
697 if (WIFSIGNALED(status)) { 708 if (WIFSIGNALED(status)) {
698 *exit_code = -1; 709 *exit_code = -1;
699 return true; 710 return true;
700 } 711 }
701 *exit_code = WEXITSTATUS(status); 712 *exit_code = WEXITSTATUS(status);
702 return true; 713 return true;
703 } 714 }
704 715
705 bool WaitForSingleProcess(ProcessHandle handle, int64 wait_milliseconds) { 716 bool WaitForSingleProcess(ProcessHandle handle, int64 wait_milliseconds) {
706 bool waitpid_success;
707 int status; 717 int status;
708 if (wait_milliseconds == base::kNoTimeout) 718 pid_t pid;
709 waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1); 719 if (wait_milliseconds == base::kNoTimeout) {
710 else 720 pid = (HANDLE_EINTR(waitpid(handle, &status, 0)) == handle);
711 status = WaitpidWithTimeout(handle, wait_milliseconds, &waitpid_success);
712 if (status != -1) {
713 DCHECK(waitpid_success);
714 return WIFEXITED(status);
715 } else { 721 } else {
722 pid = WaitpidWithTimeout(handle, &status, wait_milliseconds);
723 }
724
725 if (pid <= 0)
716 return false; 726 return false;
727
728 return WIFEXITED(status);
729 }
730
731 bool CrashAwareSleep(ProcessHandle handle, int64 wait_milliseconds) {
732 int status;
733 pid_t pid = WaitpidWithTimeout(handle, &status, wait_milliseconds);
734 if (pid < 0) {
735 // If waitpid failed then the process probably didn't exist prior to the
736 // call.
737 return true;
738 } else if (pid == 0) {
739 return false;
740 } else {
741 return !(WIFEXITED(status) || WIFSIGNALED(status));
717 } 742 }
718 } 743 }
719 744
720 bool CrashAwareSleep(ProcessHandle handle, int64 wait_milliseconds) {
721 bool waitpid_success;
722 int status = WaitpidWithTimeout(handle, wait_milliseconds, &waitpid_success);
723 if (status != -1) {
724 DCHECK(waitpid_success);
725 return !(WIFEXITED(status) || WIFSIGNALED(status));
726 } else {
727 // If waitpid returned with an error, then the process doesn't exist
728 // (which most probably means it didn't exist before our call).
729 return waitpid_success;
730 }
731 }
732
733 int64 TimeValToMicroseconds(const struct timeval& tv) { 745 int64 TimeValToMicroseconds(const struct timeval& tv) {
734 static const int kMicrosecondsPerSecond = 1000000; 746 static const int kMicrosecondsPerSecond = 1000000;
735 int64 ret = tv.tv_sec; // Avoid (int * int) integer overflow. 747 int64 ret = tv.tv_sec; // Avoid (int * int) integer overflow.
736 ret *= kMicrosecondsPerSecond; 748 ret *= kMicrosecondsPerSecond;
737 ret += tv.tv_usec; 749 ret += tv.tv_usec;
738 return ret; 750 return ret;
739 } 751 }
740 752
741 // Executes the application specified by |cl| and wait for it to exit. Stores 753 // Executes the application specified by |cl| and wait for it to exit. Stores
742 // the output (stdout) in |output|. If |do_search_path| is set, it searches the 754 // the output (stdout) in |output|. If |do_search_path| is set, it searches the
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
891 const ProcessFilter* filter) { 903 const ProcessFilter* filter) {
892 bool exited_cleanly = 904 bool exited_cleanly =
893 WaitForProcessesToExit(executable_name, wait_milliseconds, 905 WaitForProcessesToExit(executable_name, wait_milliseconds,
894 filter); 906 filter);
895 if (!exited_cleanly) 907 if (!exited_cleanly)
896 KillProcesses(executable_name, exit_code, filter); 908 KillProcesses(executable_name, exit_code, filter);
897 return exited_cleanly; 909 return exited_cleanly;
898 } 910 }
899 911
900 } // namespace base 912 } // namespace base
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698