| // Copyright (C) 2014 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "aemu/base/threads/AndroidThreadStore.h" |
| |
| #ifdef _WIN32 |
| #include "aemu/base/memory/LazyInstance.h" |
| #endif |
| |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| // Set to 1 to print debug messages. |
| #define DEBUG_THREAD_STORE 0 |
| |
| #if DEBUG_THREAD_STORE |
| # define D(...) do { printf("%s:%d: ", __FUNCTION__, __LINE__); printf(__VA_ARGS__); fflush(stdout); } while (0) |
| #else |
| # define D(...) ((void)0) |
| #endif |
| |
| namespace android { |
| namespace base { |
| namespace guest { |
| |
| #ifdef _WIN32 |
| |
| namespace { |
| |
| // The ThreadStore implementation on Windows is very tricky, because |
| // TlsAlloc() doesn't allow one to provide a destructor function. As |
| // such threads are expected to destroy all TLS values explicitely. |
| // |
| // To solve this issue, this source file provides a static method called |
| // ThreadStore::OnThreadExit() that must be called when a thread exits, |
| // which will cleanup all values for the current thread. |
| // |
| // But this forces us to track thread-specific values ourselves. |
| |
| // Maximum amount of thread-specific slots supported by this implementation. |
| enum { |
| kMaxTlsSlots = 64 |
| }; |
| |
| // TlsSlotArray is a thread-specific array of values. Instances will |
| // be stored in a Win32 TLS value controlled by a single master TLS |
| // key. |
| // |
| typedef void* TlsSlotArray[kMaxTlsSlots]; |
| |
| // Global state shared by all threads |
| class GlobalState { |
| public: |
| GlobalState() { |
| D("Entering\n"); |
| mMasterTls = TlsAlloc(); |
| D("Master TLS = %d\n", (int)mMasterTls); |
| InitializeCriticalSection(&mSection); |
| mLastIndex = 0; |
| ::memset(mDestructors, 0, sizeof(mDestructors)); |
| D("Exiting\n"); |
| } |
| |
| // Register a new TLS key, or return -1 on error (too many keys). |
| // |destroy| is the destructor function for the key. |
| int registerKey(ThreadStoreBase::Destructor* destroy) { |
| D("Entering destroy=%p\n", destroy); |
| int ret = -1; |
| EnterCriticalSection(&mSection); |
| if (mLastIndex < kMaxTlsSlots) { |
| ret = mLastIndex++; |
| mDestructors[ret] = destroy; |
| } |
| LeaveCriticalSection(&mSection); |
| D("Exiting newKey=%d\n", ret); |
| return ret; |
| } |
| |
| void unregisterKey(int key) { |
| D("key=%d\n", key); |
| if (key < 0 || key >= kMaxTlsSlots) { |
| D("Invalid key\n"); |
| return; |
| } |
| |
| // Note: keys are not reusable, but remove the destructor to avoid |
| // crashes in leaveCurrentThread() when it points to a function that |
| // is going to be unloaded from the process' address space. |
| EnterCriticalSection(&mSection); |
| mDestructors[key] = NULL; |
| LeaveCriticalSection(&mSection); |
| D("Exiting\n"); |
| } |
| |
| // Get the current thread-local value for a given |key|. |
| void* getValue(int key) const { |
| D("Entering key=%d\n", key); |
| if (key < 0 || key >= kMaxTlsSlots) { |
| D("Invalid key, result=NULL\n"); |
| return NULL; |
| } |
| |
| TlsSlotArray* array = getArray(); |
| void* ret = (*array)[key]; |
| D("Exiting keyValue=%p\n", ret); |
| return ret; |
| } |
| |
| // Set the current thread-local |value| for a given |key|. |
| void setValue(int key, void* value) { |
| D("Entering key=%d\n",key); |
| if (key < 0 || key >= kMaxTlsSlots) { |
| D("Invalid key, returning\n"); |
| return; |
| } |
| |
| TlsSlotArray* array = getArray(); |
| (*array)[key] = value; |
| D("Exiting\n"); |
| } |
| |
| // Call this when a thread exits to destroy all its thread-local values. |
| void leaveCurrentThread() { |
| D("Entering\n"); |
| TlsSlotArray* array = |
| reinterpret_cast<TlsSlotArray*>(TlsGetValue(mMasterTls)); |
| if (!array) { |
| D("Exiting, no thread-local data in this thread\n"); |
| return; |
| } |
| |
| for (size_t n = 0; n < kMaxTlsSlots; ++n) { |
| void* value = (*array)[n]; |
| if (!value) { |
| continue; |
| } |
| (*array)[n] = NULL; |
| |
| // NOTE: In theory, a destructor could reset the slot to |
| // a new value, and we would have to loop in this function |
| // in interesting ways. In practice, ignore the issue. |
| EnterCriticalSection(&mSection); |
| ThreadStoreBase::Destructor* destroy = mDestructors[n]; |
| LeaveCriticalSection(&mSection); |
| if (destroy) { |
| D("Calling destructor %p for key=%d, with value=%p\n", |
| destroy, (int)n, value); |
| (*destroy)(value); |
| } |
| } |
| TlsSetValue(mMasterTls, NULL); |
| ::free(array); |
| D("Exiting\n"); |
| } |
| |
| private: |
| // Return the thread-local array of TLS slots for the current thread. |
| // Cannot return NULL. |
| TlsSlotArray* getArray() const { |
| D("Entering\n"); |
| TlsSlotArray* array = |
| reinterpret_cast<TlsSlotArray*>(TlsGetValue(mMasterTls)); |
| if (!array) { |
| array = reinterpret_cast<TlsSlotArray*>( |
| ::calloc(sizeof(*array), 1)); |
| TlsSetValue(mMasterTls, array); |
| D("Allocated new array at %p\n", array); |
| } else { |
| D("Retrieved array at %p\n", array); |
| } |
| return array; |
| } |
| |
| DWORD mMasterTls; |
| CRITICAL_SECTION mSection; |
| int mLastIndex; |
| ThreadStoreBase::Destructor* mDestructors[kMaxTlsSlots]; |
| }; |
| |
| LazyInstance<GlobalState> gGlobalState = LAZY_INSTANCE_INIT; |
| |
| } // namespace |
| |
| ThreadStoreBase::ThreadStoreBase(Destructor* destroy) { |
| D("Entering this=%p destroy=%p\n", this, destroy); |
| mKey = gGlobalState->registerKey(destroy); |
| D("Exiting this=%p key=%d\n", this, mKey); |
| } |
| |
| ThreadStoreBase::~ThreadStoreBase() { |
| D("Entering this=%p\n", this); |
| GlobalState* state = gGlobalState.ptr(); |
| state->unregisterKey(mKey); |
| D("Exiting this=%p\n", this); |
| } |
| |
| void* ThreadStoreBase::get() const { |
| D("Entering this=%p\n", this); |
| void* ret = gGlobalState->getValue(mKey); |
| D("Exiting this=%p value=%p\n", this, ret); |
| return ret; |
| } |
| |
| void ThreadStoreBase::set(void* value) { |
| D("Entering this=%p value=%p\n", this, value); |
| gGlobalState->setValue(mKey, value); |
| D("Exiting this=%p\n", this); |
| } |
| |
| // static |
| void ThreadStoreBase::OnThreadExit() { |
| gGlobalState->leaveCurrentThread(); |
| } |
| |
| #else // !_WIN32 |
| |
| ThreadStoreBase::ThreadStoreBase(Destructor* destroy) { |
| int ret = pthread_key_create(&mKey, destroy); |
| if (ret != 0) { |
| fprintf(stderr, |
| "Could not create thread store key: %s\n", |
| strerror(ret)); |
| exit(1); |
| } |
| } |
| |
| ThreadStoreBase::~ThreadStoreBase() { |
| pthread_key_delete(mKey); |
| } |
| |
| #endif // !_WIN32 |
| |
| } // namespace guest |
| } // namespace base |
| } // namespace android |