0

I have two separate programs in C++, one that writes to two named pipes in unpredictable intervals and one that should wait to read new content from the pipes whenever available. For simplicity, here my writer only writes two times to the pipes (1st: "One" "Tree" , 2nd: "Two" "Fogs").

My writer program is:

#include <unistd.h>
#include <iostream>

int main()
{
    int fd1, fd2;
    const char* myfifo1 = "/tmp/myfifo1";
    const char* myfifo2 = "/tmp/myfifo2";

    mkfifo(myfifo1, 0666);
    mkfifo(myfifo2, 0666);

    fd1 = open(myfifo1, O_WRONLY);
    fd2 = open(myfifo2, O_WRONLY);

    write(fd1, "One", sizeof("One"));
    write(fd2, "Tree", sizeof("Tree"));

    sleep(5);

    write(fd1, "Two", sizeof("Two"));
    write(fd2, "Fogs", sizeof("Fogs"));

    close(fd1);
    close(fd2);

    unlink(myfifo1);
    unlink(myfifo2);
}   

My reader program is:

#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

#define MAX_BUF 1024

int main()
{
    int fd1, fd2;
    const char * myfifo1 = "/tmp/myfifo1";
    const char * myfifo2 = "/tmp/myfifo2";
    char buf1[MAX_BUF], buf2[MAX_BUF];
    
    fd1 = open(myfifo1, O_RDONLY);
    fd2 = open(myfifo2, O_RDONLY);
    
    while (1) {
        read(fd1, buf1, MAX_BUF);
        read(fd2, buf2, MAX_BUF);
        
        printf("Received: %s\n", buf1);
        printf("Received: %s\n", buf2);
    }

    close(fd1);
    close(fd2);
    return 0;
}

I do not want the reader to terminate or close the connection between the reads, I need it to remain active and wait until the writer writes something new in the named pipe. However, by simultaneously running these two programs (in different cores, first the writer and then the reader) I get:

Received: One
Received: Tree
Received: Two
Received: Fogs
Received: Two
Received: Fogs
Received: Two
Received: Fogs
Received: Two
Received: Fogs
      ...
      ...

The wanted behavior would be:

Received: One
Received: Tree
Received: Two
Received: Fogs

and then nothing (the reader should wait for another write).

I understand that I should somehow clear the buffer after I read it, but isn't it the default behavior of read? Since it finished reading "Two" (and also "Fogs"), why does it keep reading it and does not wait for new content?

What modifications should I make?

Thank you very much in advance!

drescherjm
  • 10,365
  • 5
  • 44
  • 64
vivi22
  • 1
  • 3
    The pipe is destroyed by the writer program. There is nothing to wait for. `read` doesn't read the old message again, it errors out. You would know that if you checked the result of every I/O operation, as you should have. – n. m. could be an AI Mar 18 '21 at 14:52
  • 2
    You might also be interested in looking at [select](https://man7.org/linux/man-pages/man2/select.2.html) rather than having your reader busy wait for new messages. – Nathan Pierson Mar 18 '21 at 14:57
  • read errors out, your program doesn't look for the error, since there was an error the contents of buf1 and buf2 wasn't updated, so your program prints the old contents. – user253751 Mar 18 '21 at 15:02
  • Your post is tagged C++ but the code is C. Did you mean to tag it as C instead? – JDługosz Mar 18 '21 at 15:30
  • 1
    @NathanPierson Its a blocking read rather than a busy wait. But a slightly more complex application would use `select()` to block on multiple pipes simultaneously for one to become available. – Martin York Mar 18 '21 at 15:33
  • Thank you. I noticed that if the reader starts first (so the connection is only established by the reader, not the writer yet), the read returns -1 and I get multiple "Received: ". Is there a proper way for the reader to wait until the writer connects to the pipe? My intuition would be that I would get no output at all, and the read would block until the connection was made by both programs, but apparently I do get outputs. – vivi22 Mar 18 '21 at 17:01

1 Answers1

1

Closing a pipe signals end-of-file to the other end.

read will read zero bytes (and return zero) whenever it encounters an end-of-file condition. Any subsequent read after the end-of-file is a no-op.

If you want to continue reading from the same pipe, you need to check the result of read, and if it returns zero, close the pipe and open it again.

Of course you should not unlink the pipe if you need someone else to continue using it.

One other thing to note: read (1) doesn't terminate the buffer with the 0 byte and (2) doesn't necessarily read the exact amount of bytes that was written by a single write. You should detect the end-of-message yourself, rather than relying on read and write to do it for you. It is entirely possible that read will read a half of the string without the terminating null character, then your printf will print garbage.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • How can I detect the end-of-message when the reader does not know the number of bytes that should be read? One way is for the writer to calculate this number and send it to the reader before the message. Is there another, simpler way? Please help. Thank you. – vivi22 Mar 26 '21 at 16:16
  • There are no messages in the API. You need to design a protocol that implements messages. Sending a message after its length is one possible way to do it. If you are sending C strings, you may want to send a 0 terminator at the sending end and *detect it* at the receiving end, this is another way of doing it. – n. m. could be an AI Mar 27 '21 at 09:05