The author makes a good point about the discipline imposed by not having exceptions. Programmers tend to write code in one of two modes: the quick-and-dirty mode where you consistently don't check return values, or the built-to-last mode where you consistently do. If you start in the first mode and have to fix a bug, you often have to add error checking all up and down the call chain from where it occurs to where it'…
How can C Programs be so Reliable? (2008)
161–170 of 230 posts
Re: How can C Programs be so Reliable? (2008)
#162Earlier quoted context omitted.
> The trick is to almost never ever catch exceptions. I strongly disagree. Not catching exceptions leaks abstraction layers. If I have a Prefs::save() method, I don't want it throwing a DiskFullException when the Prefs class is an abstraction of a preferences datastore. I don't care what is the final store, as long as it fits the abstraction. A well designed abstraction will catch and wrap the exception into somethin…
I don't think you understand proper exception handling. Catching and wrapping DiskFullException is pretty pointless because what are you going to do about it? Nothing. It's nonsensical for a preferences class to deal with that situation. Instead let it bubble up and so that the caller has the option of handling it, for example by showing a dialog "Delete temporary files and try again?" You'll never be able to catch a…
Re: How can C Programs be so Reliable? (2008)
#163Earlier quoted context omitted.
> The trick is to almost never ever catch exceptions. I strongly disagree. Not catching exceptions leaks abstraction layers. If I have a Prefs::save() method, I don't want it throwing a DiskFullException when the Prefs class is an abstraction of a preferences datastore. I don't care what is the final store, as long as it fits the abstraction. A well designed abstraction will catch and wrap the exception into somethin…
"A well designed abstraction will catch and wrap the exception into something that makes sense at that level of abstraction, never leaking implementation details." This makes recovering from the error rather difficult. If the problem is that a disk is full, I need to do something about the disk being full (maybe ask the user to delete some files). If the problem is that the disk was disconnected, I need to do somethi…
On the other hand, the most revered architectures we have aren't leaky. You don't see network stack code trying to recover from Ethernet collisions at the IP level, or app logic trying to salvage an SQL transaction when a restriction has been tripped. The price for non leaky abstractions is not zero, but the gains are also definitely not zero.
Re: How can C Programs be so Reliable? (2008)
#164Earlier quoted context omitted.
What if you application is the fly-by-wire for an airliner? Can you imagine that there might be better options than just calling abort(3)?
Embedded systems like this do not use dynamic memory allocation.
My point is: sometimes it is worth trying to recover when malloc() fails.
Re: How can C Programs be so Reliable? (2008)
#165There are multiple factors that contribute, but here's a few that I haven't (to the best of my recollection) seen mentioned so far: tooling (crucial), "do the simplest thing that could possibly work" attitude brought about by (lack of) a built-in collections library and simple syntax, and lack of rapidly changing requirements during development. Tools for C are powerful, mature, and available on near every platform.…
> Tools for C are powerful, mature, and available on near every platform. Every single C programmer that I know uses multiple memory debuggers, for example: e.g., valgrind for leak checks and more on smaller code segments, LLVM address sanitizer on unit test runs, jemalloc or tcmalloc/google-perftools, and more. This is exactly the point. C developers need to make use of external tools to improve the language's flank…
Re: How can C Programs be so Reliable? (2008)
#166There are multiple factors that contribute, but here's a few that I haven't (to the best of my recollection) seen mentioned so far: tooling (crucial), "do the simplest thing that could possibly work" attitude brought about by (lack of) a built-in collections library and simple syntax, and lack of rapidly changing requirements during development. Tools for C are powerful, mature, and available on near every platform.…
>Tools for C are powerful, mature, and available on near every platform. If only this was true for embedded platforms! I have no tools at my disposal aside from a vendor supplied debugger. No pre-existing test infrastructure exists, what we have we have had to build up ourselves. One of C's last remaining strongholds is in embedded, and the tooling in this world is atrocious! (Though some of the instruction and perfo…
C is everywhere.
I'm in a similar situation with embedded stuff at the moment. We're basically stuck with debug-by-printf over a serial port, far from ideal!
I have worked on other embedded projects that were better, for instance you can make a gdb server that operates over serial or tcp/ip and allows you to use gdb (or even graphical debuggers like ddd) on a host machine to step through code as it runs on your device. This does rely on there being resources available to do this though I guess...
Re: How can C Programs be so Reliable? (2008)
#167Earlier quoted context omitted.
C libraries don't just exit. A C executable could in some circumstances just fprintf() to stderr and exit(1) but usually even that requires sane deinitialization so before exiting you will effectively end up back in main() from the lower levels. C libraries generally handle their errors on some level, possibly jumping directly out from the lowest levels and then doing deinit before passing back an error code to the c…
Exactly. Writing libraries in C means you have to be much more strict than simple throwaway single execution binaries. What our company has ended up doing is wrappering most low level functions with the common code around it. For example, fopen() may fail and return EINTR if the process received a signal just as it was opening a file (we use signals to tell processes to reread their config so this does happen albeit…
Sorry pal, you're doing it wrong. At least, this is not the way I've done it and seen it done in large C code bases.
You're supposed to have only one return statement, and one block that frees everything. For example if you initialize `foo`, `bar`, and `qux` to `NULL`. Then testing these pointers for `NULL` de facto tells you how far you got in the function, and which buffers need to be freed. Just before your one single return statement (can't emphasize this enough) you call `free` on all of them regardless of success or failure. It's much more composeable than what you described - allocations for `foo`, `bar`, `qux` can fail, the ones that will be not yet allocated at that point in time will be `NULL`, and `free(NULL)` is a harmless no-op.
None of this business of "I've got to return now, let's see, how many of these buffers do I need to release at this point in time?", with varying amounts of the same free statement appearing redundantly. Write the cleanup block once when the pointers are about to fall out of scope, have it able to run in both success and failure cases and be done with it. Think of it as a more manual RAII if you like.
As for what to replace those early `return`s with, the two common schools are `goto` into the cleanup block, or repeatedly checking some kind of failure status variable before performing new actions.
Re: How can C Programs be so Reliable? (2008)
#168Earlier quoted context omitted.
> Tools for C are powerful, mature, and available on near every platform. Every single C programmer that I know uses multiple memory debuggers, for example: e.g., valgrind for leak checks and more on smaller code segments, LLVM address sanitizer on unit test runs, jemalloc or tcmalloc/google-perftools, and more. This is exactly the point. C developers need to make use of external tools to improve the language's flank…
Type safety is a lie. Everything is a piece of memory. C does not hide this from you, it's a design feature and not a bug.
So speaks an Assembly developer
> C does not hide this from you, it's a design feature and not a bug.
I guess the design goal was to make security exploits as easy as taking caddies from children.
Re: How can C Programs be so Reliable? (2008)
#169Earlier quoted context omitted.
Are the servers you're building serving concurrent clients? An exception could take out multiple in-flight requests. (not against your idea, just curious how you handle it)
One advantage of forking servers; kill and reboot the parent, and you don't loose in-flight connections. That said, I do this as well, even the best behaved daemon can get... funky... after a few months. Planned outages for a daemon restart are ok in my experience, particularly if you can fail over to other nodes as part of a rolling restart. Of course, this refers to planned restarts, though forking servers helps wi…
Re: How can C Programs be so Reliable? (2008)
#170Earlier quoted context omitted.
If your server's running Limux, it's going to kill your process with no questions asked if you run out of memory. You're better off practicing crash-only error recovery and having robust clients that can handle a reconnect. HTTP is stateless already, so crash and restart all you want!
The OOM killer is more likely to kill some other process and trash your server. Thankfully that sort of behavior has been vastly reduced since the thing was introduced, but disabling overcommit for high-reliability applications is still a reasonable course of action.
I had a server last week in which swap hadn't been configured. A compilation job took all memory and the OOM started thrashing. Thankfully there's always one SSH session open but I couldn't kill anything, sync or shutdown; fork failed with insufficient memory.
Left if thrashing overnight and had to power-kill it next day.