diff --git a/build/src.pro b/build/src.pro index a4da405..775084c 100644 --- a/build/src.pro +++ b/build/src.pro @@ -22,7 +22,7 @@ HEADERS += \ ../include/ipc.h \ ../include/def.h \ ../include/rw_lock.h \ - ../include/thread_local_ptr.h + ../include/tls_pointer.h SOURCES += \ ../src/shm.cpp \ @@ -31,7 +31,8 @@ SOURCES += \ unix { SOURCES += \ - ../src/platform/shm_linux.cpp + ../src/platform/shm_linux.cpp \ + ../src/platform/tls_pointer_linux.cpp LIBS += -lrt @@ -44,7 +45,7 @@ else:win32 { SOURCES += \ ../src/platform/shm_win.cpp \ - ../src/platform/thread_local_ptr_win.cpp + ../src/platform/tls_pointer_win.cpp LIBS += -lKernel32 diff --git a/include/thread_local_ptr.h b/include/thread_local_ptr.h deleted file mode 100644 index 4d3675b..0000000 --- a/include/thread_local_ptr.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -#include // std::forward - -#if defined(WINCE) || defined(_WIN32_WCE) || \ - defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \ - defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) -#include // DWORD, ::Tls... -#define IPC_OS_WIN_ -#else -#include // pthread_... -#endif - -namespace ipc { - -#if defined(IPC_OS_WIN_) - -#define IPC_THREAD_LOCAL_KEY_ DWORD -#define IPC_THREAD_LOCAL_SET(KEY, PTR) (::TlsSetValue(KEY, (LPVOID)PTR) == TRUE) -#define IPC_THREAD_LOCAL_GET(KEY) (::TlsGetValue(KEY)) - -void thread_local_create(IPC_THREAD_LOCAL_KEY_& key, void (*destructor)(void*)); -void thread_local_delete(IPC_THREAD_LOCAL_KEY_ key); - -#define IPC_THREAD_LOCAL_CREATE(KEY, DESTRUCTOR) thread_local_create(KEY, DESTRUCTOR) -#define IPC_THREAD_LOCAL_DELETE(KEY) thread_local_delete(KEY) - -#else /*!IPC_OS_WIN_*/ - -#define IPC_THREAD_LOCAL_KEY_ pthread_key_t -#define IPC_THREAD_LOCAL_CREATE(KEY, DESTRUCTOR) pthread_key_create(&KEY, DESTRUCTOR) -#define IPC_THREAD_LOCAL_DELETE(KEY) pthread_key_delete(KEY) -#define IPC_THREAD_LOCAL_SET(KEY, PTR) (pthread_setspecific(KEY, (void*)PTR) == 0) -#define IPC_THREAD_LOCAL_GET(KEY) pthread_getspecific(KEY) - -#endif/*!IPC_OS_WIN_*/ - -//////////////////////////////////////////////////////////////// -/// Thread-local pointer -//////////////////////////////////////////////////////////////// - -/* - - - 1. In Windows, if you do not compile thread_local_ptr.cpp, - use thread_local_ptr will cause memory leaks. - - 2. You need to set the thread_local_ptr's storage manually: - ``` - thread_local_ptr p; - if (!p) p = new int(123); - ``` - Just like an ordinary pointer. Or you could just call create: - ``` - thread_local_ptr p; - p.create(123); - ``` -*/ - -template -class thread_local_ptr { - IPC_THREAD_LOCAL_KEY_ key_; - -public: - using value_type = T; - - thread_local_ptr() { - IPC_THREAD_LOCAL_CREATE(key_, [](void* p) { - delete static_cast(p); - }); - } - - ~thread_local_ptr() { - IPC_THREAD_LOCAL_DELETE(key_); - } - - template - T* create(P&&... params) { - auto ptr = static_cast(*this); - if (ptr == nullptr) { - return (*this) = new T(std::forward

(params)...); - } - return ptr; - } - - T* operator=(T* ptr) { - IPC_THREAD_LOCAL_SET(key_, ptr); - return ptr; - } - - operator T*() const { return static_cast(IPC_THREAD_LOCAL_GET(key_)); } - - T& operator* () { return *static_cast(*this); } - const T& operator* () const { return *static_cast(*this); } - - T* operator->() { return static_cast(*this); } - const T* operator->() const { return static_cast(*this); } -}; - -} // namespace ipc diff --git a/include/tls_pointer.h b/include/tls_pointer.h new file mode 100644 index 0000000..6f6d833 --- /dev/null +++ b/include/tls_pointer.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include + +namespace ipc { +namespace tls { + +using key_t = std::uint64_t; +using destructor_t = void (*)(void*); + +enum : key_t { + invalid_value = (std::numeric_limits::max)() +}; + +key_t create (destructor_t destructor = nullptr); +void release(key_t key); + +bool set(key_t key, void* ptr); +void* get(key_t key); + +//////////////////////////////////////////////////////////////// +/// Thread-local pointer +//////////////////////////////////////////////////////////////// + +/* + + + 1. In Windows, if you do not compile thread_local_ptr.cpp, + use thread_local_ptr will cause memory leaks. + + 2. You need to set the thread_local_ptr's storage manually: + ``` + thread_local_ptr p; + if (!p) p = new int(123); + ``` + Just like an ordinary pointer. Or you could just call create: + ``` + thread_local_ptr p; + p.create(123); + ``` +*/ + +template +class pointer { + key_t key_; + +public: + using value_type = T; + + pointer() { + key_ = tls::create([](void* p) { delete static_cast(p); }); + } + + ~pointer() { + tls::release(key_); + } + + template + T* create(P&&... params) { + auto ptr = static_cast(*this); + if (ptr == nullptr) { + return (*this) = new T { std::forward

