Can close of pipe's write end fail leaving reading process blocked?

35 views Asked by At

Let's consider a common scenario

  1. Create a pipe and fork (assuming no error happens)

    pipe (pipefd); fork();
    
  2. A parent process writes to pipe, closes it and waits for its child

    write(pipefd[1], str, strlen(str);
    close(pipefd[1]);
    wait(NULL);
    
  3. The child process reads from pipe until EOF

    while ((len = read(pipefd[0], buf, BUF_SIZE)) > 0)
        write(STDOUT_FILENO, &buf, len);
    close(pipefd[0]);
    exit(len == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
    

Calling close(pipefd[1]) on parent can potentially fail with an error due to some reasons. What will happen on the child's end in this case? Should child also receive read error or can remain blocked on the read call? Can this code lead to a dead lock (child on read, parent on wait) in this case? What is a correct way to handle this situation?

1

There are 1 answers

1
John Bollinger On BEST ANSWER

As a preliminary matter, you do not describe the child process closing pipefd[1]. All else notwithstanding, the child must do that before it can expect its read(pipefd[0]) calls ever to report an EOF condition.

Calling close(pipefd[1]) on parent can potentially fail with an error due to some reasons.

Yes, in general, and in the sense that close() may return -1 and set errno. But that does not necessarily imply that the file descriptor was not closed. This is a tricky situation. You should read the NOTES section of the close(2) manual page for a discussion, which is too long to reproduce here.

What will happen on the child's end in this case? Should child also receive read error or can remain blocked on the read call?

It depends. There is no answer at the level of generality at which the question is posed.

On some systems, such as Linux, close() always closes the specified file descriptor, provided that it is initially a valid, open file descriptor, even in cases where it ultimately reports an error. On such systems, it is likely, but not guaranteed, that the child's read() would not, then, block indefinitely.

Can this code lead to a dead lock (child on read, parent on wait) in this case?

If the parent ignores a failure to close() the write end of the pipe? Yes, the child continuing to block on read() is within the possibilities allowed by POSIX. If the parent then proceeds to wait()ing on the child then that could deadlock.

What is a correct way to handle this situation?

If close() fails then the parent should report an error in some way -- diagnostic message, exiting with a failure status, or returning an error code, for example. It could consider also trying to force the child to terminate by, for example, sending it a SIGTERM or even a SIGKILL. Even without killing the child, you have to assume in this case that there may have been data loss or corruption, so killing the child does not make you any worse off in that regard.