Blame mfbt/ThreadLocal.h

Packit f0b94e
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
Packit f0b94e
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
Packit f0b94e
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
/* Cross-platform lightweight thread local data wrappers. */
Packit f0b94e
Packit f0b94e
#ifndef mozilla_ThreadLocal_h
Packit f0b94e
#define mozilla_ThreadLocal_h
Packit f0b94e
Packit f0b94e
#if !defined(XP_WIN)
Packit f0b94e
#include <pthread.h>
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
#include "mozilla/Assertions.h"
Packit f0b94e
#include "mozilla/Attributes.h"
Packit f0b94e
#include "mozilla/TypeTraits.h"
Packit f0b94e
Packit f0b94e
namespace mozilla {
Packit f0b94e
Packit f0b94e
namespace detail {
Packit f0b94e
Packit f0b94e
#ifdef XP_MACOSX
Packit f0b94e
#if defined(__has_feature)
Packit f0b94e
#if __has_feature(cxx_thread_local)
Packit f0b94e
#define MACOSX_HAS_THREAD_LOCAL
Packit f0b94e
#endif
Packit f0b94e
#endif
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
/*
Packit f0b94e
 * Thread Local Storage helpers.
Packit f0b94e
 *
Packit f0b94e
 * Usage:
Packit f0b94e
 *
Packit f0b94e
 * Do not directly instantiate this class.  Instead, use the
Packit f0b94e
 * MOZ_THREAD_LOCAL macro to declare or define instances.  The macro
Packit f0b94e
 * takes a type name as its argument.
Packit f0b94e
 *
Packit f0b94e
 * Declare like this:
Packit f0b94e
 * extern MOZ_THREAD_LOCAL(int) tlsInt;
Packit f0b94e
 * Define like this:
Packit f0b94e
 * MOZ_THREAD_LOCAL(int) tlsInt;
Packit f0b94e
 * or:
Packit f0b94e
 * static MOZ_THREAD_LOCAL(int) tlsInt;
Packit f0b94e
 *
Packit f0b94e
 * Only static-storage-duration (e.g. global variables, or static class members)
Packit f0b94e
 * objects of this class should be instantiated. This class relies on
Packit f0b94e
 * zero-initialization, which is implicit for static-storage-duration objects.
Packit f0b94e
 * It doesn't have a custom default constructor, to avoid static initializers.
Packit f0b94e
 *
Packit f0b94e
 * API usage:
Packit f0b94e
 *
Packit f0b94e
 * // Create a TLS item.
Packit f0b94e
 * //
Packit f0b94e
 * // Note that init() should be invoked before the first use of set()
Packit f0b94e
 * // or get().  It is ok to call it multiple times.  This must be
Packit f0b94e
 * // called in a way that avoids possible races with other threads.
Packit f0b94e
 * MOZ_THREAD_LOCAL(int) tlsKey;
Packit f0b94e
 * if (!tlsKey.init()) {
Packit f0b94e
 *   // deal with the error
Packit f0b94e
 * }
Packit f0b94e
 *
Packit f0b94e
 * // Set the TLS value
Packit f0b94e
 * tlsKey.set(123);
Packit f0b94e
 *
Packit f0b94e
 * // Get the TLS value
Packit f0b94e
 * int value = tlsKey.get();
Packit f0b94e
 */
Packit f0b94e
Packit f0b94e
// Integral types narrower than void* must be extended to avoid
Packit f0b94e
// warnings from valgrind on some platforms.  This helper type
Packit f0b94e
// achieves that without penalizing the common case of ThreadLocals
Packit f0b94e
// instantiated using a pointer type.
Packit f0b94e
template <typename S>
Packit f0b94e
struct Helper {
Packit f0b94e
  typedef uintptr_t Type;
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
template <typename S>
Packit f0b94e
struct Helper<S*> {
Packit f0b94e
  typedef S* Type;
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
#if defined(XP_WIN)
Packit f0b94e
/*
Packit f0b94e
 * ThreadLocalKeyStorage uses Thread Local APIs that are declared in
Packit f0b94e
 * processthreadsapi.h. To use this class on Windows, include that file
Packit f0b94e
 * (or windows.h) before including ThreadLocal.h.
Packit f0b94e
 *
Packit f0b94e
 * TLS_OUT_OF_INDEXES is a #define that is used to detect whether
Packit f0b94e
 * an appropriate header has been included prior to this file
Packit f0b94e
 */
Packit f0b94e
#if defined(TLS_OUT_OF_INDEXES)
Packit f0b94e
/* Despite not being used for MOZ_THREAD_LOCAL, we expose an implementation for
Packit f0b94e
 * Windows for cases where it's not desirable to use thread_local */
Packit f0b94e
template <typename T>
Packit f0b94e
class ThreadLocalKeyStorage {
Packit f0b94e
 public:
Packit f0b94e
  ThreadLocalKeyStorage() : mKey(TLS_OUT_OF_INDEXES) {}
Packit f0b94e
Packit f0b94e
  inline bool initialized() const { return mKey != TLS_OUT_OF_INDEXES; }
Packit f0b94e
Packit f0b94e
  inline void init() { mKey = TlsAlloc(); }
Packit f0b94e
Packit f0b94e
  inline T get() const {
Packit f0b94e
    void* h = TlsGetValue(mKey);
Packit f0b94e
    return static_cast<T>(reinterpret_cast<typename Helper<T>::Type>(h));
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  inline bool set(const T aValue) {
Packit f0b94e
    void* h =
Packit f0b94e
        reinterpret_cast<void*>(static_cast<typename Helper<T>::Type>(aValue));
Packit f0b94e
    return TlsSetValue(mKey, h);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
 private:
Packit f0b94e
  unsigned long mKey;
Packit f0b94e
};
Packit f0b94e
#endif
Packit f0b94e
#else
Packit f0b94e
template <typename T>
Packit f0b94e
class ThreadLocalKeyStorage {
Packit f0b94e
 public:
Packit f0b94e
  constexpr ThreadLocalKeyStorage() : mKey(0), mInited(false) {}
Packit f0b94e
Packit f0b94e
  inline bool initialized() const { return mInited; }
Packit f0b94e
Packit f0b94e
  inline void init() { mInited = !pthread_key_create(&mKey, nullptr); }
Packit f0b94e
Packit f0b94e
  inline T get() const {
Packit f0b94e
    void* h = pthread_getspecific(mKey);
Packit f0b94e
    return static_cast<T>(reinterpret_cast<typename Helper<T>::Type>(h));
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  inline bool set(const T aValue) {
Packit f0b94e
    void* h =
Packit f0b94e
        reinterpret_cast<void*>(static_cast<typename Helper<T>::Type>(aValue));
Packit f0b94e
    return !pthread_setspecific(mKey, h);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
 private:
Packit f0b94e
  pthread_key_t mKey;
Packit f0b94e
  bool mInited;
Packit f0b94e
};
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
template <typename T>
Packit f0b94e
class ThreadLocalNativeStorage {
Packit f0b94e
 public:
Packit f0b94e
  // __thread does not allow non-trivial constructors, but we can
Packit f0b94e
  // instead rely on zero-initialization.
Packit f0b94e
  inline bool initialized() const { return true; }
Packit f0b94e
Packit f0b94e
  inline void init() {}
Packit f0b94e
Packit f0b94e
  inline T get() const { return mValue; }
Packit f0b94e
Packit f0b94e
  inline bool set(const T aValue) {
Packit f0b94e
    mValue = aValue;
Packit f0b94e
    return true;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
 private:
Packit f0b94e
  T mValue;
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
template <typename T, template <typename U> class Storage>
Packit f0b94e
class ThreadLocal : public Storage<T> {
Packit f0b94e
 public:
Packit f0b94e
  MOZ_MUST_USE inline bool init();
Packit f0b94e
Packit f0b94e
  void infallibleInit() {
Packit f0b94e
    MOZ_RELEASE_ASSERT(init(), "Infallible TLS initialization failed");
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  inline T get() const;
Packit f0b94e
Packit f0b94e
  inline void set(const T aValue);
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
template <typename T, template <typename U> class Storage>
Packit f0b94e
inline bool ThreadLocal<T, Storage>::init() {
Packit f0b94e
  static_assert(mozilla::IsPointer<T>::value || mozilla::IsIntegral<T>::value,
Packit f0b94e
                "mozilla::ThreadLocal must be used with a pointer or "
Packit f0b94e
                "integral type");
Packit f0b94e
  static_assert(sizeof(T) <= sizeof(void*),
Packit f0b94e
                "mozilla::ThreadLocal can't be used for types larger than "
Packit f0b94e
                "a pointer");
Packit f0b94e
Packit f0b94e
  if (!Storage<T>::initialized()) {
Packit f0b94e
    Storage<T>::init();
Packit f0b94e
  }
Packit f0b94e
  return Storage<T>::initialized();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
template <typename T, template <typename U> class Storage>
Packit f0b94e
inline T ThreadLocal<T, Storage>::get() const {
Packit f0b94e
  MOZ_ASSERT(Storage<T>::initialized());
Packit f0b94e
  return Storage<T>::get();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
template <typename T, template <typename U> class Storage>
Packit f0b94e
inline void ThreadLocal<T, Storage>::set(const T aValue) {
Packit f0b94e
  MOZ_ASSERT(Storage<T>::initialized());
Packit f0b94e
  bool succeeded = Storage<T>::set(aValue);
Packit f0b94e
  if (!succeeded) {
Packit f0b94e
    MOZ_CRASH();
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
#if (defined(XP_WIN) || defined(MACOSX_HAS_THREAD_LOCAL)) && \
Packit f0b94e
    !defined(__MINGW32__)
Packit f0b94e
#define MOZ_THREAD_LOCAL(TYPE)               \
Packit f0b94e
  thread_local mozilla::detail::ThreadLocal< \
Packit f0b94e
      TYPE, mozilla::detail::ThreadLocalNativeStorage>
Packit f0b94e
#elif defined(HAVE_THREAD_TLS_KEYWORD)
Packit f0b94e
#define MOZ_THREAD_LOCAL(TYPE)           \
Packit f0b94e
  __thread mozilla::detail::ThreadLocal< \
Packit f0b94e
      TYPE, mozilla::detail::ThreadLocalNativeStorage>
Packit f0b94e
#else
Packit f0b94e
#define MOZ_THREAD_LOCAL(TYPE) \
Packit f0b94e
  mozilla::detail::ThreadLocal<TYPE, mozilla::detail::ThreadLocalKeyStorage>
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
}  // namespace detail
Packit f0b94e
}  // namespace mozilla
Packit f0b94e
Packit f0b94e
#endif /* mozilla_ThreadLocal_h */