|
|
Subscribe / Log in / New account

code feedback ...

code feedback ...

Posted Sep 13, 2011 6:00 UTC (Tue) by jzbiciak (guest, #5246)
In reply to: code feedback ... by vapier
Parent article: Ensuring data reaches disk

another common mistake: the size/nmemb args are swapped ... the size is "1" (since sizeof(*buf) is 1 (a char)), and the number of elements is "ret". once you fix the arg order, the method of clobbering the value of ret won't work in the "if" check ...

I don't think it's an error. In the example, if fwrite returns anything other than '1', then it reports an error. This is an "all-or-nothing" fwrite. If it fails, 'ret' will be 0, otherwise it will be 1. The semantic is "write 1 buffer of size 'ret' bytes."

I see nothing wrong with this, and it matches the if (ret != 1) statement that follows. Sure, you don't get to find out how many bytes did get written, but the code wasn't interested in that anyway. And, it's one less variable that's "live across call," so the resulting compiler output may be fractionally smaller/faster. (While I can think of smaller microoptimizations, this type of microoptimization is pretty far down the list, I must admit.)

Personally, I think the code might be clearer breaking 'ret' up into multiple variables. For example, if you did switch size/nmemb, you might rewrite the loop like so:

      while (tot_written < nrbytes) {
              int remaining = nrbytes - tot_written;
              int to_read   = remaining > MY_BUF_SIZE ? MY_BUF_SIZE : remaining;

              read_ret = read(sockfd, buf, to_read);
              if (read_ret <= 0) {
                      if (errno == EINTR)
                              continue;
                      return read_ret;
              }
              write_ret = fwrite((void *)buf, 1, read_ret, outfp);
              tot_written += write_ret;
              if (write_ret != read_ret)
                      return ferror(outfp);
      }

Written that way, you could easily add a way to return how many bytes did get written.

Also, the return value is inconsistent. I think "return ferror(outfp)" is wrong. ferror returns non-zero on an error, but it isn't guaranteed to be negative. The other paths through this function return positive values on success, so shouldn't it be simply "return -1;" to match the read error path (which also simply returns -1, and maybe should be written as such)? ie:

      while (tot_written < nrbytes) {
              int remaining = nrbytes - tot_written;
              int to_read   = remaining > MY_BUF_SIZE ? MY_BUF_SIZE : remaining;

              read_ret = read(sockfd, buf, to_read);
              if (read_ret <= 0) {
                      if (errno == EINTR)
                              continue;
                      return -1;
              }
              write_ret = fwrite((void *)buf, 1, read_ret, outfp);
              tot_written += write_ret;
              if (write_ret != read_ret)
                      return -1;
      }


to post comments

code feedback ...

Posted Sep 13, 2011 6:08 UTC (Tue) by jzbiciak (guest, #5246) [Link]

Err... I guess the read error path returns -1 or 0, which again I think may be an error, unless you wanted to return 0 when the connection drops before "nrbytes" gets read. Oops.

That raises a different question: If you exit early due to the socket dropping, you won't fflush/fsync. Seems like you want a 'break' if read returned 0 and errno != EINTR, don't you?

code feedback ...

Posted Sep 14, 2011 5:33 UTC (Wed) by vapier (guest, #15768) [Link]

in the past i've been bitten where fwrite was given a char* and size==num bytes to write and nmemb==1 (like in the example here). but perhaps that was a bug in the lower layers (it was a ppc/glibc setup). i do know that size==sizeof(*buf) has always worked for me ;).


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds