Improve waitpid error handling
authorAlexey Spiridonov <lesha@fb.com>
Mon, 27 Jul 2015 02:32:03 +0000 (19:32 -0700)
committerfacebook-github-bot-4 <folly-bot@fb.com>
Mon, 27 Jul 2015 03:22:10 +0000 (20:22 -0700)
Summary: Using `checkUnixError` after `waitpid()` is confusing and useless, because the only two possible errors are `ECHILD` (some other part of the program waited for this process!?) and `EINVAL` (invalid options, which are hardcoded in both `Subprocess::wait()` and `poll()`). Neither of these are recoverable. Moreover, even if the caller catches the exception, `~Subprocess` is still booby-trapped to `abort()` since its status remains `RUNNING`.

In short, just abort.

Reviewed By: @yfeldblum

Differential Revision: D2079415

folly/Subprocess.cpp
folly/Subprocess.h

index 7ecc8de486634aa315187438808b9a358eb5393d..b517c55df33c54377ebb886b4bbd052b964ef69b 100644 (file)
@@ -542,7 +542,10 @@ ProcessReturnCode Subprocess::poll() {
   DCHECK_GT(pid_, 0);
   int status;
   pid_t found = ::waitpid(pid_, &status, WNOHANG);
-  checkUnixError(found, "waitpid");
+  // The spec guarantees that EINTR does not occur with WNOHANG, so the only
+  // two remaining errors are ECHILD (other code reaped the child?), or
+  // EINVAL (cosmic rays?), both of which merit an abort:
+  PCHECK(found != -1) << "waitpid(" << pid_ << ", &status, WNOHANG)";
   if (found != 0) {
     // Though the child process had quit, this call does not close the pipes
     // since its descendants may still be using them.
@@ -568,7 +571,9 @@ ProcessReturnCode Subprocess::wait() {
   do {
     found = ::waitpid(pid_, &status, 0);
   } while (found == -1 && errno == EINTR);
-  checkUnixError(found, "waitpid");
+  // The only two remaining errors are ECHILD (other code reaped the
+  // child?), or EINVAL (cosmic rays?), and both merit an abort:
+  PCHECK(found != -1) << "waitpid(" << pid_ << ", &status, WNOHANG)";
   // Though the child process had quit, this call does not close the pipes
   // since its descendants may still be using them.
   DCHECK_EQ(found, pid_);
index b7c377e6cee87963b74811acaba0d677c08f72be..0354b4b21e56d11450fbd2db98fc41650ac33de4 100644 (file)
@@ -410,11 +410,12 @@ class Subprocess {
   ProcessReturnCode returnCode() const { return returnCode_; }
 
   /**
-   * Poll the child's status and return it, return -1 if the process
-   * is still running.  NOTE that it is illegal to call poll again after
-   * poll indicated that the process has terminated, or to call poll on a
-   * process that hasn't been successfully started (the constructor threw an
-   * exception).
+   * Poll the child's status and return it. Return the exit status if the
+   * subprocess had quit, or RUNNING otherwise.  Throws an std::logic_error
+   * if called on a Subprocess whose status is no longer RUNNING.  No other
+   * exceptions are possible.  Aborts on egregious violations of contract,
+   * e.g. if you wait for the underlying process without going through this
+   * Subprocess instance.
    */
   ProcessReturnCode poll();
 
@@ -426,9 +427,10 @@ class Subprocess {
   bool pollChecked();
 
   /**
-   * Wait for the process to terminate and return its status.
-   * Similarly to poll, it is illegal to call wait after the process
-   * has already been reaped or if the process has not successfully started.
+   * Wait for the process to terminate and return its status.  Like poll(),
+   * the only exception this can throw is std::logic_error if you call this
+   * on a Subprocess whose status is RUNNING.  Aborts on egregious
+   * violations of contract, like an out-of-band waitpid(p.pid(), 0, 0).
    */
   ProcessReturnCode wait();