Live data from Hacker News

How can C Programs be so Reliable? (2008)

tratt.net

111–120 of 230 posts

Re: How can C Programs be so Reliable? (2008)

#111
post #21

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'…

Your experience with languages with exceptions seem to come from people who misuse them. Randomly placing catch clauses around in the code is not good practice, even if perhaps a majority of all programmers in safe languages code that way. That causes latent bugs that are incredibly hard to debug. The trick is to almost never ever catch exceptions. For example, in his post he describes a bug caused by accessing beyon…

> 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 something that makes sense at that level of abstraction, never leaking implementation details.

Re: How can C Programs be so Reliable? (2008)

#112
post #82

Earlier quoted context omitted.

If malloc() fails now, it might succeed again later. So you can just go on doing everything else you were doing, then try the memory-hungry operation again in the future. For example, this could be important in systems where you might be controlling physical hardware at the same time as reading commands from a network. It's probably a good idea to maintain control of the hardware even if you don't have enough memory…

It's an even better idea to make the hardware fail safe, so you can let the program die and not worry too much about it. This does not apply in all cases (cars), but it does apply in many (trains, like a deadman switch for the computer). For a vivid example of why this is an important approach, read about the Therac-25.

Absolutely, but I would still write my software as though the hardware could fail deadly unless doing so made the system less reliable.

Re: How can C Programs be so Reliable? (2008)

#113
post #105
post #93

Earlier quoted context omitted.

Yes! Most software I've seen can be dramatically improved by removing exception handlers. (And then fixing the underlying problems). On the server side I prefer one exception handler which is the OS, it will kill your process, release and free all memory, file handlers, etc. Then I wrap it in a bash script / other monitoring tool, and have it immediately restart whenever it fails. As for the server itself I'll also a…

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 with unplanned exceptions as well.

Re: How can C Programs be so Reliable? (2008)

#114
post #110
post #63

Popular C software is so reliable because of enormous amount of effort spent developing, testing, and polishing it over many years - not because error handling in C is superior to that in higher-level languages. If your python script sticks around for 30 years constantly being used by millions of people - I bet it will be rock solid as well. All other things [1] being equal [2], a program written in a higher-level la…

