title: Linux non-canonical input mode works different in Linux and WSL
body: <p>The program below demonstrates the issue. When you build it with <code>clang main.c -o main</code>, run, and press a few keys, you'll see that the program always stops at &quot;Calling poll()...&quot; on Linux. Now if you do the same on an up-to-date WSL (Windows Subsystem for Linux), you will see that it stops at &quot;Calling read()...&quot;. In other words, on Linux it's <code>poll()</code> that blocks, on WSL it's <code>read()</code>.</p>
  <p>The program basically sets the input to non-canonical mode, with <code>VTIME</code> and <code>VMIN</code> also set 0. If I'm reading the two references below right</p>
  <ul>
  <li><a href="https://www.gnu.org/software/libc/manual/html_node/Canonical-or-Not.html" rel="nofollow noreferrer">https://www.gnu.org/software/libc/manual/html_node/Canonical-or-Not.html</a></li>
  <li><a href="https://www.gnu.org/software/libc/manual/html_node/Noncanonical-Input.html" rel="nofollow noreferrer">https://www.gnu.org/software/libc/manual/html_node/Noncanonical-Input.html</a></li>
  </ul>
  <p>then I think in this case <code>read()</code> should never block and I should never see the program wait after printing &quot;Calling read()...&quot;. Yet on WSL this is the case.</p>
  <p>Now if you pass <code>-DNONBLOCK</code> when compiling and run, both on Linux and WSL the program always blocks after printing &quot;Calling poll()...&quot;, which is the expected behavior.</p>
  <p>I'm wondering if this is a bug on WSL, or I'm misreading the non-canonical mode documentation.</p>
  <p>Program:</p>
  <pre><code>// clang main.c -o main
  
  #include &lt;errno.h&gt;
  #include &lt;fcntl.h&gt;
  #include &lt;poll.h&gt;
  #include &lt;stdbool.h&gt;
  #include &lt;stdint.h&gt;
  #include &lt;stdio.h&gt;
  #include &lt;string.h&gt;
  #include &lt;termios.h&gt;
  #include &lt;unistd.h&gt;
  
  void main_loop();
  bool read_until_empty();
  
  int main()
  {
  #if defined(NONBLOCK)
      int old_stdin_flags = fcntl(STDIN_FILENO, F_GETFL);
      fcntl(STDIN_FILENO, F_SETFL, old_stdin_flags | O_NONBLOCK);
      printf(&quot;Enabled non-blocking mode\n&quot;);
  #endif
  
      bool fail = false;
  
      int tty = open(&quot;/dev/tty&quot;, O_RDWR);
      if (tty == -1) {
          printf(&quot;Unable to open /dev/tty\n&quot;);
          fail = true;
          goto cleanup;
      }
  
      struct termios old_termios;
      if (tcgetattr(STDIN_FILENO, &amp;old_termios) == -1) {
          printf(&quot;tcgetattr failed\n&quot;);
          fail = true;
          goto cleanup;
      }
  
      struct termios new_termios = old_termios;
      cfmakeraw(&amp;new_termios);
      // These really need to be after cfmakeraw() !!!!
      new_termios.c_cc[VMIN] = 0;
      new_termios.c_cc[VTIME] = 0;
      if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &amp;new_termios) == -1) {
          printf(&quot;tcsetattr failed\n&quot;);
          fail = true;
          goto cleanup;
      }
  
      printf(&quot;Type 'q' to quit.\n&quot;);
      main_loop();
  
  cleanup:
      tcsetattr(STDIN_FILENO, TCSAFLUSH, &amp;old_termios);
  
  #if defined(NONBLOCK)
      fcntl(STDIN_FILENO, F_SETFL, old_stdin_flags);
      printf(&quot;Restored stdin flags\n&quot;);
  #endif
  
      if (fail) {
          return 1;
      }
  }
  
  void main_loop()
  {
      printf(&quot;main_loop\n&quot;);
      struct pollfd fds[1] = { { .fd = STDIN_FILENO, .events = POLLIN } };
      
      for (;;)
      {
          printf(&quot;Calling poll()...\n&quot;);
          int poll_ret = poll(fds, 1, -1);
          if (poll_ret &gt; 0)
          {
              printf(&quot;stdin ready for reading\n&quot;);
              if (read_until_empty())
              {
                  return;
              }
          }
      }
  }
  
  bool read_until_empty()
  {
      uint8_t buf[10000];
  
      for (;;)
      {
          printf(&quot;Calling read()...\n&quot;);
          ssize_t n_read = read(STDIN_FILENO, buf, 10000);
  
          if (n_read == -1)
          {
              if (errno == EAGAIN || errno == EWOULDBLOCK)
              {
                  printf(&quot;EAGAIN or EWOULDBLOCK\n&quot;);
                  return false;
              }
              else
              {
                  printf(&quot;Error %d: %s\n&quot;, errno, strerror(errno));
                  return false;
              }
          }
          else if (n_read == 0)
          {
              printf(&quot;stdin is empty\n&quot;);
              return false;
          }
          else
          {
              printf(&quot;Read %zd bytes\n&quot;, n_read);
              if (buf[0] == 3 || buf[0] == 113)
              {
                  return true;
              }
          }
      }
  }
  </code></pre>