4 ms·
A C Heisenbug in the wild
- Swannie 15y agoI think "Side effect free" in assert() is the biggest takeaway from this.
- jesstaa 15y agoAvoiding this kind of bug is one of the reasons why Go doesn't have assert().
- _delirium 15y agoA C compiler could warn on large classes of side-effecting code inside assert(), as he suggests, but it'd be tricky to decide exactly which kinds of side effects to warn on to avoid too much noise. At the very least, I/O stuff would probably have to be excluded, or else you'd spuriously warn on the common pattern of dumping some debugging info to stderr within a call like assert(everything_ok()).
- deleted 15y ago[deleted]
- abcd_f 15y agoTrivial stuff, really. Useful to know - sure. Worth several pages of the blog post - hardly. Now let's see the HN fanboism in action. It is Jacques' after all.
- JoachimSchipper 15y agoYou're not necessarily wrong, but try being less nasty about it.
- barrkel 15y agoThis is why the Delphi compiler codebase (written in C) uses two functions, "invariant()" and "invariant_if_debug()", instead of library-supplied asserts. The second gets compiled out in release; the first triggers internal compiler error diagnostic even in production.
- xxpor 15y agoI was under the impression that Delphi was written in Delphi.
- barrkel 15y agoThe Delphi IDE is written in Delphi, but the Delphi compiler is written in C.
- xxpor 15y agoAh, interesting. I'll have to tell that to my dad, who always told me that Delphi was one of the earliest self hosting languages.
- ryanpetrich 15y agoDelphi is an object-oriented variant of Pascal. Pascal has been self-hosted since the late seventies.
- Cushman 15y agoI don't mean to be that guy, but this is the sort of bug that makes me wonder about the state of professional programming. Even if you're not fluent in C, as a programmer it should be intuitively obvious what's going on here. Of course we all have brain farts now and then, but there's no way this code should survive a second, let alone a third reading. Or am I just being conceited?
- schrototo 15y agoI thought the same thing, as bugs go this one seemed rather trivial. Having said that, it is tremendously easy to overlook even the most obvious of errors in code you yourself wrote, even and especially when going over it multiple times.
- alexgartrell 15y agoIt's non-obvious that asserts would just be #define'd out of existence when NDEBUG is defined if you haven't been told that specifically.
- to3m 15y agoAs you might be told specifically by, for example... the documentation?! The biggest surprise for me is that this error survived more than one attempt to remove it...
- Swannie 15y agoDoesn't surprise me if it's in a 20k loc program. Very easy to overlook the obvious. That said, like the previous comments, if someone had seriously looked for this issue, you'd have hoped they'd have found this pretty quickly.
- to3m 15y agoNo, that's a fair comment. I have spent a surprising proportion of the past 10 years moving initialization calls from <random problem-causing place> into `main', so whenever I see a call to something like `someinitialization' I just assume it's in `main' along with all of the rest of them. But it could easily be somewhere creepy and random, and the side-effects a bit hard to spot, making it harder to find. (Which, of course, is why you should always put all of this stuff in `main' ;)
- zerd 15y agoThis is why we should read man pages: man assert: "This may create Heisenbugs which go away when debugging is turned on."
- icecommander 15y agoThe Python analog would be something like this: assert (object() < object()) == False (try running this in the interpreter a few times)
- coconutrandom 15y agoWoah... so this is coercing it to a string and the address in hex is being compared. Am I off on this?
- leot 15y agoYes. Python is not that mysterious. For generic objects, the __lt__ and __gt__ methods are simply defined the only way that's sensible, which is effectively as a pointer location comparison.
- leot 15y agoNot really. The example given is equivalent in badness to: g = Timer() assert g.startTimer() == 0 ... and then wondering why the timer doesn't start when you run your program in "optimized" mode.
- newobj 15y agoThat's not a Heisenbug. That's just a classic newbie mistake. If it's made by a non-newbie, then I highly question their competence.
- ootachi 15y agoThis is the reason why I think it would be preferable for assert() to use eager evaluation. Any optimizing compiler will optimize away the useless check if debug mode is off, even if function calls obscure it (as long as the compiler can reason about purity).