Using fifo open in non-blocking mode with select

17,822

The Q1 is expected by select() or poll(). See the linked question. A graceful resolution is to open another fd on the same fifo and close the original.

I believe the Q2 was also expected on some versions of kernel. The man 7 fifo have a paragraph about it:

   Under Linux, opening a FIFO for read and write will succeed both in 
   blocking and nonblocking mode.  POSIX leaves this behavior undefined.
   This can be used to open a FIFO for writing while there are no
   readers available.

That paragraph seems to claim that you can successfully open the write end of a fifo anytime, as observed in Q2 by the original author.

Though it seems to contradict the previous paragraph as the original question quoted also from the man 7 fifo page that is basically saying the open shall fail instead of succeed:

   A process can open a FIFO in nonblocking mode.  In this case, opening
   for read-only succeeds even if no one has opened on the write side
   yet and opening for write-only fails with ENXIO (no such device or
   address) unless the other end has already been opened.

I'm seeing opening the write end in non-blocking mode shall fail when the read end is not open, on a 4.9.37 kernel. It must have changed from version 3.8 to 4.9, I guess.

Share:
17,822
Victor Dodon
Author by

Victor Dodon

Updated on July 03, 2022

Comments

  • Victor Dodon
    Victor Dodon almost 2 years

    I have two processes A and B. The communication flow is always A -> B, but I need to do it using a named pipe, because I must use the pipe file descriptor in a select call inside the B process, and the data written to the pipe must persist when any or both of the processes exit.

    The pipe is opened in non-blocking mode on both ends. In process A:

    int push_fifo_fd = open(FIFO_NAME, O_WRONLY | O_NONBLOCK | O_CREAT, 0644);
    

    In process B:

    int fd = open(FIFO_NAME, O_RDONLY | O_NONBLOCK | O_CREAT, 0644);
    

    Q1. The process B uses curl multi interface, so I get the fd_sets of the curl multi handle and add the "fd" descriptor to the read fd_set, than make a call to select, to get the file descriptors available for reads and writes. In every call to select, "fd" is contained in the result read fd_set, but read returns 0, even if the write end is opened. This causes the process B to use 100% of processor time. I mention that I don't know to order in which the ends of the pipe are opened. The relevant code from B:

    while (1)
    {
        fd_set read_fds, write_fds, err_fds;
    
        FD_ZERO(&read_fds);
        FD_ZERO(&write_fds);
        FD_ZERO(&err_fds);
    
        FD_SET(fifo_fd, &read_fds);
        // some code
        ccode = curl_multi_fdset(curlm, &read_fds, &write_fds, &err_fds, &max_fd);
        max_fd = MAX(max_fd, fifo_fd);
    
        rc = select(max_fd + 1, &read_fds, &write_fds, &err_fds, &timeout);
        switch (rc)
        {
            case -1:
                WARN("select");
                continue;
    
            case 0:
            default:
                {
                    if (FD_ISSET(fifo_fd, &read_fds))
                    {
                        // read from the fifo_fd
                    }
    
                    /* Now look at the handles that need attention */
                    int old_running_handles = running_handles;
    
                    ccode = curl_multi_perform(curlm, &running_handles);
                    if (ccode != CURLM_OK && ccode != CURLM_CALL_MULTI_PERFORM)
                    {
                        WARN("curl_multi_perform error: %s", curl_multi_strerror(ccode));
                        continue;
                    }
    
                    if (running_handles != old_running_handles)
                    {
                        CURLMsg *curl_msg;
                        int left_msgs = 0;
                        while ((curl_msg = curl_multi_info_read(curlm, &left_msgs)) != NULL)
                        {
                            // treat each easy handle
                        }
                    }
                }
                break;
        }
    }
    

    Q2. In "man 7 fifo" is said "A process can open a FIFO in nonblocking mode. In this case, opening for read-only will succeed even if no-one has opened on the write side yet, opening for write-only will fail with ENXIO (no such device or address) unless the other end has already been opened." but the process A always can open successfully the write end of the pipe in non-blocking mode even the read end is not opened. Why is that? The platform on which I test is Ubuntu server 12.04.3, kernel 3.8.0-29.

  • John Bollinger
    John Bollinger almost 6 years
    This answer is not responsive to either fork of the OP's question. It appears to be intend to respond to the second, "Q2" part, but the quotation presented describes a different situation than the OP asks about.
  • minghua
    minghua over 5 years
    Noticed the previous answer is in the same view regarding Q2. Though I did not understand it before writing this answer up.