blob: 224d3b25471466a8ed3a092e65968b7a15477fc2 [file] [log] [blame]
John Park398c74a2018-10-20 21:17:39 -07001#ifndef AOS_COMPLEX_THREAD_LOCAL_H_
2#define AOS_COMPLEX_THREAD_LOCAL_H_
Brian Silverman2fa99cb2014-09-06 00:53:00 -04003
4#include <assert.h>
5
6#include <type_traits>
7#include <utility>
8
9namespace aos {
10
11// Instances form a (per-thread) list of destructor functions to call when the
12// thread exits.
13// Only ComplexThreadLocal should use this.
14struct ComplexThreadLocalDestructor {
15 // Adds this to the list of destructors in this thread.
16 void Add();
17 // Removes this from the list of destructors in this thread. ::aos::Dies if it
18 // is not there.
19 void Remove();
20
21 void (*function)(void *);
22 void *param;
23
24 ComplexThreadLocalDestructor *next;
25};
26
27// Handles creating a thread-local (per type) object with non-trivial
28// constructor and/or destructor. It will be correctly destroyed on thread exit.
29//
30// Each thread using an instantiation of this class has its own independent slot
31// for storing a T. An instance of T is not actually constructed until a thread
32// calls Create, after which a pointer to it will be returned from get() etc
33// until after Clear is called.
34//
35// Example usage:
36// class Something {
37// private:
38// class Data {
39// public:
40// Data(const ::std::string &value) : value_(value) {}
41//
42// int DoSomething() {
43// if (cached_result_ == 0) {
44// // Do something expensive with value_ and store it in
45// // cached_result_.
46// }
47// return cached_result_;
48// }
49//
50// private:
51// const ::std::string value_;
52// int cached_result_ = 0;
53// };
54// ComplexThreadLocal<Data> thread_local_;
55// ::std::string a_string_;
56//
57// int DoSomething() {
58// thread_local_.Create(a_string_);
59// return thread_local_->DoSomething();
60// }
61// };
62//
63// The current implementation is based on
64// <http://stackoverflow.com/questions/12049684/gcc-4-7-on-linux-pthreads-nontrivial-thread-local-workaround-using-thread-n>.
65// TODO(brians): Change this to just simple standard C++ thread_local once all
66// of our compilers have support.
67template <typename T>
68class ComplexThreadLocal {
69 public:
70 // Actually creates the object in this thread if there is not one there
71 // already.
72 // args are all perfectly forwarded to the constructor.
73 template <typename... Args>
74 void Create(Args &&... args) {
75 if (initialized) return;
76 new (&storage) T(::std::forward<Args>(args)...);
77 destructor.function = PlacementDelete;
78 destructor.param = &storage;
79 destructor.Add();
80 initialized = true;
81 }
82
83 // Removes the object in this thread (if any), including calling its
84 // destructor.
85 void Clear() {
86 if (!initialized) return;
87 destructor.Remove();
88 PlacementDelete(&storage);
89 initialized = false;
90 }
91
92 // Returns true if there is already an object in this thread.
93 bool created() const { return initialized; }
94
95 // Returns the object currently created in this thread or nullptr.
96 T *operator->() const {
97 return get();
98 }
99 T *get() const {
100 if (initialized) {
101 return static_cast<T *>(static_cast<void *>(&storage));
102 } else {
103 return nullptr;
104 }
105 }
106
107 private:
108 typedef typename ::std::aligned_storage<
109 sizeof(T), ::std::alignment_of<T>::value>::type Storage;
110
111 // Convenient helper for calling a destructor.
112 static void PlacementDelete(void *t) { static_cast<T *>(t)->~T(); }
113
114 // True iff this storage has been initialized.
115 static __thread bool initialized;
116 // Where we actually store the object for this thread (if any).
117 static __thread Storage storage;
118 // The linked list element representing this storage.
119 static __thread ComplexThreadLocalDestructor destructor;
120};
121
122template <typename T>
123__thread bool ComplexThreadLocal<T>::initialized;
124template <typename T>
125__thread typename ComplexThreadLocal<T>::Storage ComplexThreadLocal<T>::storage;
126template <typename T>
127__thread ComplexThreadLocalDestructor ComplexThreadLocal<T>::destructor;
128
129} // namespace aos
130
John Park398c74a2018-10-20 21:17:39 -0700131#endif // AOS_COMPLEX_THREAD_LOCAL_H_