Библиотека реализующая ThreadLocal<T>
От: Caracrist https://1pwd.org/
Дата: 13.04.12 00:24
Оценка:
Покритикуйте имплементацию обёртки для 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. В подавляющем большинстве случаев магии выгрузки библиотек нет, и проблем с этим связанных решать не надо.
~~~~~
~lol~~
~~~ Single Password Solution
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.