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