1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,215 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * vim: set ts=8 et sw=2 tw=80: 1.6 + */ 1.7 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.10 + 1.11 +#include <errno.h> 1.12 +#include <signal.h> 1.13 +#include <sys/types.h> 1.14 +#include <sys/wait.h> 1.15 + 1.16 +#include "base/eintr_wrapper.h" 1.17 +#include "base/message_loop.h" 1.18 +#include "base/process_util.h" 1.19 + 1.20 +#include "chrome/common/process_watcher.h" 1.21 + 1.22 +// Maximum amount of time (in milliseconds) to wait for the process to exit. 1.23 +// XXX/cjones: fairly arbitrary, chosen to match process_watcher_win.cc 1.24 +static const int kMaxWaitMs = 2000; 1.25 + 1.26 +namespace { 1.27 + 1.28 +bool 1.29 +IsProcessDead(pid_t process) 1.30 +{ 1.31 + bool exited = false; 1.32 + // don't care if the process crashed, just if it exited 1.33 + base::DidProcessCrash(&exited, process); 1.34 + return exited; 1.35 +} 1.36 + 1.37 + 1.38 +class ChildReaper : public base::MessagePumpLibevent::SignalEvent, 1.39 + public base::MessagePumpLibevent::SignalWatcher 1.40 +{ 1.41 +public: 1.42 + explicit ChildReaper(pid_t process) : process_(process) 1.43 + { 1.44 + } 1.45 + 1.46 + virtual ~ChildReaper() 1.47 + { 1.48 + // subclasses should have cleaned up |process_| already 1.49 + DCHECK(!process_); 1.50 + 1.51 + // StopCatching() is implicit 1.52 + } 1.53 + 1.54 + // @override 1.55 + virtual void OnSignal(int sig) 1.56 + { 1.57 + DCHECK(SIGCHLD == sig); 1.58 + DCHECK(process_); 1.59 + 1.60 + // this may be the SIGCHLD for a process other than |process_| 1.61 + if (IsProcessDead(process_)) { 1.62 + process_ = 0; 1.63 + StopCatching(); 1.64 + } 1.65 + } 1.66 + 1.67 +protected: 1.68 + void WaitForChildExit() 1.69 + { 1.70 + DCHECK(process_); 1.71 + HANDLE_EINTR(waitpid(process_, NULL, 0)); 1.72 + } 1.73 + 1.74 + pid_t process_; 1.75 + 1.76 +private: 1.77 + DISALLOW_EVIL_CONSTRUCTORS(ChildReaper); 1.78 +}; 1.79 + 1.80 + 1.81 +// Fear the reaper 1.82 +class ChildGrimReaper : public ChildReaper, 1.83 + public Task 1.84 +{ 1.85 +public: 1.86 + explicit ChildGrimReaper(pid_t process) : ChildReaper(process) 1.87 + { 1.88 + } 1.89 + 1.90 + virtual ~ChildGrimReaper() 1.91 + { 1.92 + if (process_) 1.93 + KillProcess(); 1.94 + } 1.95 + 1.96 + // @override 1.97 + virtual void Run() 1.98 + { 1.99 + // we may have already been signaled by the time this runs 1.100 + if (process_) 1.101 + KillProcess(); 1.102 + } 1.103 + 1.104 +private: 1.105 + void KillProcess() 1.106 + { 1.107 + DCHECK(process_); 1.108 + 1.109 + if (IsProcessDead(process_)) { 1.110 + process_ = 0; 1.111 + return; 1.112 + } 1.113 + 1.114 + if (0 == kill(process_, SIGKILL)) { 1.115 + // XXX this will block for whatever amount of time it takes the 1.116 + // XXX OS to tear down the process's resources. might need to 1.117 + // XXX rethink this if it proves expensive 1.118 + WaitForChildExit(); 1.119 + } 1.120 + else { 1.121 + CHROMIUM_LOG(ERROR) << "Failed to deliver SIGKILL to " << process_ << "!" 1.122 + << "("<< errno << ")."; 1.123 + } 1.124 + process_ = 0; 1.125 + } 1.126 + 1.127 + DISALLOW_EVIL_CONSTRUCTORS(ChildGrimReaper); 1.128 +}; 1.129 + 1.130 + 1.131 +class ChildLaxReaper : public ChildReaper, 1.132 + public MessageLoop::DestructionObserver 1.133 +{ 1.134 +public: 1.135 + explicit ChildLaxReaper(pid_t process) : ChildReaper(process) 1.136 + { 1.137 + } 1.138 + 1.139 + virtual ~ChildLaxReaper() 1.140 + { 1.141 + // WillDestroyCurrentMessageLoop() should have reaped process_ already 1.142 + DCHECK(!process_); 1.143 + } 1.144 + 1.145 + // @override 1.146 + virtual void OnSignal(int sig) 1.147 + { 1.148 + ChildReaper::OnSignal(sig); 1.149 + 1.150 + if (!process_) { 1.151 + MessageLoop::current()->RemoveDestructionObserver(this); 1.152 + delete this; 1.153 + } 1.154 + } 1.155 + 1.156 + // @override 1.157 + virtual void WillDestroyCurrentMessageLoop() 1.158 + { 1.159 + DCHECK(process_); 1.160 + 1.161 + WaitForChildExit(); 1.162 + process_ = 0; 1.163 + 1.164 + // XXX don't think this is necessary, since destruction can only 1.165 + // be observed once, but can't hurt 1.166 + MessageLoop::current()->RemoveDestructionObserver(this); 1.167 + delete this; 1.168 + } 1.169 + 1.170 +private: 1.171 + DISALLOW_EVIL_CONSTRUCTORS(ChildLaxReaper); 1.172 +}; 1.173 + 1.174 +} // namespace <anon> 1.175 + 1.176 + 1.177 +/** 1.178 + * Do everything possible to ensure that |process| has been reaped 1.179 + * before this process exits. 1.180 + * 1.181 + * |grim| decides how strict to be with the child's shutdown. 1.182 + * 1.183 + * | child exit timeout | upon parent shutdown: 1.184 + * +--------------------+---------------------------------- 1.185 + * force=true | 2 seconds | kill(child, SIGKILL) 1.186 + * force=false | infinite | waitpid(child) 1.187 + * 1.188 + * If a child process doesn't shut down properly, and |grim=false| 1.189 + * used, then the parent will wait on the child forever. So, 1.190 + * |force=false| is expected to be used when an external entity can be 1.191 + * responsible for terminating hung processes, e.g. automated test 1.192 + * harnesses. 1.193 + */ 1.194 +void 1.195 +ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process, 1.196 + bool force) 1.197 +{ 1.198 + DCHECK(process != base::GetCurrentProcId()); 1.199 + DCHECK(process > 0); 1.200 + 1.201 + if (IsProcessDead(process)) 1.202 + return; 1.203 + 1.204 + MessageLoopForIO* loop = MessageLoopForIO::current(); 1.205 + if (force) { 1.206 + ChildGrimReaper* reaper = new ChildGrimReaper(process); 1.207 + 1.208 + loop->CatchSignal(SIGCHLD, reaper, reaper); 1.209 + // |loop| takes ownership of |reaper| 1.210 + loop->PostDelayedTask(FROM_HERE, reaper, kMaxWaitMs); 1.211 + } else { 1.212 + ChildLaxReaper* reaper = new ChildLaxReaper(process); 1.213 + 1.214 + loop->CatchSignal(SIGCHLD, reaper, reaper); 1.215 + // |reaper| destroys itself after destruction notification 1.216 + loop->AddDestructionObserver(reaper); 1.217 + } 1.218 +}