Thread overview | ||||||
---|---|---|---|---|---|---|
|
January 22, 2019 Corrupt Unwinding on Errors | ||||
---|---|---|---|---|
| ||||
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 |
January 22, 2019 Re: Corrupt Unwinding on Errors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Eyal Lotem | 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 |
January 22, 2019 Re: Corrupt Unwinding on Errors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Iain Buclaw | 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.
|
January 24, 2019 Re: Corrupt Unwinding on Errors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Eyal Lotem | On 1/22/19 4:48 AM, Eyal Lotem wrote: > 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. 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. -Steve |
Copyright © 1999-2021 by the D Language Foundation