Live data from Hacker News

How can C Programs be so Reliable? (2008)

tratt.net

31–40 of 230 posts

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

#31
post #26

Earlier quoted context omitted.

"Discipline" is indeed the right word. I was taught C at Epitech. A single segfault, no matter insidious, was a valid reason to render a whole project NULL. We often had evaluations ran with LD_LIBRATY_PATH=malloc_that_fails.so or just piping /dev/urandom to stdin... Needless to say, calling exit() when a call to malloc() failed wasn't an acceptable recover routine.

> 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 don't get the sense that there was any attempt to recover from errors - it sounds more like they were enforcing that error checking occurred, by replacing `malloc` with one that just returned `NULL` always. It sounds like the goal was to make sure that one didn't assume `malloc` would always succeed and just use the memory.

Indeed, recovery is basically futile in this case and your program is going to shut down pretty quickly either way. Maybe you'll get the chance to tell the user that you ran out of memory before you die, which seems polite.

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

#32
post #2

Adjusted for effort I don't know of any evidence that they are...

How does effort affect reliability? Even if you consider one as consequence of the other ("it requires more effort to make a reliable C program than the equivalent program in $language") that doesn't make the final product any less reliable.

it is more about the attitude of a C programmer versus the attitude of a, say, JavaScript or Ruby programmer. Whilest the latter merely assumes "nah, the VM will catch all null pointers as soft exceptions for me, and all exceptions I don't catch, my caller should." the first kind of programmer has learned (the hard way) to respect as much of the error codes a function call can return. "The hard way" is the malicious smiled SIGSEGV dynamic-programming language programmers may laughter about, because it hardly crashes your program, one may say.

However, I do believe, that this attitude to think first (how to program right) rather than trying to remember (what could have caused that many 500's on my HTTP server) which I think is the better approach to write more reliable software.

Dynamic languages are said to be more convenient for web sites, for example, I'm not denying that one, but those languages just shift the problems back into the future, where, when time has come, you may or may not be willing to attempt to fix the bug you introduced days/weeks/months ago, depending on the urgency.

On programming environments (such as C), where types are more statically typed, errors have to be handled manually and with caution, memory has to be managed (more or less) always with an ownership in mind, those programs, that think about these topics from the very beginning, and iron out those remaining bugs over time, are - from my point of view - the more reliable ones.

So I can out of my distance second this blog post. It was interesting to read.

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

#33
post #26

Earlier quoted context omitted.

"Discipline" is indeed the right word. I was taught C at Epitech. A single segfault, no matter insidious, was a valid reason to render a whole project NULL. We often had evaluations ran with LD_LIBRATY_PATH=malloc_that_fails.so or just piping /dev/urandom to stdin... Needless to say, calling exit() when a call to malloc() failed wasn't an acceptable recover routine.

> 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.

The most common way to indicate that an error has occurred in a C function is to return non-zero. If this is done consistently, and return values are always checked, an error condition will eventually make its way up to main, where you can fail gracefully.

For example:

    int a(void)
    {
        ...
        if (oops) {
            return 1;
        }
        return 0;
    }

    int b(void)
    {
        if (a(...) != 0) {
            return 1;
        }
        return 0;
    }

    int main(void)
    {
        if (b(...) != 0) {
            exit(EXIT_FAILURE);
        }
        return EXIT_SUCCESS;
    }
(This means that void functions should be avoided.)

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

#34
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 don't get the sense that there was any attempt to recover from errors - it sounds more like they were enforcing that error checking occurred, by replacing `malloc` with one that just returned `NULL` always. It sounds like the goal was to make sure that one didn't assume `malloc` would always succeed and just use the memory. Indeed, recovery is basically futile in this case and your program is going to shut down pre…

In systems that overcommit memory (like Linux), malloc() can return non-NULL and then crash when you read or write that address because the system doesn't have enough real memory to back that virtual address.

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

#35
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 don't get the sense that there was any attempt to recover from errors - it sounds more like they were enforcing that error checking occurred, by replacing `malloc` with one that just returned `NULL` always. It sounds like the goal was to make sure that one didn't assume `malloc` would always succeed and just use the memory. Indeed, recovery is basically futile in this case and your program is going to shut down pre…

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)?

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

#36

Earlier quoted context omitted.

How does effort affect reliability? Even if you consider one as consequence of the other ("it requires more effort to make a reliable C program than the equivalent program in $language") that doesn't make the final product any less reliable.

It does when you only have a set amount of effort that you can dedicate on a project. Which is usually the case.

I am not sure that's a useful criterion in practice, though. People are fond of saying that languages will not make you orders of magnitude more productive, and I think the corollary is that on average they will not make you orders of magnitude less productive either. (Controlling of course for characteristics of problems and such.)

The problem is that there's not really a great way to reason about this, either. If you're talking about how long it takes to write a program in C vs Java, how do you measure time spent writing code vs debugging vs bug fixing, et al?

All in all it's certainly possible it takes longer to write code in C, but for all anyone knows, writing more reliable code up front means fewer bugs later on. Code which is more performant by default may lead to fewer performance regressions when someone induces pathological GC behavior. Contrariwise, there's stuff like memory corruption as the OP describes, which is probably way more difficult in a native environment instead of a hosted one.

It's hard not to reach the conclusion that we're all just talking out of our hats anyway (myself included).

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

#37
"Once one has understood a concept such as pointers (arguably the trickiest concept in low-level languages, having no simple real-world analogy) "

huh?

So when you write a letter to someone, you actually attach their house onto the outside of the envelope? No of course you don't. You write their ADDRESS on the envelope.

All this lore about how difficult pointers are, is bunk. The syntax of pointers vs addresses vs dereferenced pointer values, can be difficult at first ... and can be very difficult if you read and/or write unecessarily obfuscated code ... but there is no need.

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

#38
post #19

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

It depends on the problem you're trying to solve, I think. Let's consider a command line application that fetches a URL, like wget. Without exceptions, you would check the return code of all the system functions you call (dns, sockets, etc), and if any of them fail you can't really recover. You just write out an error message and exit. With exceptions, you could wrap the whole thing and if any system function throws…

But what if the exception was in your code and not the system call? This is incredibly dangerous.

Monads are perfect for this. Really combines the best of both worlds without all the unspecified/undefined behavior.

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

#40
post #8

Earlier quoted context omitted.

Wait, HN is good? The community is interesting, but I've never been particularly impressed by the implementation. In particular, the "feature" where "More" links and such randomly expire after a while just screams "shoddy implementation" to me.

The thing is, the "more" links are specific to each user and generated page. They're not just "page 3 at the time you click on the link", but the actual next 30 links following the 30 it's previously shown you. As a result, it needs to store state for each of these links it's created (IIRC, the fnid is a reference to the closure containing that state). For obvious memory management purposes, these need to be expired…

I understand that it's a tough problem and can totally understand solving it by just making the links quit working like that. But that sort of thing doesn't strike me as "good", even if it is highly pragmatic.
Post reply on HN