1 // Copyright (C) 2014 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "android/base/threads/AndroidThreadStore.h"
16
17 #ifdef _WIN32
18 #include "android/base/memory/LazyInstance.h"
19 #endif
20
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 // Set to 1 to print debug messages.
27 #define DEBUG_THREAD_STORE 0
28
29 #if DEBUG_THREAD_STORE
30 # define D(...) do { printf("%s:%d: ", __FUNCTION__, __LINE__); printf(__VA_ARGS__); fflush(stdout); } while (0)
31 #else
32 # define D(...) ((void)0)
33 #endif
34
35 namespace android {
36 namespace base {
37 namespace guest {
38
39 #ifdef _WIN32
40
41 namespace {
42
43 // The ThreadStore implementation on Windows is very tricky, because
44 // TlsAlloc() doesn't allow one to provide a destructor function. As
45 // such threads are expected to destroy all TLS values explicitely.
46 //
47 // To solve this issue, this source file provides a static method called
48 // ThreadStore::OnThreadExit() that must be called when a thread exits,
49 // which will cleanup all values for the current thread.
50 //
51 // But this forces us to track thread-specific values ourselves.
52
53 // Maximum amount of thread-specific slots supported by this implementation.
54 enum {
55 kMaxTlsSlots = 64
56 };
57
58 // TlsSlotArray is a thread-specific array of values. Instances will
59 // be stored in a Win32 TLS value controlled by a single master TLS
60 // key.
61 //
62 typedef void* TlsSlotArray[kMaxTlsSlots];
63
64 // Global state shared by all threads
65 class GlobalState {
66 public:
GlobalState()67 GlobalState() {
68 D("Entering\n");
69 mMasterTls = TlsAlloc();
70 D("Master TLS = %d\n", (int)mMasterTls);
71 InitializeCriticalSection(&mSection);
72 mLastIndex = 0;
73 ::memset(mDestructors, 0, sizeof(mDestructors));
74 D("Exiting\n");
75 }
76
77 // Register a new TLS key, or return -1 on error (too many keys).
78 // |destroy| is the destructor function for the key.
registerKey(ThreadStoreBase::Destructor * destroy)79 int registerKey(ThreadStoreBase::Destructor* destroy) {
80 D("Entering destroy=%p\n", destroy);
81 int ret = -1;
82 EnterCriticalSection(&mSection);
83 if (mLastIndex < kMaxTlsSlots) {
84 ret = mLastIndex++;
85 mDestructors[ret] = destroy;
86 }
87 LeaveCriticalSection(&mSection);
88 D("Exiting newKey=%d\n", ret);
89 return ret;
90 }
91
unregisterKey(int key)92 void unregisterKey(int key) {
93 D("key=%d\n", key);
94 if (key < 0 || key >= kMaxTlsSlots) {
95 D("Invalid key\n");
96 return;
97 }
98
99 // Note: keys are not reusable, but remove the destructor to avoid
100 // crashes in leaveCurrentThread() when it points to a function that
101 // is going to be unloaded from the process' address space.
102 EnterCriticalSection(&mSection);
103 mDestructors[key] = NULL;
104 LeaveCriticalSection(&mSection);
105 D("Exiting\n");
106 }
107
108 // Get the current thread-local value for a given |key|.
getValue(int key) const109 void* getValue(int key) const {
110 D("Entering key=%d\n", key);
111 if (key < 0 || key >= kMaxTlsSlots) {
112 D("Invalid key, result=NULL\n");
113 return NULL;
114 }
115
116 TlsSlotArray* array = getArray();
117 void* ret = (*array)[key];
118 D("Exiting keyValue=%p\n", ret);
119 return ret;
120 }
121
122 // Set the current thread-local |value| for a given |key|.
setValue(int key,void * value)123 void setValue(int key, void* value) {
124 D("Entering key=%d\n",key);
125 if (key < 0 || key >= kMaxTlsSlots) {
126 D("Invalid key, returning\n");
127 return;
128 }
129
130 TlsSlotArray* array = getArray();
131 (*array)[key] = value;
132 D("Exiting\n");
133 }
134
135 // Call this when a thread exits to destroy all its thread-local values.
leaveCurrentThread()136 void leaveCurrentThread() {
137 D("Entering\n");
138 TlsSlotArray* array =
139 reinterpret_cast<TlsSlotArray*>(TlsGetValue(mMasterTls));
140 if (!array) {
141 D("Exiting, no thread-local data in this thread\n");
142 return;
143 }
144
145 for (size_t n = 0; n < kMaxTlsSlots; ++n) {
146 void* value = (*array)[n];
147 if (!value) {
148 continue;
149 }
150 (*array)[n] = NULL;
151
152 // NOTE: In theory, a destructor could reset the slot to
153 // a new value, and we would have to loop in this function
154 // in interesting ways. In practice, ignore the issue.
155 EnterCriticalSection(&mSection);
156 ThreadStoreBase::Destructor* destroy = mDestructors[n];
157 LeaveCriticalSection(&mSection);
158 if (destroy) {
159 D("Calling destructor %p for key=%d, with value=%p\n",
160 destroy, (int)n, value);
161 (*destroy)(value);
162 }
163 }
164 TlsSetValue(mMasterTls, NULL);
165 ::free(array);
166 D("Exiting\n");
167 }
168
169 private:
170 // Return the thread-local array of TLS slots for the current thread.
171 // Cannot return NULL.
getArray() const172 TlsSlotArray* getArray() const {
173 D("Entering\n");
174 TlsSlotArray* array =
175 reinterpret_cast<TlsSlotArray*>(TlsGetValue(mMasterTls));
176 if (!array) {
177 array = reinterpret_cast<TlsSlotArray*>(
178 ::calloc(sizeof(*array), 1));
179 TlsSetValue(mMasterTls, array);
180 D("Allocated new array at %p\n", array);
181 } else {
182 D("Retrieved array at %p\n", array);
183 }
184 return array;
185 }
186
187 DWORD mMasterTls;
188 CRITICAL_SECTION mSection;
189 int mLastIndex;
190 ThreadStoreBase::Destructor* mDestructors[kMaxTlsSlots];
191 };
192
193 LazyInstance<GlobalState> gGlobalState = LAZY_INSTANCE_INIT;
194
195 } // namespace
196
ThreadStoreBase(Destructor * destroy)197 ThreadStoreBase::ThreadStoreBase(Destructor* destroy) {
198 D("Entering this=%p destroy=%p\n", this, destroy);
199 mKey = gGlobalState->registerKey(destroy);
200 D("Exiting this=%p key=%d\n", this, mKey);
201 }
202
~ThreadStoreBase()203 ThreadStoreBase::~ThreadStoreBase() {
204 D("Entering this=%p\n", this);
205 GlobalState* state = gGlobalState.ptr();
206 state->unregisterKey(mKey);
207 D("Exiting this=%p\n", this);
208 }
209
get() const210 void* ThreadStoreBase::get() const {
211 D("Entering this=%p\n", this);
212 void* ret = gGlobalState->getValue(mKey);
213 D("Exiting this=%p value=%p\n", this, ret);
214 return ret;
215 }
216
set(void * value)217 void ThreadStoreBase::set(void* value) {
218 D("Entering this=%p value=%p\n", this, value);
219 gGlobalState->setValue(mKey, value);
220 D("Exiting this=%p\n", this);
221 }
222
223 // static
OnThreadExit()224 void ThreadStoreBase::OnThreadExit() {
225 gGlobalState->leaveCurrentThread();
226 }
227
228 #else // !_WIN32
229
230 ThreadStoreBase::ThreadStoreBase(Destructor* destroy) {
231 int ret = pthread_key_create(&mKey, destroy);
232 if (ret != 0) {
233 fprintf(stderr,
234 "Could not create thread store key: %s\n",
235 strerror(ret));
236 exit(1);
237 }
238 }
239
240 ThreadStoreBase::~ThreadStoreBase() {
241 pthread_key_delete(mKey);
242 }
243
244 #endif // !_WIN32
245
246 } // namespace guest
247 } // namespace base
248 } // namespace android
249