0

When using a FIFO on a single process, it looks like after both ends have been opened and one is then closed, it is not possible to reuse the FIFO. Any attempt to reopen the closed end fails or the returned file descriptor is useless.

Is it possible to work around this behavior, or do we have to keep both ends of the FIFO open until we are absolutely sure we don't need it anymore?

Here is some test code that shows and attempt to reopen a closed write end of a FIFO:

#include <iostream>
#include <cstdio>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

using namespace std;

int main(int argc, const char **argv)
{
      cout << "Creating an instance of a named pipe..." << endl;
      mode_t prevMask = umask(0);
      if (mknod("my_pipe", S_IFIFO | 0666, 0))
         return -1;
      umask(prevMask);

      cout << "Opening Read end..." << endl;
      int fdM = open("my_pipe", O_RDONLY | O_NONBLOCK);
      if (fdM == -1)
         return -1;

      cout << "Opening Write end..." << endl;
      int fdS = open("my_pipe", O_WRONLY | O_NONBLOCK);
      if (fdS == -1)
         return -1;

      cout << "Sending data to pipe..." << endl;
      const char *data = "Hello my friend!";
      ssize_t NbOfBytesWritten = write(fdS, data, strlen(data));
      if (NbOfBytesWritten < 0)
         return -1;
      cout << "Number of bytes sent: " << NbOfBytesWritten << endl;

      cout << "Closing Write end..." << endl;
      if (close(fdS))
         return -1;

      cout << "Reopening Write end..." << endl;
      fdS = open("my_pipe", O_WRONLY | O_NONBLOCK);
      if (fdS == -1)
      {
         cout << "open() - failed("<< errno << "): " << strerror(errno) << '.';
         remove("my_pipe");
         return -1;
      }

      cout << "Sending some more data to pipe..." << endl;
      data = "What's up?";
      NbOfBytesWritten = write(fdS, data, strlen(data));
      if (NbOfBytesWritten < 0)
         return -1;
      cout << "Number of bytes sent: " << NbOfBytesWritten << endl;

      cout << "Reading data from pipe..." << endl;
      char buff[128];
      ssize_t numBytesRead = read(fdM, buff, 127);
      if (NbOfBytesWritten < 0)
         return -1;
      buff[numBytesRead] = '\0'; // null terminate the string
      cout << "Number of bytes read: " << numBytesRead << endl;
      cout << "Message: " << buff << endl;

      cout << "Closing Write end..." << endl;
      if (close(fdS))
         return -1;

      cout << "Closing Read end..." << endl;
      if (close(fdM))
         return -1;

      cout << "Deleting pipe..." << endl;
      if (remove("my_pipe"))
         return -1;

   return 0;
}

Here is the output:

Creating an instance of a named pipe...
Opening Read end...
Opening Write end...
Sending data to pipe...
Number of bytes sent: 16
Closing Write end...
Reopening Write end...
open() - failed(6): No such device or address.

I also tested similar code trying to reopen a closed read end (While the write end was kept open). In that case the open() function succeed, but the read() function using the file descriptor returned by open() fails with: Communication error on send. (70)

EDIT:

I'm using CYGWIN.

Guett31
  • 258
  • 2
  • 15
  • This runs with no errors on my Linux. – Petr Skocik Dec 17 '16 at 01:19
  • You need to keep both ends open. When all the write ends are closed, the reader gets EOF. And when all the read ends are closed, the writer gets `EPIPE` if they try to write. – Barmar Dec 17 '16 at 01:23
  • @Barmar - The question is: "Why is it not possible to reopen the end of a FIFO?" It is not about the effect of writing to, or reading from, a FIFO that is not connected on both ends. – Guett31 Dec 17 '16 at 02:10
  • Actually, I take it back. I think you can reopen the write end. I just did a test where I ran `sleep 500 < /tmp/fifo` in one terminal. Then in another terminal I did `echo foo > /tmp/fifo; echo bar > /tmp/fifo`. – Barmar Dec 17 '16 at 02:15
  • Your output doesn't match the program. Where is the `Reading data from pipe...` message? – Barmar Dec 17 '16 at 02:20
  • The program does not get there. It fails on "fdS = open("my_pipe", O_WRONLY | O_NONBLOCK);" when trying to reopen the closed write end. – Guett31 Dec 17 '16 at 02:30

1 Answers1

1

You code works fine on Linux. I think the issue you are running into is that named pipes on CYGWIN don't work very well and fail to follow POSIX semantics. See FIFO (named pipe) is broken on Cygwin. Likely the same problem you have.

TrentP
  • 4,240
  • 24
  • 35