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

Side by Side Diff: chrome/common/process_watcher_mac.cc

Issue 496007: Make ProcessWatcher use kqueues on Mac. (Closed)
Patch Set: No need for a thread Created 11 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
« no previous file with comments | « chrome/common/process_watcher.h ('k') | chrome/common/process_watcher_posix.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/common/process_watcher.h"
6
7 #include <errno.h>
8 #include <signal.h>
9 #include <sys/event.h>
10 #include <sys/types.h>
11 #include <sys/wait.h>
12
13 #include "base/eintr_wrapper.h"
14 #include "base/file_util.h"
15 #include "base/time.h"
16
17 namespace {
18
19 // Return true if the given child is dead. This will also reap the process.
20 // Doesn't block.
21 bool IsChildDead(pid_t child) {
22 const pid_t result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
23 if (result == -1) {
24 PLOG(ERROR) << "call to waitpid() failed.";
25 } else if (result > 0) {
26 // The child has died.
27 return true;
28 }
29
30 return false;
31 }
32
33 // Reap |child| process.
34 // This call blocks until completion.
35 void BlockingReap(pid_t child) {
36 const pid_t result = HANDLE_EINTR(waitpid(child, NULL, 0));
37 if (result == -1) {
38 PLOG(ERROR) << "waitpid(" << child << ")";
39 NOTREACHED();
40 }
41 }
42
43 // Waits for |timeout| seconds for the given |child| to exit and reap it.
44 // If the child doesn't exit within a couple of seconds, kill it.
45 void WaitForChildToDie(pid_t child, unsigned timeout) {
46 int kq = HANDLE_EINTR(kqueue());
47 file_util::ScopedFD auto_close(&kq);
48 if (kq == -1) {
49 PLOG(ERROR) << "Failed to create kqueue";
50 return;
51 }
52
53 struct kevent event_to_add = {0};
54 EV_SET(&event_to_add, child, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
55 // Register interest with kqueue.
56 int result = HANDLE_EINTR(kevent(kq, &event_to_add, 1, NULL, 0, NULL));
57 if (result == -1 && errno == ESRCH) {
58 // A "No Such Process" error is fine, the process may have died already
59 // and been reaped by someone else.
60 return;
61 }
62
63 if (result == -1) {
64 PLOG(ERROR) << "Failed to register event to listen for death of pid "
65 << child;
66 return;
67 }
68
69 struct kevent event = {0};
70
71 DCHECK(timeout != 0);
72
73 int num_processes_that_died = -1;
74 using base::Time;
75 using base::TimeDelta;
76 // We need to keep track of the elapsed time - if kevent() returns
77 // EINTR in the middle of blocking call we want to make up what's left
78 // of the timeout.
79 TimeDelta time_left = TimeDelta::FromSeconds(timeout);
80 Time wakeup = Time::Now() + time_left;
81 while(time_left.InMilliseconds() > 0) {
82 const struct timespec timeout = time_left.ToTimeSpec();
83 num_processes_that_died = kevent(kq, NULL, 0, &event, 1, &timeout);
84 if (num_processes_that_died >= 0)
85 break;
86 if (num_processes_that_died == -1 && errno == EINTR) {
87 time_left = wakeup - Time::Now();
88 continue;
89 }
90
91 // If we got here, kevent() must have returned -1.
92 PLOG(ERROR) << "kevent() failed";
93 break;
94 }
95
96 if (num_processes_that_died == -1) {
97 PLOG(ERROR) << "kevent failed";
98 return;
99 }
100 if (num_processes_that_died == 1) {
101 if (event.fflags & NOTE_EXIT &&
102 event.ident == static_cast<uintptr_t>(child)) {
103 // Process died, it's safe to make a blocking call here since the
104 // kqueue() notification occurs when the process is already zombified.
105 BlockingReap(child);
106 return;
107 } else {
108 PLOG(ERROR) << "kevent() returned unexpected result - ke.fflags ="
109 << event.fflags
110 << " ke.ident ="
111 << event.ident
112 << " while listening for pid="
113 << child;
114 }
115 }
116
117 // If we got here the child is still alive so kill it...
118 if (kill(child, SIGKILL) == 0) {
119 // SIGKILL is uncatchable. Since the signal was delivered, we can
120 // just wait for the process to die now in a blocking manner.
121 BlockingReap(child);
122 } else {
123 PLOG(ERROR) << "While waiting for " << child << " to terminate we"
124 << " failed to deliver a SIGKILL signal";
125 }
126 }
127
128 } // namespace
129
130 void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process) {
131 // If the child is already dead, then there's nothing to do.
132 if (IsChildDead(process)) {
133 return;
134 }
135 WaitForChildToDie(process, 2);
136 }
OLDNEW
« no previous file with comments | « chrome/common/process_watcher.h ('k') | chrome/common/process_watcher_posix.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698