Why is stdout buffering?
Asked Answered
G

3

14

I am trying to learn the libuv api and wrote the following test:

#include <stdio.h>
#include <stdlib.h>
#include <uv.h>

void timer_cb(uv_timer_t* timer) {
    int* i = timer->data;
    --*i;
    if(*i == 0) {
       uv_timer_stop(timer);
    }
    printf("timer %d\n", *i);
    //fflush(stdout);
}

int main() {
    uv_loop_t* loop = uv_default_loop();
    uv_timer_t* timer = malloc(sizeof(uv_timer_t));
    uv_timer_init(loop, timer);
    int i = 5;
    timer->data = &i;
    uv_timer_start(timer, timer_cb, 1000, 2000);

    uv_run(loop, UV_RUN_DEFAULT);

    printf("Now quitting.\n");
    uv_close(timer, 0);
    uv_loop_close(loop);

    return 0;
}

When run it, no output is displayed until the program finishes running, and then all the output is displayed at once. If I uncomment the fflush line it works as expected, writing every 2 seconds.

Can someone please explain this to me? Why is stdout not flushed after the newline, as is explained here and in other places? Why do I need tomanually flush it?

Ginnie answered 23/6, 2016 at 12:4 Comment(10)
"Why is stdout buffering" - Why not?Siena
This answer may be useful to you: https://mcmap.net/q/20820/-does-printf-always-flush-the-buffer-on-encountering-a-newlineUnstable
@AndrewHenle Windows, with mingw64 on msysGinnie
@Olaf I understood (see answer I linked to at the end) that the buffer flushes on a newlineGinnie
@baruch: Where did you find that in the standard?Siena
@LPs I tried that. Didn't helpGinnie
@LPs As I said in the comments to the answers, I already tried thatGinnie
Are you using Eclipse? Take a look at this forumAnother
stdout is usually line buffered if output is going to a terminal. If it is going to a file, it is usually not line buffered.Headspring
Funny, it works as expected with gcc 10.2.0, target x86_64-pc-msys, Windows 10. Works started from mintty as well as Windows Console. Only if I pipe it (e.g. to cat) it fully buffers, consistent with the fact that the pipe is not a tty. Do you run the program from an IDE that displays the output in a widget that does not pretend to be a Console? (For what it's worth, an equivalent program with Windows Sleep() built as debug, run from VS, output in a Console Window, also uses linebuffering, so it's clearly not a Windows issue.)Wakeup
W
18

Stream buffering is implementation-defined.

Per 7.21.3 Files, paragraph 3 of the C Standard:

When a stream is unbuffered, characters are intended to appear from the source or at the destination as soon as possible. Otherwise characters may be accumulated and transmitted to or from the host environment as a block. When a stream is fully buffered, characters are intended to be transmitted to or from the host environment as a block when a buffer is filled. When a stream is line buffered, characters are intended to be transmitted to or from the host environment as a block when a new-line character is encountered. Furthermore, characters are intended to be transmitted as a block to the host environment when a buffer is filled, when input is requested on an unbuffered stream, or when input is requested on a line buffered stream that requires the transmission of characters from the host environment. Support for these characteristics is implementation-defined, and may be affected via the setbuf and setvbuf functions.

The type of buffering is dependent on your implementation, and your implementation apparently isn't line-buffering in your example.

Whereat answered 23/6, 2016 at 12:15 Comment(2)
I set it to line buffering with setvbuf(stdout, NULL, _IOLBF, 0); and it still doesn't work. Only if I set it to no buffering (_IONBF) does it workGinnie
As linked in that answer, on Windows there is no line buffering. I assume that is the issue msdn.microsoft.com/en-us/library/86cebhfs.aspx#Anchor_3Ginnie
T
7

There is no strict requirement, that stdout is line buffered. It may be fully buffered as well (or not buffered at all), in which case \n does not trigger to flush the stream.

C11 (N1570) 7.21.3/7 Files:

As initially opened, the standard error stream is not fully buffered; the standard input and standard output streams are fully buffered if and only if the stream can be determined not to refer to an interactive device.

C11 (N1570) 5.1.2.3/7 Program execution:

What constitutes an interactive device is implementation-defined.

You could try to force specific type of buffering by setvbuf standard function. For instance, to set line buffering for stdout, you may try with:

setvbuf(stdout, buff, _IOLBF, size);

where buff is declared as character array of size elements (e.g. 1024).

Note that setvbuf has to be called before any other I/O operation, that is performed to the stream.

Tacnaarica answered 23/6, 2016 at 12:18 Comment(2)
I set it to line buffering with setvbuf(stdout, NULL, _IOLBF, 0); and it still doesn't work. Only if I set it to no buffering (_IONBF) does it workGinnie
@baruch: AFAIR, setvbuf requires to supply it with self-prepared buffer. Otherwise, it will not work.Tacnaarica
H
1

For some reason, your system is deciding that your stdout is not interactive. Are you doing some strange redirect of stdout or doing something weird with your terminal? You should be able to override using setbuf or you can use stderr instead of stdout.

Hyponasty answered 23/6, 2016 at 12:19 Comment(2)
This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post. - From ReviewBaldridge
I think it includes 2 answers: use setvbuf or stderr. The third answer is only implied, to figure out why the system is mistaking stdout for non-interactive.Hyponasty

© 2022 - 2024 — McMap. All rights reserved.