56

When I learned C, teacher told me all day long: "Don't use goto, that's a bad habit, it's ugly, it's dangerous!" and so on.

Why then, do some kernel programmers use goto, for example in this function, where it could be replaced with a simple

while(condition) {} 

or

do {} while(condition);

I can't understand that. Is it better in some cases to use goto instead of while/do-while? And if so, why?

kR105
  • 864
  • 9
  • 15
musicmatze
  • 4,124
  • 7
  • 33
  • 48
  • 13
    Interesting question. As a side note, I wouldn't criticize `goto` that much. It is _usually_ a bad smell and stuff, but sometimes it is worth using to keep the code simpler - often for cleanup and handling error situations in complicated functions. Don't go religious about our little `goto` brother, it has it's uses :) – Piotr Zierhoffer Oct 21 '12 at 19:06
  • I currently do not have any opinion on this. I just noticed the source code while reading some C to educate myself about the language and remembered, some of my teachers always said these things about the `goto` statement. – musicmatze Oct 21 '12 at 19:13
  • 2
    Well, yes, that's what they usually say :) And it is often true. For teaching purposes "often" can be easily translated to "always", to keep things simpler – Piotr Zierhoffer Oct 21 '12 at 19:15
  • 5
    Ask him to describe the action taken by a `break;` statement in a structured loop (like while, for, etc) without using the words `goto` ,`leave`, or `jump` (the asm equivalent of `goto`). He wants you to assume using `goto` is a sign of after-thought patching of poorly thought-out code logic. In many/most cases he's probably right, but the keyword there is "probably". It is NOT always the case. While learning the language, it is best to avoid it for sanity-sake, but don't shoot the guy that truly knows what they're doing with it. – WhozCraig Oct 21 '12 at 19:18
  • 3
    http://www.xkcd.com/292/ – DaveShaw Oct 21 '12 at 23:04
  • 2
    [Here's an email](http://programmers.stackexchange.com/a/154980/27881) from Robert Love about one of the reasons `goto` is used in the linux kernel. – Izkata Oct 21 '12 at 23:33
  • The re-try loop is *almost* never taken in practice and the code flows doesn't look like a loop, it's a choice and goto usage highlights the try/cas/again approach. I, myself, use `for(;;){ if (cas) break; }` construct to handle CAS. – bestsss Oct 25 '12 at 16:38

3 Answers3

61

Historical context: We should remember that Dijkstra wrote Goto Considered Harmful in 1968, when a lot of programmers used goto as a replacement for structured programming (if, while, for, etc.).

It's 44 years later, and it's rare to find this use of goto in the wild. Structured programming has already won, long ago.

Case analysis:

The example code looks like this:

    SETUP...
again:
    COMPUTE SOME VALUES...
    if (cmpxchg64(ptr, old_val, val) != old_val)
        goto again;

The structured version looks like this:

SETUP...
do {
    COMPUTE SOME VALUES...
} while (cmpxchg64(ptr, old_val, val) != old_val);

When I look at the structured version, I immediately think, "it's a loop". When I look at the goto version, I think of it as a straight line with a "try again" case at the end.

The goto version has both SETUP and COMPUTE SOME VALUES on the same column, which emphasizes that most of the time, control flow passes through both. The structured version puts SETUP and COMPUTE SOME VALUES on different columns, which emphasizes that control may pass through them differently.

The question here is what kind of emphasis do you want to put in the code? You can compare this with goto for error handling:

Structured version:

if (do_something() != ERR) {
    if (do_something2() != ERR) {
        if (do_something3() != ERR) {
            if (do_something4() != ERR) {
                ...

Goto version:

if (do_something() == ERR)  // Straight line
    goto error;             // |
if (do_something2() == ERR) // |
    goto error;             // |
if (do_something3() == ERR) // |
    goto error;             // V
if (do_something4() == ERR) // emphasizes normal control flow
    goto error;

The code generated is basically the same, so we can think of it as a typographical concern, like indentation.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • 7
    [Kernel programmers agree](http://programmers.stackexchange.com/a/154980/27881). If you had ERR1, ERR2... ERRN, then you could `goto clean_err1;`/etc and reverse the changes as though it was stack. – Izkata Oct 21 '12 at 23:35
  • 4
    +1 for showing how goto can emphasize the expected control flow. – Leo Oct 22 '12 at 06:35
  • 1
    If code discovers in the middle of an inner loop that an outer loop needs to exit or restart, or if it discovers in the middle of a `switch` statement that an enclosing loop needs to exit, how should that be written without using `goto` or adding extraneous flags (which are even worse)? – supercat Oct 24 '16 at 21:52
  • @supercat: That's a matter of programming style which is hard to answer without seeing specifics. It's certainly no easier to answer that question than a question like "how should I exit a loop"--well, you can break, use the condition, use a flag, use a goto, use a return, etc... there are just too many possible choices, no definitive answer is possible. – Dietrich Epp Oct 24 '16 at 22:12
32

In the case of this example, I suspect it was about retrofitting SMP support into code that was originally written in a non-SMP-safe way. Adding a goto again; path is a lot simpler and less invasive than restructuring the function.

I can't say I like this style much, but I also think it's misguided to avoid goto for ideological reasons. One special case of goto usage (different from this example) is where goto is only used to move forward in a function, never backwards. This class of usages never results in loop constructs arising out of goto, and it's almost always the simplest, clearest way to implement the needed behavior (which is usually cleaning up and returning on error).

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • +1 for statement about goto ;-) And the reason seems quite logical as well. On the other hand, isn't it possible that `goto` could create a different result from a branch predictor point of view? – Piotr Zierhoffer Oct 21 '12 at 19:09
  • 1
    IIRC the `again:` loop method originates from the BSD network code. And, it is very clean, since the typical code path contains no (conditional) jump. The exceptional path does. – wildplasser Oct 21 '12 at 19:09
  • 1
    Ideally a compiler should treat a loop written with `goto` identically to one written with loop constructs, i.e. both should first be represented as conditional branches (`if ... goto ...;`) and then the optimizer should do its thing. I'm not sure to what extent this is true with present-day compilers, but it's an important property to have in order to avoid promoting coding styles where the author chooses a loop representation based on which one generates the best code rather than which one is most logical/self-documenting. – R.. GitHub STOP HELPING ICE Oct 21 '12 at 19:12
  • 3
    It's usually true with current compilers; they optimize the code flow graph after the `while` has been deconstructed into a conditional `goto` internally. – Donal Fellows Oct 22 '12 at 09:28
1

Very good question, and I think only the author(s) can provide a definitive answer. I'll add my bit of speculation by saying that it could have started with using it for error handling, as explained by @Izkata and the gates were then open to using it for basic loops as well.

The error handling usage is a legitimate one in systems programming, in my opinion. A function gradually allocates memory as it progresses, and if an error is encountered, it will goto the appropriate label to free the resources in reverse order from that point.

So, if the error occurs after the first allocation it will jump to the last error label, to free only one resource. Likewise, if the error occurs after the last allocation, it will jump to the first error label and run from there, freeing all the resources until the end of the function. Such pattern for error handling still needs to be used carefully, especially when modifying code, valgrind and unit tests are highly recommended. But it is arguably more readable and maintainable than the alternative approaches.

One golden rule of using goto is to avoid the so-called spaghetti code. Try drawing lines between each goto statement and its respective label. If you have lines crossing, well, you've crossed a line :). Such usage of goto is very hard to read and a common source of hard to track bugs, as they would be found in languages like BASIC that relied on it for flow control.

If you do just one simple loop you won't get crossing lines, so it's still readable and maintainable, becoming largely a matter of style. That said, since they can just as easily be done with the language provided loop keywords, as you've indicated in your question, my recommendation would still be to avoid using goto for loops, simply because the for, do/while or while constructs are more elegant by design.

Nagev
  • 10,835
  • 4
  • 58
  • 69