aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@google.com>2014-03-10 15:26:00 +0100
committerDavid 'Digit' Turner <digit@google.com>2014-03-11 18:02:57 +0100
commit47752bc9b62d974d82ba10bfc3633b72d10afcbd (patch)
treee5dbd1b3f20ff54654e6f5f2db2a0805431a7845
parentaac93f1585bc9bdf8a57b6ed3c47c24f56a3990a (diff)
downloadsdk-47752bc9b62d974d82ba10bfc3633b72d10afcbd.zip
sdk-47752bc9b62d974d82ba10bfc3633b72d10afcbd.tar.gz
sdk-47752bc9b62d974d82ba10bfc3633b72d10afcbd.tar.bz2
emulator/opengl: Remove Android-specific thread_store.
This patch removes the use of the 'thread_store' class from <utils/threads.h> by providing its own implementation instead under shared/emugl/common/thread_store.h, plus appropriate unit tests. Note that unlike the Android version, this properly destroys the thread-local values on thread exit (instead of leaking them). + Provide a LazyInstance class used to perform thread-safe lazy initialization of static variables without the use of C++ constructors. Change-Id: Iabe01fbd713c6872b5fe245d7255c3c03749a88a
-rw-r--r--emulator/opengl/host/libs/Translator/EGL/EglThreadInfo.cpp31
-rw-r--r--emulator/opengl/host/libs/Translator/EGL/ThreadInfo.cpp58
-rw-r--r--emulator/opengl/host/libs/libOpenglRender/ThreadInfo.cpp19
-rw-r--r--emulator/opengl/shared/OpenglOsUtils/Android.mk7
-rw-r--r--emulator/opengl/shared/OpenglOsUtils/osThreadUnix.cpp3
-rw-r--r--emulator/opengl/shared/OpenglOsUtils/osThreadWin.cpp3
-rw-r--r--emulator/opengl/shared/emugl/common/Android.mk5
-rw-r--r--emulator/opengl/shared/emugl/common/lazy_instance.cpp101
-rw-r--r--emulator/opengl/shared/emugl/common/lazy_instance.h156
-rw-r--r--emulator/opengl/shared/emugl/common/lazy_instance_unittest.cpp146
-rw-r--r--emulator/opengl/shared/emugl/common/thread_store.cpp242
-rw-r--r--emulator/opengl/shared/emugl/common/thread_store.h110
-rw-r--r--emulator/opengl/shared/emugl/common/thread_store_unittest.cpp146
13 files changed, 990 insertions, 37 deletions
diff --git a/emulator/opengl/host/libs/Translator/EGL/EglThreadInfo.cpp b/emulator/opengl/host/libs/Translator/EGL/EglThreadInfo.cpp
index 1b403f2..6481774 100644
--- a/emulator/opengl/host/libs/Translator/EGL/EglThreadInfo.cpp
+++ b/emulator/opengl/host/libs/Translator/EGL/EglThreadInfo.cpp
@@ -16,26 +16,33 @@
#include "EglThreadInfo.h"
#include "EglOsApi.h"
-EglThreadInfo::EglThreadInfo():m_err(EGL_SUCCESS),m_api(EGL_OPENGL_ES_API) {}
+#include "emugl/common/lazy_instance.h"
+#include "emugl/common/thread_store.h"
-#include <cutils/threads.h>
+namespace {
-static thread_store_t s_tls = THREAD_STORE_INITIALIZER;
-
-static void tlsDestruct(void *ptr)
-{
- if (ptr) {
- EglThreadInfo *ti = (EglThreadInfo *)ptr;
- delete ti;
+class EglThreadInfoStore : public emugl::ThreadStore {
+public:
+ EglThreadInfoStore() : emugl::ThreadStore(&destructor) {}
+private:
+ static void destructor(void* value) {
+ delete static_cast<EglThreadInfo*>(value);
}
-}
+};
+
+} // namespace
+
+EglThreadInfo::EglThreadInfo() :
+ m_err(EGL_SUCCESS), m_api(EGL_OPENGL_ES_API) {}
+
+static emugl::LazyInstance<EglThreadInfoStore> s_tls = LAZY_INSTANCE_INIT;
EglThreadInfo* EglThreadInfo::get(void)
{
- EglThreadInfo *ti = (EglThreadInfo *)thread_store_get(&s_tls);
+ EglThreadInfo *ti = static_cast<EglThreadInfo*>(s_tls->get());
if (!ti) {
ti = new EglThreadInfo();
- thread_store_set(&s_tls, ti, tlsDestruct);
+ s_tls->set(ti);
}
return ti;
}
diff --git a/emulator/opengl/host/libs/Translator/EGL/ThreadInfo.cpp b/emulator/opengl/host/libs/Translator/EGL/ThreadInfo.cpp
index 4f5d75f..1571b3a 100644
--- a/emulator/opengl/host/libs/Translator/EGL/ThreadInfo.cpp
+++ b/emulator/opengl/host/libs/Translator/EGL/ThreadInfo.cpp
@@ -14,16 +14,47 @@
* limitations under the License.
*/
-#include <stdio.h>
#include "ThreadInfo.h"
-//#define TRACE_THREADINFO
-#ifdef TRACE_THREADINFO
+#include "emugl/common/lazy_instance.h"
+#include "emugl/common/thread_store.h"
+
+#include <stdio.h>
+
+// Set TRACE_THREADINFO to 1 to debug creation/destruction of ThreadInfo
+// instances.
+#define TRACE_THREADINFO 0
+
+#if TRACE_THREADINFO
#define LOG_THREADINFO(x...) fprintf(stderr, x)
#else
#define LOG_THREADINFO(x...)
#endif
+namespace {
+
+class ThreadInfoStore : public ::emugl::ThreadStore {
+public:
+ ThreadInfoStore() : ::emugl::ThreadStore(&destructor) {}
+
+ size_t getInstanceCount() const { return mNumInstances; }
+
+private:
+ static void destructor(void* value) {
+ LOG_THREADINFO("%s: EFL %p (%d instances)\n", __FUNCTION__,
+ value, mNumInstances);
+ delete static_cast<ThreadInfo*>(value);
+ mNumInstances--;
+ }
+
+ static size_t mNumInstances;
+};
+
+size_t ThreadInfoStore::mNumInstances = 0;
+
+} // namespace
+
+
void ThreadInfo::updateInfo(ContextPtr eglCtx,
EglDisplay* dpy,
GLEScontext* glesCtx,
@@ -37,27 +68,16 @@ void ThreadInfo::updateInfo(ContextPtr eglCtx,
objManager = manager;
}
-#include <cutils/threads.h>
-static thread_store_t s_tls = THREAD_STORE_INITIALIZER;
-static int active_instance = 0;
-static void tlsDestruct(void *ptr)
-{
- active_instance--;
- LOG_THREADINFO("tlsDestruct EGL %lx %d\n", (long)ptr, active_instance);
- if (ptr) {
- ThreadInfo *ti = (ThreadInfo *)ptr;
- delete ti;
- }
-}
+static ::emugl::LazyInstance<ThreadInfoStore> s_tls = LAZY_INSTANCE_INIT;
ThreadInfo *getThreadInfo()
{
- ThreadInfo *ti = (ThreadInfo *)thread_store_get(&s_tls);
+ ThreadInfo *ti = static_cast<ThreadInfo*>(s_tls->get());
if (!ti) {
ti = new ThreadInfo();
- thread_store_set(&s_tls, ti, tlsDestruct);
- active_instance++;
- LOG_THREADINFO("getThreadInfo EGL %lx %d\n", (long)ti, active_instance);
+ s_tls->set(ti);
+ LOG_THREADINFO("%s: EGL %p (%d instances)\n", __FUNCTION__,
+ ti, (int)ThreadInfoStore::getInstanceCount());
}
return ti;
}
diff --git a/emulator/opengl/host/libs/libOpenglRender/ThreadInfo.cpp b/emulator/opengl/host/libs/libOpenglRender/ThreadInfo.cpp
index 566ca40..5337009 100644
--- a/emulator/opengl/host/libs/libOpenglRender/ThreadInfo.cpp
+++ b/emulator/opengl/host/libs/libOpenglRender/ThreadInfo.cpp
@@ -15,17 +15,28 @@
*/
#include "ThreadInfo.h"
-#include <cutils/threads.h>
+#include "emugl/common/lazy_instance.h"
+#include "emugl/common/thread_store.h"
-static thread_store_t s_tls = THREAD_STORE_INITIALIZER;
+namespace {
+
+class ThreadInfoStore : public ::emugl::ThreadStore {
+public:
+ ThreadInfoStore() : ::emugl::ThreadStore(NULL) {}
+};
+
+} // namespace
+
+static ::emugl::LazyInstance<ThreadInfoStore> s_tls = LAZY_INSTANCE_INIT;
RenderThreadInfo::RenderThreadInfo() {
- thread_store_set(&s_tls, this, NULL);
+ s_tls->set(this);
}
RenderThreadInfo::~RenderThreadInfo() {
+ s_tls->set(NULL);
}
RenderThreadInfo* RenderThreadInfo::get() {
- return (RenderThreadInfo*)thread_store_get(&s_tls);
+ return static_cast<RenderThreadInfo*>(s_tls->get());
}
diff --git a/emulator/opengl/shared/OpenglOsUtils/Android.mk b/emulator/opengl/shared/OpenglOsUtils/Android.mk
index 8a6c18b..d8807d0 100644
--- a/emulator/opengl/shared/OpenglOsUtils/Android.mk
+++ b/emulator/opengl/shared/OpenglOsUtils/Android.mk
@@ -10,6 +10,7 @@ LOCAL_PATH := $(call my-dir)
host_common_SRC_FILES := osDynLibrary.cpp
host_common_LDLIBS :=
+host_common_INCLUDES := $(LOCAL_PATH)
ifeq ($(HOST_OS),windows)
host_common_SRC_FILES += \
@@ -29,15 +30,17 @@ endif
### 32-bit host library ####
$(call emugl-begin-host-static-library,libOpenglOsUtils)
- $(call emugl-export,C_INCLUDES,$(LOCAL_PATH))
+ $(call emugl-export,C_INCLUDES,$(host_common_INCLUDES))
LOCAL_SRC_FILES = $(host_common_SRC_FILES)
$(call emugl-export,LDLIBS,$(host_common_LDLIBS))
+ $(call emugl-import,libemugl_common)
$(call emugl-end-module)
### 64-bit host library ####
$(call emugl-begin-host-static-library,lib64OpenglOsUtils)
- $(call emugl-export,C_INCLUDES,$(LOCAL_PATH))
+ $(call emugl-export,C_INCLUDES,$(host_common_INCLUDES))
LOCAL_SRC_FILES = $(host_common_SRC_FILES)
$(call emugl-export,LDLIBS,$(host_common_LDLIBS))
$(call emugl-export,CFLAGS,-m64)
+ $(call emugl-import,lib64emugl_common)
$(call emugl-end-module)
diff --git a/emulator/opengl/shared/OpenglOsUtils/osThreadUnix.cpp b/emulator/opengl/shared/OpenglOsUtils/osThreadUnix.cpp
index 8cd73a9..ef2bebc 100644
--- a/emulator/opengl/shared/OpenglOsUtils/osThreadUnix.cpp
+++ b/emulator/opengl/shared/OpenglOsUtils/osThreadUnix.cpp
@@ -15,6 +15,8 @@
*/
#include "osThread.h"
+#include "emugl/common/thread_store.h"
+
#include <stdint.h>
namespace osUtils {
@@ -88,6 +90,7 @@ Thread::thread_main(void *p_arg)
self->m_exitStatus = ret;
pthread_mutex_unlock(&self->m_lock);
+ ::emugl::ThreadStore::OnThreadExit();
return (void*)(uintptr_t)ret;
}
diff --git a/emulator/opengl/shared/OpenglOsUtils/osThreadWin.cpp b/emulator/opengl/shared/OpenglOsUtils/osThreadWin.cpp
index 2d563f8..1a30b97 100644
--- a/emulator/opengl/shared/OpenglOsUtils/osThreadWin.cpp
+++ b/emulator/opengl/shared/OpenglOsUtils/osThreadWin.cpp
@@ -15,6 +15,8 @@
*/
#include "osThread.h"
+#include "emugl/common/thread_store.h"
+
namespace osUtils {
Thread::Thread() :
@@ -95,6 +97,7 @@ Thread::thread_main(void *p_arg)
Thread *self = (Thread *)p_arg;
int ret = self->Main();
self->m_isRunning = false;
+ ::emugl::ThreadStore::OnThreadExit();
return ret;
}
diff --git a/emulator/opengl/shared/emugl/common/Android.mk b/emulator/opengl/shared/emugl/common/Android.mk
index ae1c213..f1c20b5 100644
--- a/emulator/opengl/shared/emugl/common/Android.mk
+++ b/emulator/opengl/shared/emugl/common/Android.mk
@@ -6,7 +6,9 @@ LOCAL_PATH := $(call my-dir)
### emugl_common host library ###########################################
commonSources := \
+ lazy_instance.cpp \
smart_ptr.cpp \
+ thread_store.cpp \
host_commonSources := $(commonSources)
@@ -19,11 +21,14 @@ LOCAL_SRC_FILES := $(host_commonSources)
$(call emugl-export,CFLAGS,-m64)
$(call emugl-end-module)
+
### emugl_common_unittests ##############################################
host_commonSources := \
+ lazy_instance_unittest.cpp \
mutex_unittest.cpp \
smart_ptr_unittest.cpp \
+ thread_store_unittest.cpp \
$(call emugl-begin-host-executable,emugl_common_host_unittests)
LOCAL_SRC_FILES := $(host_commonSources)
diff --git a/emulator/opengl/shared/emugl/common/lazy_instance.cpp b/emulator/opengl/shared/emugl/common/lazy_instance.cpp
new file mode 100644
index 0000000..ee715fa
--- /dev/null
+++ b/emulator/opengl/shared/emugl/common/lazy_instance.cpp
@@ -0,0 +1,101 @@
+// 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/lazy_instance.h"
+
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN 1
+# include <windows.h>
+#else
+# include <sched.h>
+#endif
+
+namespace emugl {
+namespace internal {
+
+typedef LazyInstanceState::AtomicType AtomicType;
+
+#if defined(__GNUC__)
+static inline void compilerBarrier() {
+ __asm__ __volatile__ ("" : : : "memory");
+}
+#else
+#error "Your compiler is not supported"
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+# define acquireBarrier() compilerBarrier()
+# define releaseBarrier() compilerBarrier()
+#else
+# error "Your CPU is not supported"
+#endif
+
+static inline AtomicType loadAcquire(AtomicType volatile* ptr) {
+ AtomicType ret = *ptr;
+ acquireBarrier();
+ return ret;
+}
+
+static inline void storeRelease(AtomicType volatile* ptr, AtomicType value) {
+ releaseBarrier();
+ *ptr = value;
+}
+
+static int atomicCompareAndSwap(AtomicType volatile* ptr,
+ int expected,
+ int value) {
+#ifdef _WIN32
+ return InterlockedCompareExchange(ptr, value, expected);
+#elif defined(__GNUC__)
+ return __sync_val_compare_and_swap(ptr, expected, value);
+#else
+#error "Your compiler is not supported"
+#endif
+}
+
+static void yieldThread() {
+#ifdef _WIN32
+ ::Sleep(0);
+#else
+ sched_yield();
+#endif
+}
+
+bool LazyInstanceState::inInitState() {
+ return loadAcquire(&mState) == STATE_INIT;
+}
+
+bool LazyInstanceState::needConstruction() {
+ AtomicType state = loadAcquire(&mState);
+ if (mState == STATE_DONE)
+ return false;
+
+ state = atomicCompareAndSwap(&mState, STATE_INIT, STATE_CONSTRUCTING);
+ if (state == STATE_INIT)
+ return true;
+
+ do {
+ yieldThread();
+ state = loadAcquire(&mState);
+ } while (state != STATE_DONE);
+
+ return false;
+}
+
+void LazyInstanceState::doneConstructing() {
+ storeRelease(&mState, STATE_DONE);
+}
+
+} // namespace internal
+} // namespace emugl
diff --git a/emulator/opengl/shared/emugl/common/lazy_instance.h b/emulator/opengl/shared/emugl/common/lazy_instance.h
new file mode 100644
index 0000000..6641c93
--- /dev/null
+++ b/emulator/opengl/shared/emugl/common/lazy_instance.h
@@ -0,0 +1,156 @@
+// 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.
+
+#ifndef EMUGL_COMMON_LAZY_INSTANCE_H
+#define EMUGL_COMMON_LAZY_INSTANCE_H
+
+#include <new>
+
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN 1
+# include <windows.h>
+#endif
+
+namespace emugl {
+namespace internal {
+
+// A LazyInstance is a helper template that can be used to perform
+// thread-safe lazy initialization of static C++ objects without forcing
+// the generation of C++ static constructors in the final executable.
+//
+// In a nutshell, you can replace a statement like:
+//
+// static Foo gFoo;
+//
+// With:
+//
+// static LazyInstance<Foo> gFoo = LAZY_INSTANCE_INIT;
+//
+// In the first case, a hidden static C++ constructor is embedded in the
+// final executable, and executed at *load* *time* to call the Foo::Foo
+// constructor on the gFoo object.
+//
+// On the second case, gFoo will only be initialized lazily, i.e. the first
+// time any code actually tries to access the variable.
+//
+// Note that access is slightly different, i.e.:
+//
+// gFoo.get() returns a reference to the lazy-initialized object.
+// gFoo.ptr() returns a pointer to it.
+// gFoo->Something() is equivalent to doing gFoo.ptr()->Something().
+//
+// 'gFoo' is stored in the .bss section and this doesn't use heap allocation.
+// This class can only be used to perform lazy initialization through the
+// class' default constructor. For more specialized cases, you will have
+// to create a derived class, e.g.:
+//
+// class FoorWithDefaultParams : public Foo {
+// public:
+// FooWithDefaultParams() : Foo(<default-parameters>) {}
+// };
+//
+// LazyInstance<FooWithDefaultParams> gFoo = LAZY_INSTANCE_INIT;
+//
+// The implementation of LazyInstance relies on atomic operations and
+// POD-struct class definitions, i.e. one that doesn't have any constructor,
+// destructor, virtual members, or private ones, and that can be
+// zero-initialized at link time.
+//
+// You can also use LazyInstance<> instances as static local variables,
+// e.g.:
+//
+// Foo* getFooSingleton() {
+// static LazyInstance<Foo> sFoo = LAZY_INSTANCE_INIT;
+// return sFoo.ptr();
+// }
+//
+// This is useful on Windows which doesn't support thread-safe lazy
+// initialization of static C++ local variables, or when the code is
+// compiled with -fno-threadsafe-statics.
+//
+// This class is heavily inspired by Chromium's implementation of the
+// same-named class (see $CHROMIUM/src/base/lazy_instance.h).
+
+// Atomic state variable type. Used to ensure to synchronize concurrent
+// initialization and access without incurring the full cost of a mutex
+// lock/unlock.
+struct LazyInstanceState {
+ enum {
+ STATE_INIT = 0,
+ STATE_CONSTRUCTING = 1,
+ STATE_DONE = 2,
+ };
+
+ bool inInitState();
+ bool needConstruction();
+ void doneConstructing();
+
+#ifdef _WIN32
+ typedef LONG volatile AtomicType;
+#else
+ typedef int volatile AtomicType;
+#endif
+
+ volatile AtomicType mState;
+};
+
+#define LAZY_INSTANCE_STATE_INIT \
+ { ::emugl::internal::LazyInstanceState::STATE_INIT }
+
+} // namespace internal
+
+// LazyInstance template definition, see comment above for usage
+// instructions. It is crucial to make this a POD-struct compatible
+// type [1].
+//
+// [1] http://en.wikipedia.org/wiki/Plain_Old_Data_Structures
+//
+template <class T>
+struct LazyInstance {
+ bool hasInstance() const { return !mState.inInitState(); }
+
+ T& get() const { return *ptr(); }
+
+ T* ptr() const;
+
+ const T* operator->() const { return ptr(); }
+
+ T* operator->() { return ptr(); }
+
+ T& operator*() { return get(); }
+
+ // Really private, do not use.
+ union {
+ mutable internal::LazyInstanceState mState;
+ double mPadding;
+ };
+ mutable char mStorage[sizeof(T)];
+};
+
+// Initialization value, must resolve to all-0 to ensure the object
+// instance is actually placed in the .bss
+#define LAZY_INSTANCE_INIT { { LAZY_INSTANCE_STATE_INIT }, { 0 } }
+
+template <class T>
+T* LazyInstance<T>::ptr() const {
+ if (mState.needConstruction()) {
+ new (mStorage) T();
+ mState.doneConstructing();
+ }
+ return reinterpret_cast<T*>(mStorage);
+}
+
+} // namespace emugl
+
+#endif // EMUGL_COMMON_LAZY_INSTANCE_H
diff --git a/emulator/opengl/shared/emugl/common/lazy_instance_unittest.cpp b/emulator/opengl/shared/emugl/common/lazy_instance_unittest.cpp
new file mode 100644
index 0000000..824845f
--- /dev/null
+++ b/emulator/opengl/shared/emugl/common/lazy_instance_unittest.cpp
@@ -0,0 +1,146 @@
+// 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/lazy_instance.h"
+
+#include "emugl/common/mutex.h"
+#include "emugl/common/testing/test_thread.h"
+
+#include <gtest/gtest.h>
+
+namespace emugl {
+
+namespace {
+
+class Foo {
+public:
+ Foo() : mValue(42) {}
+ int get() const { return mValue; }
+ void set(int value) { mValue = value; }
+ ~Foo() { mValue = 13; }
+private:
+ int mValue;
+};
+
+class StaticCounter {
+public:
+ StaticCounter() {
+ Mutex::AutoLock lock(mMutex);
+ mCounter++;
+ }
+
+ int getValue() const {
+ Mutex::AutoLock lock(mMutex);
+ return mCounter;
+ }
+
+private:
+ static Mutex mMutex;
+ static int mCounter;
+};
+
+// NOTE: This introduces a static C++ constructor for this object file,
+// but that's ok because a LazyInstance<Mutex> should not be used to
+// test the behaviour of LazyInstance :-)
+Mutex StaticCounter::mMutex;
+int StaticCounter::mCounter = 0;
+
+} // namespace
+
+TEST(LazyInstance, HasInstance) {
+ LazyInstance<Foo> foo_instance = LAZY_INSTANCE_INIT;
+ EXPECT_FALSE(foo_instance.hasInstance());
+ EXPECT_FALSE(foo_instance.hasInstance());
+ foo_instance.ptr();
+ EXPECT_TRUE(foo_instance.hasInstance());
+}
+
+TEST(LazyInstance, Simple) {
+ LazyInstance<Foo> foo_instance = LAZY_INSTANCE_INIT;
+ Foo* foo1 = foo_instance.ptr();
+ EXPECT_TRUE(foo1);
+ EXPECT_EQ(42, foo_instance->get());
+ foo1->set(10);
+ EXPECT_EQ(10, foo_instance->get());
+ EXPECT_EQ(foo1, foo_instance.ptr());
+}
+
+// For the following test, launch 1000 threads that each try to get
+// the instance pointer of a lazy instance. Then verify that they're all
+// the same value.
+//
+// The lazy instance has a special constructor that will increment a
+// global counter. This allows us to ensure that it is only called once.
+//
+
+namespace {
+
+// The following is the shared structure between all threads.
+struct MultiState {
+ MultiState(LazyInstance<StaticCounter>* staticCounter) :
+ mMutex(), mStaticCounter(staticCounter), mCount(0) {}
+
+ enum {
+ kMaxThreads = 1000,
+ };
+
+ Mutex mMutex;
+ LazyInstance<StaticCounter>* mStaticCounter;
+ size_t mCount;
+ void* mValues[kMaxThreads];
+ TestThread* mThreads[kMaxThreads];
+};
+
+// The thread function for the test below.
+static void* threadFunc(void* param) {
+ MultiState* state = static_cast<MultiState*>(param);
+ Mutex::AutoLock lock(state->mMutex);
+ if (state->mCount < MultiState::kMaxThreads) {
+ state->mValues[state->mCount++] = state->mStaticCounter->ptr();
+ }
+ return NULL;
+}
+
+} // namespace
+
+TEST(LazyInstance, MultipleThreads) {
+ LazyInstance<StaticCounter> counter_instance = LAZY_INSTANCE_INIT;
+ MultiState state(&counter_instance);
+ const size_t kNumThreads = MultiState::kMaxThreads;
+
+ // Create all threads.
+ for (size_t n = 0; n < kNumThreads; ++n) {
+ state.mThreads[n] = new TestThread(threadFunc, &state);
+ }
+
+ // Wait for their completion.
+ for (size_t n = 0; n < kNumThreads; ++n) {
+ state.mThreads[n]->join();
+ }
+
+ // Now check that the constructor was only called once.
+ EXPECT_EQ(1, counter_instance->getValue());
+
+ // Now compare all the store values, they should be the same.
+ StaticCounter* expectedValue = counter_instance.ptr();
+ for (size_t n = 0; n < kNumThreads; ++n) {
+ EXPECT_EQ(expectedValue, state.mValues[n]) << "For thread " << n;
+ }
+
+ for (size_t n = 0; n < kNumThreads; ++n) {
+ delete state.mThreads[n];
+ }
+}
+
+} // namespace emugl
diff --git a/emulator/opengl/shared/emugl/common/thread_store.cpp b/emulator/opengl/shared/emugl/common/thread_store.cpp
new file mode 100644
index 0000000..ea64c6f
--- /dev/null
+++ b/emulator/opengl/shared/emugl/common/thread_store.cpp
@@ -0,0 +1,242 @@
+// 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"
+
+#ifdef _WIN32
+#include "emugl/common/lazy_instance.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 emugl {
+
+#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(ThreadStore::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) {
+ (*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);
+ ThreadStore::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;
+ ThreadStore::Destructor* mDestructors[kMaxTlsSlots];
+};
+
+LazyInstance<GlobalState> gGlobalState = LAZY_INSTANCE_INIT;
+
+} // namespace
+
+ThreadStore::ThreadStore(Destructor* destroy) {
+ D("Entering this=%p destroy=%p\n", this, destroy);
+ mKey = gGlobalState->registerKey(destroy);
+ D("Exiting this=%p key=%d\n", this, mKey);
+}
+
+ThreadStore::~ThreadStore() {
+ D("Entering this=%p\n", this);
+ GlobalState* state = gGlobalState.ptr();
+ state->unregisterKey(mKey);
+ D("Exiting this=%p\n", this);
+}
+
+void* ThreadStore::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 ThreadStore::set(void* value) {
+ D("Entering this=%p value=%p\n", this, value);
+ gGlobalState->setValue(mKey, value);
+ D("Exiting this=%p\n", this);
+}
+
+// static
+void ThreadStore::OnThreadExit() {
+ gGlobalState->leaveCurrentThread();
+}
+
+#else // !_WIN32
+
+ThreadStore::ThreadStore(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);
+ }
+}
+
+ThreadStore::~ThreadStore() {
+ pthread_key_delete(mKey);
+}
+
+#endif // !_WIN32
+
+} // namespace emugl
diff --git a/emulator/opengl/shared/emugl/common/thread_store.h b/emulator/opengl/shared/emugl/common/thread_store.h
new file mode 100644
index 0000000..5fd08bd
--- /dev/null
+++ b/emulator/opengl/shared/emugl/common/thread_store.h
@@ -0,0 +1,110 @@
+// 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.
+
+#ifndef EMUGL_COMMON_THREAD_STORE_H
+#define EMUGL_COMMON_THREAD_STORE_H
+
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN 1
+# include <windows.h>
+#else
+# include <pthread.h>
+#endif
+
+namespace emugl {
+
+// A class to model storage of thread-specific values, that can be
+// destroyed on thread exit.
+//
+// Note that on Windows, a thread must call OnThreadExit() explicitly
+// here to ensure that the values are probably discarded. This is an
+// unfortunate requirement of the Win32 API, which doesn't support
+// destructors at all.
+//
+// There are various hacks on the web to try to achieve this automatically
+// (e.g. [1]) but they rely on using the Microsoft build tools,
+// which doesn't work for us.
+//
+// Note another important issue with ThreadStore instances: if you create
+// one instance in a shared library, you need to make sure that it is
+// always destroyed before the library is unloaded. Otherwise, future
+// thread exit will likely crash, due to calling a destructor function
+// that is no longer in the process' address space.
+//
+// Finally, destroying an instance does _not_ free the corresponding values,
+// because doing so properly requires coordinating all participating threads,
+// which is impossible to achieve in the most general case. Thus, consider
+// that thread-local values are always leaked on library unload, or on
+// program exit.
+//
+// [1] http://stackoverflow.com/questions/14538159/about-tls-callback-in-windows
+
+class ThreadStore {
+public:
+ // Type of a function used to destroy a thread-specific value that
+ // was previously assigned by calling set().
+ typedef void (Destructor)(void* value);
+
+ // Initialize instance so that is hold keys that must be destroyed
+ // on thread exit by calling |destroy|.
+ explicit ThreadStore(Destructor* destroy);
+
+ // NOTE: Destructor don't free the thread-local values, but are required
+ // to avoid crashes (see note above).
+ ~ThreadStore();
+
+ // Retrieve current thread-specific value from store.
+#ifdef _WIN32
+ void* get() const;
+#else
+ inline void* get() const {
+ return pthread_getspecific(mKey);
+ }
+#endif
+
+ // Set the new thread-specific value.
+#ifdef _WIN32
+ void set(void* value);
+#else
+ inline void set(void* value) {
+ pthread_setspecific(mKey, value);
+ }
+#endif
+
+#ifdef _WIN32
+ // Each thread should call this function on exit to ensure that
+ // all corresponding TLS values are properly freed.
+ static void OnThreadExit();
+#else
+ // Nothing to do on Posix.
+ static inline void OnThreadExit() {}
+#endif
+
+private:
+ // Ensure you can't create an empty ThreadStore instance, or simply
+ // copy it in any way.
+ ThreadStore();
+ ThreadStore(const ThreadStore&);
+ ThreadStore& operator=(const ThreadStore&);
+
+#ifdef _WIN32
+ int mKey;
+#else
+ pthread_key_t mKey;
+#endif
+};
+
+} // namespace emugl
+
+#endif // EMUGL_COMMON_THREAD_STORE_H
diff --git a/emulator/opengl/shared/emugl/common/thread_store_unittest.cpp b/emulator/opengl/shared/emugl/common/thread_store_unittest.cpp
new file mode 100644
index 0000000..6b5dddb
--- /dev/null
+++ b/emulator/opengl/shared/emugl/common/thread_store_unittest.cpp
@@ -0,0 +1,146 @@
+// 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 <gtest/gtest.h>
+
+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<ThreadStore*>(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