2

I have a function which writes binary data to a file or stdout. However the call to fwrite() sometimes fails, UNLESS I fflush(stderr) or print a report to stderr before attempting to fwrite.

Is this normal behaviour, or indicative of some underlying memory problem? It's difficult to debug because as soon as I fprint to stderr the data I'm trying to fwrite to stdout, the "problem" disappears.

Here's a very simplified version of the function.

int writebinary(FILE *fpout, void *data, long ntowrite) {

    long i; 

    /* lets say we know the input data is double float */
    double *p = data; 

    /* including this test line makes the function work! */
    for(i=0; i<ntowrite;i++) fprintf(stderr,"data[%d]=%g\n",i,p[i]);

    /* attempt to write data[] as a single block of bytes */
    m= fwrite(data,(size_t)(ntowrite*sizeof(double)),1,fpout);

    if(m!=1) {
        fprintf(stderr,"Write error: %d\n",ferror(fpout));
        return(-1); 
    }
    else return(m);
}

Any wisdom appreciated :)

JWDN
  • 382
  • 3
  • 13
  • 1
    Do you have any example code? – Drew McGowen Jun 28 '13 at 13:17
  • Providing any example code makes our work easy – Chinna Jun 28 '13 at 13:18
  • it's embedded in some very complicated code - was hoping to spare you the details - let me check the errno first.... – JWDN Jun 28 '13 at 13:22
  • 1
    I saw a program that crashed unless you enable logging. It turned out that the logging library used a semaphore, preventing a race condition between two threads. `stderr` writes happen synchronously, so your code may be in a similar situation. – Sergey Kalinichenko Jun 28 '13 at 13:24
  • ferror(fpout) returns null, so no help there! – JWDN Jun 28 '13 at 13:37
  • If the original program is multithreaded I'd look in the direction *dasblinkenlight* pointed too. Extensive (relativly slow) logging significantly influences the app's timing. And could lead to a classical Heisenbug. So a race is suddenly won by the one meant to win. – alk Jun 28 '13 at 14:10
  • 2
    OT: Wouldn't `... = fwrite(data, sizeof(double), (size_t) ntowrite, fpout);` be more straight forward? – alk Jun 28 '13 at 14:12
  • No multi-threading but repeated calls because the contents of the array pointed to by *data changes with time. @alk, thanks for pointing out the errno issue, but as for fwrite-ing a single block of bytes, there are reasons :) – JWDN Jun 28 '13 at 14:21
  • @alk Thanks, hadn't realised that. I'll delete my original comment – simonc Jun 28 '13 at 14:26
  • 1. You report "call to fwrite() sometimes fails". What is the failure evidence? What is `errno`? 2. Maybe try `fflush(fpout)` before/after `fwrite()` - `fflush(stdout)` may be a side effect of `fprintf(stderr...` 3. Is the value of `ntowrite` _large_ (for your system)? – chux - Reinstate Monica Jun 28 '13 at 15:18
  • @chux: Just deleted my hint, that `fwrite()` does not set `error`. – alk Jun 28 '13 at 15:28
  • Sorry to repeat: what is the evidence of the failure? – chux - Reinstate Monica Jun 28 '13 at 16:16
  • @chux, the variable "m" returns zero, instead of 1, indicating failure to write the block of bytes. This seems to be a bit of a cryptic one for everyone - if there's nothing really obvious I may have to put it down to a peculiarity, stick to flushing stderr, and remove the question :) - but it would be nice to understand what might be happening. – JWDN Jun 28 '13 at 17:02
  • Suggest calling `clearerr(fpout)` instead of `fprintf(stderr` and calling `fflush(fpout)` after the `fwrite(...fpout)`. I think you have a error on stdout (or stderr) - from _something_, that `fprintf(stderr` is clearing. You could check ferror(fpout) _before_ writing. BTW: "ferror(fpout) returns null": maybe you mean "ferror(fpout) returns 0". – chux - Reinstate Monica Jun 28 '13 at 18:23

1 Answers1

0

This is not normal behavior.
Recommend the following. There are a number of things in the posting which are of concern (see far below) but do not point to the problem - but something is wrong.

int writebinary2(FILE *fpout, void *data, long ntowrite) {
  size_t n = (size_t) ntowrite;
  double *dp = (double *) data;
  // pedantic checks
  if ((!fpout) || (!data) || (dp != data) || (ntowrite <= 0) || (n != ntowrite)) {
    fprintf(stderr,"Bad parameter\n");
    return(0);
  }
  clearerr(stderr);  // only needed for debug
  clearerr(fpout);
  /* attempt to write blocks of doubles */
  size_t m = fwrite(dp, sizeof(double), n, fpout);
  if (m != n) {
    fprintf(stderr,"Write error: %d\n",ferror(fpout));
    clearerr(fpout);
    return(-1);
  }
  if (fflush(fpout)) {
    fprintf(stderr,"Flush error: %d\n",ferror(fpout));
    clearerr(fpout);
    return(-1);
  }
  return(1);
}
  1. ntowrite s/b size_t, an unsigned integer. ntowrite = 0 may cause confusion on m. ntowrite < 0 will cause issues.
  2. m is not declared, assumed size_t.
  3. format "data[%d]=%g\n" s/b "data[%ld]=%g\n"
  4. i s/b size_t.
  5. (size_t)(ntowrite*sizeof(double)) s/b ((size_t)ntowrite)*sizeof(double). See #7.
  6. fprintf(stderr... should not be needed. That is the question.
  7. fwrite(..., size, n ...) works even if size * n overflow. So safer than OP.
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256