C++程序  |  103行  |  2.91 KB

// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A simple cross platform thread local storage implementation.
//
// This is a drop-in replacement of __thread keyword. If your compiler
// toolchain supports __thread keyword, the user of this code should
// be as fast as the code which uses __thread. Chrome's
// base::ThreadLocalPointer and base::ThreadLocalStorage cannot be as
// fast as __thread.
// TODO(crbug.com/249345): If pthread_getspecific is slow for our use,
// expose bionic's internal TLS and stop using pthread_getspecific
// based implementation.
//
// Usage:
//
// Before (linux):
//
// __thread Foo* foo;
// foo = new Foo();
// foo->func();
//
//
// After:
//
// DEFINE_THREAD_LOCAL(Foo*, foo);
// foo.Ref() = new Foo();
// foo.Ref()->func();
//
// Thread local PODs are zero-initialized.
// Thread local non-PODs are initialized with the default constructor.

#ifndef THREAD_LOCAL_H_
#define THREAD_LOCAL_H_

#include <errno.h>
#include <pthread.h>

#include "log.h"

#ifdef __linux__

#define DEFINE_THREAD_LOCAL(Type, name) thread_local Type name
#define TLS_REF(x) x

#else

// Thread local storage implementation which uses pthread.
// Note that DEFINE_THREAD_LOCAL creates a global variable just like
// thread local storage based on __thread keyword. So we should not use
// constructor in ThreadLocal class to avoid static initializator.
template <typename Type>
void ThreadLocalDestructor(void* ptr) {
  delete reinterpret_cast<Type>(ptr);
}

template<typename Type, pthread_key_t* key>
void ThreadLocalInit() {
  if (pthread_key_create(key, ThreadLocalDestructor<Type>))
    ERROR("Failed to create a pthread key for TLS errno=%d", errno);
}

template<typename Type, pthread_key_t* key, pthread_once_t* once>
class ThreadLocal {
 public:
  Type& Ref() {
    return *GetPointer();
  }
  Type Get() {
    return Ref();
  }
  void Set(const Type& value) {
    Ref() = value;
  }
  Type* GetPointer() {
    pthread_once(once, ThreadLocalInit<Type*, key>);
    Type* value = reinterpret_cast<Type*>(pthread_getspecific(*key));
    if (value) return value;
    // new Type() for PODs means zero initialization.
    value = new Type();
    int error = pthread_setspecific(*key, value);
    if (error != 0)
      ERROR("Failed to set a TLS: error=%d", error);
    return value;
  }
};

// We need a namespace for name##_key and name##_once since template parameters
// do not accept unnamed values such as static global variables.
#define DEFINE_THREAD_LOCAL(Type, name)                 \
  namespace {                                           \
  pthread_once_t name##_once = PTHREAD_ONCE_INIT;       \
  pthread_key_t name##_key;                             \
  }                                                     \
  ThreadLocal<Type, &name##_key, &name##_once> name;

#define TLS_REF(x) x.Ref()

#endif

#endif  // THREAD_LOCAL_H_