From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,FREEMAIL_FROM,MAILING_LIST_MULTI autolearn=ham autolearn_force=no version=3.4.4 Received: (qmail 10465 invoked from network); 10 Mar 2023 15:55:11 -0000 Received: from minnie.tuhs.org (2600:3c01:e000:146::1) by inbox.vuxu.org with ESMTPUTF8; 10 Mar 2023 15:55:11 -0000 Received: from minnie.tuhs.org (localhost [IPv6:::1]) by minnie.tuhs.org (Postfix) with ESMTP id E21A541552; Sat, 11 Mar 2023 01:55:07 +1000 (AEST) Received: from mail-lf1-x12c.google.com (mail-lf1-x12c.google.com [IPv6:2a00:1450:4864:20::12c]) by minnie.tuhs.org (Postfix) with ESMTPS id EB19241551 for ; Sat, 11 Mar 2023 01:55:02 +1000 (AEST) Received: by mail-lf1-x12c.google.com with SMTP id j11so7160372lfg.13 for ; Fri, 10 Mar 2023 07:55:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; t=1678463701; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=n/K/U8r36ddfX5RUN6K/0yG3BswFgJpF9ILGtyR6ENY=; b=YLEzlxDlQMAZMHtvreNAvT0G/hsImRiAv8c7QKZd5u5L/76eBocrE8ymb6eKoZ0zbr PvX2FDbuEth+ZuLWncxd028oIPw3RXhWYZB+Cn+Biyl1zYiwH7tMMCVSGjKmDuFZCX+X nIz8hLTegYXd/tMgnGbvmBIw0w8Fz2VoMPlGuzN7E9qMIlVki6pvqBQon2FyaQXYOzqC fq9S95zxnTge4JHXKFcYk5UtVhWp2rhUhWCSWEz9hGxmjgQ1/Fvy2lqz+r0AY7l0tZLn hr/Qyy2YIoM7A9MHsWSuidIIDHuWJbZBVNwXyEv6QylEYS0GJoQSUrLSkCo54HFD6+KG 2vAg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1678463701; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=n/K/U8r36ddfX5RUN6K/0yG3BswFgJpF9ILGtyR6ENY=; b=ANxmBph6aHFjYX/rn87es+LVMqBFiIIMesbuNr2Bj3Ar2W8UjBNyZZLfo+Q4XZbJtJ wT9KSdTPUgbctpfM7+gcifEoTSwpyycLBTj5HS+bG5TwA9oJ+DIJjfv1x3wXEgytmnhe P4R00sKfxhHyJDNVJ0lvQ2KjsFP37eoE7yVvRAXBFKW9LIOA8ukO8Cz/whNDuMx5CeWX nHW0Ip+fuBjzzGlswm27ykUfx+EHC8Iqrwqis8xRuZx79u7In2HeOZ8KOFhDRsaVKVWd o15UXUo3ztEiqoMOXXP2G5b2PMKDvlUF2hct9TfXmWENGPk0HMASU4Q50ab6nRU9emLj ylJg== X-Gm-Message-State: AO0yUKV8udDXbbfco0V0TwGNa9dLGQxyqu3Vap8+JpSH1VNLUujIkY0f 9V6rr3c3i91OwEDb3RIneVzEyXKNuVUzPXtdWRJIXPscv4w= X-Google-Smtp-Source: AK7set9Z4acFFBXuyVtEDRd1z3E4dLRhjV4F0mrp1LBlToFLfPBpD37zphq6CyBV7yuQV9Z2mBSPAbzO0g0f5MN1uRk= X-Received: by 2002:a05:6512:102c:b0:4d5:ca32:7bc3 with SMTP id r12-20020a056512102c00b004d5ca327bc3mr8142342lfr.10.1678463700672; Fri, 10 Mar 2023 07:55:00 -0800 (PST) MIME-Version: 1.0 References: <20230310113708.AD55518C080@mercury.lcs.mit.edu> In-Reply-To: <20230310113708.AD55518C080@mercury.lcs.mit.edu> From: Dan Cross Date: Fri, 10 Mar 2023 10:54:24 -0500 Message-ID: To: Noel Chiappa Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Message-ID-Hash: VNQ3Q4EE5SIBPTHWYIEF2KC4ZDODKU7J X-Message-ID-Hash: VNQ3Q4EE5SIBPTHWYIEF2KC4ZDODKU7J X-MailFrom: crossd@gmail.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: segaloco@protonmail.com, tuhs@tuhs.org X-Mailman-Version: 3.3.6b1 Precedence: list Subject: [TUHS] Re: I can't drive 55: "GOTO considered harmful" 55th anniversary List-Id: The Unix Heritage Society mailing list Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: On Fri, Mar 10, 2023 at 6:37=E2=80=AFAM Noel Chiappa 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 followi= ng > posts, most people here are unfamiliar with them. Too bad, they are a gre= at > 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`, 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 =3D "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, a= nd > 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 com= piler > support: on() became a macro for 'if _on("condition_name")'; _on() was a > partially-assembler procedure which i) stacked the handler (I forget wher= e 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 ret= urn > 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 =3D 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.