digitalmars.D - Corrupt Unwinding on Errors
- Eyal Lotem (26/26) Jan 22 2019 Hi,
- Iain Buclaw (7/26) Jan 22 2019 Throwing an Error inside a nothrow function is allowed, unlike an
- Eyal Lotem (7/12) Jan 22 2019 If you're not supposed to recover, then what's the point of
- Steven Schveighoffer (28/43) Jan 24 2019 So a couple things here:
Hi, I've hit a serious bug due to skipped dtors when errors are being thrown. Whether dtors will run or be skipped is quite unpredictable, as it depends on `nothrow` inference[1]. dtors are often absolutely crucial for program correctness, and skipping them is very dangerous. Thus, unwinding while skipping dtors may easily create corruption, so I'll term this "corrupt unwinding". During corrupt unwinding, running scope() code is dangerous - as it runs in a potentially corrupt state, and indeed a PR exists to not run scope() code in this state [2]. The question is, why have corrupt unwinding at all? If errors thrown should not properly unwind - why not call a tls-installed handler before terminating the program (with no unwinding)? If errors do unwind - why not run the dtors and allow actual recovery? The performance cost for predictable, sane semantics is well worth it. Side-note: resuming execution after corrupt unwinding is rarely practically possible in non-trivial programs. Knowing the effects of all potentially skipped dtors in all program and library code - especially as the program is maintained over time - is impractical. [1] https://issues.dlang.org/show_bug.cgi?id=19602 [2] https://github.com/dlang/dmd/pull/6896
Jan 22 2019
On Tue, 22 Jan 2019 at 09:15, Eyal Lotem via Digitalmars-d <digitalmars-d puremagic.com> wrote:Hi, I've hit a serious bug due to skipped dtors when errors are being thrown. Whether dtors will run or be skipped is quite unpredictable, as it depends on `nothrow` inference[1]. dtors are often absolutely crucial for program correctness, and skipping them is very dangerous. Thus, unwinding while skipping dtors may easily create corruption, so I'll term this "corrupt unwinding". During corrupt unwinding, running scope() code is dangerous - as it runs in a potentially corrupt state, and indeed a PR exists to not run scope() code in this state [2]. The question is, why have corrupt unwinding at all? If errors thrown should not properly unwind - why not call a tls-installed handler before terminating the program (with no unwinding)? If errors do unwind - why not run the dtors and allow actual recovery? The performance cost for predictable, sane semantics is well worth it.Throwing an Error inside a nothrow function is allowed, unlike an Exception. As for what happens if that were to ever happen is undefined, because you're not supposed to recover from it. -- Iain
Jan 22 2019
On Tuesday, 22 January 2019 at 09:14:30 UTC, Iain Buclaw wrote:On Tue, 22 Jan 2019 at 09:15, Eyal Lotem via Digitalmars-d <digitalmars-d puremagic.com> wrote: Throwing an Error inside a nothrow function is allowed, unlike an Exception. As for what happens if that were to ever happen is undefined, because you're not supposed to recover from it.If you're not supposed to recover, then what's the point of corrupt unwinding of the stack? It would be much safer to terminate the program right there, allowing a user-installed handler to do its thing before termination. Not unwinding is far better than *corrupt* unwinding.
Jan 22 2019
On 1/22/19 4:48 AM, Eyal Lotem wrote:On Tuesday, 22 January 2019 at 09:14:30 UTC, Iain Buclaw wrote:So a couple things here: 1. It's not possible to correctly recover from Errors because of the corrupt unwinding, as you say. This is because D compilers take advantage of nothrow attribution to avoid building costly unwinding handlers in functions. It's in fact unwinding the stack, but the compiler *has omitted* the unwinding code that would normally be executed. 2. If you don't unwind, this means that throwing an error isn't exactly throwing. It's basically aborting. That is, the advantage of catching the error in an outer scope is lost. I agree with you that corrupt unwinding can cause more problems than no unwinding. I think it should be made possible to at least *opt-in* to aborting instead of unwinding. Another possibility is not to unwind the stack, but simply pop the stack without executing any unwinding (even proper unwinding code), until you get to the handler, which must assume nothing has been unwound in the case of an Error. This would be similar to the abort mechanism, but gives the control to the appropriate catch statement. A problem with druntime today is that Throwable is caught in the thread startup and main routines. No recovery is attempted, the system is aborted, but there's still an attempt to print out the stack trace. See here: https://github.com/dlang/druntime/blob/master/src/rt/dmain2.d#L480-L484 This assumes some things are still available, e.g. C malloc and stderr, when really, an Error could actually mean that they aren't valid to be used. I'd also like to see an option to have nothrow still generate unwinding code, especially in debug/non-release mode. -SteveOn Tue, 22 Jan 2019 at 09:15, Eyal Lotem via Digitalmars-d <digitalmars-d puremagic.com> wrote: Throwing an Error inside a nothrow function is allowed, unlike an Exception. As for what happens if that were to ever happen is undefined, because you're not supposed to recover from it.If you're not supposed to recover, then what's the point of corrupt unwinding of the stack? It would be much safer to terminate the program right there, allowing a user-installed handler to do its thing before termination. Not unwinding is far better than *corrupt* unwinding.
Jan 24 2019