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

     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 }

mercurial