Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 et sw=2 tw=80:
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include <errno.h>
9 #include <signal.h>
10 #include <sys/types.h>
11 #include <sys/wait.h>
13 #include "base/eintr_wrapper.h"
14 #include "base/message_loop.h"
15 #include "base/process_util.h"
17 #include "chrome/common/process_watcher.h"
19 // Maximum amount of time (in milliseconds) to wait for the process to exit.
20 // XXX/cjones: fairly arbitrary, chosen to match process_watcher_win.cc
21 static const int kMaxWaitMs = 2000;
23 namespace {
25 bool
26 IsProcessDead(pid_t process)
27 {
28 bool exited = false;
29 // don't care if the process crashed, just if it exited
30 base::DidProcessCrash(&exited, process);
31 return exited;
32 }
35 class ChildReaper : public base::MessagePumpLibevent::SignalEvent,
36 public base::MessagePumpLibevent::SignalWatcher
37 {
38 public:
39 explicit ChildReaper(pid_t process) : process_(process)
40 {
41 }
43 virtual ~ChildReaper()
44 {
45 // subclasses should have cleaned up |process_| already
46 DCHECK(!process_);
48 // StopCatching() is implicit
49 }
51 // @override
52 virtual void OnSignal(int sig)
53 {
54 DCHECK(SIGCHLD == sig);
55 DCHECK(process_);
57 // this may be the SIGCHLD for a process other than |process_|
58 if (IsProcessDead(process_)) {
59 process_ = 0;
60 StopCatching();
61 }
62 }
64 protected:
65 void WaitForChildExit()
66 {
67 DCHECK(process_);
68 HANDLE_EINTR(waitpid(process_, NULL, 0));
69 }
71 pid_t process_;
73 private:
74 DISALLOW_EVIL_CONSTRUCTORS(ChildReaper);
75 };
78 // Fear the reaper
79 class ChildGrimReaper : public ChildReaper,
80 public Task
81 {
82 public:
83 explicit ChildGrimReaper(pid_t process) : ChildReaper(process)
84 {
85 }
87 virtual ~ChildGrimReaper()
88 {
89 if (process_)
90 KillProcess();
91 }
93 // @override
94 virtual void Run()
95 {
96 // we may have already been signaled by the time this runs
97 if (process_)
98 KillProcess();
99 }
101 private:
102 void KillProcess()
103 {
104 DCHECK(process_);
106 if (IsProcessDead(process_)) {
107 process_ = 0;
108 return;
109 }
111 if (0 == kill(process_, SIGKILL)) {
112 // XXX this will block for whatever amount of time it takes the
113 // XXX OS to tear down the process's resources. might need to
114 // XXX rethink this if it proves expensive
115 WaitForChildExit();
116 }
117 else {
118 CHROMIUM_LOG(ERROR) << "Failed to deliver SIGKILL to " << process_ << "!"
119 << "("<< errno << ").";
120 }
121 process_ = 0;
122 }
124 DISALLOW_EVIL_CONSTRUCTORS(ChildGrimReaper);
125 };
128 class ChildLaxReaper : public ChildReaper,
129 public MessageLoop::DestructionObserver
130 {
131 public:
132 explicit ChildLaxReaper(pid_t process) : ChildReaper(process)
133 {
134 }
136 virtual ~ChildLaxReaper()
137 {
138 // WillDestroyCurrentMessageLoop() should have reaped process_ already
139 DCHECK(!process_);
140 }
142 // @override
143 virtual void OnSignal(int sig)
144 {
145 ChildReaper::OnSignal(sig);
147 if (!process_) {
148 MessageLoop::current()->RemoveDestructionObserver(this);
149 delete this;
150 }
151 }
153 // @override
154 virtual void WillDestroyCurrentMessageLoop()
155 {
156 DCHECK(process_);
158 WaitForChildExit();
159 process_ = 0;
161 // XXX don't think this is necessary, since destruction can only
162 // be observed once, but can't hurt
163 MessageLoop::current()->RemoveDestructionObserver(this);
164 delete this;
165 }
167 private:
168 DISALLOW_EVIL_CONSTRUCTORS(ChildLaxReaper);
169 };
171 } // namespace <anon>
174 /**
175 * Do everything possible to ensure that |process| has been reaped
176 * before this process exits.
177 *
178 * |grim| decides how strict to be with the child's shutdown.
179 *
180 * | child exit timeout | upon parent shutdown:
181 * +--------------------+----------------------------------
182 * force=true | 2 seconds | kill(child, SIGKILL)
183 * force=false | infinite | waitpid(child)
184 *
185 * If a child process doesn't shut down properly, and |grim=false|
186 * used, then the parent will wait on the child forever. So,
187 * |force=false| is expected to be used when an external entity can be
188 * responsible for terminating hung processes, e.g. automated test
189 * harnesses.
190 */
191 void
192 ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process,
193 bool force)
194 {
195 DCHECK(process != base::GetCurrentProcId());
196 DCHECK(process > 0);
198 if (IsProcessDead(process))
199 return;
201 MessageLoopForIO* loop = MessageLoopForIO::current();
202 if (force) {
203 ChildGrimReaper* reaper = new ChildGrimReaper(process);
205 loop->CatchSignal(SIGCHLD, reaper, reaper);
206 // |loop| takes ownership of |reaper|
207 loop->PostDelayedTask(FROM_HERE, reaper, kMaxWaitMs);
208 } else {
209 ChildLaxReaper* reaper = new ChildLaxReaper(process);
211 loop->CatchSignal(SIGCHLD, reaper, reaper);
212 // |reaper| destroys itself after destruction notification
213 loop->AddDestructionObserver(reaper);
214 }
215 }