Покритикуйте имплементацию обёртки для thread local объектов:
+ tls а не эмулятор, со всеми вытекающими
+ простая имплементация
+ проблем мало, и они декларированны
+ работает
| ThreadLocal.h |
| #include "windows.h"
class ThreadLocalBase
{
public:
virtual void DestructThreadLocalInstance() = 0;
void * GetValue()
{
return TlsGetValue(GetIndex());
}
void SetValue(void * val)
{
TlsSetValue(GetIndex(), val);
}
DWORD GetIndex()
{
return m_tlsIndex;
}
protected:
DWORD m_tlsIndex;
~ThreadLocalBase(){}
};
DWORD AddAtThreadExit(ThreadLocalBase *);
void RemoveAtThreadExit(ThreadLocalBase *);
template<typename T>
class ThreadLocal : protected ThreadLocalBase
{
public:
ThreadLocal()
{
m_tlsIndex = AddAtThreadExit(this);
}
~ThreadLocal()
{
ThreadLocal::DestructThreadLocalInstance();
RemoveAtThreadExit(this);
}
T * operator->()
{
return &(**this);
}
T& operator*()
{
T * val = (T*)GetValue();
if (!val)
{
val = new T();
SetValue(val);
}
return *val;
}
private:
virtual void DestructThreadLocalInstance() override
{
void * val = GetValue();
if (val)
{
delete (T*)val;
SetValue((void*)0);
}
}
};
|
| |
| ThreadLocal.cpp |
| #include "ThreadAtExit.h"
#include <set>
volatile long mutex = 0;
struct lock
{
lock() { while (InterlockedExchange(&mutex, 1) == 1) SwitchToThread();}
~lock() { InterlockedExchange(&mutex, 0);}
};
std::set<ThreadLocalBase *> AtExitSet;
DWORD AddAtThreadExit(ThreadLocalBase * tlb)
{
lock _;
AtExitSet.insert(tlb);
return TlsAlloc();
}
void RemoveAtThreadExit(ThreadLocalBase * tlb)
{
lock _;
AtExitSet.erase(tlb);
TlsFree(tlb->GetIndex());
}
void DestructThreadLocals()
{
lock _;
for(std::set<ThreadLocalBase *>::iterator it = AtExitSet.begin(), it_end = AtExitSet.end();
it != it_end;
++it)
{
(*it)->DestructThreadLocalInstance();
}
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
DestructThreadLocals();
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
|
| |
| ThreadLocal.def |
| LIBRARY ThreadLocal
EXPORTS
AddAtThreadExit @1
RemoveAtThreadExit @2
|
| |
Сразу оговорюсь, что это не продакшен код, а всего лиш собранная
на коленке и доведённая до
состояния стояния идея, решающая возможно основную, но относительно узкую задачу.
Соответственно, самодельный спин мютекс логично заменить чем-то принятым на месте работы, а имена функций, классов и переменных подогнать под местные конвенции.
О недостатках:
1. Не достаточно настраевоемо
2. Windows specific
3. Требует дополнительный Dll
4. Не работает с Delay Load (в деструкторе вызывается экспортируемая функция)
5. Выгрузка бинарника со статиком приводит к потере объекта для каждого из построивших себе инстанс оставшихся потоков.
Моё мнение об этих недостатках:
1. можно расширить по мере надобности
2. увы
(если идея ясна, то не сложно переписать на pthread)
3. в большинстве случаев не проблема
4. По большому счёту, нет такой нужды
5. В подавляющем большинстве случаев магии выгрузки библиотек нет, и проблем с этим связанных решать не надо.