8

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.

Victor Dodon
  • 1,796
  • 3
  • 18
  • 27
  • If it's important that the file designated by `FIFO_NAME` in fact be a FIFO, then it is counterproductive to use `O_CREAT` when you open it for either writing or reading. If `open()` ever does have to create it then it will do so as an ordinary file. – John Bollinger Jun 17 '18 at 20:14
  • The behavior described in this question seems to contradict the documentation of the relevant system interfaces. Ordinarily, I would give the OP a chance to provide an MCVE, but given that it's a five-year-old necro'd question, it seems unlikely that an MCVE will be forthcoming. Going straight to voting to close, instead. – John Bollinger Jun 17 '18 at 20:23
  • I'm seeing the Q1 problem on kernel 4.9.37, but not the Q2 problem. – minghua Aug 25 '18 at 08:40
  • You must treat the case where select() returns zero separately. and **not** inspect the FD_SETs in that case. – wildplasser Dec 26 '18 at 11:01
  • Suppose separate reader and writer processes open a FIFO. On most systems, if the writer writes to the FIFO and exits, and the reader also exits without reading the written information, then the unread information is discarded when the last process with an open file descriptor for the FIFO closes it (e.g. because it exits). Thus, using a FIFO won't satisfy the requirement _"the data written to the pipe must persist when any or both of the processes exit"_ on most systems. _[…continued…]_ – Jonathan Leffler Aug 25 '19 at 04:56
  • _[…continuation…]_ . Demo: `mkfifo FIFO; (echo "Hello World" > FIFO) & (echo "Hi" < FIFO); (echo "Goodbye, Cruel World!" > FIFO) & cat FIFO; rm -f FIFO` — The output is `Hi` and `Goodbye, Cruel World!` (plus job control information). The information `Hello World!` is lost. Change `echo "Hi" < FIFO` to `cat FIFO` and the `Hello World!` is seen (and no "Hi" because that is not written anywhere). – Jonathan Leffler Aug 25 '19 at 04:58

2 Answers2

2

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.

minghua
  • 5,981
  • 6
  • 45
  • 71
  • Noticed the previous answer is in the same view regarding Q2. Though I did not understand it before writing this answer up. – minghua Aug 25 '18 at 17:01
0

Because:

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. A process that uses both ends of the connection in order to communicate with itself should be very careful to avoid deadlocks.

from http://linux.die.net/man/7/fifo

  • 1
    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. – John Bollinger Jun 17 '18 at 20:17