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 "aemu/base/threads/AndroidThreadStore.h"
16
17 #ifdef _WIN32
18 #include "aemu/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 gfxstream {
36 namespace guest {
37
38 #ifdef _WIN32
39
40 namespace {
41
42 // The ThreadStore implementation on Windows is very tricky, because
43 // TlsAlloc() doesn't allow one to provide a destructor function. As
44 // such threads are expected to destroy all TLS values explicitely.
45 //
46 // To solve this issue, this source file provides a static method called
47 // ThreadStore::OnThreadExit() that must be called when a thread exits,
48 // which will cleanup all values for the current thread.
49 //
50 // But this forces us to track thread-specific values ourselves.
51
52 // Maximum amount of thread-specific slots supported by this implementation.
53 enum {
54 kMaxTlsSlots = 64
55 };
56
57 // TlsSlotArray is a thread-specific array of values. Instances will
58 // be stored in a Win32 TLS value controlled by a single master TLS
59 // key.
60 //
61 typedef void* TlsSlotArray[kMaxTlsSlots];
62
63 // Global state shared by all threads
64 class GlobalState {
65 public:
GlobalState()66 GlobalState() {
67 D("Entering\n");
68 mMasterTls = TlsAlloc();
69 D("Master TLS = %d\n", (int)mMasterTls);
70 InitializeCriticalSection(&mSection);
71 mLastIndex = 0;
72 ::memset(mDestructors, 0, sizeof(mDestructors));
73 D("Exiting\n");
74 }
75
76 // Register a new TLS key, or return -1 on error (too many keys).
77 // |destroy| is the destructor function for the key.
registerKey(ThreadStoreBase::Destructor * destroy)78 int registerKey(ThreadStoreBase::Destructor* destroy) {
79 D("Entering destroy=%p\n", destroy);
80 int ret = -1;
81 EnterCriticalSection(&mSection);
82 if (mLastIndex < kMaxTlsSlots) {
83 ret = mLastIndex++;
84 mDestructors[ret] = destroy;
85 }
86 LeaveCriticalSection(&mSection);
87 D("Exiting newKey=%d\n", ret);
88 return ret;
89 }
90
unregisterKey(int key)91 void unregisterKey(int key) {
92 D("key=%d\n", key);
93 if (key < 0 || key >= kMaxTlsSlots) {
94 D("Invalid key\n");
95 return;
96 }
97
98 // Note: keys are not reusable, but remove the destructor to avoid
99 // crashes in leaveCurrentThread() when it points to a function that
100 // is going to be unloaded from the process' address space.
101 EnterCriticalSection(&mSection);
102 mDestructors[key] = NULL;
103 LeaveCriticalSection(&mSection);
104 D("Exiting\n");
105 }
106
107 // Get the current thread-local value for a given |key|.
getValue(int key) const108 void* getValue(int key) const {
109 D("Entering key=%d\n", key);
110 if (key < 0 || key >= kMaxTlsSlots) {
111 D("Invalid key, result=NULL\n");
112 return NULL;
113 }
114
115 TlsSlotArray* array = getArray();
116 void* ret = (*array)[key];
117 D("Exiting keyValue=%p\n", ret);
118 return ret;
119 }
120
121 // Set the current thread-local |value| for a given |key|.
setValue(int key,void * value)122 void setValue(int key, void* value) {
123 D("Entering key=%d\n",key);
124 if (key < 0 || key >= kMaxTlsSlots) {
125 D("Invalid key, returning\n");
126 return;
127 }
128
129 TlsSlotArray* array = getArray();
130 (*array)[key] = value;
131 D("Exiting\n");
132 }
133
134 // Call this when a thread exits to destroy all its thread-local values.
leaveCurrentThread()135 void leaveCurrentThread() {
136 D("Entering\n");
137 TlsSlotArray* array =
138 reinterpret_cast<TlsSlotArray*>(TlsGetValue(mMasterTls));
139 if (!array) {
140 D("Exiting, no thread-local data in this thread\n");
141 return;
142 }
143
144 for (size_t n = 0; n < kMaxTlsSlots; ++n) {
145 void* value = (*array)[n];
146 if (!value) {
147 continue;
148 }
149 (*array)[n] = NULL;
150
151 // NOTE: In theory, a destructor could reset the slot to
152 // a new value, and we would have to loop in this function
153 // in interesting ways. In practice, ignore the issue.
154 EnterCriticalSection(&mSection);
155 ThreadStoreBase::Destructor* destroy = mDestructors[n];
156 LeaveCriticalSection(&mSection);
157 if (destroy) {
158 D("Calling destructor %p for key=%d, with value=%p\n",
159 destroy, (int)n, value);
160 (*destroy)(value);
161 }
162 }
163 TlsSetValue(mMasterTls, NULL);
164 ::free(array);
165 D("Exiting\n");
166 }
167
168 private:
169 // Return the thread-local array of TLS slots for the current thread.
170 // Cannot return NULL.
getArray() const171 TlsSlotArray* getArray() const {
172 D("Entering\n");
173 TlsSlotArray* array =
174 reinterpret_cast<TlsSlotArray*>(TlsGetValue(mMasterTls));
175 if (!array) {
176 array = reinterpret_cast<TlsSlotArray*>(
177 ::calloc(sizeof(*array), 1));
178 TlsSetValue(mMasterTls, array);
179 D("Allocated new array at %p\n", array);
180 } else {
181 D("Retrieved array at %p\n", array);
182 }
183 return array;
184 }
185
186 DWORD mMasterTls;
187 CRITICAL_SECTION mSection;
188 int mLastIndex;
189 ThreadStoreBase::Destructor* mDestructors[kMaxTlsSlots];
190 };
191
192 LazyInstance<GlobalState> gGlobalState = LAZY_INSTANCE_INIT;
193
194 } // namespace
195
ThreadStoreBase(Destructor * destroy)196 ThreadStoreBase::ThreadStoreBase(Destructor* destroy) {
197 D("Entering this=%p destroy=%p\n", this, destroy);
198 mKey = gGlobalState->registerKey(destroy);
199 D("Exiting this=%p key=%d\n", this, mKey);
200 }
201
~ThreadStoreBase()202 ThreadStoreBase::~ThreadStoreBase() {
203 D("Entering this=%p\n", this);
204 GlobalState* state = gGlobalState.ptr();
205 state->unregisterKey(mKey);
206 D("Exiting this=%p\n", this);
207 }
208
get() const209 void* ThreadStoreBase::get() const {
210 D("Entering this=%p\n", this);
211 void* ret = gGlobalState->getValue(mKey);
212 D("Exiting this=%p value=%p\n", this, ret);
213 return ret;
214 }
215
set(void * value)216 void ThreadStoreBase::set(void* value) {
217 D("Entering this=%p value=%p\n", this, value);
218 gGlobalState->setValue(mKey, value);
219 D("Exiting this=%p\n", this);
220 }
221
222 // static
OnThreadExit()223 void ThreadStoreBase::OnThreadExit() {
224 gGlobalState->leaveCurrentThread();
225 }
226
227 #else // !_WIN32
228
229 ThreadStoreBase::ThreadStoreBase(Destructor* destroy) {
230 int ret = pthread_key_create(&mKey, destroy);
231 if (ret != 0) {
232 fprintf(stderr,
233 "Could not create thread store key: %s\n",
234 strerror(ret));
235 exit(1);
236 }
237 }
238
239 ThreadStoreBase::~ThreadStoreBase() {
240 pthread_key_delete(mKey);
241 }
242
243 #endif // !_WIN32
244
245 } // namespace guest
246 } // namespace gfxstream
247