[go: up one dir, main page]

|
|
Log in / Subscribe / Register

Implicit stack-unwinding exceptions are terrible

Implicit stack-unwinding exceptions are terrible

Posted Sep 22, 2024 9:49 UTC (Sun) by summentier (subscriber, #100638)
In reply to: Exceptions? by zorro
Parent article: Best practices for error handling in kernel Rust

Implicit stack-unwinding exceptions of the Ada/C++/Java/Python variety are, to my mind, a straight-forward language design mistake, certainly in this age of nontrivial codebases and nontrivial parallelism. Rust did well by not including them.

1. Exception safety together with RAII seems simple but is actually fiendishly difficult to get right – Stroustup's paper on execption safety in something as basic as an array has 17 pages, auxiliary base types, etc.

2. Add parallelism to the mix, and you have to reintroduce a monad (`Result`, `std::future`) anyway. Oh, by the way, are you sure your network of threads is notified and unwound in the correct order by the default behaviour?

3. Are you sure the calling code knows which exceptions to expect? Are you sure you know which ones you are passing along without even knowing from third-party libraries? Are you sure none of these libraries are adding or changing expections in new versions? The function signature is certainly of no help to you there...

4. As the C++ FAQ points out, exceptions allow you to factor out code to helper functions. Sure, but are the exceptions the helper raises still relevant to the caller of the upper-level function? Is, e.g., "log2 expects positive argument" something you want to expose from a config file parser rather than "Invalid entry: expect n to be positive"? Something you want to guarantee and maintain?

In an ideal world, I would like to have a "decay" mechanism, where Results, when assigned to a variable that is not Result, are unpacked and a panic raised if that fails, just to free up some mental space of new programmers or computational scientists who do not want to think about error paths all the time. Other than that, I think Rust gets this right.


to post comments

Implicit stack-unwinding exceptions are terrible

Posted Sep 22, 2024 13:37 UTC (Sun) by zorro (subscriber, #45643) [Link] (1 responses)

As to your first and second points, the whole purpose of Rust is for the compiler to check that your (parallel) code is safe and free from race conditions. From a code safety perspective, propagating errors explicitly using ? is really not that different from propagating errors implicitly using an exception*

As to your third point, fail-fast philosophy ([1]) is that you do not need to know what exceptions to expect as they are always "fatal" (leading to an abort of the transaction, after which the system continues as if the transaction never happened). An exception that you *can* handle is not an error and should not be modeled as an exception. *In that sense, Rust's Result type makes sense.* But at some point, if all my code can do with an error is to pass it to the caller then it may as well be an exception as it is extremely unlikely that the calling code will be able to do anything else with it either.

As to your final point, I don't think it is only new programmers and computational scientists that don't want to think about exceptions. I don't want to think about them either, and I've been programming for more than 25 years. In fact, all catch blocks in my C# code are required to have a comment explaining why the exception must be caught and handled (rather than implicitly propagating it) and why it is not possible to prevent the exception from happening in the first place.

[1] "Need Robust Software? Make It Fragile" (https://www.yegor256.com/2015/08/25/fail-fast.html).

*One area in which I think exceptions are problematic is in asynchronous code, but I'm a "keep-it-simple" kind of guy and avoid asynchronous code like the plague.

Implicit stack-unwinding exceptions are terrible

Posted Sep 22, 2024 13:54 UTC (Sun) by mb (subscriber, #50428) [Link]

>if all my code can do with an error is to pass it to the caller then it may as well be an exception as it is
>extremely unlikely that the calling code will be able to do anything else with it either.

That is pretty much not true in general and especially not true for kernel code.
There almost always is *some* point in the call chain that can handle the error. And it almost never is at the tip. It's somewhere in the midde (e.g. re-queue, re-try, try something else, etc...) or at the bottom (fail syscall).

Exceptions are a nightmare.
They are invisible in almost all of the program code.

Exceptions just require that every path eventually has a catch-all block in addition to the known-error-catch-blocks, because "huh, *that* exception was possible??".

Implicit stack-unwinding exceptions are terrible

Posted Oct 6, 2024 9:59 UTC (Sun) by ssokolow (guest, #94568) [Link] (1 responses)

There's an essay from 2005 named Exception Handling Considered Harmful which basically agrees. It argues two points:
  1. That, in real-world use, exception handling introduces a hidden control flow path at essentially every line of code, which is unreasonable and potentially impossible for even experts to manage correctly.
  2. That the implicit SQL-esque transaction-rollback nature of exception handling through stack unwinding does not map well to multi-threaded execution.

Implicit stack-unwinding exceptions are terrible

Posted Oct 6, 2024 10:19 UTC (Sun) by Wol (subscriber, #4433) [Link]

I'm trying to remember how PLP (and by implication, PL/1) handled exceptions.

Iirc, by default, when an exception triggered, it "return"ed to where it came from. It was standard practice to register a longjmp if you wanted it to return somewhere else (which could be anywhere in the call stack). So in practice, exceptions were simply a good mechanism for "on error goto cleanup", but were rather more powerful if required.

I'm guessing this "default to return to call site" meant that they weren't quite so bad as the exceptions being discussed here ...

Cheers,
Wol


Copyright © 2026, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds