The Unix Heritage Society mailing list
 help / color / mirror / Atom feed
From: Dan Cross <crossd@gmail.com>
To: Noel Chiappa <jnc@mercury.lcs.mit.edu>
Cc: segaloco@protonmail.com, tuhs@tuhs.org
Subject: [TUHS] Re: I can't drive 55: "GOTO considered harmful" 55th anniversary
Date: Fri, 10 Mar 2023 10:54:24 -0500	[thread overview]
Message-ID: <CAEoi9W6=F=0Lod1jBkY5bmygAh4u5t_XT7qSVq2aBCzCSqz66w@mail.gmail.com> (raw)
In-Reply-To: <20230310113708.AD55518C080@mercury.lcs.mit.edu>

On Fri, Mar 10, 2023 at 6:37 AM Noel Chiappa <jnc@mercury.lcs.mit.edu> wrote:
>
>     > From: Warner Losh
>
>     > In C I use it all the time to do goto err for common error recovery
>     > because C doesn't have anything better.
>
> That's because C doesn't have 'conditions'. (Apparently, from the following
> posts, most people here are unfamiliar with them. Too bad, they are a great
> idea. OK summary here:
>
>   http://gunkies.org/wiki/Condition_handler
>
> for those who are unfamiliar with the idea.)

I don't know if I'd say they're a great idea.

The problem with exceptions (nee conditions, though I most often
associate the term "condition" with Lisp, and in particular Common
Lisp's implementation has a rather different flavor in the ability to
restart execution _at the point where the condition was raised_, even
if the handler is conceptually much higher up in the call stack) is
that they introduce non-linear control flow, which can be very
difficult to reason about. This is especially challenging in code that
may allocate resources and must manually deallocate them (such as C);
without some notion of RAII or finalizers for arbitrary objects. It's
really easy to introduce leaks in code with exceptions, and while
often this is for memory, where you're ok if you're in a garbage
collected language, you're gonna have a bad day when it's for
something like file descriptors (which are much scarcer than memory).
Unless pretty much everything is behind a stack guard, or whatever the
moral equivalent in your language is, you're constrained to handling
the errors at many places in the call stack, in which case, why
bother?

But the point about error handling and the use of `goto` in C in lieu
of something better is well taken, and conditions are _a_ reasonable
mechanism for dealing with the issue. I'd argue that a `Result` monad
and some short-circuiting sugar used in conjunction with RAII is
another that is better. For example in Rust, the result type interacts
with the `?` operator so that, if a call returns a `Result<T, E>`, the
T will be unwrapped if the result is Ok(T), otherwise, the code will
return `Err(E)`. So one can write code that has the brevity of
exceptions without introducing the control-flow weirdness:

    fn make_request(host: &str) -> std::io::Result<()> {
        let req = "hi\r\n".as_bytes();
        std::net::TcpStream::connect(host)?.write(req)?;
        Ok(())
    }

(Note that the TCP stream will be "dropped" after the call to `write`,
and the drop impl on the TcpStream type will close the socket.)

Combined with pattern matching on the error type, this is quite expressive.

> I was at one point writing a compiler using a recursive descent parser, and
> decided the code would be a lot simpler/cleaner if I had them. (If, for
> example, one discovers discovers an un-expected 'end of file', there can be
> an extremely large number of procedure invocations in between where that is
> discovered, and where it is desirable to handle it. So every possible
> intervening procedure would have to have an 'unexpected EOF' return value,
> one would have to write code in every possible intervening procedure to
> _handle_ an 'unexpected EOF' return value, etc.)'
>
> (Yes, I could have used setjmp/longjmp; those are effectively a limited
> version of condition handlers.)
>
> Since C has a stack, it was relatively easy to implement, without any compiler
> support: on() became a macro for 'if _on("condition_name")'; _on() was a
> partially-assembler procedure which i) stacked the handler (I forget where I
> put it; I may have created a special 'condition stack', to avoid too many
> changes to the main C stack), and ii) patched the calling procedure's return
> point to jump to some code that unstacked the handler, and iii) returned
> 'false'. If the condition occurred, a return from _on() was simulated,
> returning 'true', etc.
>
> So the code had things like:
>
>         on ("unexpected EOF") {
>                 code to deal with it
>                 }
>
> With no compiler support, it added a tiny bit of overhead
> (stacking/unstacking conditions), but not too bad.
>
> John Wroclawski and someone implemented a very similar thing
> entirely in C; IIRC it was built on top of setjmp/longjmp. I don't
> recall how it dealt with un-stacking handlers on exit (which mine
> did silently).

The plan9 kernel has something remarkably similar; there is a
pre-process error stack containing the local equivalent of a bunch of
`jmp_buf`'s.  One could write, `if (waserror()) { /* handle cleanup */
}` where, `waserror` would push a jmp_buf onto the stack a la the
`setjmp` equivalent. Code later on could call `error(Ewhatever)` and
that would cache the error somewhere in the proc struct and invoke the
`longjmp` equivalent to jump back to the label at the top of the
stack, where `waserror()` would now return 1. Things would have to
manually `poperror()`, to pop the stack. I'm told that the plan9 C
compilers were callee-save in part to keep these state labels svelte.

This got pulled into the Akaros kernel at one point, and for a while,
we had someone working with us who was pretty prominent in the Linux
community. What was interesting was that he was so used to the `goto
err;` convention from that world that he just could not wrap his head
around how the `waserror()` stuff worked; at one point there was a
sequence like,

    char *foo;

    if (waserror()) {
        free(foo);
        return -1;
    }
    foo = malloc(len);
    something();
    free(foo);
    return 0;

...and the guy just couldn't get how the code inside of the
`waserror()` wasn't trashing the system, since obviously the malloc
was done after `waserror()`, and so the pointer was meaningless at
that point. It took quite a while to explain what was going on.

Btw: I was once told by a reliable authority that the Go developers
considered implementing exceptions, but decided against it because of
the cognitive load it imposes.

        - Dan C.

  parent reply	other threads:[~2023-03-10 15:55 UTC|newest]

Thread overview: 75+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-10 11:37 Noel Chiappa
2023-03-10 11:51 ` [TUHS] Conditions, AKA exceptions. (Was: I can't drive 55: "GOTO considered harmful" 55th anniversary) Ralph Corderoy
2023-03-10 15:54 ` Dan Cross [this message]
2023-03-12  7:39   ` [TUHS] Re: I can't drive 55: "GOTO considered harmful" 55th anniversary Anthony Martin
2023-03-12 11:40     ` Dan Cross
2023-03-12 16:40       ` Paul Winalski
2023-03-13  3:25       ` John Cowan
2023-03-13 10:40         ` Alejandro Colomar (man-pages)
2023-03-13 12:19           ` Dan Cross
2023-03-13 12:43             ` [TUHS] [TUHS]: C dialects (was: I can't drive 55: "GOTO considered harmful" 55th anniversary) Alejandro Colomar
2023-03-13 12:46               ` [TUHS] " Dan Cross
2023-03-13 16:00               ` Paul Winalski
2023-03-13 19:00                 ` Clem Cole
2023-03-13 19:09                   ` Larry McVoy
2023-03-13 19:17                   ` Steve Nickolas
2023-03-13 20:26                     ` Dan Cross
2023-03-13 22:25                       ` Alejandro Colomar (man-pages)
2023-03-13 19:24                   ` [TUHS] Re: [TUHS]: C dialects Luther Johnson
2023-03-13 19:38                     ` Luther Johnson
2023-03-14 19:48                     ` John Cowan
2023-03-14 19:56                       ` Joseph Holsten
2023-03-14 20:01                       ` Luther Johnson
2023-03-13 20:48                   ` [TUHS] Re: [TUHS]: C dialects (was: I can't drive 55: "GOTO considered harmful" 55th anniversary) Paul Winalski
2023-03-13 20:56                     ` Bakul Shah
2023-03-14  1:06                     ` Larry McVoy
2023-03-13 21:00                   ` Paul Winalski
2023-03-13 21:07                     ` Bakul Shah
2023-03-13 21:14                       ` Dan Cross
2023-03-13 22:15                         ` Dave Horsfall
2023-03-13 22:47                           ` Dave Horsfall
2023-03-14  0:23                             ` Dan Cross
2023-03-14  0:21                           ` Dan Cross
2023-03-14 13:52                             ` Chet Ramey
2023-03-14  1:27                         ` Bakul Shah
2023-03-13 21:28                       ` Paul Winalski
2023-03-14 10:04                       ` [TUHS] C dialects Ralph Corderoy
2023-03-14 20:02                         ` [TUHS] " John Cowan
2023-03-14 21:34                           ` Thomas Paulsen
2023-03-14  0:38                     ` [TUHS] Re: [TUHS]: C dialects (was: I can't drive 55: "GOTO considered harmful" 55th anniversary) John Cowan
2023-03-14  2:49                 ` Theodore Ts'o
2023-03-14  3:06                   ` G. Branden Robinson
  -- strict thread matches above, loose matches on Subject: below --
2023-03-10 15:37 [TUHS] Re: I can't drive 55: "GOTO considered harmful" 55th anniversary Noel Chiappa
2023-03-10 15:46 ` Larry McVoy
2023-03-10 16:04 ` Dan Cross
2023-03-10 18:55 ` Ron Natalie
2023-03-10 19:04   ` Dan Cross
2023-03-10 19:35     ` segaloco via TUHS
2023-03-10 11:51 Noel Chiappa
2023-03-10 14:16 ` Ronald Natalie
2023-03-10 14:39   ` John Cowan
2023-03-10 16:30   ` Phil Budne
2023-03-10 17:50     ` Steffen Nurpmeso
2023-03-10 17:57       ` Paul Winalski
2023-03-10 18:12         ` Lawrence Stewart
2023-03-10 17:28   ` Clem Cole
2023-03-10 17:54     ` Paul Winalski
2023-03-09 23:01 [TUHS] " Steffen Nurpmeso
2023-03-09 23:18 ` [TUHS] " segaloco via TUHS
2023-03-09 23:21   ` Warner Losh
2023-03-09 23:31     ` Luther Johnson
2023-03-09 23:44       ` josh
2023-03-09 23:54       ` Warner Losh
2023-03-10  0:54         ` segaloco via TUHS
2023-03-10  1:08           ` Warner Losh
2023-03-10 10:08             ` Ralph Corderoy
2023-03-10 11:37               ` arnold
2023-03-10 11:56                 ` Ralph Corderoy
2023-03-10 11:59                   ` arnold
2023-03-10 12:11                     ` Ralph Corderoy
2023-03-10  6:15     ` Dave Horsfall
2023-03-10 16:55       ` Steffen Nurpmeso
2023-03-10 17:02         ` Bakul Shah
2023-03-12 20:47         ` Dave Horsfall
2023-03-12 21:50           ` Warner Losh
2023-03-12 22:27             ` Steffen Nurpmeso
2023-03-10  1:31   ` Rich Morin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAEoi9W6=F=0Lod1jBkY5bmygAh4u5t_XT7qSVq2aBCzCSqz66w@mail.gmail.com' \
    --to=crossd@gmail.com \
    --cc=jnc@mercury.lcs.mit.edu \
    --cc=segaloco@protonmail.com \
    --cc=tuhs@tuhs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).