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