(params)... }; + } + return ptr; + } + + T* operator=(T* ptr) { + set(key_, ptr); + return ptr; + } + + operator T*() const { return static_cast(get(key_)); } + + T& operator* () { return *static_cast(*this); } + const T& operator* () const { return *static_cast(*this); } + + T* operator->() { return static_cast(*this); } + const T* operator->() const { return static_cast(*this); } +}; + +} // namespace tls +} // namespace ipc diff --git a/src/ipc.cpp b/src/ipc.cpp index 7e79d38..2a81ab5 100644 --- a/src/ipc.cpp +++ b/src/ipc.cpp @@ -13,7 +13,7 @@ #include "def.h" #include "circ_queue.h" #include "rw_lock.h" -#include "thread_local_ptr.h" +#include "tls_pointer.h" namespace { @@ -36,7 +36,7 @@ using guard_t = std::unique_ptr, void(*)(handle_ * https://sourceforge.net/p/mingw-w64/bugs/727/ */ /*thread_local*/ -thread_local_ptr>> recv_caches__; +tls::pointer>> recv_caches__; std::unordered_map h2q__; rw_lock h2q_lc__; diff --git a/src/platform/tls_pointer_linux.cpp b/src/platform/tls_pointer_linux.cpp new file mode 100644 index 0000000..07a6cee --- /dev/null +++ b/src/platform/tls_pointer_linux.cpp @@ -0,0 +1,29 @@ +#include "tls_pointer.h" + +#include // pthread_... + +namespace ipc { +namespace tls { + +key_t create(destructor_t destructor) { + pthread_key_t k; + if (pthread_key_create(&k, destructor) == 0) { + return static_cast(k); + } + return invalid_value; +} + +void release(key_t key) { + pthread_key_delete(static_cast(key)); +} + +bool set(key_t key, void* ptr) { + return pthread_setspecific(static_cast(key), ptr) == 0; +} + +void* get(key_t key) { + return pthread_getspecific(static_cast(key)); +} + +} // namespace tls +} // namespace ipc diff --git a/src/platform/thread_local_ptr_win.cpp b/src/platform/tls_pointer_win.cpp similarity index 63% rename from src/platform/thread_local_ptr_win.cpp rename to src/platform/tls_pointer_win.cpp index 33e5be3..e6f70c5 100644 --- a/src/platform/thread_local_ptr_win.cpp +++ b/src/platform/tls_pointer_win.cpp @@ -1,4 +1,4 @@ -#include "thread_local_ptr.h" +#include "tls_pointer.h" #include // ::Tls... #include // std::unordered_map @@ -18,71 +18,85 @@ namespace ipc { */ namespace { - struct tls_data { - using destructor_t = void(*)(void*); - using map_t = std::unordered_map; - static DWORD& key() { - static IPC_THREAD_LOCAL_KEY_ rec_key = ::TlsAlloc(); - return rec_key; - } +struct tls_data { + using destructor_t = void(*)(void*); + using map_t = std::unordered_map; - static map_t* records(map_t* rec) { - IPC_THREAD_LOCAL_SET(key(), rec); - return rec; - } + static DWORD& key() { + static DWORD rec_key = ::TlsAlloc(); + return rec_key; + } - static map_t* records() { - return static_cast(IPC_THREAD_LOCAL_GET(key())); - } + static map_t* records(map_t* rec) { + ::TlsSetValue(key(), static_cast(rec)); + return rec; + } - IPC_THREAD_LOCAL_KEY_ key_ = 0; - destructor_t destructor_ = nullptr; + static map_t* records() { + return static_cast(::TlsGetValue(key())); + } - tls_data() = default; + tls::key_t key_ = tls::invalid_value; + destructor_t destructor_ = nullptr; - tls_data(IPC_THREAD_LOCAL_KEY_ key, destructor_t destructor) - : key_ (key) - , destructor_(destructor) - {} + tls_data() = default; - tls_data(tls_data&& rhs) : tls_data() { - (*this) = std::move(rhs); - } + tls_data(tls::key_t key, destructor_t destructor) + : key_ (key) + , destructor_(destructor) + {} - tls_data& operator=(tls_data&& rhs) { - key_ = rhs.key_; - destructor_ = rhs.destructor_; - rhs.key_ = 0; - rhs.destructor_ = nullptr; - return *this; - } + tls_data(tls_data&& rhs) : tls_data() { + (*this) = std::move(rhs); + } - ~tls_data() { - if (destructor_) destructor_(IPC_THREAD_LOCAL_GET(key_)); - } - }; -} + tls_data& operator=(tls_data&& rhs) { + key_ = rhs.key_; + destructor_ = rhs.destructor_; + rhs.key_ = 0; + rhs.destructor_ = nullptr; + return *this; + } -void thread_local_create(IPC_THREAD_LOCAL_KEY_& key, void (*destructor)(void*)) { - key = ::TlsAlloc(); - if (key == TLS_OUT_OF_INDEXES) return; + ~tls_data() { + if (destructor_) destructor_(tls::get(key_)); + } +}; + +} // internal-linkage + +namespace tls { + +key_t create(destructor_t destructor) { + key_t key = static_cast(::TlsAlloc()); + if (key == TLS_OUT_OF_INDEXES) return invalid_value; auto rec = tls_data::records(); if (!rec) rec = tls_data::records(new tls_data::map_t); - if (!rec) return; + if (!rec) return key; rec->emplace(key, tls_data{ key, destructor }); + return key; } -void thread_local_delete(IPC_THREAD_LOCAL_KEY_ key) { +void release(key_t key) { auto rec = tls_data::records(); if (!rec) return; rec->erase(key); - ::TlsFree(key); + ::TlsFree(static_cast(key)); } -//////////////////////////////////////////////////////////////// -/// Call destructors on thread exit -//////////////////////////////////////////////////////////////// +bool set(key_t key, void* ptr) { + return ::TlsSetValue(static_cast(key), + static_cast(ptr)) == TRUE; +} + +void* get(key_t key) { + return static_cast(::TlsGetValue(static_cast(key))); +} + +} // namespace tls + +namespace { void OnThreadExit() { auto rec = tls_data::records(); @@ -95,6 +109,12 @@ void NTAPI OnTlsCallback(PVOID, DWORD dwReason, PVOID) { if (dwReason == DLL_THREAD_DETACH) OnThreadExit(); } +} // internal-linkage + +//////////////////////////////////////////////////////////////// +/// Call destructors on thread exit +//////////////////////////////////////////////////////////////// + #if defined(_MSC_VER) #if defined(IPC_OS_WIN64_) @@ -102,8 +122,7 @@ void NTAPI OnTlsCallback(PVOID, DWORD dwReason, PVOID) { #pragma comment(linker, "/INCLUDE:_tls_used") #pragma comment(linker, "/INCLUDE:_tls_xl_b__") -extern "C" -{ +extern "C" { # pragma const_seg(".CRT$XLB") extern const PIMAGE_TLS_CALLBACK _tls_xl_b__; const PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback; @@ -115,8 +134,7 @@ extern "C" #pragma comment(linker, "/INCLUDE:__tls_used") #pragma comment(linker, "/INCLUDE:__tls_xl_b__") -extern "C" -{ +extern "C" { # pragma data_seg(".CRT$XLB") PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback; # pragma data_seg() @@ -131,15 +149,13 @@ extern "C" #if defined(__MINGW64__) || (__MINGW64_VERSION_MAJOR) || \ (__MINGW32_MAJOR_VERSION > 3) || ((__MINGW32_MAJOR_VERSION == 3) && (__MINGW32_MINOR_VERSION >= 18)) -extern "C" -{ +extern "C" { IPC_CRTALLOC__(".CRT$XLB") PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback; } #else /*!__MINGW*/ -extern "C" -{ +extern "C" { ULONG _tls_index__ = 0; IPC_CRTALLOC__(".tls$AAA") char _tls_start__ = 0; @@ -150,8 +166,7 @@ extern "C" IPC_CRTALLOC__(".CRT$XLZ") PIMAGE_TLS_CALLBACK _tls_xl_z__ = 0; } -extern "C" NX_CRTALLOC_(".tls") const IMAGE_TLS_DIRECTORY _tls_used = -{ +extern "C" NX_CRTALLOC_(".tls") const IMAGE_TLS_DIRECTORY _tls_used = { (ULONG_PTR)(&_tls_start__ + 1), (ULONG_PTR) &_tls_end__, (ULONG_PTR) &_tls_index__,