jart / cosmopolitan

build-once run-anywhere c library
ISC License
17.71k stars 605 forks source link

Unbuffered stdio for character devices #181

Open f-carraro opened 3 years ago

f-carraro commented 3 years ago

Hello, I tried compiling the following sample program as an αcτµαlly pδrταblε εxεcµταblε:

int main() {

   char str[100];

   printf("Enter a value :");
   scanf("%s", str);

   printf("\nYou entered: %s", str );

   return 0;
}

At least on my machine the resulting executable seems to wait for the input from scanf before displaying the first printf message. After typing something the output is:

asdf
Enter a value :
You entered: asdf

I followed the windows tutorial for compiling and also tried compiling on WSL.

jart commented 3 years ago

This behavior is working as intended. The fix for you is to add an fflush(stdout) after your first printf statement. Cosmopolitan currently line-buffers stdout if it's a character device. However, as it turns out, glibc unbuffers it. I'd like to be more compatible with glibc since we're already paying for the fstat call. We'd need to update lines like this to use _IONBF:

https://github.com/jart/cosmopolitan/blob/b8f38cf55d84bac7d735ebbe58e1d9bf1956de39/libc/stdio/stdout.c#L39-L41

f-carraro commented 3 years ago

Another distinct behaviour I found when compiling the same program is that during the scanf procedure, after typing a phrase, pressing backspace deletes a whole word instead of a single character.

Perhaps it's related, otherwise I can open another issue.

jart commented 3 years ago

Cosmopolitan doesn't change ICANON by default so your operating system or terminal would most likely be causing that one.

f-carraro commented 3 years ago

Is there a way to enforce the correct behaviour? It seems to be reading raw. Arrow keys do not work either. I'm using Windows's cmd. The input problem does not happen if I compile without cosmopolitan libc.

etwoo commented 10 months ago

Cosmopolitan currently line-buffers stdout if it's a character device. However, as it turns out, glibc unbuffers it. I'd like to be more compatible with glibc since we're already paying for the fstat call. We'd need to update lines like this to use _IONBF:

https://github.com/jart/cosmopolitan/blob/b8f38cf55d84bac7d735ebbe58e1d9bf1956de39/libc/stdio/stdout.c#L39-L41

If Cosmopolitan no longer already pays for the fstat call, would that invalidate the earlier suggestion to change lines like the above to use _IONBF?

I ask because I was looking at the contributions welcome label for a potential first contribution -- thank you for labeling good starter issues in this manner, by the way! While looking into this labeled issue, I noticed that __stdout_init() no longer seems to call ischardev(), as of 398f0c16fb:

  /*
   * Unlike other C libraries we don't bother calling fstat() to check
   * if stdio is a character device and we instead choose to always line
   * buffer it. We need it because there's no way to use the unbuffer
   * command on a statically linked binary. This still goes fast. We
   * value latency more than throughput, and stdio isn't the best api
   * when the goal is throughput.
   */
  stdout->bufmode = _IOLBF;

Put another way: if the fstat check is no longer cost-free, would it no longer make sense to be more compatible with glibc, making this a candidate for wontfix?

Sidenote: apologies for any errors on my part in this message or any practices I've misunderstood from the contributing guidelines. I'm new to GitHub collaboration in general and the Cosmopolitan project in particular. Please do correct, redirect, or moderate this message however is most appropriate!