ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial