ThreadCache* ThreadCache::CreateCacheIfNecessary() {
// Initialize per-thread data if necessary
ThreadCache* heap = NULL;
{
//加锁
SpinLockHolder h(Static::pageheap_lock());
//获得pid,若尚未初始化,则赋值为0
// Early on in glibc's life, we cannot even call pthread_self()
pthread_t me;
if (!tsd_inited_) {
memset(&me, 0, sizeof(me));
} else {
me = pthread_self();
}
//找到一个匹配pid的ThreadCache
// This may be a recursive malloc call from pthread_setspecific()
// In that case, the heap for this thread has already been created
// and added to the linked list. So we search for that first.
for (ThreadCache* h = thread_heaps_; h != NULL; h = h->next_) {
if (h->tid_ == me) {
heap = h;
break;
}
}
//如果找不到,则创建之
//在这个函数里面,新申请的heap会挂载到thread_heaps_链表中去
if (heap == NULL)
heap = NewHeap(me);
}
// We call pthread_setspecific() outside the lock because it may
// call malloc() recursively. We check for the recursive call using
// the "in_setspecific_" flag so that we can avoid calling
// pthread_setspecific() if we are already inside pthread_setspecific().
if (!heap->in_setspecific_ && tsd_inited_) {
//如果已经初始化,就需要设置线程本地存储相关的数据
heap->in_setspecific_ = true;
//设置动态的线程本地存储
perftools_pthread_setspecific(heap_key_, heap);
#ifdef HAVE_TLS
/*
#ifdef HAVE_TLS
__thread ThreadCache* ThreadCache::threadlocal_heap_
#endif
*/
//如果支持静态的线程本地存储,那么设置之,以加速访问
// Also keep a copy in __thread for faster retrieval
threadlocal_heap_ = heap;
#endif
heap->in_setspecific_ = false;
}
return heap;
}
若线程已经初始化,则通过下面的函数来获得线程绑定的ThreadCache。
inline ThreadCache* ThreadCache::GetThreadHeap() {
#ifdef HAVE_TLS
//如果支持TLS,则直接返回,以加速访问
// __thread is faster, but only when the kernel supports it
if (KernelSupportsTLS())
return threadlocal_heap_;
#endif
//否则从动态分配的线程本地存储中获得
return reinterpret_cast<ThreadCache *>(perftools_pthread_getspecific(
heap_key_));
}
void ThreadCache::InitTSD() {
ASSERT(!tsd_inited_);
//创建一个动态tls 槽,
perftools_pthread_key_create(&heap_key_, DestroyThreadCache);
tsd_inited_ = true;
// We may have used a fake pthread_t for the main thread. Fix it.
pthread_t zero;
memset(&zero, 0, sizeof(zero));
SpinLockHolder h(Static::pageheap_lock());
for (ThreadCache* h = thread_heaps_; h != NULL; h = h->next_) {
//如果存在一个已经初始化,但是尚未分配(pid = 0)的ThreadCache,则据为己有。
//这里只是做个标记,到CreateCacheIfNecessary函数中就会自动识别
if (h->tid_ == zero) {
h->tid_ = pthread_self();
}
}
}
对于Windows平台perftools_pthread_key_create将跑到下面的函数
pthread_key_t PthreadKeyCreate(void (*destr_fn)(void*)) {
// Semantics are: we create a new key, and then promise to call
// destr_fn with TlsGetValue(key) when the thread is destroyed
// (as long as TlsGetValue(key) is not NULL).
pthread_key_t key = TlsAlloc();
if (destr_fn) { // register it
// If this assert fails, we'll need to support an array of destr_fn_infos
assert(destr_fn_info.destr_fn == NULL);
destr_fn_info.destr_fn = destr_fn;
destr_fn_info.key_for_destr_fn_arg = key;
}
return key;
}
An optional destructor function may be associated with each key value. At thread exit,
if a key value has a non-NULL destructor pointer,
and the thread has a non-NULL value associated with that key,
the value of the key is set to NULL, and then the function pointed to
is called with the previously associated value as its sole argument.
The order of destructor calls is unspecified if more than one destructor
exists for a thread when it exits.
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
// When destr_fn eventually runs, it's supposed to take as its
// argument the tls-value associated with key that pthread_key_create
// creates. (Yeah, it sounds confusing but it's really not.) We
// store the destr_fn/key pair in this data structure. Because we
// store this in a single var, this implies we can only have one
// destr_fn in a program! That's enough in practice. If asserts
// trigger because we end up needing more, we'll have to turn this
// into an array.
struct DestrFnClosure {
void (*destr_fn)(void*);
pthread_key_t key_for_destr_fn_arg;
};
static DestrFnClosure destr_fn_info; // initted to all NULL/0.
BOOL WINAPI DllMain(HINSTANCE h, DWORD dwReason, PVOID pv) {
if (dwReason == DLL_THREAD_DETACH)
on_tls_callback(h, dwReason, pv);
else if (dwReason == DLL_PROCESS_DETACH)
on_process_term();
return TRUE;
}
static void NTAPI on_tls_callback(HINSTANCE h, DWORD dwReason, PVOID pv) {
if (dwReason == DLL_THREAD_DETACH) { // thread is being destroyed!
on_process_term();
}
}
static int on_process_term(void) {
if (destr_fn_info.destr_fn) {
void *ptr = TlsGetValue(destr_fn_info.key_for_destr_fn_arg);
// This shouldn't be necessary, but in Release mode, Windows
// sometimes trashes the pointer in the TLS slot, so we need to
// remove the pointer from the TLS slot before the thread dies.
TlsSetValue(destr_fn_info.key_for_destr_fn_arg, NULL);
if (ptr) // pthread semantics say not to call if ptr is NULL
(*destr_fn_info.destr_fn)(ptr);
}
return 0;
}
在这个回调中,线程本地存储的内存最终被归还于中央空闲链表区
void ThreadCache::DestroyThreadCache(void* ptr) {
// Note that "ptr" cannot be NULL since pthread promises not
// to invoke the destructor on NULL values, but for safety,
// we check anyway.
if (ptr == NULL)
return;
#ifdef HAVE_TLS
// Prevent fast path of GetThreadHeap() from returning heap.
threadlocal_heap_ = NULL;
#endif
DeleteCache(reinterpret_cast<ThreadCache*>(ptr));
}