You all joke that this doesn’t happen in practice, but something like this literally just bit me and it took me a few too many minutes to figure out what was going on. I use a bash script as my BROWSER which calls another bash script to launch or communicate with my browser that I run inside a container. The script that my BROWSER script calls has some debug output that it prints to stderr. I use mutt as my email cli…
I'm actually really curious about this setup. Can you go into a bit more detail about how it works ?
Bugs in Hello World
241–250 of 262 posts
Re: Bugs in Hello World
#242A real world example of catching (some, but certainly not all) fflush(), ferror() etc. cases is what "git" does at the end of its execution, the first highlighted line is where it's returning from whatever function implements a built-in ("status", "pull", "log" etc. etc.): : https://github.com/git/git/blob/v2.35.0/git.c#L464-L483 Doing something similar would be a good addition to any non-trivial C program that emits…
I work as a sysadmin and only write the odd program/script (Python, Perl, Bash). In the past, I’ve run into the problem of not being able to write to a log file (disk full or insufficient permissions) so I now check for these situations when opening or writing to a file. A while ago, I started learning C in my personal time and am curious about this issue. If `close()` fails, I’m guessing there’s not much else the pr…
This is really more about POSIX and FS semantics than C (although ultimately you end up using the C ABI or kernel system calls, which are closer to C than e.g. Python).
POSIX gives implementations enough leeway to have close() and fsync() do pretty much whatever they want as far as who returns what error goes, as long as not returning an error means your data made it to storage.
But in practice close() is typically 1=1 mapped to the file itself, while fsync() is many=1 (even though both take a "fd"). I.e. many implementations (including the common consumer OS's like Windows, OSX & Linux) have some notion of unrelated outstanding I/O calls being "flushed" by the first process to call fsync().
IIRC on ext3 fsync() was pretty much equivalent to sync(), i.e. it would sync all outstanding I/O writes. I believe that at least Windows and OSX have a notion of doing something similar, but for all outstanding writes to a "leaf" on the filesystem, i.e. an fsync() to a file in a directory will sync all outstanding I/O in that directory implicitly.
Of course none of that is anything you can rely on under POSIX, where you not only have to fsync() each and every file you write, but must not forget to also flush the relevant directory metadata too.
All of which is to say that you might be out of space when close() happens, but by the time you'd fsync() you may no longer be out of space, consider a write filling up the disk and something that frees up data on disk happening concurrently.
If you know your OS and FS semantics you can often get huge speedups by leaning into more lazily syncing data to disk, which depending on your program may be safe, e.g. you write 100 files, fsync() the last one, and know the OS/FS syncs the other 99 implicitly.
But none of that is portable, and you might start losing data on another OS or FS. The only thing that's portable is exhaustively checking errors after every system call, and acting appropriately.
Re: Bugs in Hello World
#243Earlier quoted context omitted.
I'm actually really curious about this setup. Can you go into a bit more detail about how it works ?
I run Firefox inside a systemd-nspawn[0] container. I wrote a little wrapper around systemd-nspawn that I call arch-lwc[1] which kinda mimics the docker CLI. I have another script to coordinate the Firefox-specific stuff. [0]: https://wiki.archlinux.org/title/Systemd-nspawn [1]: https://github.com/b0o/arch-lwc
Re: Bugs in Hello World
#244Earlier quoted context omitted.
I think they are arguing that it didn’t fail, it did everything you asked of it (it didn’t claim to successfully print hello world in every scenario, just to attempt to write to the buffer you gave it, which it did).
It seems possible they are arguing: _exit(write(1,"Hello World!\n",13)); is the correct one, but to me that just kicks the can. What should happen here? os.rename(x,y) print("success!") should this exit nonzero? The file did get renamed and progress was made, even if some unrelated problem occurs, so some animation for a users benefit who is probably dealing with some other problems thinking piping to /dev/full was a…
return printf("Hello, world!\n")
would suffice.Edit : bugs are easy
Re: Bugs in Hello World
#245I’m disappointed. I expected some obscure edgecase (like “Main is usually a function…” [1]) but instead that’s about scope handling, contract design and responsibility shift. “Hello world” method simply calls an API to a text interface. It uses simple call, to a simple interface that is expected to be ever present. I don’t find any bug there. It won’t work if such interface isn’t available, is blocked or doesn’t exis…
Modern languages do this by default, using exceptions, or force you to check return values using Result or alike.
Even in C, when compiled through some more strict linter, this would fail because ignored return value should be prefixed with (void).
In either case I think the main takeaway from the article is that a language where even hello world has such pitfalls, isn’t suitable, given the many other better options today.
Re: Bugs in Hello World
#246Earlier quoted context omitted.
> if something is picking up the output and parsing it as JSON, it can deduce from a failed parse that the program didn't complete, rather than going by termination status. This is bad advice. Consider output that might be truncated but can't be detected (mentioned in the article). The exit status is the only reliable way to detect failures (unless you have a separate communication channel and send a final success me…
My remark "if a program produces output with a well-defined syntax" was intended specifically to consider such cases, and set them aside. I didn't communicate that clearly: syntax can be "well-defined" yet truncatable. I meant some kind of syntax that is invalid if any suffix is missing, including the entire message, or else an object of an unexpected type is produced. (In the case of JSON, valid JSON could be output…
It should always behave the same. The exit code of a program is the agreed upon standard for this.
Re: Bugs in Hello World
#247The article doesn't explain how to fix the bug! Talk about leaving your audience hanging.
Re: Bugs in Hello World
#248 int main(void)
{
char the_terminal[] = "Hello World!\n"
return 0;
}Re: Bugs in Hello World
#249Earlier quoted context omitted.
On my machine: $ cat main.c #include int main(void) { int n = printf("Hello, World!\n"); return n /dev/full $ echo $? 0 Problem is, printf is buffered. When redirecting to /dev/full writing to the buffer works. The problem only manifests when the buffer is flushed. This works, as write is not buffered; #include int main(void) { int n = write(1, "Hello, World!\n", 14); return n But even then, to find out what exactly…
I was expecting that printf would auto-flush at the newline, but either it doesn’t, or doesn’t report an error? An explicit fflush would probably work. In any case, hello world examples are used for two purposes, (a) providing a simplest possible program that you can run (with an indication that it worked), and (b) to give a taste of the respective programming language. At least for the latter case, it would be usefu…
The question is, how much of a taste? We could also include structs, manual memory management, pointers, macros, #ifdef guards; because these are all so very common to C programs, they are definitely part of the languages flavour.
Also, which of the error handling techniques in C should be included, because the language doesn't have a standard one, there are no exceptions or multiple returns. Even within the most basic libraries we have everything from checking global error states, inbound messenger variables, magic number returns, errorflags, ...
Again, hello.c is supposed to be simple. As simple as possible. Yes, that excludes a lot of things. These things are what all the other chapters of "The C Programming Language" are about.
As a starter, hello.c is great.
Re: Bugs in Hello World
#250Earlier quoted context omitted.
Whether the program fails or not, is a matter of specification. printf("Hello, World!\n") Is me saying: "Do a write syscall to stdout. I don't care what the return value is, I don't care if the flush is successfull if stdout happens to be buffered." If that is what I want to do, aka. what the program is specified to do, then it didn't fail.
To me, your example says: "I forgot about the return value." The way I learned C, if you really, really want to say "I don't care what the return value is" you'd explicitly cast it, nicely documenting your active non-caring about the return value for future code readers: (void) printf("Hello, World!\n"); Although, in general, ignoring the return value from things like puts() and printf() is a bad idea, for reasons th…