I hate class destructors with a burning passion
Mathias LANG
geod24 at gmail.com
Sun Jun 6 11:54:40 UTC 2021
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](https://github.com/dlang/druntime/blob/5c1873c7d95510f7e922f49ab46d815203f25471/src/core/exception.d#L279). 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 ?
### `AA.remove` allocate(d)
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](https://issues.dlang.org/show_bug.cgi?id=21442).
### `assert` failure allocate(d)
I'm not talking about message formatting here, but pure `assert(a
!= a)` allocating.
This bug has been known for [almost a
decade](https://issues.dlang.org/show_bug.cgi?id=7349) and was
finally fixed in v2.097.0 ([it wasn't that
hard](https://github.com/dlang/druntime/pull/3476))
### `throw`ing from the constructor of GC-allocated `class` is
broken since 2.096.0
Why did we have an assertion failure in our destructor ?
Well turns out there was [an `assert` checking a
magic](https://github.com/vibe-d/vibe-core/issues/283), and it
was being triggered. Why ? Because of a [regression introduced in
v2.096.0](https://issues.dlang.org/show_bug.cgi?id=21989).
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...
More information about the Digitalmars-d
mailing list