raspberrypi / pico-sdk

BSD 3-Clause "New" or "Revised" License
3.68k stars 917 forks source link

following newline problem #726

Closed Wirtos closed 2 months ago

Wirtos commented 2 years ago

The code doesn't output anything via serial usb, but works in case fflush is there:

int main() {
    stdio_init_all();
    tusb_init();
    while (!tud_cdc_connected()) { sleep_ms(100); } 
    fprintf(stdout, "any string"); // same with fwrite
    // flush(stdout);
    puts(""); // print a newline
    return 0;
}

The following code also works fine:

int main() {
    stdio_init_all();
    tusb_init();
    while (!tud_cdc_connected()) { sleep_ms(100); } 
    fprintf(stdout, "any string\n");
    return 0;
}

Is the problem caused by some crt implementation detail? Host: Windows 11 Command: putty (plink -serial -sercfg 115200,8,n,1,N COM2)

einsteinx2 commented 2 years ago

This isn't a bug, it's exactly how stdout is meant to work (same in Linux on a Raspberry Pi for example). It buffers the output unless either the buffer limit is reached, a new line character is sent, or it's manually flushed.

On the other hand, stderr immediately outputs without requiring a new line or flush, but to my knowledge stderr is currently not implemented yet in the Pico SDK.

On Linux, you can disable the buffering by setting the buffer to NULL like so: setbuf(stdout, NULL);, but I haven't tested if that works on the Pico or not. Though in generally you probably want the buffering, and manual flushing or adding new lines is the "correct" solution.

Wirtos commented 2 years ago

This isn't a bug, it's exactly how stdout is meant to work (same in Linux on a Raspberry Pi for example). It buffers the output unless either the buffer limit is reached, a new line character is sent, or it's manually flushed.

Stdout is line buffered, yes. As in the second example puts("") acts like printf("\n"), yet it doesn't flush for some reason and this snippet didn't output a thing when I tested (might be mistaken, don't have access to my pico rn)

fprintf(stdout, "any string"); 
puts(""); 
flush(stdout);

That's why I think there's a buffering issue. Will try to reproduce with a more obvious example on why it's broken IMO

kilograham commented 1 year ago

i read a similar issue recently that maybe puts/printf are using distinct buffering

noname22 commented 10 months ago

I'm having the same issue with fwrite() to stdio. The buffer being written must end with newline or it simply won't get written, unless it's followed by a manual fflush(). I would like to emphasize that this is not some PEBCAK issue with normal line buffering but an actual bug in the stdio implementation. The data is not buffered to be written later, it disappears.

Consider the following code

char buffer[] = "pico";

printf("Hi, my name is ");
int nmemb_written = fwrite(buffer, sizeof(buffer), 1, stdout);
assert(nmemb_written == 1);
printf("!\n");

On a functioning stdio implementation this outputs the expected Hi, my name is pico!

However, on the pico (I'm using serial over USB), it instead outputs Hi, my name is !

kilograham commented 2 months ago

FYI stdio_flush() now works

You can set PICO_STDIO_SHORT_CIRCUIT_CLIB_FUNCS=1 in your target_compile_options if you want to mix printfand fprintf(stdout). The reason we don't do that by default is that otherwise you pull in all the C library FILE handling code