title: Is select() + non-blocking write() possible on a blocking pipe or socket?
body: <p>The situation is that I have a <em>blocking</em> pipe or socket fd to which I want to <code>write()</code> without blocking, so I do a <code>select()</code> first, but that still doesn't guarantee that <code>write()</code> will not block.</p>
  
  <p>Here is the data I have gathered. Even if <code>select()</code> indicates that
  writing is possible, writing more than <code>PIPE_BUF</code> bytes can block.
  However, writing at most <code>PIPE_BUF</code> bytes doesn't seem to block in
  practice, but it is not mandated by the <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html" rel="nofollow">POSIX spec</a>.</p>
  
  <p>That only specifies atomic behavior. <a href="https://docs.python.org/2/library/select.html" rel="nofollow">Python(!) documentation</a> states that:</p>
  
  <blockquote>
    <p>Files reported as ready for writing by <code>select()</code>, <code>poll()</code> or similar
    interfaces in this module are guaranteed to not block on a write of up
    to <code>PIPE_BUF</code> bytes. This value is guaranteed by POSIX to be at least
    <code>512</code>.</p>
  </blockquote>
  
  <p>In the following test program, set <code>BUF_BYTES</code> to say <code>100000</code> to block in
  <code>write()</code> on Linux, FreeBSD or Solaris following a successful select. I
  assume that named pipes have similar behavior to anonymous pipes.</p>
  
  <p>Unfortunately the same can happen with blocking sockets. Call
  <code>test_socket()</code> in <code>main()</code> and use a largish <code>BUF_BYTES</code> (<code>100000</code> is good
  here too). It's unclear whether there is a safe buffer size like
  <code>PIPE_BUF</code> for sockets.</p>
  
  <pre class="lang-c prettyprint-override"><code>#include &lt;sys/socket.h&gt;
  #include &lt;netinet/in.h&gt;
  #include &lt;arpa/inet.h&gt;
  #include &lt;stdio.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;unistd.h&gt;
  #include &lt;errno.h&gt;
  #include &lt;string.h&gt;
  #include &lt;sys/types.h&gt;
  #include &lt;limits.h&gt;
  #include &lt;stdio.h&gt;
  #include &lt;sys/select.h&gt;
  #include &lt;unistd.h&gt;
  
  #define BUF_BYTES PIPE_BUF
  char buf[BUF_BYTES];
  
  int
  probe_with_select(int nfds, fd_set *readfds, fd_set *writefds,
                    fd_set *exceptfds)
  {
      struct timeval timeout = {0, 0};
      int n_found = select(nfds, readfds, writefds, exceptfds, &amp;timeout);
      if (n_found == -1) {
          perror("select");
      }
      return n_found;
  }
  
  void
  check_if_readable(int fd)
  {
      fd_set fdset;
      FD_ZERO(&amp;fdset);
      FD_SET(fd, &amp;fdset);
      printf("select() for read on fd %d returned %d\n",
             fd, probe_with_select(fd + 1, &amp;fdset, 0, 0));
  }
  
  void
  check_if_writable(int fd)
  {
      fd_set fdset;
      FD_ZERO(&amp;fdset);
      FD_SET(fd, &amp;fdset);
      int n_found = probe_with_select(fd + 1, 0, &amp;fdset, 0);
      printf("select() for write on fd %d returned %d\n", fd, n_found);
      /* if (n_found == 0) { */
      /*     printf("sleeping\n"); */
      /*     sleep(2); */
      /*     int n_found = probe_with_select(fd + 1, 0, &amp;fdset, 0); */
      /*     printf("retried select() for write on fd %d returned %d\n",  */
      /*            fd, n_found); */
      /* } */
  }
  
  void
  test_pipe(void)
  {
      int pipe_fds[2];
      size_t written;
      int i;
      if (pipe(pipe_fds)) {
          perror("pipe failed");
          _exit(1);
      }
      printf("read side pipe fd: %d\n", pipe_fds[0]);
      printf("write side pipe fd: %d\n", pipe_fds[1]);
      for (i = 0; ; i++) {
          printf("i = %d\n", i);
          check_if_readable(pipe_fds[0]);
          check_if_writable(pipe_fds[1]);
          written = write(pipe_fds[1], buf, BUF_BYTES);
          if (written == -1) {
              perror("write");
              _exit(-1);
          }
          printf("written %d bytes\n", written);
      }
  }
  
  void
  serve()
  {
      int listenfd = 0, connfd = 0;
      struct sockaddr_in serv_addr;
  
      listenfd = socket(AF_INET, SOCK_STREAM, 0);
      memset(&amp;serv_addr, '0', sizeof(serv_addr));
  
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
      serv_addr.sin_port = htons(5000);
  
      bind(listenfd, (struct sockaddr*)&amp;serv_addr, sizeof(serv_addr));
  
      listen(listenfd, 10);
  
      connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
  
      sleep(10);
  }
  
  int
  connect_to_server()
  {
      int sockfd = 0, n = 0;
      struct sockaddr_in serv_addr;
  
      if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) &lt; 0) {
          perror("socket");
          exit(-1);
      }
  
      memset(&amp;serv_addr, '0', sizeof(serv_addr));
  
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_port = htons(5000);
  
      if(inet_pton(AF_INET, "127.0.0.1", &amp;serv_addr.sin_addr) &lt;= 0) {
          perror("inet_pton");
          exit(-1);
      }
  
      if (connect(sockfd, (struct sockaddr *)&amp;serv_addr, sizeof(serv_addr)) &lt; 0) {
          perror("connect");
          exit(-1);
      }
  
      return sockfd;
  }
  
  void
  test_socket(void)
  {
      if (fork() == 0)  {
          serve();
      } else {
          int fd;
          int i;
          int written;
          sleep(1);
          fd = connect_to_server();
  
          for (i = 0; ; i++) {
              printf("i = %d\n", i);
              check_if_readable(fd);
              check_if_writable(fd);
              written = write(fd, buf, BUF_BYTES);
              if (written == -1) {
                  perror("write");
                  _exit(-1);
              }
              printf("written %d bytes\n", written);
          }
      }
  }
  
  int
  main(void)
  {
      test_pipe();
      /* test_socket(); */
  }
  </code></pre>