Comparing Exceptions and Errors

Ola Fosheim Grøstad ola.fosheim.grostad at gmail.com
Sun Jun 5 16:27:07 UTC 2022


Ok, so I am a bit confused about what is Error and what is not… 
According to core.exception there is wide array of runtime Errors:

```
RangeError
ArrayIndexError
ArraySliceError
AssertError
FinalizeError
OutOfMemoryError
InvalidMemoryOperationError
ForkError
SwitchError
```

I am not sure that any of these should terminate anything outside 
the offending actor, but I could be wrong as it is hard to tell 
exactly when some of those are thrown.

InvalidMemoryOperationError sound bad, of course, but the docs 
says «An invalid memory operation error occurs in circumstances 
when the garbage collector has detected an operation it cannot 
reliably handle. The default D GC is not re-entrant, so this can 
happen due to allocations done from within finalizers called 
during a garbage collection cycle.»

This sounds more like an issue that needs fixing…


On Sunday, 5 June 2022 at 14:24:39 UTC, Ali Çehreli wrote:
> The code is written in a way that both arrays will *always* 
> have equal number of elements. And there is a "silly" check for 
> that invariant. So far so good. The issue is what to do *when* 
> that assert fails.
>
> Are you sure that it was a silly programmer mistake? I am not 
> sure at all.

Ok, so this is a question of the layers:

```
-------- top layer ------
D          | E
            |
----- middle layer ------
B          | C
            |
            |
---- bottom layer -------
A

````

If the failed assert is happening in a lower layer A then code on 
the outer layer should fault (or roll back a transaction). 
Whether it is reasonable to capture that error and suppress it 
depends on how independent you want those layers to be in your 
architecture. It also depends on the nature of layer A.

If the failed assert happens in middle layer section B, then the 
D would be affected, but not A, C or E.

The key issue is that the nature of layers is informal in the 
language (in fact, in most languages, a weakness), so only the 
programmer can tell what is reasonable or not.

In fact, when we think about it; most aspects about what is 
expected from a program is informal… so it is very difficult to 
make judgment at the compiler level.

> Is the only other culprit the runtime kernel? I really don't 
> know who else may be involved.

I mean the application's «custom actor kernel», a hardened piece 
of code that is not modified frequently and heavily tested. The 
goal is to keep uncertainty local to an actor so that you can 
toss out misbehaving actors and keep the rest of the system 
working smoothly (99.5% of the time, 50 minutes downtime per 
week).

Actors are expected to contain bugs because the game system is 
continuously modified (to balance the game play, to get new 
content, more excitement, whatever…). This is why we want 100% 
@safe code as a feature.


> There are also bugs in unrelated actor code writing over each 
> others' memory.

But that cannot happen if I decide to make actors 100% @safe and 
only let them interact with each other through my «custom actor 
kernel»?


> You are free to choose to catch Errors and continue under the 
> assumption that it is safe to do so.

Now I am confused!! Steven wrote «I've thought in the past that 
throwing an error really should not throw, but log the error 
(including the call stack), and then exit without even attempting 
to unwind the stack.»

Surely, the perspective being promoted is to make sure that 
Errors cannot be stopped from propagating? That is why this 
becomes a discussion?

If an Error can propagate through "nothrow" then the compiler 
should emit handler code for it and issue a warning. If you don't 
want that then the programmer should safe guard against it, 
meaning: manually catch and abort or do manual checks in all 
locations above it where Errors can arise. The compiler knows 
where.

Not really sure why D has "nothrow", it doesn't really fit with 
the rest of the language? To interface with C++ perhaps?? If that 
is the case, just adopt C++ "noexcept" semantics, use assert() 
for debugging only, in "nothrow" code. And discourage the use of 
"nothrow". Heck, throw in a compiler switch to turn off "nothrow" 
if that is safe.


> The advice in the article still holds for me. I think the main 
> difference is in the assumptions we make about an Errors: Is it 
> a logic error in actor code or some crazy state that will cause 
> weirder results if we continue. We can't know for sure.

And this is no different from other languages with a runtime. You 
cannot be sure, but it probably isn't a runtime issue, and even 
if it was… players will be more upset by not being able to play 
than to have some weird effects happening. Same for chat service. 
Same for being able to read Wikipedia-caches (worse to have no 
access than to have 1% of pages garbled on display until the 
server is updated).

Different settings need different solutions. So, maybe 
interfacing with C++ requires "nothrow", but if that is the only 
reason… why let everybody pay a price for it?

(I use "noexcept" in my C++ code, but that is more an act of 
documentation, and that clearly falls apart in D with Error.)



More information about the Digitalmars-d-learn mailing list