C++11: GCC 4.8 static thread_local std::unique_ptr

2019-04-19 03:19发布

问题:

I need to store a unique pointer for every thread that will be accessed through a macro. I thought I should solve this with a singleton and static thread_local std::unique_ptr objects. Here is a simplified version of the code:

main.cpp

#include <thread>
#include <vector>
#include <iostream>
#include <mutex>
using namespace std;

#include "yay.hpp"

mutex coutMutex;

void yay(int id)
{
    int* yayPtr = getYay();

    // I know this is bad
    coutMutex.lock();
    cout << "Yay nr. " << id << " address: " << yayPtr << endl;
    coutMutex.unlock();
}

int main()
{
    vector<thread> happy;
    for(int i = 0; i < thread::hardware_concurrency(); i++)
    {
        happy.push_back(thread(yay, i));
    }

    for(auto& smile : happy)
    {
        smile.join();
    }
    return 0;
}

yay.hpp

#ifndef BE_HAPPY
#define BE_HAPPY

#include <memory>
class Yay
{
    private:
        static thread_local std::unique_ptr<int> yay;
        Yay() = delete;
        Yay(const Yay&) = delete;
        ~Yay() {}
    public:
        static int* getYay()
        {
            if(!yay.get())
            {
                yay.reset(new int);
            }
            return yay.get();
        }
};

#define getYay() Yay::getYay()

#endif

yay.cpp

#include "yay.hpp"

thread_local std::unique_ptr<int>  Yay::yay = nullptr;

If I compile this with gcc 4.8.1:

g++ -std=c++11 -pthread -o yay main.cpp yay.cpp

I get:

/tmp/cceSigGT.o: In function `_ZTWN3Yay3yayE':
main.cpp:(.text._ZTWN3Yay3yayE[_ZTWN3Yay3yayE]+0x5): undefined reference to `_ZTHN3Yay3yayE'
collect2: error: ld returned 1 exit status

I was hoping I might get more information from clang, however it works perfectly fine with clang 3.4:

clang++ -std=c++11 -pthread -o yay main.cpp yay.cpp

And running the program yields the result I was expecting:

Yay nr. 2 address: 0x7fcd780008e0
Yay nr. 0 address: 0x7fcd880008e0
Yay nr. 1 address: 0x7fcd800008e0
Yay nr. 3 address: 0x7fcd700008e0
Yay nr. 4 address: 0x7fcd740008e0
Yay nr. 5 address: 0x7fcd680008e0
Yay nr. 6 address: 0x7fcd6c0008e0
Yay nr. 7 address: 0x7fcd600008e0

I'm not sure what I'm doing wrong here, is it not possible to have static thread_local unique_ptr objects? It works with simple types like int or 'naked' pointers.

Edit:

It's possible that this is a bug that is related to http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55800

Edit2:

Workaround 1: compile the one file with clang (yay.cpp)

Workaround 2 (horrible and unportable): compile yay.cpp to assembly first, add

.globl _ZTWN3Yay3yayE
_ZTWN3Yay3yayE = __tls_init

to the assembly file, compile to object file, link with the rest

回答1:

I experimented with this by defining a do-nothing ctor for Yay in Yay.hpp:

- Yay() = delete;
+ Yay() {}

When I did that, the error message became:

/tmp/cc8gDxIg.o: In function `TLS wrapper function for Yay::yay':
main.cpp:(.text._ZTWN3Yay3yayE[_ZTWN3Yay3yayE]+0x5): undefined reference to `TLS init function for Yay::yay'

That led me to GCC bug 55800. The bug exists in GCC versions up through 4.8.2, and is fixed in 4.8.3 and 4.9. In discussion threads I found on duplicate GCC bug 59364, the decision has been made not to back port the fix. Therefore, your assembler hack appears to be the only solution available until you move to 4.9.



回答2:

For me it turned out I was linking with -Wl,--image-base,0x14000000 and removing that or changing it to -Wl,--image-base,0x10000000 and the problem went away. No idea what is actually going on here...

I believe I was bumping up into gcc bug 64697 since it was indeed a static thread_local -std=c++11. It worked in 32 bit but not 64? Very odd...gcc 8.2.0 mingw w64 cross compile:

 librtmfp.a(IOSocket.o):IOSocket.cpp:(.text+0x46c4): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `TLS init function for Base::ThreadQueue::_PCurrent'

Original hint here.