Home | History | Annotate | Download | only in common
      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