clinuxunixfflush

Is it necessary to use fflush more than once?


In APUE (Advanced programming in UNIX environment), there are a lots of err_quit, err_sys and like them.

The source of for example err_quit is as follows:

void err_quit(const char *fmt, ...) {
    va_list     ap;

    va_start(ap, fmt);
    err_doit(0, 0, fmt, ap);
    va_end(ap);
    exit(1);
}

static void err_doit(int errnoflag, int error, const char *fmt, va_list ap) {
    char    buf[MAXLINE];

    vsnprintf(buf, MAXLINE-1, fmt, ap);
    if (errnoflag)
        snprintf(buf+strlen(buf), MAXLINE-strlen(buf)-1, ": %s",
          strerror(error));
    strcat(buf, "\n");
    fflush(stdout);     /* in case stdout and stderr are the same */  /* LINE A*/
    fputs(buf, stderr);
    fflush(NULL);       /* flushes all stdio output streams */        /* LINE B*/
}

My question is LINE A necessary, because of existence of LINE B ? I can not understand the comments inLINE A.


Solution

  • This program apparently prints to both stdout and stderr. On Linux, the standard error stream is unbuffered by default. When the standard output stream is connected a pipe or file, it is fully buffered by default, and then the output to these streams can become mixed. Unbuffered means the stream software sends output to the device as soon as the program writes it. Fully buffered means the stream software does not immediately send output to the device but instead records it internally in a buffer and sends it to the device only when the buffer is full or a flush is requested. This is done for efficiency, because it reduces the number of system calls and I/O operations needed to process the output. Each buffered stream has a separate buffer.

    For example, suppose the program wrote some text to stdout before this routine err_doit was called, perhaps the text Continuing program operations, everything is good.\n, and then err_doit is called and writes Error, the deaggregating fluxinator reported a zizzification error.\n to both stderr. What can happen with no fflush calls is:

    The result is the pipe or file receives this text:

    Continuing program operations,Error, the deaggregating fluxinator reported a zizzification error.
     everything is good.
    

    So the user ends up seeing chopped and mixed messages in the pipe or file. That is not what we want. To avoid that, the program flushes stdout before it writes to stderr. This ensures the pipe or file will receive:

    Continuing program operations, everything is good.
    Error, the deaggregating fluxinator reported a zizzification error.
    

    You usually will not see this when you run a program at the command line and let the standard output and standard error connect to the terminal. This is because, when standard output is connected to a terminal, the default is that it is line buffered, meaning the program will record output internally and only send it to the device when the buffer is full, a new-line character is written, or a flush is requested. So, whenever the program writes a line containing a new-line character, the output up to that character is immediately sent to the terminal. That prevents interweaving of stdout and stderr data as long as the program ends each output with a new-line character.

    That explains the first fflush(stdout);. The reason for the later fflush(NULL); (which requests flushing of all streams with possibly pending output) is not clear. It will flush the standard error stream if it is buffered, but that may or may not be the author’s intent.