Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: printf() isn't working as expected #1256

Open
patricus3 opened this issue Aug 10, 2024 · 5 comments
Open

Bug: printf() isn't working as expected #1256

patricus3 opened this issue Aug 10, 2024 · 5 comments
Labels
low severity Used to report low severity bugs (e.g. cosmetic issues, non critical UI glitches)

Comments

@patricus3
Copy link

patricus3 commented Aug 10, 2024

Contact Details

contact me on here

What happened?

hello.
found a weird issue with printf, I don't know why this happens, but if I compile my example calculator app, it doesn't print to stdout until you fill out all needed stuff, here's the C code.

#include<stdio.h>
#include<stdlib.h>
int main(){
    printf("welcome in  calculator.");
    char *choice=malloc(sizeof(char));
    printf("type in the operator");
    scanf("%c",choice);
    switch(*choice)
    {
        case '/':
            free(choice);
            printf("first number");
            int64_t *num1=malloc(sizeof(int64_t));
            int64_t *num2=malloc(sizeof(int64_t));
            printf("first number");
            scanf("%lld",num1);
            printf("second number");
            scanf("%lld",num2);
            int64_t *result=malloc(sizeof(int64_t));
            *result =*num1 / *num2;
            free(num1);
            free(num2);
            printf("%lld",*result);
            free(result);
            break;
        case '+':
            free(choice);
            int64_t *Num1=malloc(sizeof(int64_t));
            int64_t *Num2=malloc(sizeof(int64_t));
            printf("type in first number");
            scanf("%lld",Num1);
            printf("second number");
            scanf("%lld",Num2);
            int64_t *Result=malloc(sizeof(int64_t));
            *Result= *Num1+ *Num2;
            free(Num1);
            free(Num2);
            printf("%lld",*Result);
            free(Result);
            break;
        case '*':
            free(choice);
            int64_t *n1=malloc(sizeof(int64_t));
            int64_t *n2=malloc(sizeof(int64_t));
            scanf("%lld",n1);
            scanf("%lld",n2);
            int64_t *res=malloc(sizeof(int64_t));
            *res= *n1 * *n2;
            free(n1);
            free(n2);
            printf("%lld",*res);
            free(res);
            break;
        case '-':
            free(choice);
            int64_t *N1=malloc(sizeof(int64_t));
            int64_t *N2=malloc(sizeof(int64_t));
            printf("type in the first number");
            scanf("%lld",N1);
            printf("type in second number");
            scanf("%lld",N2);
            int64_t *Res=malloc(sizeof(int64_t));
            *Res= *N1- *N2;
            free(N1);
            free(N2);
            printf("%lld",*Res);
            free(Res);
            break;
    }
}

Version

cosmocc (GCC) 14.1.0

What operating system are you seeing the problem on?

Mac

Relevant log output

No response

@patricus3 patricus3 added the high severity Used to report high severity bugs (Malfunctioning hinder important workflow) label Aug 10, 2024
@randomtwdude
Copy link

cosmos seems to have stdout line-buffered, just use setbuf(stdout, NULL) or some \n in your strings.

@patricus3
Copy link
Author

patricus3 commented Aug 10, 2024 via email

@randomtwdude
Copy link

libc/stdio/stdout.c contains the reason why. You can maybe just change the mode there, but I don't know if it will work. (just adding one setbuf at the start of your code is probably easier, or you could use fflush instead)

@G4Vi
Copy link
Collaborator

G4Vi commented Aug 15, 2024

With glibc in ttys, stdout is also line buffered. glibc appears to flush stdout when stdin is read from. Personally, I wouldn't depend on this behavior as it doesn't appear to be guaranteed by C, but this may be something we can improve.

https://stackoverflow.com/questions/66815208/how-is-printf-getting-flushed-before-scanf-is-executed

@G4Vi G4Vi added low severity Used to report low severity bugs (e.g. cosmetic issues, non critical UI glitches) accepted We intend to address this issue. and removed high severity Used to report high severity bugs (Malfunctioning hinder important workflow) labels Aug 15, 2024
@G4Vi
Copy link
Collaborator

G4Vi commented Aug 16, 2024

Upon further consideration, I'm not convinced we should change this. musl has good reasoning why they don't:

Rich Felker:

There's nothing detectable here because there's nothing wrong with the
program; the bug is in the programmer's expectation that the output
be visible.
It's possible to implement the behavior the programmer here desired,
the optional flushing of line-buffered output streams before reading
input. This would not help detect the bug in expectaions though; it
would just help mask it. The reason this behavior is not present in
musl is because it does not scale with significant numbers of stdio
streams open, and can even produce deadlock conditions in
multithreaded programs where there is no semantic deadlock but the
additional flushing produces an extraneous operation on a stream in a
way that causes deadlock.

https://inbox.vuxu.org/musl/CAAMnvke+NhDbGahX5mbzOpsWEPzPf4K-0dYE-ZMHJTr5xSEGhQ@mail.gmail.com/T/

To reproduce the deadlock condition I made a demo program, save to wr_stdio.c

#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main(){
    const unsigned pipesize = fcntl(1, F_GETPIPE_SZ);
    fprintf(stderr, "pipesize %u\n", pipesize);

    // fill up the stdout pipe
    static char buf[4096];
    for(unsigned i = 0; i < (pipesize / sizeof(buf)); i++) {
        write(1, buf, sizeof(buf));
    }
    // print without flushing
    printf("write without flushing");
    fprintf(stderr, "the printf itself is harmless\n");
    getchar(); // reading should be
    fprintf(stderr, "glibc never gets here as we are deadlocked writing to stdout\n");
}

Compile and run with cosmocc.

cosmocc -o wr_stdio_cosmo wr_stdio.c && ./wr_stdio_cosmo | sleep 10000

After you press ENTER, the final output should look something like:

pipesize 65536
the printf itself is harmless

glibc never gets here as we are deadlocked writing to stdout

Now if we do the same with gcc with glibc:

gcc -o wr_stdio_gcc wr_stdio.c && stdbuf --output=L ./wr_stdio_gcc | sleep 10000
pipesize 65536
the printf itself is harmless


To easily reproduce, stdbuf --output=L forces the stdout buffering to line mode as glibc doesn't use line mode with pipes.

@G4Vi G4Vi removed the accepted We intend to address this issue. label Aug 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
low severity Used to report low severity bugs (e.g. cosmetic issues, non critical UI glitches)
Projects
None yet
Development

No branches or pull requests

3 participants