Unittests pass, and then an invalid memory operation happens after?

Liam McGillivray yoshi.pit.link.mario at gmail.com
Wed Mar 27 21:43:48 UTC 2024


In my current [game 
project](https://github.com/LiamM32/Open_Emblem), [something 
strange](https://github.com/LiamM32/Open_Emblem/issues/20) has 
happened as of a recent commit. When running `dub test`, all the 
unittests appear to pass, but then after the last unittest has 
concluded an "Invalid memory operation" happens. Removing a few 
lines replaces this error with a segfault, but either way, these 
errors shouldn't be happening. Weirdly, the commit where these 
errors first appear did nothing but replace a single class-member 
function with a seemingly identical function through a mixin 
template.

The errors seem to be related to object deletion. The traceback 
output for the first error, and where GDB points to for the 
second error, is the destructor for my `Unit` class.

You see, every `Unit` object is associated with a particular 
`Map` object and `Faction` object, which it hold references to. 
Those objects in turn each have an array of `Unit` objects that 
they are associated with. In the `Unit` destructor, I have it 
call the `removeUnit` function in both the associated `Map` and 
`Faction` objects. The errors are happening in either the `Unit` 
destructor itself, or the `removeUnit` function that it calls. 
Until the commit that introduced these errors, the `removeUnit` 
function was written directly in the `Map` class in it's module, 
but this commit replaced it with the mixin template 
`UnitArrayManagement`, which `Faction` also uses.

`Unit` destructor:
```
     ~this() {
         this.alive = false;
         if (this.map !is null) this.map.removeUnit(this);
         if (this.faction !is null) this.faction.removeUnit(this);
         if (this.currentTile !is null) this.currentTile.occupant 
= null;
     }
```

```
template UnitArrayManagement(alias Unit[] unitsArray) {
     bool removeUnit(Unit unit) {
         import std.algorithm.searching;
         writeln("Doing `Map.removeUnit`");
         Unit[] shiftedUnits = unitsArray.find(unit);
         ushort unitKey = cast(ushort)(unitsArray.length - 
shiftedUnits.length);
         debug {
             writeln("unitsArray: ");
             foreach (listedUnit; unitsArray) 
writeln(listedUnit.name~", ");
             writeln("shiftedUnits: ");
             foreach (listedUnit; shiftedUnits) 
writeln(listedUnit.name~", ");
         }
         if (shiftedUnits.length > 0) {
             debug writeln("shiftedUnits.length > 0");
             unitsArray[$-shiftedUnits.length] = null;
             for (ushort i=0; i<shiftedUnits.length-1; i++) {
                 debug writeln("In loop. unitKey = ", unitKey, ", 
i = ", i);
                 unitsArray[unitKey+i] = unitsArray[unitKey+i+1];
             }
             unitsArray.length--;
             return true;
         } else return false;
     }
}
```

The first error happens because I inserted some uses of `writeln` 
for debugging purposes in the new version of `removeUnit` 
(because I haven't figured out how to do the same thing with 
GDB), in which I try to get it to print the names of all the 
units in the array before deleting any of them. I suppose that it 
might get a `Invalid memory operation` when trying to access a 
member of a `Unit` object that no longer exists, but this 
shouldn't be happening. When that other `Unit` object got 
destroyed, the destructor should have called this same 
`removeUnit` function to remove it's reference from the array.

I read that the garbage collector *sometimes* but not *always* 
calls destructors on deletion, which sounds crazy to me. Is this 
a case of one unit being deleted without the destructor and then 
the next unit (of the same `Map` object) having the destructor 
called?

Are destructors normally called after a program is concluded?

The second error, which can be achieved by removing the instances 
of `writeln` in `removeUnit` (making it seemingly identical now 
to the version of this function previously defined in the `Map` 
class) is also strange. It seems to be a case of the `Unit` 
object calling a `Map` object that no longer exists. However, 
that's also strange, as the `Map` object is supposed to delete 
all it's associated units on destruction.

I wrote these destructors so that objects wouldn't have 
references floating around on their deletion, yet now I'm getting 
errors from the destructors.

So why are these things even happening *after* the unittests have 
been run? What else do I need to know about object destruction? 
What may be happening?


More information about the Digitalmars-d-learn mailing list