Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 13
An alternative TLS implementation to fix a crash on musl/aarch64#600
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
8d2b63702614cf9840b2fa4aa19217889cef38c9cdb387308c2b7014a632c1ca3d8950792a4a3f9c9b2886c2e2e2e788fd282a3465cf4a1c337b80fc19028613c88911de7f18d4c822d4ce78a45328e7e757ff41e6e0dc5aba47605e184c8File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,168 @@ | ||
| /* | ||
| * Copyright 2026, Datadog, Inc. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| #ifndef _THREADLOCAL_H | ||
| #define _THREADLOCAL_H | ||
| #include <cassert> | ||
| #include <cstring> | ||
| #include <pthread.h> | ||
| #include "arch.h" | ||
| /** | ||
| * This file implements an alternative to C/C++ thread local. | ||
| * Due to some restrictions of the language implementations, especially, on musl/aarch64, | ||
| * they cannot be safely used in profiler. | ||
| * | ||
| * How to use? | ||
| * A ThreadLocal should be declared as a static variable, e.g. | ||
| * | ||
| * static void* create_my_object() { | ||
| * return new MyObject(); | ||
| * } | ||
| * | ||
| * static void delete_my_object(void* p) { | ||
| * MyObject* obj = (MyObject*)p; | ||
| * delete obj; | ||
| * } | ||
| * | ||
| * static ThreadLocal<MyObject*, create_my_object, delete_my_object> var; | ||
| * MyObject* value = var.get(); | ||
| * ... | ||
| * var.clear(); | ||
| * ... | ||
| * MyObject* new_value = new MyObject(); | ||
| * var.set(new_value); | ||
| * ... | ||
| * var.clear(); | ||
| * | ||
| */ | ||
| // The function to create value if it does not exist | ||
| typedef void* (*CREATE_FUNC)(void); | ||
| // Cleanup the value when deleting the key | ||
| typedef void (*CLEAN_FUNC)(void*); | ||
| template <typename T, CREATE_FUNC C = nullptr, CLEAN_FUNC F = nullptr> | ||
| class ThreadLocal { | ||
| protected: | ||
| pthread_key_t _key; | ||
| bool _key_valid; | ||
| public: | ||
| ThreadLocal(const ThreadLocal&) = delete; | ||
| ThreadLocal& operator=(const ThreadLocal&) = delete; | ||
| ThreadLocal() { | ||
| static_assert(sizeof(T) == sizeof(void*), | ||
| "ThreadLocal<T> requires sizeof(T)==sizeof(void*); use a pointer type or add a specialization"); | ||
| _key_valid = pthread_key_create(&_key, F) == 0; | ||
| // What to do if we can not create a key? | ||
| // We probably want to shutdown profiler gracefully, instead of | ||
| // aborting user application - We will need this mechanism globally, | ||
| // defer to a separate task. | ||
| assert(_key_valid); | ||
| } | ||
zhengyu123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| ~ThreadLocal() { | ||
| if(_key_valid) { | ||
| pthread_key_delete(_key); | ||
| } else { | ||
| assert(false && "Invalid pthread key"); | ||
| } | ||
zhengyu123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| /** | ||
| * set(nullptr) will result in the value being recreated when get() is called | ||
| * when CREATE_FUNC is not nullptr. | ||
| * Note: caller is responsible to free old value, which mirrors thread_local | ||
| */ | ||
| void set(T value) { | ||
zhengyu123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. zhengyu123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MEDIUM · completeness] Suggestion: in voidset(T value) {
void* prev = pthread_getspecific(_key);
int err = pthread_setspecific(_key, (constvoid*)value);
assert(err == 0);
if (prev != nullptr && F != nullptr) F(prev);
}Alternatively, document that | ||
| assert(_key_valid && "Invalid pthread key"); | ||
| int err = pthread_setspecific(_key, reinterpret_cast<const void*>(value)); | ||
| assert(err == 0); | ||
| } | ||
Copilot marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
rkennke marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| T get() { | ||
| assert(_key_valid && "Invalid pthread key"); | ||
| void* p = pthread_getspecific(_key); | ||
| if (p == nullptr && C != nullptr) { | ||
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [LOW · robustness] Suggestion: document that | ||
| p = C(); | ||
zhengyu123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| set((T)p); | ||
| } | ||
| return reinterpret_cast<T>(p); | ||
| } | ||
| // Clear the value | ||
| void clear() { | ||
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MEDIUM · correctness] Suggestion: always zero the TSD slot and only call voidclear() {
void* p = pthread_getspecific(_key);
if (p != nullptr) {
pthread_setspecific(_key, nullptr);
if (F != nullptr) F(p);
}
} | ||
| assert(_key_valid && "Invalid pthread key"); | ||
| void* p = pthread_getspecific(_key); | ||
| if (p == nullptr) return; | ||
| int err = pthread_setspecific(_key, nullptr); | ||
| // Safety: if reset the value failed, get() can see staled value if | ||
| // it is freed. | ||
| if (err == 0 && F != nullptr) { | ||
| F(p); | ||
| } | ||
| } | ||
Copilot marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| }; | ||
| template <> | ||
| class ThreadLocal<double> { | ||
| protected: | ||
| pthread_key_t _key; | ||
| bool _key_valid; | ||
| public: | ||
| ThreadLocal(const ThreadLocal&) = delete; | ||
| ThreadLocal& operator=(const ThreadLocal&) = delete; | ||
| ThreadLocal() { | ||
| // Only support 64-bit platforms, double and void* are the same size | ||
| static_assert(sizeof(void*) == 8); | ||
| static_assert(sizeof(double) == 8); | ||
| _key_valid = pthread_key_create(&_key, nullptr) == 0; | ||
| // What to do if we can not create a key? | ||
| assert(_key_valid && "Invalid pthread key"); | ||
| } | ||
Copilot marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| ~ThreadLocal() { | ||
| if(_key_valid) { | ||
| pthread_key_delete(_key); | ||
| } else { | ||
| assert(_key_valid && "Invalid pthread key"); | ||
| } | ||
zhengyu123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| // double <--> u64 cast, preserve bit format | ||
| // Can use std::bit_cast after upgrade C++ version to 20 | ||
| void set(double value) { | ||
| assert(_key_valid && "Invalid pthread key"); | ||
| u64 val; | ||
| memcpy(&val, &value, sizeof(value)); | ||
| int err = pthread_setspecific(_key, reinterpret_cast<const void*>(val)); | ||
| assert(err == 0); | ||
| } | ||
Copilot marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| double get() { | ||
| assert(_key_valid && "Invalid pthread key"); | ||
| void* p = pthread_getspecific(_key); | ||
| if (p == nullptr) { | ||
| return 0.0; | ||
| } | ||
| u64 val = reinterpret_cast<u64>(p); | ||
| double value; | ||
| memcpy(&value, &val, sizeof(val)); | ||
| return value; | ||
| } | ||
| void clear() { | ||
zhengyu123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| assert(_key_valid && "Invalid pthread key"); | ||
| int err = pthread_setspecific(_key, nullptr); | ||
| assert(err == 0); | ||
| } | ||
Copilot marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| }; | ||
| #endif // _THREADLOCAL_H | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.