| |
 | Posted by Jonathan M Davis in reply to Walter Bright | Permalink Reply |
|
Jonathan M Davis 
Posted in reply to Walter Bright
| On Friday, July 4, 2025 11:48:15 AM Mountain Daylight Time Walter Bright via Digitalmars-d wrote:
> On 7/4/2025 12:21 AM, Jonathan M Davis wrote:
> > Even if recovering is not acceptable, if the proper clean up is done when the stack is unwound, then it's possible to use destructors, scope statements, and catch blocks to get additional information about the state of the program as the stack unwinds. If the proper clean up is not done as the stack unwinds, then those destructors, scope statements, and catch statements will either not be run (meaning that any debugging information which could have been obtained from them wouldn't be), and/or only some of them will be run. And of course, for each such piece of clean up code that's skipped, the more invalid the state of the program becomes, making it that much riskier for any of the code that does run while the stack unwinds to log any information about the state of the program.
>
> Executing clean up code (i.e. destructors) is not at all about logging errors, because they happen through the normal error-free operation of the program. If you were logging normal usage, you'd want the logs from before the fault happened, not after.
The programmer isn't necessarily looking to log normal usage. In plenty of cases, they may be trying to get additional information specifically because an Error was thrown, and they want that information in order to have some hope of debugging the problem (especially if this isn't a common problem or it's a user who isn't a programmer who encounters it).
Timon uses stuff like scope(failure) and catch(Error e) { ... throw e; } right now for getting information out of his program when an Error is thrown, with the caveat that not all of the clean up code gets run, making the whole endeavor riskier than it would be otherwise. I don't know exactly what information he gets out of it, but if I understand correctly, it's used to produce the information that gets put into an error window in the UI which the user is then supposed to copy the information from in a bug report (and then presumably, when they close that window, it closes the program, since Timon isn't trying to make the program continue to run after that). This isn't stuff that gets logged during normal operation. It's specifically for getting information about what the program was doing that resulted in the Error so that he has some hope of debugging it in spite of the fact that he's dealing with a user who isn't tech-savvy.
And just in general, if scope(failure) is being used, the programmer may want to log addtional information that they wouldn't have wanted to log otherwise. For instance, I do this in unit tests when the assertion failure is inside nested loops, and I need to know which iterations each loop is in when the failure occurs (and certainly wouldn't want to log anything if there weren't a failure).
Destructors probably aren't going to be used to get additional information, since those do run when there are no Errors, but scope(failure) and catch(Error e) { ... throw e; } can certainly be used specifically for when a Throwable of some kind is thrown, and having those be skipped at any point means missing out on whatever information the programmer was trying to get on failure, and having the destructors skipped would mean that the code with scope(failure) or catch(Error) would then be dealing with code that was potentially in a state that wasn't memory safe, because clean up code was skipped, whereas if the clean up code hadn't been skipped, it might have been perfectly memory safe even though an Error was in flight.
Even if you think that it's too risky to have the clean up code run for Errors as the default behavior, there are clearly use cases where getting additional information about the state of the program is worth far more than the risk that doing the clean up code might cause further problems - especially when in many cases, Errors are thrown _before_ anything that isn't memory safe is done (e.g. array bounds checking throws an Error before accessing the memory out-of-bounds, not after). And at least having the option to configure a program such that the full clean up code is run even with Errors would make getting information out of a program as it terminates due to an Error more memory safe and less error-prone - as well as simply making it so that more information can be got out of the program, because the clean up code that's used to get information is actually all run. So, the folks who need that behavior can have it even if it's not the default.
- Jonathan M Davis
|