Over the past few months, more than half of the critical bug we've encountered which were not due to our own code are related to destructors. Specifically, destructors being invoked by the GC.
In case you need a refresher, allocating from a destructor that is called from the GC leads to an
InvalidMemoryOperationError. Which is pretty bad, because you don't even know WHERE the allocation happen, because
InvalidMemoryOperation does not give you a stack trace. Why ? Because generating a stack trace allocates. This also mean you can't debug other failures in destructors, such as when an
assert fails (see below).
(Stack trace allocating can be fixed BTW, but requires a breaking change, because the
interface we use provides you with already-formatted
const(char) instead of structured data, but that's another discussion).
When such an
InvalidMemoryOperation happens on your computer and is easy to debug, great. But when it happens once every blue moon on the production server... Not so great.
So what are the critical bugs we've seen over the last 6 months ?
We use Vibe.d extensively in our server app, and Vibe.d tries to clean after itself.
I think it's a reasonable assumption that, if you have an associative array, you should be able to call
remove on it from a destructor, provided you can ensure it hasn't been collected.
Except, before v2.095.0, you couldn't, because
remove might have called
shrink, which allocates. This bug has been fixed now.
assert failure allocate(d)
throwing from the constructor of GC-allocated
class is broken since 2.096.0
This is the top 3 that we could track down. We currently have a random crash which seems to trigger when a C++ object is destructed by the GC (to be precise: the GC finalizes a
class that holds an
std::vector), but that might just be our bindings...