Add a method to ProcessReaper to forget a tracked pid

Allow callers to remove a tracked pid if tracking is
no longer desired (e.g., if the caller has reaped the
process themselves.)

BUG=chromium:535910

Change-Id: Ie056c54a66c5aed539d2a77a41135f9b2da66582
diff --git a/brillo/process_reaper.cc b/brillo/process_reaper.cc
index 5ee1195..c2f81b2 100644
--- a/brillo/process_reaper.cc
+++ b/brillo/process_reaper.cc
@@ -45,6 +45,10 @@
   return true;
 }
 
+bool ProcessReaper::ForgetChild(pid_t pid) {
+  return watched_processes_.erase(pid) != 0;
+}
+
 bool ProcessReaper::HandleSIGCHLD(const struct signalfd_siginfo& sigfd_info) {
   // One SIGCHLD may correspond to multiple terminated children, so ignore
   // sigfd_info and reap any available children.
diff --git a/brillo/process_reaper.h b/brillo/process_reaper.h
index d88f572..937c88c 100644
--- a/brillo/process_reaper.h
+++ b/brillo/process_reaper.h
@@ -44,6 +44,13 @@
                      pid_t pid,
                      const ChildCallback& callback);
 
+  // Stop watching child process |pid|.  This is useful in situations
+  // where the child process may have been reaped outside of the signal
+  // handler, or the caller is no longer interested in being notified about
+  // this child process anymore.  Returns true if a child was removed from
+  // the watchlist.
+  bool ForgetChild(pid_t pid);
+
  private:
   // SIGCHLD handler for the AsynchronousSignalHandler. Always returns false
   // (meaning that the signal handler should not be unregistered).
diff --git a/brillo/process_reaper_unittest.cc b/brillo/process_reaper_unittest.cc
index 499b908..ae888d2 100644
--- a/brillo/process_reaper_unittest.cc
+++ b/brillo/process_reaper_unittest.cc
@@ -118,4 +118,24 @@
   brillo_loop_.Run();
 }
 
+TEST_F(ProcessReaperTest, ReapKilledAndForgottenChild) {
+  pid_t pid = ForkChildAndExit(0);
+  EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::Bind(
+      [this](const siginfo_t& info) {
+        ADD_FAILURE() << "Child process was still tracked.";
+        this->brillo_loop_.BreakLoop();
+      })));
+  EXPECT_TRUE(process_reaper_.ForgetChild(pid));
+
+  // A second call should return failure.
+  EXPECT_FALSE(process_reaper_.ForgetChild(pid));
+
+  // Run the loop with a timeout, as the BreakLoop() above is not expected.
+  brillo_loop_.PostDelayedTask(FROM_HERE,
+                               base::Bind(&MessageLoop::BreakLoop,
+                                          base::Unretained(&brillo_loop_)),
+                               base::TimeDelta::FromMilliseconds(100));
+  brillo_loop_.Run();
+}
+
 }  // namespace brillo