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

Side by Side Diff: base/process_util_posix.cc

Issue 18555002: Split out process killing functions from base/process_util.h into base/process/kill.h. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 5 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 | « base/process_util_mac.mm ('k') | base/process_util_win.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 Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
67 // Set the process's "environment" (i.e. the thing that setenv/getenv 67 // Set the process's "environment" (i.e. the thing that setenv/getenv
68 // work with). 68 // work with).
69 void SetEnvironment(char** env) { 69 void SetEnvironment(char** env) {
70 #if defined(OS_MACOSX) 70 #if defined(OS_MACOSX)
71 *_NSGetEnviron() = env; 71 *_NSGetEnviron() = env;
72 #else 72 #else
73 environ = env; 73 environ = env;
74 #endif 74 #endif
75 } 75 }
76 76
77 int WaitpidWithTimeout(ProcessHandle handle, int64 wait_milliseconds,
78 bool* success) {
79 // This POSIX version of this function only guarantees that we wait no less
80 // than |wait_milliseconds| for the process to exit. The child process may
81 // exit sometime before the timeout has ended but we may still block for up
82 // to 256 milliseconds after the fact.
83 //
84 // waitpid() has no direct support on POSIX for specifying a timeout, you can
85 // either ask it to block indefinitely or return immediately (WNOHANG).
86 // When a child process terminates a SIGCHLD signal is sent to the parent.
87 // Catching this signal would involve installing a signal handler which may
88 // affect other parts of the application and would be difficult to debug.
89 //
90 // Our strategy is to call waitpid() once up front to check if the process
91 // has already exited, otherwise to loop for wait_milliseconds, sleeping for
92 // at most 256 milliseconds each time using usleep() and then calling
93 // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and
94 // we double it every 4 sleep cycles.
95 //
96 // usleep() is speced to exit if a signal is received for which a handler
97 // has been installed. This means that when a SIGCHLD is sent, it will exit
98 // depending on behavior external to this function.
99 //
100 // This function is used primarily for unit tests, if we want to use it in
101 // the application itself it would probably be best to examine other routes.
102 int status = -1;
103 pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
104 static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds.
105 int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds.
106 int64 double_sleep_time = 0;
107
108 // If the process hasn't exited yet, then sleep and try again.
109 TimeTicks wakeup_time = TimeTicks::Now() +
110 TimeDelta::FromMilliseconds(wait_milliseconds);
111 while (ret_pid == 0) {
112 TimeTicks now = TimeTicks::Now();
113 if (now > wakeup_time)
114 break;
115 // Guaranteed to be non-negative!
116 int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds();
117 // Sleep for a bit while we wait for the process to finish.
118 if (sleep_time_usecs > max_sleep_time_usecs)
119 sleep_time_usecs = max_sleep_time_usecs;
120
121 // usleep() will return 0 and set errno to EINTR on receipt of a signal
122 // such as SIGCHLD.
123 usleep(sleep_time_usecs);
124 ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
125
126 if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) &&
127 (double_sleep_time++ % 4 == 0)) {
128 max_sleep_time_usecs *= 2;
129 }
130 }
131
132 if (success)
133 *success = (ret_pid != -1);
134
135 return status;
136 }
137
138 #if !defined(OS_LINUX) || \ 77 #if !defined(OS_LINUX) || \
139 (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__)) 78 (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__))
140 void ResetChildSignalHandlersToDefaults() { 79 void ResetChildSignalHandlersToDefaults() {
141 // The previous signal handlers are likely to be meaningless in the child's 80 // The previous signal handlers are likely to be meaningless in the child's
142 // context so we reset them to the defaults for now. http://crbug.com/44953 81 // context so we reset them to the defaults for now. http://crbug.com/44953
143 // These signal handlers are set up at least in browser_main_posix.cc: 82 // These signal handlers are set up at least in browser_main_posix.cc:
144 // BrowserMainPartsPosix::PreEarlyInitialization and stack_trace_posix.cc: 83 // BrowserMainPartsPosix::PreEarlyInitialization and stack_trace_posix.cc:
145 // EnableInProcessStackDumping. 84 // EnableInProcessStackDumping.
146 signal(SIGHUP, SIG_DFL); 85 signal(SIGHUP, SIG_DFL);
147 signal(SIGINT, SIG_DFL); 86 signal(SIGINT, SIG_DFL);
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 // Now ask the kernel again and check that no restorer will leak. 153 // Now ask the kernel again and check that no restorer will leak.
215 if (sys_rt_sigaction(signum, NULL, &act) || act.k_sa_restorer) { 154 if (sys_rt_sigaction(signum, NULL, &act) || act.k_sa_restorer) {
216 RAW_LOG(FATAL, "Cound not fix sa_restorer."); 155 RAW_LOG(FATAL, "Cound not fix sa_restorer.");
217 } 156 }
218 #endif // !defined(NDEBUG) 157 #endif // !defined(NDEBUG)
219 } 158 }
220 } 159 }
221 #endif // !defined(OS_LINUX) || 160 #endif // !defined(OS_LINUX) ||
222 // (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__)) 161 // (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__))
223 162
224 TerminationStatus GetTerminationStatusImpl(ProcessHandle handle,
225 bool can_block,
226 int* exit_code) {
227 int status = 0;
228 const pid_t result = HANDLE_EINTR(waitpid(handle, &status,
229 can_block ? 0 : WNOHANG));
230 if (result == -1) {
231 DPLOG(ERROR) << "waitpid(" << handle << ")";
232 if (exit_code)
233 *exit_code = 0;
234 return TERMINATION_STATUS_NORMAL_TERMINATION;
235 } else if (result == 0) {
236 // the child hasn't exited yet.
237 if (exit_code)
238 *exit_code = 0;
239 return TERMINATION_STATUS_STILL_RUNNING;
240 }
241
242 if (exit_code)
243 *exit_code = status;
244
245 if (WIFSIGNALED(status)) {
246 switch (WTERMSIG(status)) {
247 case SIGABRT:
248 case SIGBUS:
249 case SIGFPE:
250 case SIGILL:
251 case SIGSEGV:
252 return TERMINATION_STATUS_PROCESS_CRASHED;
253 case SIGINT:
254 case SIGKILL:
255 case SIGTERM:
256 return TERMINATION_STATUS_PROCESS_WAS_KILLED;
257 default:
258 break;
259 }
260 }
261
262 if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
263 return TERMINATION_STATUS_ABNORMAL_TERMINATION;
264
265 return TERMINATION_STATUS_NORMAL_TERMINATION;
266 }
267
268 } // anonymous namespace 163 } // anonymous namespace
269 164
270 ProcessId GetCurrentProcId() { 165 ProcessId GetCurrentProcId() {
271 return getpid(); 166 return getpid();
272 } 167 }
273 168
274 ProcessHandle GetCurrentProcessHandle() { 169 ProcessHandle GetCurrentProcessHandle() {
275 return GetCurrentProcId(); 170 return GetCurrentProcId();
276 } 171 }
277 172
(...skipping 20 matching lines...) Expand all
298 193
299 void CloseProcessHandle(ProcessHandle process) { 194 void CloseProcessHandle(ProcessHandle process) {
300 // See OpenProcessHandle, nothing to do. 195 // See OpenProcessHandle, nothing to do.
301 return; 196 return;
302 } 197 }
303 198
304 ProcessId GetProcId(ProcessHandle process) { 199 ProcessId GetProcId(ProcessHandle process) {
305 return process; 200 return process;
306 } 201 }
307 202
308 // Attempts to kill the process identified by the given process
309 // entry structure. Ignores specified exit_code; posix can't force that.
310 // Returns true if this is successful, false otherwise.
311 bool KillProcess(ProcessHandle process_id, int exit_code, bool wait) {
312 DCHECK_GT(process_id, 1) << " tried to kill invalid process_id";
313 if (process_id <= 1)
314 return false;
315 bool result = kill(process_id, SIGTERM) == 0;
316 if (result && wait) {
317 int tries = 60;
318
319 if (RunningOnValgrind()) {
320 // Wait for some extra time when running under Valgrind since the child
321 // processes may take some time doing leak checking.
322 tries *= 2;
323 }
324
325 unsigned sleep_ms = 4;
326
327 // The process may not end immediately due to pending I/O
328 bool exited = false;
329 while (tries-- > 0) {
330 pid_t pid = HANDLE_EINTR(waitpid(process_id, NULL, WNOHANG));
331 if (pid == process_id) {
332 exited = true;
333 break;
334 }
335 if (pid == -1) {
336 if (errno == ECHILD) {
337 // The wait may fail with ECHILD if another process also waited for
338 // the same pid, causing the process state to get cleaned up.
339 exited = true;
340 break;
341 }
342 DPLOG(ERROR) << "Error waiting for process " << process_id;
343 }
344
345 usleep(sleep_ms * 1000);
346 const unsigned kMaxSleepMs = 1000;
347 if (sleep_ms < kMaxSleepMs)
348 sleep_ms *= 2;
349 }
350
351 // If we're waiting and the child hasn't died by now, force it
352 // with a SIGKILL.
353 if (!exited)
354 result = kill(process_id, SIGKILL) == 0;
355 }
356
357 if (!result)
358 DPLOG(ERROR) << "Unable to terminate process " << process_id;
359
360 return result;
361 }
362
363 bool KillProcessGroup(ProcessHandle process_group_id) {
364 bool result = kill(-1 * process_group_id, SIGKILL) == 0;
365 if (!result)
366 DPLOG(ERROR) << "Unable to terminate process group " << process_group_id;
367 return result;
368 }
369
370 // A class to handle auto-closing of DIR*'s. 203 // A class to handle auto-closing of DIR*'s.
371 class ScopedDIRClose { 204 class ScopedDIRClose {
372 public: 205 public:
373 inline void operator()(DIR* x) const { 206 inline void operator()(DIR* x) const {
374 if (x) { 207 if (x) {
375 closedir(x); 208 closedir(x);
376 } 209 }
377 } 210 }
378 }; 211 };
379 typedef scoped_ptr_malloc<DIR, ScopedDIRClose> ScopedDIR; 212 typedef scoped_ptr_malloc<DIR, ScopedDIRClose> ScopedDIR;
(...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after
769 const LaunchOptions& options, 602 const LaunchOptions& options,
770 ProcessHandle* process_handle) { 603 ProcessHandle* process_handle) {
771 return LaunchProcess(cmdline.argv(), options, process_handle); 604 return LaunchProcess(cmdline.argv(), options, process_handle);
772 } 605 }
773 606
774 void RaiseProcessToHighPriority() { 607 void RaiseProcessToHighPriority() {
775 // On POSIX, we don't actually do anything here. We could try to nice() or 608 // On POSIX, we don't actually do anything here. We could try to nice() or
776 // setpriority() or sched_getscheduler, but these all require extra rights. 609 // setpriority() or sched_getscheduler, but these all require extra rights.
777 } 610 }
778 611
779 TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
780 return GetTerminationStatusImpl(handle, false /* can_block */, exit_code);
781 }
782
783 TerminationStatus WaitForTerminationStatus(ProcessHandle handle,
784 int* exit_code) {
785 return GetTerminationStatusImpl(handle, true /* can_block */, exit_code);
786 }
787
788 bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
789 int status;
790 if (HANDLE_EINTR(waitpid(handle, &status, 0)) == -1) {
791 NOTREACHED();
792 return false;
793 }
794
795 if (WIFEXITED(status)) {
796 *exit_code = WEXITSTATUS(status);
797 return true;
798 }
799
800 // If it didn't exit cleanly, it must have been signaled.
801 DCHECK(WIFSIGNALED(status));
802 return false;
803 }
804
805 bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code,
806 base::TimeDelta timeout) {
807 bool waitpid_success = false;
808 int status = WaitpidWithTimeout(handle, timeout.InMilliseconds(),
809 &waitpid_success);
810 if (status == -1)
811 return false;
812 if (!waitpid_success)
813 return false;
814 if (WIFSIGNALED(status)) {
815 *exit_code = -1;
816 return true;
817 }
818 if (WIFEXITED(status)) {
819 *exit_code = WEXITSTATUS(status);
820 return true;
821 }
822 return false;
823 }
824
825 #if defined(OS_MACOSX)
826 // Using kqueue on Mac so that we can wait on non-child processes.
827 // We can't use kqueues on child processes because we need to reap
828 // our own children using wait.
829 static bool WaitForSingleNonChildProcess(ProcessHandle handle,
830 base::TimeDelta wait) {
831 DCHECK_GT(handle, 0);
832 DCHECK(wait.InMilliseconds() == base::kNoTimeout || wait > base::TimeDelta());
833
834 int kq = kqueue();
835 if (kq == -1) {
836 DPLOG(ERROR) << "kqueue";
837 return false;
838 }
839 file_util::ScopedFD kq_closer(&kq);
840
841 struct kevent change = {0};
842 EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
843 int result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
844 if (result == -1) {
845 if (errno == ESRCH) {
846 // If the process wasn't found, it must be dead.
847 return true;
848 }
849
850 DPLOG(ERROR) << "kevent (setup " << handle << ")";
851 return false;
852 }
853
854 // Keep track of the elapsed time to be able to restart kevent if it's
855 // interrupted.
856 bool wait_forever = wait.InMilliseconds() == base::kNoTimeout;
857 base::TimeDelta remaining_delta;
858 base::TimeTicks deadline;
859 if (!wait_forever) {
860 remaining_delta = wait;
861 deadline = base::TimeTicks::Now() + remaining_delta;
862 }
863
864 result = -1;
865 struct kevent event = {0};
866
867 while (wait_forever || remaining_delta > base::TimeDelta()) {
868 struct timespec remaining_timespec;
869 struct timespec* remaining_timespec_ptr;
870 if (wait_forever) {
871 remaining_timespec_ptr = NULL;
872 } else {
873 remaining_timespec = remaining_delta.ToTimeSpec();
874 remaining_timespec_ptr = &remaining_timespec;
875 }
876
877 result = kevent(kq, NULL, 0, &event, 1, remaining_timespec_ptr);
878
879 if (result == -1 && errno == EINTR) {
880 if (!wait_forever) {
881 remaining_delta = deadline - base::TimeTicks::Now();
882 }
883 result = 0;
884 } else {
885 break;
886 }
887 }
888
889 if (result < 0) {
890 DPLOG(ERROR) << "kevent (wait " << handle << ")";
891 return false;
892 } else if (result > 1) {
893 DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result "
894 << result;
895 return false;
896 } else if (result == 0) {
897 // Timed out.
898 return false;
899 }
900
901 DCHECK_EQ(result, 1);
902
903 if (event.filter != EVFILT_PROC ||
904 (event.fflags & NOTE_EXIT) == 0 ||
905 event.ident != static_cast<uintptr_t>(handle)) {
906 DLOG(ERROR) << "kevent (wait " << handle
907 << "): unexpected event: filter=" << event.filter
908 << ", fflags=" << event.fflags
909 << ", ident=" << event.ident;
910 return false;
911 }
912
913 return true;
914 }
915 #endif // OS_MACOSX
916
917 bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
918 ProcessHandle parent_pid = GetParentProcessId(handle);
919 ProcessHandle our_pid = Process::Current().handle();
920 if (parent_pid != our_pid) {
921 #if defined(OS_MACOSX)
922 // On Mac we can wait on non child processes.
923 return WaitForSingleNonChildProcess(handle, wait);
924 #else
925 // Currently on Linux we can't handle non child processes.
926 NOTIMPLEMENTED();
927 #endif // OS_MACOSX
928 }
929
930 bool waitpid_success;
931 int status = -1;
932 if (wait.InMilliseconds() == base::kNoTimeout) {
933 waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1);
934 } else {
935 status = WaitpidWithTimeout(
936 handle, wait.InMilliseconds(), &waitpid_success);
937 }
938
939 if (status != -1) {
940 DCHECK(waitpid_success);
941 return WIFEXITED(status);
942 } else {
943 return false;
944 }
945 }
946
947 // Return value used by GetAppOutputInternal to encapsulate the various exit 612 // Return value used by GetAppOutputInternal to encapsulate the various exit
948 // scenarios from the function. 613 // scenarios from the function.
949 enum GetAppOutputInternalResult { 614 enum GetAppOutputInternalResult {
950 EXECUTE_FAILURE, 615 EXECUTE_FAILURE,
951 EXECUTE_SUCCESS, 616 EXECUTE_SUCCESS,
952 GOT_MAX_OUTPUT, 617 GOT_MAX_OUTPUT,
953 }; 618 };
954 619
955 // Executes the application specified by |argv| and wait for it to exit. Stores 620 // Executes the application specified by |argv| and wait for it to exit. Stores
956 // the output (stdout) in |output|. If |do_search_path| is set, it searches the 621 // the output (stdout) in |output|. If |do_search_path| is set, it searches the
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after
1112 bool GetAppOutputWithExitCode(const CommandLine& cl, 777 bool GetAppOutputWithExitCode(const CommandLine& cl,
1113 std::string* output, 778 std::string* output,
1114 int* exit_code) { 779 int* exit_code) {
1115 // Run |execve()| with the current environment and store "unlimited" data. 780 // Run |execve()| with the current environment and store "unlimited" data.
1116 GetAppOutputInternalResult result = GetAppOutputInternal( 781 GetAppOutputInternalResult result = GetAppOutputInternal(
1117 cl.argv(), NULL, output, std::numeric_limits<std::size_t>::max(), true, 782 cl.argv(), NULL, output, std::numeric_limits<std::size_t>::max(), true,
1118 exit_code); 783 exit_code);
1119 return result == EXECUTE_SUCCESS; 784 return result == EXECUTE_SUCCESS;
1120 } 785 }
1121 786
1122 bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
1123 base::TimeDelta wait,
1124 const ProcessFilter* filter) {
1125 bool result = false;
1126
1127 // TODO(port): This is inefficient, but works if there are multiple procs.
1128 // TODO(port): use waitpid to avoid leaving zombies around
1129
1130 base::TimeTicks end_time = base::TimeTicks::Now() + wait;
1131 do {
1132 NamedProcessIterator iter(executable_name, filter);
1133 if (!iter.NextProcessEntry()) {
1134 result = true;
1135 break;
1136 }
1137 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
1138 } while ((end_time - base::TimeTicks::Now()) > base::TimeDelta());
1139
1140 return result;
1141 }
1142
1143 bool CleanupProcesses(const FilePath::StringType& executable_name,
1144 base::TimeDelta wait,
1145 int exit_code,
1146 const ProcessFilter* filter) {
1147 bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
1148 if (!exited_cleanly)
1149 KillProcesses(executable_name, exit_code, filter);
1150 return exited_cleanly;
1151 }
1152
1153 #if !defined(OS_MACOSX)
1154
1155 namespace {
1156
1157 // Return true if the given child is dead. This will also reap the process.
1158 // Doesn't block.
1159 static bool IsChildDead(pid_t child) {
1160 const pid_t result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
1161 if (result == -1) {
1162 DPLOG(ERROR) << "waitpid(" << child << ")";
1163 NOTREACHED();
1164 } else if (result > 0) {
1165 // The child has died.
1166 return true;
1167 }
1168
1169 return false;
1170 }
1171
1172 // A thread class which waits for the given child to exit and reaps it.
1173 // If the child doesn't exit within a couple of seconds, kill it.
1174 class BackgroundReaper : public PlatformThread::Delegate {
1175 public:
1176 BackgroundReaper(pid_t child, unsigned timeout)
1177 : child_(child),
1178 timeout_(timeout) {
1179 }
1180
1181 // Overridden from PlatformThread::Delegate:
1182 virtual void ThreadMain() OVERRIDE {
1183 WaitForChildToDie();
1184 delete this;
1185 }
1186
1187 void WaitForChildToDie() {
1188 // Wait forever case.
1189 if (timeout_ == 0) {
1190 pid_t r = HANDLE_EINTR(waitpid(child_, NULL, 0));
1191 if (r != child_) {
1192 DPLOG(ERROR) << "While waiting for " << child_
1193 << " to terminate, we got the following result: " << r;
1194 }
1195 return;
1196 }
1197
1198 // There's no good way to wait for a specific child to exit in a timed
1199 // fashion. (No kqueue on Linux), so we just loop and sleep.
1200
1201 // Wait for 2 * timeout_ 500 milliseconds intervals.
1202 for (unsigned i = 0; i < 2 * timeout_; ++i) {
1203 PlatformThread::Sleep(TimeDelta::FromMilliseconds(500));
1204 if (IsChildDead(child_))
1205 return;
1206 }
1207
1208 if (kill(child_, SIGKILL) == 0) {
1209 // SIGKILL is uncatchable. Since the signal was delivered, we can
1210 // just wait for the process to die now in a blocking manner.
1211 if (HANDLE_EINTR(waitpid(child_, NULL, 0)) < 0)
1212 DPLOG(WARNING) << "waitpid";
1213 } else {
1214 DLOG(ERROR) << "While waiting for " << child_ << " to terminate we"
1215 << " failed to deliver a SIGKILL signal (" << errno << ").";
1216 }
1217 }
1218
1219 private:
1220 const pid_t child_;
1221 // Number of seconds to wait, if 0 then wait forever and do not attempt to
1222 // kill |child_|.
1223 const unsigned timeout_;
1224
1225 DISALLOW_COPY_AND_ASSIGN(BackgroundReaper);
1226 };
1227
1228 } // namespace
1229
1230 void EnsureProcessTerminated(ProcessHandle process) {
1231 // If the child is already dead, then there's nothing to do.
1232 if (IsChildDead(process))
1233 return;
1234
1235 const unsigned timeout = 2; // seconds
1236 BackgroundReaper* reaper = new BackgroundReaper(process, timeout);
1237 PlatformThread::CreateNonJoinable(0, reaper);
1238 }
1239
1240 void EnsureProcessGetsReaped(ProcessHandle process) {
1241 // If the child is already dead, then there's nothing to do.
1242 if (IsChildDead(process))
1243 return;
1244
1245 BackgroundReaper* reaper = new BackgroundReaper(process, 0);
1246 PlatformThread::CreateNonJoinable(0, reaper);
1247 }
1248
1249 #endif // !defined(OS_MACOSX)
1250
1251 } // namespace base 787 } // namespace base
OLDNEW
« no previous file with comments | « base/process_util_mac.mm ('k') | base/process_util_win.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698