diff --git a/src/base/threading.h b/src/base/threading.h index b77a7df..b6e8675 100644 --- a/src/base/threading.h +++ b/src/base/threading.h @@ -41,33 +41,27 @@ namespace tcmalloc { using TlsKey = DWORD; +constexpr inline TlsKey kInvalidTLSKey = TLS_OUT_OF_INDEXES; + ATTRIBUTE_VISIBILITY_HIDDEN TlsKey WinTlsKeyCreate(void (*destr_fn)(void*)); /* windows/port.cc */ ATTRIBUTE_VISIBILITY_HIDDEN inline int CreateTlsKey(TlsKey *pkey, void (*destructor)(void*)) { TlsKey key = WinTlsKeyCreate(destructor); - if (key != TLS_OUT_OF_INDEXES) { - *(pkey) = key; - return 0; - } - else { + if (key == TLS_OUT_OF_INDEXES) { return GetLastError(); } -} -ATTRIBUTE_VISIBILITY_HIDDEN inline void* GetTlsValue(TlsKey key) { - DWORD err = GetLastError(); - void* rv = TlsGetValue(key); - if (err) SetLastError(err); - return rv; + *(pkey) = key; + return 0; } + +ATTRIBUTE_VISIBILITY_HIDDEN inline void* GetTlsValue(TlsKey key) { + return TlsGetValue(key); +} + ATTRIBUTE_VISIBILITY_HIDDEN inline int SetTlsValue(TlsKey key, const void* value) { - if (TlsSetValue(key, (LPVOID)value)) { - return 0; - } - else { - return GetLastError(); - } + return !TlsSetValue(key, (LPVOID)value); } ATTRIBUTE_VISIBILITY_HIDDEN inline uintptr_t SelfThreadId() { @@ -76,7 +70,6 @@ ATTRIBUTE_VISIBILITY_HIDDEN inline uintptr_t SelfThreadId() { return static_cast(GetCurrentThreadId()); } - } // namespace tcmalloc #else @@ -88,15 +81,50 @@ namespace tcmalloc { using TlsKey = pthread_key_t; +// I've checked several implementations and they're all implementing +// pthread_key_t as some kind of integer type. Sometimes signed, +// sometimes unsigned, and occasionally of different width +// (basically, int or long). +// +// Notably, however, POSIX is explicitly _not_ requiring +// pthread_key_t to be of some integer type. So we're somewhat into +// non-portable territory here. But in practice we should be okay, +// not just given current practice, but also keeping in mind how to +// "reasonably" implement thread-specific values. Some applies, sadly, to +// C11 tss_t type. +// +// Another potentially tricky aspect is what values to consider +// invalid. POSIX also says nothing about this, sadly. Solaris has +// nonportable PTHREAD_ONCE_KEY_NP, which would be sensible to have +// in standard (even without pthread_key_create_once_np), but we +// have what we have. It's value is (int)(-1). And, indeed, -1 seems +// like most sensible value. +constexpr inline TlsKey kInvalidTLSKey = static_cast(~uintptr_t{0}); +static_assert(sizeof(pthread_key_t) <= sizeof(uintptr_t)); + ATTRIBUTE_VISIBILITY_HIDDEN inline int CreateTlsKey(TlsKey *pkey, void (*destructor)(void*)) { - return pthread_key_create(pkey, destructor); + int err = pthread_key_create(pkey, destructor); + if (err != 0) { + return err; + } + // It is super-implausible that we'll be able to create "invalid" + // tls key value, but just in case, we check and re-create if we end + // up getting one. + if (*pkey != kInvalidTLSKey) { + return 0; + } + + return CreateTlsKey(pkey, destructor); } + ATTRIBUTE_VISIBILITY_HIDDEN inline void* GetTlsValue(TlsKey key) { return pthread_getspecific(key); } + ATTRIBUTE_VISIBILITY_HIDDEN inline int SetTlsValue(TlsKey key, const void* value) { return pthread_setspecific(key, value); } + ATTRIBUTE_VISIBILITY_HIDDEN inline uintptr_t SelfThreadId() { // Most platforms (with notable exception of windows C runtime) can // use address of errno as a quick and portable and recursion-free