/* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #pragma once #include #include #include namespace facebook { /////////////////////////////////////////////////////////////////////////////// /** * A thread-local object is a "global" object within a thread. This is useful * for writing apartment-threaded code, where nothing is actullay shared * between different threads (hence no locking) but those variables are not * on stack in local scope. To use it, just do something like this, * * ThreadLocal static_object; * static_object->data_ = ...; * static_object->doSomething(); * * ThreadLocal static_number; * int value = *static_number; * * So, syntax-wise it's similar to pointers. T can be primitive types, and if * it's a class, there has to be a default constructor. */ template class ThreadLocal { public: /** * Constructor that has to be called from a thread-neutral place. */ ThreadLocal() : m_key(0), m_cleanup(OnThreadExit) { initialize(); } /** * As above but with a custom cleanup function */ typedef void (*CleanupFunction)(void* obj); explicit ThreadLocal(CleanupFunction cleanup) : m_key(0), m_cleanup(cleanup) { FBASSERT(cleanup); initialize(); } /** * Access object's member or method through this operator overload. */ T *operator->() const { return get(); } T &operator*() const { return *get(); } T *get() const { return (T*)pthread_getspecific(m_key); } T* release() { T* obj = get(); pthread_setspecific(m_key, NULL); return obj; } void reset(T* other = NULL) { T* old = (T*)pthread_getspecific(m_key); if (old != other) { FBASSERT(m_cleanup); m_cleanup(old); pthread_setspecific(m_key, other); } } private: void initialize() { int ret = pthread_key_create(&m_key, m_cleanup); if (ret != 0) { const char *msg = "(unknown error)"; switch (ret) { case EAGAIN: msg = "PTHREAD_KEYS_MAX (1024) is exceeded"; break; case ENOMEM: msg = "Out-of-memory"; break; } (void) msg; FBASSERTMSGF(0, "pthread_key_create failed: %d %s", ret, msg); } } static void OnThreadExit(void *obj) { if (NULL != obj) { delete (T*)obj; } } pthread_key_t m_key; CleanupFunction m_cleanup; }; }