Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a cleaner way to use the write() function reliably?

Tags:

c

linux

gcc

sockets

I read the man pages, and my understanding is that if write() fails and sets the errno to EAGAIN or EINTR, I may perform the write() again, so I came up with the following code:

  ret = 0;
  while(ret != count) {
    write_count = write(connFD, (char *)buf + ret,  count);
    while (write_count < 0) {
      switch(errno) {
        case EINTR:
        case EAGAIN:
          write_count = write(connFD, (char *)buf + ret,  count -ret);
          break;
        default:
          printf("\n The value of ret is : %d\n", ret);
          printf("\n The error number is : %d\n", errno);      
          ASSERT(0);
      }
    }
    ret += write_count;
  }

I am performing read() and write() on sockets and handling the read() similarly as above. I am using Linux, with gcc compiler.

like image 934
AnkurVj Avatar asked Apr 26 '12 19:04

AnkurVj


People also ask

Does clean code improve performance?

Clean coding improves dependability, extensibility, and performance all around. One of the founding principles of the clean coding movement is a simple fact: Far more time is spent reading code than writing it.


2 Answers

You have a bit of a "don't repeat yourself" problem there - there's no need for two separate calls to write, nor for two nested loops.

My normal loop would look something like this:

for (int n = 0; n < count; ) {
    int ret = write(fd, (char *)buf + n, count - n);
    if (ret < 0) {
         if (errno == EINTR || errno == EAGAIN) continue; // try again
         perror("write");
         break;
    } else {
        n += ret;
    }
}

// if (n < count) here some error occurred
like image 131
Alnitak Avatar answered Sep 20 '22 11:09

Alnitak


EINTR and EAGAIN handling should often be slightly different. EAGAIN is always some kind of transient error representing the state of the socket buffer (or perhaps, more precisely, that your operation may block).

Once you've hit an EAGAIN you'd likely want to sleep a bit or return control to an event loop (assuming you're using one).

With EINTR the situation is a bit different. If your application is receiving signals non-stop, then it may be an issue in your application or environment, and for that reason I tend to have some kind of internal eintr_max counter so I am not stuck in the theoretical situation where I just continue infinitely looping on EINTR.

Alnitak's answer (sufficient for most cases) should also be saving errno somewhere, as it may be clobbered by perror() (although it may have been omitted for brevity).

like image 29
Mark Nunberg Avatar answered Sep 19 '22 11:09

Mark Nunberg