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