• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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