Мне нужно хранить уникальный указатель для каждого потока, который будет доступен через макрос. Я подумал, что должен решить эту проблему с помощью одноэлементных и статических объектов thread_local std :: unique_ptr. Вот упрощенная версия кода:
#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;
}
#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
#include "yay.hpp"
thread_local std::unique_ptr<int> Yay::yay = nullptr;
Если я скомпилирую это с gcc 4.8.1:
g++ -std=c++11 -pthread -o yay main.cpp yay.cpp
Я получил:
/tmp/cceSigGT.o: In function `_ZTWN3Yay3yayE':
main.cpp:(.text._ZTWN3Yay3yayE[_ZTWN3Yay3yayE]+0x5): undefined reference to `_ZTHN3Yay3yayE'
collect2: error: ld returned 1 exit status
Я надеялся, что смогу получить больше информации от clang, однако он прекрасно работает с clang 3.4:
clang++ -std=c++11 -pthread -o yay main.cpp yay.cpp
И запуск программы дает ожидаемый результат:
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
Я не уверен, что я делаю не так, разве нельзя иметь статические объекты thread_local unique_ptr? Он работает с простыми типами, такими как int или «голые» указатели.
Возможно, это ошибка, связанная с http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55800
Обходной путь 1: скомпилируйте один файл с помощью clang (yay.cpp)
Обходной путь 2 (ужасный и непереносимый): сначала скомпилируйте yay.cpp в сборку, добавьте
.globl _ZTWN3Yay3yayE
_ZTWN3Yay3yayE = __tls_init
в файл сборки, скомпилировать в объектный файл, связать с остальным
Я экспериментировал с этим, определяя ctor бездействия для Yay в Yay.hpp:
- Yay () = удалить; + Yay () {}
Когда я сделал это, сообщение об ошибке стало:
/tmp/cc8gDxIg.o: В функции `Функция-обертка TLS для Yay :: yay ': main.cpp :(. text._ZTWN3Yay3yayE [_ZTWN3Yay3yayE] + 0x5): неопределенная ссылка на `функцию инициализации TLS для Yay :: yay '
Это привело меня к Ошибка GCC 55800. Ошибка существует в версиях GCC до 4.8.2 и исправлена в 4.8.3 и 4.9. В обсуждении темы я нашел на дубликате Ошибка GCC 59364, было принято решение не портить исправление. Таким образом, ваш хакерский ассемблер, кажется, является единственным доступным решением, пока вы не перейдете на 4.9.
Для меня оказалось, что я связываюсь с -Wl,--image-base,0x14000000
и удалив это или изменив его на -Wl,--image-base,0x10000000
и проблема ушла. Понятия не имею, что на самом деле здесь происходит …
Я полагаю, что столкнулся с gcc bug 64697 так как это действительно был статический thread_local -std = c ++ 11. Работало в 32 битах а не в 64? Очень странно … gcc 8.2.0 mingw w64 кросс-компиляция:
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'
Оригинальная подсказка Вот.