[SOLVED] select() returns "ready" even after I closed producer (FIFO)

Hello,

I have a producer application that writes data into a FIFO, and I have a consumer application that reads data from FIFO utilizing select() and read() functions inside of an while(true) loop.

After producer is done (it closes and unlinks FIFO) select() on the consumer side keeps endlessly returning "ready" state, and read() returns 0 bytes. I've expected select() to return -1 once the FIFO was closed but it's not the case.

How do I catch that FIFO was closed on the producer side?

Thank you.

P.S. I should post my code but it's a large application, it would take me time to make it into a small prototype. I'd appreciate if someone knows the answer without me having to post the code.
Last edited on
You caught it already.
In the man page for select:
a file descriptor is also ready on end-of-file

and man page for read:
On success, the number of bytes read is returned (zero indicates end of file)

So, select is doing what it should and read is as well. The error for select would be:
EBADF
An invalid file descriptor was given in one of the sets. (Perhaps a file descriptor that was already closed, or one on which an error has occurred.) Me adding: This would be on the read side (not write side), in your program read is still open.

EINTR
A signal was caught; see signal(7).

EINVAL
nfds is negative or the value contained within timeout is invalid.

ENOMEM
unable to allocate memory for internal tables.


Also, make sure you call FD_ZERO and FD_SET each time in the loop before you call select.
Code modified from http://www.codeforge.com/article/195845
fifoWriter.c - Too lazy to convert to c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "./myfifo"
#define BUFFER_SIZE PIPE_BUF
#define TEN_MEG (1024 * 1024 * 10)

int main(int argc, char *argv[])
{
   int pipe_fd, res,open_mode = O_WRONLY, bytes_sent = 0;
   char buffer[BUFFER_SIZE + 1];

   if (access(FIFO_NAME, F_OK) == -1) {
      res = mkfifo(FIFO_NAME, 0777);
      if (res != 0) {
         fprintf(stderr, "Could not create fifo %s\n",
               FIFO_NAME);
         exit(1);
      }
   }

   printf("Process %d opening FIFO O_WRONLY\n", getpid());
   pipe_fd = open(FIFO_NAME, open_mode);
   printf("Process %d result %d\n", getpid(), pipe_fd);

   if (pipe_fd != -1) {
      while(bytes_sent < TEN_MEG) {
         sleep(6);
         printf("Writing\n");
         res = write(pipe_fd, buffer, BUFFER_SIZE);
         if (res == -1) {
            fprintf(stderr,"Write error on pipe\n");
            exit(1);
         }
         bytes_sent += res;
      }
      close(pipe_fd); 
   } else {
      exit(1);
   }
   printf("Process %d finished\n", getpid());
   return 0;
}

fifoRead.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <climits>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>
using namespace std;
#define FIFO_NAME "./myfifo"
#define BUFFER_SIZE PIPE_BUF

int main(int argc, char *argv[])
{
    int pipe_fd, res, ret,
        open_mode(O_RDONLY);
    char buffer[BUFFER_SIZE + 1];
    fd_set fdset;
    struct timeval tv;
    tv.tv_sec = 3;
    tv.tv_usec = 0;
    memset(buffer, '\0', sizeof(buffer));
    
    pipe_fd = open(FIFO_NAME, open_mode);
    cout << "Process " << getpid() << " result " << pipe_fd << endl;

    if (pipe_fd != -1) {
        do {
            FD_ZERO(&fdset);
            FD_SET(pipe_fd,&fdset);
            ret = select(pipe_fd+1,&fdset,NULL,NULL,&tv);
            if ( ret > 0) {
               if ( FD_ISSET(pipe_fd,&fdset) ) {
                   cout << "Reading" << endl;
                   res = read(pipe_fd, buffer, BUFFER_SIZE);
                   if ( res == 0 ) {
                      cout << "EOF" << endl;
                      break;
                   }
               }
               else {
                   cout << "Error" << endl;
               }
            }
            else if ( ret == 0 ) {
               cout << "Time out" << endl;
            }
            else {
               cout << "Error returned from select: " << ret << endl;
               break;
            } 
        } while (1);
        close(pipe_fd);
    } else {
      exit(1);
    }
    return 0;
}
//--------------------------------------------------------
// Execute them both in two different terminals
//--------------------------------------------------------
$ ./fifoWrite 
Process 2233 opening FIFO O_WRONLY
Process 2233 result 3
Writing
Writing
Writing
^C
$
//----------------------------------------------------
// In another terminal
//----------------------------------------------------
$ ./fifoRead 
Process 2235 result 3
Time out
Time out
Reading
Time out
Reading
Time out
Reading
Time out
Reading
EOF
$

It is up to you to take action on the read of EOF. Close and remove that fd from the list of file descriptors in the select and continue to process in your loop or not.
Last edited on

EBADF An invalid file descriptor was given in one of the sets. (Perhaps a file descriptor that was already closed, or one on which an error has occurred.) Me adding: This would be on the read side (not write side), in your program read is still open.


That is the catch - I thought if the file descriptor was closed on the write side, select() on the read side should return EBADF - which is not the case.

Thank you for help!
Last edited on
Topic archived. No new replies allowed.