diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /util/system/tls.cpp | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'util/system/tls.cpp')
-rw-r--r-- | util/system/tls.cpp | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/util/system/tls.cpp b/util/system/tls.cpp new file mode 100644 index 0000000000..c2f1a04a14 --- /dev/null +++ b/util/system/tls.cpp @@ -0,0 +1,260 @@ +#include "tls.h" +#include "mutex.h" +#include "thread.h" + +#include <util/generic/set.h> +#include <util/generic/hash.h> +#include <util/generic/intrlist.h> +#include <util/generic/singleton.h> +#include <util/generic/vector.h> + +#if defined(_unix_) + #include <pthread.h> +#endif + +using namespace NTls; + +namespace { + static inline TAtomicBase AcquireKey() { + static TAtomic cur; + + return AtomicIncrement(cur) - (TAtomicBase)1; + } + + class TGenericTlsBase { + public: + using TSmallKey = size_t; + + class TPerThreadStorage { + public: + struct TKey: public TNonCopyable { + inline TKey(TDtor dtor) + : Key(AcquireKey()) + , Dtor(dtor) + { + } + + TSmallKey Key; + TDtor Dtor; + }; + + class TStoredValue: public TIntrusiveListItem<TStoredValue> { + public: + inline TStoredValue(const TKey* key) + : Data_(nullptr) + , Dtor_(key->Dtor) + { + } + + inline ~TStoredValue() { + if (Dtor_ && Data_) { + Dtor_(Data_); + } + } + + inline void Set(void* ptr) noexcept { + Data_ = ptr; + } + + inline void* Get() const noexcept { + return Data_; + } + + private: + void* Data_; + TDtor Dtor_; + }; + + inline TStoredValue* Value(const TKey* key) { + TStoredValue*& ret = *ValuePtr((size_t)key->Key); + + if (!ret) { + THolder<TStoredValue> sv(new TStoredValue(key)); + + Storage_.PushFront(sv.Get()); + ret = sv.Release(); + } + + return ret; + } + + inline TStoredValue** ValuePtr(size_t idx) { + // do not grow vector too much + if (idx < 10000) { + if (idx >= Values_.size()) { + Values_.resize(idx + 1); + } + + return &Values_[idx]; + } + + return &FarValues_[idx]; + } + + private: + TVector<TStoredValue*> Values_; + THashMap<size_t, TStoredValue*> FarValues_; + TIntrusiveListWithAutoDelete<TStoredValue, TDelete> Storage_; + }; + + inline TPerThreadStorage* MyStorage() { +#if defined(Y_HAVE_FAST_POD_TLS) + Y_POD_STATIC_THREAD(TPerThreadStorage*) + my(nullptr); + + if (!my) { + my = MyStorageSlow(); + } + + return my; +#else + return MyStorageSlow(); +#endif + } + + virtual TPerThreadStorage* MyStorageSlow() = 0; + + virtual ~TGenericTlsBase() = default; + }; +} + +#if defined(_unix_) +namespace { + class TMasterTls: public TGenericTlsBase { + public: + inline TMasterTls() { + Y_VERIFY(!pthread_key_create(&Key_, Dtor), "pthread_key_create failed"); + } + + inline ~TMasterTls() override { + //explicitly call dtor for main thread + Dtor(pthread_getspecific(Key_)); + + Y_VERIFY(!pthread_key_delete(Key_), "pthread_key_delete failed"); + } + + static inline TMasterTls* Instance() { + return SingletonWithPriority<TMasterTls, 1>(); + } + + private: + TPerThreadStorage* MyStorageSlow() override { + void* ret = pthread_getspecific(Key_); + + if (!ret) { + ret = new TPerThreadStorage(); + + Y_VERIFY(!pthread_setspecific(Key_, ret), "pthread_setspecific failed"); + } + + return (TPerThreadStorage*)ret; + } + + static void Dtor(void* ptr) { + delete (TPerThreadStorage*)ptr; + } + + private: + pthread_key_t Key_; + }; + + using TKeyDescriptor = TMasterTls::TPerThreadStorage::TKey; +} + +class TKey::TImpl: public TKeyDescriptor { +public: + inline TImpl(TDtor dtor) + : TKeyDescriptor(dtor) + { + } + + inline void* Get() const { + return TMasterTls::Instance()->MyStorage()->Value(this)->Get(); + } + + inline void Set(void* val) const { + TMasterTls::Instance()->MyStorage()->Value(this)->Set(val); + } + + static inline void Cleanup() { + } +}; +#else +namespace { + class TGenericTls: public TGenericTlsBase { + public: + virtual TPerThreadStorage* MyStorageSlow() { + auto lock = Guard(Lock_); + + { + TPTSRef& ret = Datas_[TThread::CurrentThreadId()]; + + if (!ret) { + ret.Reset(new TPerThreadStorage()); + } + + return ret.Get(); + } + } + + inline void Cleanup() noexcept { + with_lock (Lock_) { + Datas_.erase(TThread::CurrentThreadId()); + } + } + + static inline TGenericTls* Instance() { + return SingletonWithPriority<TGenericTls, 1>(); + } + + private: + using TPTSRef = THolder<TPerThreadStorage>; + TMutex Lock_; + THashMap<TThread::TId, TPTSRef> Datas_; + }; +} + +class TKey::TImpl { +public: + inline TImpl(TDtor dtor) + : Key_(dtor) + { + } + + inline void* Get() { + return TGenericTls::Instance()->MyStorage()->Value(&Key_)->Get(); + } + + inline void Set(void* ptr) { + TGenericTls::Instance()->MyStorage()->Value(&Key_)->Set(ptr); + } + + static inline void Cleanup() { + TGenericTls::Instance()->Cleanup(); + } + +private: + TGenericTls::TPerThreadStorage::TKey Key_; +}; +#endif + +TKey::TKey(TDtor dtor) + : Impl_(new TImpl(dtor)) +{ +} + +TKey::TKey(TKey&&) noexcept = default; + +TKey::~TKey() = default; + +void* TKey::Get() const { + return Impl_->Get(); +} + +void TKey::Set(void* ptr) const { + Impl_->Set(ptr); +} + +void TKey::Cleanup() noexcept { + TImpl::Cleanup(); +} |