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.)