blob: 224d3b25471466a8ed3a092e65968b7a15477fc2 [file] [log] [blame]
#ifndef AOS_COMPLEX_THREAD_LOCAL_H_
#define AOS_COMPLEX_THREAD_LOCAL_H_
#include <assert.h>
#include <type_traits>
#include <utility>
namespace aos {
// Instances form a (per-thread) list of destructor functions to call when the
// thread exits.
// Only ComplexThreadLocal should use this.
struct ComplexThreadLocalDestructor {
// Adds this to the list of destructors in this thread.
void Add();
// Removes this from the list of destructors in this thread. ::aos::Dies if it
// is not there.
void Remove();
void (*function)(void *);
void *param;
ComplexThreadLocalDestructor *next;
};
// Handles creating a thread-local (per type) object with non-trivial
// constructor and/or destructor. It will be correctly destroyed on thread exit.
//
// Each thread using an instantiation of this class has its own independent slot
// for storing a T. An instance of T is not actually constructed until a thread
// calls Create, after which a pointer to it will be returned from get() etc
// until after Clear is called.
//
// Example usage:
// class Something {
// private:
// class Data {
// public:
// Data(const ::std::string &value) : value_(value) {}
//
// int DoSomething() {
// if (cached_result_ == 0) {
// // Do something expensive with value_ and store it in
// // cached_result_.
// }
// return cached_result_;
// }
//
// private:
// const ::std::string value_;
// int cached_result_ = 0;
// };
// ComplexThreadLocal<Data> thread_local_;
// ::std::string a_string_;
//
// int DoSomething() {
// thread_local_.Create(a_string_);
// return thread_local_->DoSomething();
// }
// };
//
// The current implementation is based on
// <http://stackoverflow.com/questions/12049684/gcc-4-7-on-linux-pthreads-nontrivial-thread-local-workaround-using-thread-n>.
// TODO(brians): Change this to just simple standard C++ thread_local once all
// of our compilers have support.
template <typename T>
class ComplexThreadLocal {
public:
// Actually creates the object in this thread if there is not one there
// already.
// args are all perfectly forwarded to the constructor.
template <typename... Args>
void Create(Args &&... args) {
if (initialized) return;
new (&storage) T(::std::forward<Args>(args)...);
destructor.function = PlacementDelete;
destructor.param = &storage;
destructor.Add();
initialized = true;
}
// Removes the object in this thread (if any), including calling its
// destructor.
void Clear() {
if (!initialized) return;
destructor.Remove();
PlacementDelete(&storage);
initialized = false;
}
// Returns true if there is already an object in this thread.
bool created() const { return initialized; }
// Returns the object currently created in this thread or nullptr.
T *operator->() const {
return get();
}
T *get() const {
if (initialized) {
return static_cast<T *>(static_cast<void *>(&storage));
} else {
return nullptr;
}
}
private:
typedef typename ::std::aligned_storage<
sizeof(T), ::std::alignment_of<T>::value>::type Storage;
// Convenient helper for calling a destructor.
static void PlacementDelete(void *t) { static_cast<T *>(t)->~T(); }
// True iff this storage has been initialized.
static __thread bool initialized;
// Where we actually store the object for this thread (if any).
static __thread Storage storage;
// The linked list element representing this storage.
static __thread ComplexThreadLocalDestructor destructor;
};
template <typename T>
__thread bool ComplexThreadLocal<T>::initialized;
template <typename T>
__thread typename ComplexThreadLocal<T>::Storage ComplexThreadLocal<T>::storage;
template <typename T>
__thread ComplexThreadLocalDestructor ComplexThreadLocal<T>::destructor;
} // namespace aos
#endif // AOS_COMPLEX_THREAD_LOCAL_H_