Program crash: GC destroys an object unexpectedly

jfondren julian.fondren at gmail.com
Sun Sep 19 16:27:55 UTC 2021


On Sunday, 19 September 2021 at 08:51:31 UTC, eugene wrote:
>> reference-containing struct that vanishes on the return of 
>> your corresponding function
> I do not think it's a problem, otherwise **both programs would 
> not work at all**.

The GC doesn't reliably punish objects living past there not 
being any references to them because it's not always operating. 
If you have a tight loop where the GC is never invoked, you can 
do what ever crazy things you want. Your program doesn't crash 
until you hit ctrl-C after all.

> Look...
> I have added stopper into an array...
>
> ```d
>     Stopper[] stoppers;
>     auto stopper = new Stopper();
>     stoppers ~= stopper;
>     stopper.run();
> ```
>
> and, you won't believe, this have fixed the problem -
> the objects, referenced by sg0 and sg1 are not destroyed 
> anymore.

This is a sufficient patch to prevent the segfault:

```
diff --git a/echo_client.d b/echo_client.d
index 1f8270e..5ec41df 100644
--- a/echo_client.d
+++ b/echo_client.d
@@ -32,7 +32,7 @@ void main(string[] args) {
          sm.run();
      }

-    auto stopper = new Stopper();
+    scope stopper = new Stopper();
      stopper.run();

      writeln(" === Hello, world! === ");
```

The `scope` stack-allocates Stopper.

This is also a sufficient patch to prevent the segfault:

```
diff --git a/echo_client.d b/echo_client.d
index 1f8270e..0b968a8 100644
--- a/echo_client.d
+++ b/echo_client.d
@@ -39,4 +39,6 @@ void main(string[] args) {
      auto md = new MessageDispatcher();
      md.loop();
      writeln(" === Goodbye, world! === ");
+    writeln(stopper.sg0.number);
+    //writeln(stopper.sg1.number);
  }
```

either one of those writelns will do it.

Without either of the above, STOPPER is destroyed a few seconds 
into a run of echo-client:

```
$ ./echo-client | grep STOPPER
'STOPPER' registered 24 (esrc.Signal)
'STOPPER' registered 25 (esrc.Signal)
'STOPPER @ INIT' got 'M0' from 'SELF'
'STOPPER' enabled 24 (esrc.Signal)
'STOPPER' enabled 25 (esrc.Signal)
(seconds pass)
stopper.Stopper.~this(): STOPPER destroyed
```

You can hit ctrl-C prior to Stopper's destruction and there's no 
segfault. (On my system, it won't show the usual 'segfault' 
message to the terminal when grep is filtering like that, but if 
you turn on coredumps you can see one is only generated with a 
ctrl-C after Stopper's destroyed.)

So this looks at first to me like a bug: dmd is allowing Stopper 
to be collected before the end of its lexical scope if it isn't 
used later in it. Except, forcing a collection right after 
`stopper.run()` doesn't destroy it.

Here's a patch that destroys Stopper almost immediately, so that 
a ctrl-C within milliseconds of the program starting will still 
segfault it. This also no longer requires the server to be active.

diff --git a/engine/edsm.d b/engine/edsm.d
index 513d8a5..ea9ac3a 100644
--- a/engine/edsm.d
+++ b/engine/edsm.d
@@ -176,6 +176,8 @@ class StageMachine {
              "'%s @ %s' got '%s' from '%s'", name, 
currentStage.name, eventName,
              m.src ? (m.src is this ? "SELF" : m.src.name) : "OS"
          );
+        import core.memory : GC;
+        GC.collect;

          if (eventName !in currentStage.reflexes) {

valgrind:

```
^C==14893== Thread 1:
==14893== Jump to the invalid address stated on the next line
==14893==    at 0x2: ???
==14893==    by 0x187A3C: void disp.MessageDispatcher.loop()
==14893==    by 0x1BED89: _Dmain
```

with Stopper's collection prevented and some logging around 
reactTo:

```
^Csi.sizeof = 128
about to react to Message(null, stopper.Stopper, 0, esrc.Signal)
'STOPPER @ IDLE' got 'S0' from 'OS'
goodbye, world
reacted
  === Goodbye, world! ===
1
ecap.EventQueue.~this
stopper.Stopper.~this(): STOPPER destroyed
```

So the problem here is that ctrl-C causes that message to come 
but Stopper's been collected and that address contains garbage. 
Since the Message in the MessageQueue should keep it alive, I 
think this is probably a bug in dmd.


More information about the Digitalmars-d-learn mailing list