title: Closing pipe does not interrupt read() in child process spawned from thread
body: <p>In a Linux application I'm spawning multiple programs via <code>fork</code>/<code>execvp</code> and redirect the standard IO streams to a pipe for IPC. I spawn a child process, write some data into the child stdin pipe, close stdin, and then read the child response from the stdout pipe. This worked fine, until I've executed multiple child processes at the same time, using independent threads per child process.</p>
  
  <p>As soon I increase the number of threads, I often find that the child processes hang while reading from stdin – although <code>read</code> should immediately exit with EOF because the stdin pipe has been closed by the parent process.</p>
  
  <p>I've managed to reproduce this behaviour in the following test program. On my systems (Fedora 23, Ubuntu 14.04; <code>g++</code> 4.9, 5, 6 and clang 3.7) the program often simply hangs after three or four child processes have exited. Child processes that have not exited are hanging at <code>read()</code>. Killing any child process that has not exited causes all other child processes to magically wake up from <code>read()</code> and the program continues normally.</p>
  
  <pre><code>#include &lt;chrono&gt;
  #include &lt;iostream&gt;
  #include &lt;mutex&gt;
  #include &lt;thread&gt;
  #include &lt;vector&gt;
  
  #include &lt;sys/fcntl.h&gt;
  #include &lt;sys/wait.h&gt;
  #include &lt;unistd.h&gt;
  
  #define HANDLE_ERR(CODE)     \
      {                        \
          if ((CODE) &lt; 0) {    \
              perror("error"); \
              quick_exit(1);   \
          }                    \
      }
  
  int main()
  {
      std::mutex stdout_mtx;
      std::vector&lt;std::thread&gt; threads;
      for (size_t i = 0; i &lt; 8; i++) {
          threads.emplace_back([&amp;stdout_mtx] {
              int pfd[2]; // Create the communication pipe
              HANDLE_ERR(pipe(pfd));
  
              pid_t pid; // Fork this process
              HANDLE_ERR(pid = fork());
              if (pid == 0) {
                  HANDLE_ERR(close(pfd[1])); // Child, close write end of pipe
                  for (;;) { // Read data from pfd[0] until EOF or other error
                      char buffer;
                      ssize_t bytes;
                      HANDLE_ERR(bytes = read(pfd[0], &amp;buffer, 1));
                      if (bytes &lt; 1) {
                          break;
                      }
  
                      // Allow time for thread switching
                      std::this_thread::sleep_for(std::chrono::milliseconds(
                          100));  // This sleep is crucial for the bug to occur
                  }
                  quick_exit(0); // Exit, do not call C++ destructors
              }
              else {
                  { // Some debug info
                      std::lock_guard&lt;std::mutex&gt; lock(stdout_mtx);
                      std::cout &lt;&lt; "Created child " &lt;&lt; pid &lt;&lt; std::endl;
                  }
  
                  // Close the read end of the pipe
                  HANDLE_ERR(close(pfd[0]));
  
                  // Send some data to the child process
                  HANDLE_ERR(write(pfd[1], "abcdef\n", 7));
  
                  // Close the write end of the pipe, wait for the process to exit
                  int status;
                  HANDLE_ERR(close(pfd[1]));
                  HANDLE_ERR(waitpid(pid, &amp;status, 0));
  
                  { // Some debug info
                      std::lock_guard&lt;std::mutex&gt; lock(stdout_mtx);
                      std::cout &lt;&lt; "Child " &lt;&lt; pid &lt;&lt; " exited with status "
                                &lt;&lt; status &lt;&lt; std::endl;
                  }
              }
          });
      }
  
      // Wait for all threads to complete
      for (auto &amp;thread : threads) {
          thread.join();
      }
  
      return 0;
  }
  </code></pre>
  
  <p>Compile using</p>
  
  <pre><code>g++ test.cpp -o test -lpthread --std=c++11
  </code></pre>
  
  <p>Note that I'm perfectly aware that mixing <code>fork</code> and threads is potentially dangerous, but please keep in mind that in the original code I'm immediately calling <code>execvp</code> after forking, and that I don't have any shared state between the child child process and the main program, except for the pipes specifically created for IPC. My original code (without the threading part) can be found <a href="https://github.com/hbp-sanncs/cypress/blob/c7e1d6babf50ece95e5e9024177cce01d3f30f55/cypress/util/process.cpp" rel="nofollow">here</a>.</p>
  
  <p>To me this almost seems like a bug in the Linux kernel, since the program continues correctly as soon as I kill any of the hanging child processes.</p>