rocksdb/thrift/lib/cpp/concurrency/ThreadLocal.h

203 lines
4.8 KiB
C++

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef THRIFT_CONCURRENCY_THREADLOCAL_H_
#define THRIFT_CONCURRENCY_THREADLOCAL_H_ 1
#include "thrift/lib/cpp/Thrift.h"
#include <pthread.h>
namespace apache { namespace thrift { namespace concurrency {
template <typename T>
class DefaultThreadLocalManager;
/**
* ThreadLocal manages thread-local storage for a particular object type.
*
* Each ThreadLocal object contains a separate instance of an object for each
* thread that accesses the ThreadLocal object.
*
* Note that you should avoid creating too many ThreadLocal objects (e.g., such
* as keeping a ThreadLocal member variable in commonly allocated objects).
* The number of ThreadLocal objects cannot be larger than the value of
* PTHREAD_KEYS_MAX, which is 1024 on many systems.
*
* The ManagerT template parameter controls how object allocation and
* deallocation should be performed. When get() is called from a thread that
* does not already have an instance of the object, Manager::allocate() is
* called. When a thread exits, Manager::destroy() is called if the thread has
* an instance of this object.
*/
template <typename T, typename ManagerT = DefaultThreadLocalManager<T> >
class ThreadLocal {
public:
typedef T DataType;
typedef ManagerT Manager;
/**
* Create a new ThreadLocal object.
*/
ThreadLocal() {
int ret = pthread_key_create(&key_, &ThreadLocal::onThreadExit);
if (ret != 0) {
throw TLibraryException("failed to allocate new thread-local key", ret);
}
}
/**
* Access this thread's local instance of the object.
*
* If there is no instance of the object in this thread, Manager::allocate()
* will be called to allocate a new instance. (Though some Manager
* implementations may return NULL, if each thread's instance must be
* expilcitly initialized.)
*/
T *get() const {
T *obj = getNoAlloc();
if (obj == NULL) {
Manager m;
obj = m.allocate();
if (obj != NULL) {
setImpl(obj);
}
}
return obj;
}
/**
* Access this thread's local instance of the object.
*
* If there is no instance of the object in this thread, NULL will be
* returned. Manager::allocate() will never be called.
*/
T *getNoAlloc() const {
return static_cast<T*>(pthread_getspecific(key_));
}
/**
* Operator overload to perform get()
*/
T *operator->() const {
return get();
}
/**
* Operator overload to perform get()
*/
T &operator*() const {
return *get();
}
/**
* Set the instance of the object to be used by this thread.
*/
void set(T* obj) {
T *old = getNoAlloc();
Manager m;
m.replace(old, obj);
setImpl(obj);
}
/**
* Clear the instance of the object used by this thread.
*
* If this thread had a non-NULL object, Manager::destroy() will be called.
*/
void clear() {
T *obj = getNoAlloc();
if (obj != NULL) {
Manager m;
m.destroy(obj);
setImpl(NULL);
}
}
private:
void setImpl(T* obj) const {
int ret = pthread_setspecific(key_, obj);
if (ret != 0) {
throw TLibraryException("failed to update thread-local key", ret);
}
}
static void onThreadExit(void* arg) {
T *obj = static_cast<T*>(arg);
if (obj != NULL) {
Manager m;
m.destroy(obj);
}
}
pthread_key_t key_;
};
template <typename T>
class DefaultThreadLocalManager {
public:
T* allocate() {
return new T;
}
void destroy(T* t) {
delete t;
}
void replace(T* oldObj, T* newObj) {
if (oldObj != newObj) {
delete oldObj;
}
}
};
template <typename T>
class DestroyOnlyThreadLocalManager {
public:
T* allocate() {
return NULL;
}
void destroy(T* t) {
delete t;
}
void replace(T* oldObj, T* newObj) {
if (oldObj != newObj) {
delete oldObj;
}
}
};
template <typename T>
class NoopThreadLocalManager {
public:
T* allocate() {
return NULL;
}
void destroy(T*) {
}
void replace(T*, T*) {
}
};
}}} // apache::thrift::concurrency
#endif // THRIFT_CONCURRENCY_THREADLOCAL_H_