The problem with exceptions (in certain contexts, of course) is precisely these assumptions. Changes in low-level routines can change what your function is able to handle. Sometimes "just let it crash" isn't an option, period. Often, the exception hierarchy doesn't expose enough information to handle an exception without outside context (vis Python's OSError). In many cases, exceptions are superior to returning error…

"I've yet to see a universally applicable error handling model, and I suspect I never will."

I'll bite:

http://www.gigamonkeys.com/book/beyond-exception-handling-co...

Re: How can C Programs be so Reliable? (2008)

#115

Earlier quoted context omitted.

Haskell's type system isn't powerful enough to guarantee no exceptions will occur, though it certainly manages NULL better and other languages like Agda go further. Try debugging an out of bounds array access in Data.Array, which IIRC a few years ago just made my program print "error" and die...

I feel the majority of libraries you will use in Haskell won't resort to errors except in two cases: One is very low-level libraries doing things like interacting with C-code or raw memory. These are dangerous in all languages unless you are careful. The second is in functions that explicitly let the caller know they may fail. For example, you can write "head aList" which will cause an error if aList is empty, howeve…

Checking before you call a partial function isn't really any different than a null check.

Re: How can C Programs be so Reliable? (2008)

#116
post #94
post #26

Earlier quoted context omitted.

> Needless to say, calling exit() when a call to malloc() failed wasn't an acceptable recover routine. What do you do when malloc fails? A bit of graceful shutdown and logging seems like it would be in order, but otherwise how do you keep rolling if mallocs start failing? It seems to me like that would indicate something has gone unusually wrong and full recovery is futile.

I grew up using the Amiga, when having memory allocation fail was routine (a standard Amiga 500 for example, came with 512KB RAM, and was rarely expanded to more than 1MB, so you would run out of memory). What you do when malloc() fails depends entirely on your application: If a desktop application on the Amiga would shutdown just because a memory allocation failed, nobody would use it. The expection was you'd gracef…

Isn't fork the real offender, which requires Linux to overcommit by default? Disabling swap shouldn't affect that, right? Just makes your problem happen later, in a somewhat non-deterministic way.

Without fork, what reason do you not disable swap? I can only think of an anonymous mmap where you want to use the OS VM as a cache system. But that's solved easily enough by providing a backing file, isn't it?

Re: How can C Programs be so Reliable? (2008)

#117
post #21

Earlier quoted context omitted.

Your experience with languages with exceptions seem to come from people who misuse them. Randomly placing catch clauses around in the code is not good practice, even if perhaps a majority of all programmers in safe languages code that way. That causes latent bugs that are incredibly hard to debug. The trick is to almost never ever catch exceptions. For example, in his post he describes a bug caused by accessing beyon…

> 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 something else about it.

The real issue here is that you are thinking of exceptions as they exist in languages like C++ and Java, where you destroy your call stack in order to locate the exception handler. Such languages make the difficult problem of error recovery that much harder. Common Lisp does it better: the handler is allowed to invoke restarts (if they exist), which the function that signaled the error sets up. This encapsulates things very neatly. The disk was full? The error is signaled by write, which sets up a restart that tries to continuing writing to the disk. At the next level of abstraction, you might have a restart to remove the half-written record from your disk. In theory, you might only need one top-level exception handler, which interacts with the user as needed to recover from errors (or politely inform them that no recovery is possible).

Re: How can C Programs be so Reliable? (2008)

#118

Earlier quoted context omitted.

"Your experience with languages with exceptions seem to come from people who misuse them." Yes, it does, because people who misuse them seem to be a majority. I think that's an important point in language (or for that matter any kind of) design. You can't just look at how well things work for the experts - a mistake made by both Common Lisp and C++ in different ways). Nor can you just consign all non-experts to some…

Somewhat OT, but I find it curious that you would, in the same breath so to speak, put closures and continuations in the same category of "hard things". To me, continuations are still pretty mysterious, but closures are a pretty simple, usable idea.

Continuations are extremely counterintuitive and should only be used as low-level building blocks. Exceptions are actually a kind of continuation, and in Common Lisp the compiler uses continuations to implement exceptions (i.e. "conditions") and the restarts system. It is common in functional languages for the compiler to use continuation passing style as an intermediate representation, where instead of returning from a function you will invoke the "return continuation" that was passed to the function as an argument (i.e. it is the code that follows the function call -- where you return to).

Re: How can C Programs be so Reliable? (2008)

#119
post #9

Tarsnap is the classic example of a C program whose reliability almost defies belief. Its codebase is large; it solves a very hard problem; and it manages to handle almost every cornercase flawlessly. (As of the latest version, it seems to handle every cornercase flawlessly, as far as I know.) But I've always wondered: if Colin were as experienced with Python as he is with C, would Tarsnap be better served by writing…

It's rarely the language that has any actual effect on the end result. The language is just a side show - the main story is about how well the developer understood the problem space and how well the abstractions and solutions he generated work to solve the problem. Language can help a bit in making it clearer and giving the programmer more time to work on the important parts - but this actually goes both ways. Not ha…

> It's rarely the language that has any actual effect on the end result.

Implementing HN in APL is left as an exercise for the reader.

Re: How can C Programs be so Reliable? (2008)

#120
post #63

Popular C software is so reliable because of enormous amount of effort spent developing, testing, and polishing it over many years - not because error handling in C is superior to that in higher-level languages. If your python script sticks around for 30 years constantly being used by millions of people - I bet it will be rock solid as well. All other things [1] being equal [2], a program written in a higher-level la…

> Most sane languages [3] give you exception hierarchy Go being a glaring exception -- and this is a good thing.

He said sane.
Post reply on HN