Thread overview
Why does logging in destructor lead to segmentation fault?
Sep 11, 2023
cc
Sep 11, 2023
Bradley Chatha
September 11, 2023

https://run.dlang.io/is/NMMpLn - minimal example. Debugging on Windows, it crashes on synchronized(mutex) with "Access violation" trying to read at zero address.

September 11, 2023

On Monday, 11 September 2023 at 17:31:17 UTC, Vladimir Marchevsky wrote:

>

https://run.dlang.io/is/NMMpLn - minimal example. Debugging on Windows, it crashes on synchronized(mutex) with "Access violation" trying to read at zero address.

core.exception.InvalidMemoryOperationError@src\core\lifetime.d(126): Invalid memory operation

log() allocates on the GC apparently, which can't occur when the druntime is being torn down and everything being finalized on program termination.

~this() {
    printf("inFinalizer: %d\n", GC.inFinalizer);
    if (!GC.inFinalizer)
        log("dtor");
}
September 11, 2023

On Monday, 11 September 2023 at 17:31:17 UTC, Vladimir Marchevsky wrote:

>

https://run.dlang.io/is/NMMpLn - minimal example. Debugging on Windows, it crashes on synchronized(mutex) with "Access violation" trying to read at zero address.

Generally you shouldn't access anything to do with the GC (or even really global state, such as the sharedLogger) within a dtor.

What's happening is that the GC is running the dtors for all objects after the main function's finished in a final cleanup, which prevents you from allocating any GC memory without crashing.

Potentially as well the GC has already cleaned up the sharedLogger causing the null read.

If you look at this example: https://run.dlang.io/is/PvyfOc

By making the class instance scope the class is allocated on the stack, and dtor-ed immediately after the function ends, rather than when DRuntime is finalising, which prevents the crash from occurring.

It's a bit of an annoying, weird quirk.

September 11, 2023

On Monday, 11 September 2023 at 18:08:45 UTC, Bradley Chatha wrote:

>

What's happening is that the GC is running the dtors for all objects after the main function's finished in a final cleanup, which prevents you from allocating any GC memory without crashing.

Potentially as well the GC has already cleaned up the sharedLogger causing the null read.

What's confusing me is that debugger stops exactly on synchronized(mutex) line inside of log implementation. Does this operation allocate any GC memory?.. Also looking through locals in debugger, logger object seems to be fine (otherwise it would crash earlier, I guess?.. At least, this points to something at non-zero address), mutex looks alive as well. Isn't it wrong to have something garbage-collected while object containing it is still alive?