clang attribute wrappers
[folly.git] / folly / Subprocess.h
index 0354b4b21e56d11450fbd2db98fc41650ac33de4..531d9181dd25f3cb38ad33bc7c236cd3c4b2f5d9 100644 (file)
@@ -126,12 +126,18 @@ class ProcessReturnCode {
   friend class Subprocess;
  public:
   enum State {
+    // Subprocess starts in the constructor, so this state designates only
+    // default-initialized or moved-out ProcessReturnCodes.
     NOT_STARTED,
     RUNNING,
     EXITED,
     KILLED
   };
 
+  // Default-initialized for convenience. Subprocess::returnCode() will
+  // never produce this value.
+  ProcessReturnCode() : ProcessReturnCode(RV_NOT_STARTED) {}
+
   // Trivially copyable
   ProcessReturnCode(const ProcessReturnCode& p) = default;
   ProcessReturnCode& operator=(const ProcessReturnCode& p) = default;
@@ -209,7 +215,7 @@ class CalledProcessError : public SubprocessError {
  public:
   explicit CalledProcessError(ProcessReturnCode rc);
   ~CalledProcessError() throw() = default;
-  const char* what() const throw() FOLLY_OVERRIDE { return what_.c_str(); }
+  const char* what() const throw() override { return what_.c_str(); }
   ProcessReturnCode returnCode() const { return returnCode_; }
  private:
   ProcessReturnCode returnCode_;
@@ -223,7 +229,7 @@ class SubprocessSpawnError : public SubprocessError {
  public:
   SubprocessSpawnError(const char* executable, int errCode, int errnoValue);
   ~SubprocessSpawnError() throw() = default;
-  const char* what() const throw() FOLLY_OVERRIDE { return what_.c_str(); }
+  const char* what() const throw() override { return what_.c_str(); }
   int errnoValue() const { return errnoValue_; }
 
  private:
@@ -241,6 +247,23 @@ class Subprocess {
   static const int PIPE_IN = -3;
   static const int PIPE_OUT = -4;
 
+  /**
+   * See Subprocess::Options::dangerousPostForkPreExecCallback() for usage.
+   * Every derived class should include the following warning:
+   *
+   * DANGER: This class runs after fork in a child processes. Be fast, the
+   * parent thread is waiting, but remember that other parent threads are
+   * running and may mutate your state.  Avoid mutating any data belonging to
+   * the parent.  Avoid interacting with non-POD data that originated in the
+   * parent.  Avoid any libraries that may internally reference non-POD data.
+   * Especially beware parent mutexes -- for example, glog's LOG() uses one.
+   */
+  struct DangerousPostForkPreExecCallback {
+    virtual ~DangerousPostForkPreExecCallback() {}
+    // This must return 0 on success, or an `errno` error code.
+    virtual int operator()() = 0;
+  };
+
   /**
    * Class representing various options: file descriptor behavior, and
    * whether to use $PATH for searching for the executable,
@@ -335,6 +358,43 @@ class Subprocess {
       return *this;
     }
 
+    /**
+     * *** READ THIS WHOLE DOCBLOCK BEFORE USING ***
+     *
+     * Run this callback in the child after the fork, just before the
+     * exec(), and after the child's state has been completely set up:
+     *  - signal handlers have been reset to default handling and unblocked
+     *  - the working directory was set
+     *  - closed any file descriptors specified via Options()
+     *  - set child process flags (see code)
+     *
+     * This is EXTREMELY DANGEROUS. For example, this innocuous-looking code
+     * can cause a fraction of your Subprocess launches to hang forever:
+     *
+     *   LOG(INFO) << "Hello from the child";
+     *
+     * The reason is that glog has an internal mutex. If your fork() happens
+     * when the parent has the mutex locked, the child will wait forever.
+     *
+     * == GUIDELINES ==
+     *
+     * - Be quick -- the parent thread is blocked until you exit.
+     * - Remember that other parent threads are running, and may mutate your
+     *   state.
+     * - Avoid mutating any data belonging to the parent.
+     * - Avoid interacting with non-POD data that came from the parent.
+     * - Avoid any libraries that may internally reference non-POD state.
+     * - Especially beware parent mutexes, e.g. LOG() uses a global mutex.
+     * - Avoid invoking the parent's destructors (you can accidentally
+     *   delete files, terminate network connections, etc).
+     * - Read http://ewontfix.com/7/
+     */
+    Options& dangerousPostForkPreExecCallback(
+        DangerousPostForkPreExecCallback* cob) {
+      dangerousPostForkPreExecCallback_ = cob;
+      return *this;
+    }
+
     /**
      * Helpful way to combine Options.
      */
@@ -350,6 +410,8 @@ class Subprocess {
     int parentDeathSignal_{0};
 #endif
     bool processGroupLeader_{false};
+    DangerousPostForkPreExecCallback*
+      dangerousPostForkPreExecCallback_{nullptr};
   };
 
   static Options pipeStdin() { return Options().stdin(PIPE); }