// 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 "emugl/common/thread_store.h" #include "emugl/common/mutex.h" #include "emugl/common/testing/test_thread.h" #include namespace emugl { namespace { // Helper class used to count instance creation and destruction. class StaticCounter { public: enum { kMaxInstances = 1000, }; StaticCounter() { Mutex::AutoLock lock(mMutex); if (mCreationCount < kMaxInstances) mInstances[mCreationCount] = this; mCreationCount++; } ~StaticCounter() { Mutex::AutoLock lock(mMutex); mDestructionCount++; } static void reset() { Mutex::AutoLock lock(mMutex); mCreationCount = 0; mDestructionCount = 0; } static size_t getCreationCount() { Mutex::AutoLock lock(mMutex); return mCreationCount; } static size_t getDestructionCount() { Mutex::AutoLock lock(mMutex); return mDestructionCount; } static void freeAll() { for (size_t n = 0; n < kMaxInstances; ++n) delete mInstances[n]; } private: static Mutex mMutex; static size_t mCreationCount; static size_t mDestructionCount; static StaticCounter* mInstances[kMaxInstances]; }; Mutex StaticCounter::mMutex; size_t StaticCounter::mCreationCount = 0; size_t StaticCounter::mDestructionCount = 0; StaticCounter* StaticCounter::mInstances[kMaxInstances]; } // namespace // Just check that we can create a new ThreadStore with an empty // destructor, and use it in the current thread. TEST(ThreadStore, MainThreadWithoutDestructor) { ThreadStore store(NULL); static int x = 42; store.set(&x); EXPECT_EQ(&x, store.get()); } // The following test checks that exiting a thread correctly deletes // any thread-local value stored in it. static void simplyDestroy(void* value) { delete (StaticCounter*) value; } static void* simpleThreadFunc(void* param) { ThreadStore* store = static_cast(param); store->set(new StaticCounter()); ThreadStore::OnThreadExit(); return NULL; } TEST(ThreadStore, ThreadsWithDestructor) { ThreadStore store(simplyDestroy); const size_t kNumThreads = 1000; TestThread* threads[kNumThreads]; StaticCounter::reset(); for (size_t n = 0; n < kNumThreads; ++n) { threads[n] = new TestThread(&simpleThreadFunc, &store); } for (size_t n = 0; n < kNumThreads; ++n) { threads[n]->join(); } EXPECT_EQ(kNumThreads, StaticCounter::getCreationCount()); EXPECT_EQ(kNumThreads, StaticCounter::getDestructionCount()); for (size_t n = 0; n < kNumThreads; ++n) { delete threads[n]; } } TEST(ThreadStore, ThreadsWithoutDestructor) { ThreadStore store(NULL); const size_t kNumThreads = 1000; TestThread* threads[kNumThreads]; StaticCounter::reset(); for (size_t n = 0; n < kNumThreads; ++n) { threads[n] = new TestThread(&simpleThreadFunc, &store); } for (size_t n = 0; n < kNumThreads; ++n) { threads[n]->join(); } EXPECT_EQ(kNumThreads, StaticCounter::getCreationCount()); EXPECT_EQ(0U, StaticCounter::getDestructionCount()); StaticCounter::freeAll(); for (size_t n = 0; n < kNumThreads; ++n) { delete threads[n]; } } } // namespace emugl