www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - scope(exit) and scope(failure) on Errors

reply Shachar Shemesh <shachar weka.io> writes:
There is a problem with scope(exit) and scope(failure) running when 
Error types exceptions are thrown. It throws the program into code paths 
that are really not healthy.

Imagine, for example, code handling linked lists. We do manipulations on 
linked lists, and put a scope(exit) that clears stuff up.

Now imagine that somewhere inside, we have an assert that makes sure 
that everything is still in order. If it's not, then an AsserError is 
thrown, which derive from Error. The scope(exit) runs, but since things 
are not where they should be (hence the assert), we segfault. All the 
useful stuff that the assert was supposed to provide us is now gone, 
replaced by cleanup code that had no business running under those 
circumstances.

Even when not harmful, this is, often, useless. Even if the cleanup 
proceeds correctly, what is the sense of cleaning up when the program is 
in serious trouble?

The above is not categorically always true. In Weka, for example, we are 
using exceptions derived from Error to force all fibers of a certain 
logical component to exit. It is useful that these exceptions are not 
caught. In those cases, there are some types of cleanups that we do want 
to take place, but not others.

Some way to control this would be appreciated.

Shachar
Apr 25 2016
next sibling parent ZombineDev <petar.p.kirov gmail.com> writes:
On Monday, 25 April 2016 at 08:42:33 UTC, Shachar Shemesh wrote:
 There is a problem with scope(exit) and scope(failure) running 
 when Error types exceptions are thrown. It throws the program 
 into code paths that are really not healthy.

 Imagine, for example, code handling linked lists. We do 
 manipulations on linked lists, and put a scope(exit) that 
 clears stuff up.

 Now imagine that somewhere inside, we have an assert that makes 
 sure that everything is still in order. If it's not, then an 
 AsserError is thrown, which derive from Error. The scope(exit) 
 runs, but since things are not where they should be (hence the 
 assert), we segfault. All the useful stuff that the assert was 
 supposed to provide us is now gone, replaced by cleanup code 
 that had no business running under those circumstances.

 Even when not harmful, this is, often, useless. Even if the 
 cleanup proceeds correctly, what is the sense of cleaning up 
 when the program is in serious trouble?

 The above is not categorically always true. In Weka, for 
 example, we are using exceptions derived from Error to force 
 all fibers of a certain logical component to exit. It is useful 
 that these exceptions are not caught. In those cases, there are 
 some types of cleanups that we do want to take place, but not 
 others.

 Some way to control this would be appreciated.

 Shachar
So, if I understand correctly, you want scope(exit) and scope(failure) code to run only when an exception not derived from Error is thrown (because otherwise the program is in an undefined state), but you need an uncatchable exception, so you can't derive from Exception? Then why don't you use this: class Fault : Throwable { /* ... */ } // Error: can only catch class objects derived from Exception in safe code, not 'Fault': try faultThrowingFunc() catch (Fault f) // // ... And change `assert (expr, "Error msg")` to `expr || abort("Error msg")`, which will cleanly terminate the program immediately, instead of throwing AssertError and choking on scope blocks. Example: http://dpaste.dzfl.pl/5a557d982230
Apr 25 2016
prev sibling next sibling parent Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Monday, April 25, 2016 11:42:33 Shachar Shemesh via Digitalmars-d wrote:
 There is a problem with scope(exit) and scope(failure) running when
 Error types exceptions are thrown. It throws the program into code paths
 that are really not healthy.

 Imagine, for example, code handling linked lists. We do manipulations on
 linked lists, and put a scope(exit) that clears stuff up.

 Now imagine that somewhere inside, we have an assert that makes sure
 that everything is still in order. If it's not, then an AsserError is
 thrown, which derive from Error. The scope(exit) runs, but since things
 are not where they should be (hence the assert), we segfault. All the
 useful stuff that the assert was supposed to provide us is now gone,
 replaced by cleanup code that had no business running under those
 circumstances.

 Even when not harmful, this is, often, useless. Even if the cleanup
 proceeds correctly, what is the sense of cleaning up when the program is
 in serious trouble?

 The above is not categorically always true. In Weka, for example, we are
 using exceptions derived from Error to force all fibers of a certain
 logical component to exit. It is useful that these exceptions are not
 caught. In those cases, there are some types of cleanups that we do want
 to take place, but not others.

 Some way to control this would be appreciated.
The language does not guaranteed that scope(exit) statements, scope(failure) statements, and destructors will be run when an Error is thrown, but neither does it guarantee that they won't be. There have been arguments about it in the past with Walter holding that they shouldn't be run when an Error is thrown, because it's worse to run them than not when you're in an invalid state (which you are by definition if an Error is thrown), whereas others have argued that it's better to try and do as much cleanup as you can while you're shutting down from an Error. For better or worse, the result is that while the language does not guarantee that they aren't run when an Error is thrown, the current implementation does run them in most cases (though there are cases where it literally can't - like when nothrow is involved). And the language provides no way to control whether they will get run on Errors. It depends entirely on the current implementation. So, if you want to guarantee that scope(failure), scope(exit), and destructors get run, you'll need to use an Exception of some kind, not an Error, and if you want to guarantee that they don't get run, you'll need to call a C function like exit or abort to forcibly kill the application. - Jonathan M Davis
Apr 25 2016
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/25/16 4:42 AM, Shachar Shemesh wrote:
 There is a problem with scope(exit) and scope(failure) running when
 Error types exceptions are thrown. It throws the program into code paths
 that are really not healthy.

 Imagine, for example, code handling linked lists. We do manipulations on
 linked lists, and put a scope(exit) that clears stuff up.

 Now imagine that somewhere inside, we have an assert that makes sure
 that everything is still in order. If it's not, then an AsserError is
 thrown, which derive from Error. The scope(exit) runs, but since things
 are not where they should be (hence the assert), we segfault. All the
 useful stuff that the assert was supposed to provide us is now gone,
 replaced by cleanup code that had no business running under those
 circumstances.

 Even when not harmful, this is, often, useless. Even if the cleanup
 proceeds correctly, what is the sense of cleaning up when the program is
 in serious trouble?

 The above is not categorically always true. In Weka, for example, we are
 using exceptions derived from Error to force all fibers of a certain
 logical component to exit. It is useful that these exceptions are not
 caught. In those cases, there are some types of cleanups that we do want
 to take place, but not others.

 Some way to control this would be appreciated.
If your scope(exit) code can only run when things are intact, why not use scope(success) instead? It's coarse though, no difference between exceptions or errors. But it may be a good stop-gap fix. A nice thing may be allowing to check on the inflight exception (or lack thereof) on scope(exit) and scope(failure). Note that scope guards I think are actually implemented via try catch statements, you could try just doing those instead (though obviously, it doesn't read as nicely). -Steve
Apr 25 2016