introduce tcmalloc::kInvalidTLSKey

The intention is to initialize tls-key variable with this invalid
value. This will help us avoid separate "tls ready" flag and possible
memory ordering issues around distinct tls key and tls-ready
variables.

On windows we use TLS_OUT_OF_INDEXES values which is properly
"impossible" tls index value. On POSIX systems we add theoretically
unportable, but practically portable assumption that tls keys are
integers. And we make value of -1 be that invalid key.
This commit is contained in:
Aliaksey Kandratsenka 2024-05-06 16:28:52 -04:00
parent 65ce9e899e
commit 46d9a6293a

View File

@ -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<uintptr_t>(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<TlsKey>(~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