Search code examples
cposixipc

Does write system call need the data to be read to give >-1 return?


Sorry for the basic question, but I can't find a straight answer that I can understand on the documentation or with generative AI.

When I do a write call over a pipe between a parent and child process, will write only return something != -1 if the data is read by a read system call?

For instance, take the following code below:

int main(int argc, char* argv[]) {
  int workers = 4;
  int pfd[2];
  int pipe_result = pipe(pfd);
  if(pipe_result != 0) {
    fail("Can't create pipe");
  }
  // Parent doesn't need write channel
  close(pfd[1]);

  for(int i = 0; i < workers; i++) {
    pid_t pid = fork();
    // Child
    if(pid == 0) {
      // Child doesn't need read channel
      close(pfd[0]);

      int count = 101;

      // Write count to pipe
      int len_sent = write(pfd[1], &count, sizeof(count));
      printf("\nChild id: %d Sent %d bytes, count: %d", getpid(), len_sent, count);
      
      // Exit so that the child processes don't loop
      close(pfd[1]);
      exit(0);
    }
    else {
      int par_count;
      // Read
      int len = read(pfd[0], &par_count, sizeof(par_count));
      printf("\nlen: %d par_count: %d", len, par_count);
    }
  }
  
  // Close read buffer in parent
  close(pfd[0]);
  return 0;
}

That code will generally output indicating that 0 or 1 write and read calls were successful. If I move the reads to be after all write calls are done (code below), then everything reports fine.

int main(int argc, char* argv[]) {
  int workers = 4;
  int pfd[2];
  int pipe_result = pipe(pfd);
  if(pipe_result != 0) {
    fail("Can't create pipe");
  }

  for(int i = 0; i < workers; i++) {
    pid_t pid = fork();
    // Child
    if(pid == 0) {
      // Child doesn't need read channel
      close(pfd[0]);

      int count = 101;

      // Write count to pipe
      int len_sent = write(pfd[1], &count, sizeof(count));
      printf("\nChild id: %d Sent %d bytes, count: %d", getpid(), len_sent, count);
      
      // Exit so that the child processes don't loop
      close(pfd[1]);
      exit(0);
    }
  }
  
  // Parent doesn't need write channel
  close(pfd[1]);

    // Parent
  for(int i = 0; i < workers; i++) {
    int par_count;
    // Read
    int len = read(pfd[0], &par_count, sizeof(par_count));
    printf("\nlen: %d par_count: %d", len, par_count);
  }

  // Close read in parent
  close(pfd[0]);
  return 0;
}

Why does this change affect the return of the write calls? I understand why it affects the read returns - that makes sense to me. But I don't understand why this change makes the write calls report -1. Is the return of write dependent on the data being read by a read call?


Solution

  • To answer the question in the title: No. write() simply stores the data in the pipe buffer and returns immediately, it doesn't wait for the pipe to be read. When not -1, the return value is the number of bytes that were written to the buffer.

    The problem in your code is that you have close(pfd[1]) before the loop that forks all the children. As a result, the children inherit the closed write end of the pipe, so they can't write to it.

    You should move that call after the loop.