Files
yoga/lib/fb/include/fb/ThreadLocal.h
2016-08-31 16:15:15 +01:00

119 lines
2.6 KiB
C++

/*
* 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 <pthread.h>
#include <errno.h>
#include <fb/assert.h>
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<MyClass> static_object;
* static_object->data_ = ...;
* static_object->doSomething();
*
* ThreadLocal<int> 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<typename T>
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;
};
}