3

I have an issue with the destruction of a thread_local static object.

#include <iostream>
#include <thread>

struct UsesLoc {
    UsesLoc() {
        loc.counter++;
    }

    struct Loc {
        Loc() {
            std::cout << "I am at   " << this << " and counter is " << counter << std::endl;
        }
        ~Loc() {
            std::cout << "I was at  " << this << " and counter is " << counter << std::endl;
        }

        int counter = 0;
    };

    static thread_local Loc loc;
};

thread_local UsesLoc::Loc UsesLoc::loc;

int main()
{
    {
        UsesLoc usesloc;
        std::cout << "loc is at " << &UsesLoc::loc << " and counter is " << UsesLoc::loc.counter << std::endl;
    }
    return 0;
}

As expected, compiling and running on https://coliru.stacked-crooked.com/a/e8bcfdaffa6a6da7 reveals that the thread_local object is always at the same location and the counter values are (0,1,1):

I am at   0x7f9dc817673c and counter is 0
loc is at 0x7f9dc817673c and counter is 1
I was at  0x7f9dc817673c and counter is 1

Conversely, when I locally compile with MinGW and run, I get, for instance,

I am at   0x507874 and counter is 0
loc is at 0x507874 and counter is 1
I was at  0x7efdd000 and counter is 2686552

Clearly, an uninitialized object at a different memory location is destroyed.

Did I oversee anything non-deterministic? How can I ensure that the correct object is destroyed?

haku
  • 43
  • 4
  • 1
    I don't have an answer, but have you considered using Meyers singletons instead of class-scope static objects? I don't know what the thread-safety/initialization/destruction guarantees are for the latter... – Max Langhof Oct 11 '19 at 10:54
  • You sure it needs to be `thread_local static` and not just `thread_local`? It might be the combination that of the two keywords that screws it. – ALX23z Oct 11 '19 at 11:07
  • @MaxLanghof If I understand correctly, this would mean moving the class-scope static to a function-scope static. I tried it but nothing changed. I also tried moving it to the global scope, but it's the same again. It seems as if, for some reason, the object is destroyed only at a point where the address is not known any more. – haku Oct 11 '19 at 11:17
  • @ALX23z Yes, a `thread_local` non-static member is not permitted IIRC. And in the global scope - which I also tried - `thread_local` and `thread_local static` are supposed to be the same. – haku Oct 11 '19 at 11:21
  • Sorry, the part about the global scope is incorrect. – haku Oct 11 '19 at 11:46
  • 1
    I think it's a bug in MinGW. Which version is it? I tried your code with one version of all compilers @ godbolt that could produce runtime output and none reproduced this. – Ted Lyngmo Oct 11 '19 at 12:23
  • That would be interesting (yet disappointing). I'm using MinGW 7.3.0. 32-bit as shipped with Qt 5.12.3 – haku Oct 11 '19 at 12:30

1 Answers1

0

After getting a hint from Ted Lyngmo that this might be a compiler bug, I did a bit of research and it seems to be indeed an issue already reported before:

That being said, the code is correct and the pointer to the object being destructed points to the same object that was constructed before when using a standard-compliant compiler.

haku
  • 43
  • 4