aboutsummaryrefslogtreecommitdiffstats
path: root/emulator
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@google.com>2014-07-09 15:52:13 +0200
committerDavid 'Digit' Turner <digit@google.com>2014-07-09 18:16:17 +0200
commit40cc9f9d50e998b0ce965b261ecf4d206a58618e (patch)
tree66272ee1b92a2267cc832618b43a59d650099e54 /emulator
parent242a075e3256fc7f369f0e5f93fd2ae91c255774 (diff)
downloadsdk-40cc9f9d50e998b0ce965b261ecf4d206a58618e.zip
sdk-40cc9f9d50e998b0ce965b261ecf4d206a58618e.tar.gz
sdk-40cc9f9d50e998b0ce965b261ecf4d206a58618e.tar.bz2
emulator/opengl: refactor Thread class.
+ Add unit test. Change-Id: I26d84cf590f6bfff3304be3f42dc0196bcc7f6d2
Diffstat (limited to 'emulator')
-rw-r--r--emulator/opengl/host/libs/libOpenglRender/RenderServer.cpp5
-rw-r--r--emulator/opengl/host/libs/libOpenglRender/RenderServer.h6
-rw-r--r--emulator/opengl/host/libs/libOpenglRender/RenderThread.cpp4
-rw-r--r--emulator/opengl/host/libs/libOpenglRender/RenderThread.h6
-rw-r--r--emulator/opengl/host/libs/libOpenglRender/render_api.cpp3
-rw-r--r--emulator/opengl/host/renderer/main.cpp2
-rw-r--r--emulator/opengl/shared/OpenglOsUtils/Android.mk4
-rw-r--r--emulator/opengl/shared/OpenglOsUtils/osThread.h60
-rw-r--r--emulator/opengl/shared/OpenglOsUtils/osThreadUnix.cpp98
-rw-r--r--emulator/opengl/shared/OpenglOsUtils/osThreadWin.cpp104
-rw-r--r--emulator/opengl/shared/emugl/common/Android.mk7
-rw-r--r--emulator/opengl/shared/emugl/common/thread.h102
-rw-r--r--emulator/opengl/shared/emugl/common/thread_pthread.cpp114
-rw-r--r--emulator/opengl/shared/emugl/common/thread_unittest.cpp109
-rw-r--r--emulator/opengl/shared/emugl/common/thread_win32.cpp122
15 files changed, 467 insertions, 279 deletions
diff --git a/emulator/opengl/host/libs/libOpenglRender/RenderServer.cpp b/emulator/opengl/host/libs/libOpenglRender/RenderServer.cpp
index de28f8f..53c65ee 100644
--- a/emulator/opengl/host/libs/libOpenglRender/RenderServer.cpp
+++ b/emulator/opengl/host/libs/libOpenglRender/RenderServer.cpp
@@ -77,7 +77,7 @@ RenderServer *RenderServer::create(char* addr, size_t addrLen)
return server;
}
-int RenderServer::Main()
+intptr_t RenderServer::main()
{
RenderThreadsSet threads;
@@ -146,8 +146,7 @@ int RenderServer::Main()
for (RenderThreadsSet::iterator t = threads.begin();
t != threads.end();
t++) {
- int exitStatus;
- (*t)->wait(&exitStatus);
+ (*t)->wait(NULL);
delete (*t);
}
threads.clear();
diff --git a/emulator/opengl/host/libs/libOpenglRender/RenderServer.h b/emulator/opengl/host/libs/libOpenglRender/RenderServer.h
index ff63c94..8be8a17 100644
--- a/emulator/opengl/host/libs/libOpenglRender/RenderServer.h
+++ b/emulator/opengl/host/libs/libOpenglRender/RenderServer.h
@@ -18,15 +18,15 @@
#include "SocketStream.h"
#include "emugl/common/mutex.h"
-#include "osThread.h"
+#include "emugl/common/thread.h"
-class RenderServer : public osUtils::Thread
+class RenderServer : public emugl::Thread
{
public:
static RenderServer *create(char* addr, size_t addrLen);
virtual ~RenderServer();
- virtual int Main();
+ virtual intptr_t main();
bool isExiting() const { return m_exiting; }
diff --git a/emulator/opengl/host/libs/libOpenglRender/RenderThread.cpp b/emulator/opengl/host/libs/libOpenglRender/RenderThread.cpp
index 19d6c1f..3dcfdb5 100644
--- a/emulator/opengl/host/libs/libOpenglRender/RenderThread.cpp
+++ b/emulator/opengl/host/libs/libOpenglRender/RenderThread.cpp
@@ -26,7 +26,7 @@
#define STREAM_BUFFER_SIZE 4*1024*1024
RenderThread::RenderThread(IOStream *stream, emugl::Mutex *lock) :
- osUtils::Thread(),
+ emugl::Thread(),
m_lock(lock),
m_stream(stream),
m_finished(false)
@@ -43,7 +43,7 @@ RenderThread *RenderThread::create(IOStream *p_stream, emugl::Mutex *lock)
return new RenderThread(p_stream, lock);
}
-int RenderThread::Main()
+intptr_t RenderThread::main()
{
RenderThreadInfo tInfo;
diff --git a/emulator/opengl/host/libs/libOpenglRender/RenderThread.h b/emulator/opengl/host/libs/libOpenglRender/RenderThread.h
index e94d782..c1f919a 100644
--- a/emulator/opengl/host/libs/libOpenglRender/RenderThread.h
+++ b/emulator/opengl/host/libs/libOpenglRender/RenderThread.h
@@ -21,9 +21,9 @@
#include "renderControl_dec.h"
#include "emugl/common/mutex.h"
-#include "osThread.h"
+#include "emugl/common/thread.h"
-class RenderThread : public osUtils::Thread
+class RenderThread : public emugl::Thread
{
public:
static RenderThread* create(IOStream* p_stream, emugl::Mutex* mutex);
@@ -32,7 +32,7 @@ public:
private:
RenderThread(IOStream* p_stream, emugl::Mutex* mutex);
- virtual int Main();
+ virtual intptr_t main();
private:
emugl::Mutex *m_lock;
diff --git a/emulator/opengl/host/libs/libOpenglRender/render_api.cpp b/emulator/opengl/host/libs/libOpenglRender/render_api.cpp
index 5c9ffb1..42df668 100644
--- a/emulator/opengl/host/libs/libOpenglRender/render_api.cpp
+++ b/emulator/opengl/host/libs/libOpenglRender/render_api.cpp
@@ -243,8 +243,7 @@ int stopOpenGLRenderer(void)
else if (s_renderThread) {
// wait for the thread to exit
- int status;
- ret = s_renderThread->wait(&status);
+ ret = s_renderThread->wait(NULL);
delete s_renderThread;
s_renderThread = NULL;
diff --git a/emulator/opengl/host/renderer/main.cpp b/emulator/opengl/host/renderer/main.cpp
index 2c3f8e2..470e692 100644
--- a/emulator/opengl/host/renderer/main.cpp
+++ b/emulator/opengl/host/renderer/main.cpp
@@ -141,7 +141,7 @@ int main(int argc, char *argv[])
//
// run the server listener loop
//
- server->Main();
+ server->main();
#else
//
// on windows we need to handle messages for the
diff --git a/emulator/opengl/shared/OpenglOsUtils/Android.mk b/emulator/opengl/shared/OpenglOsUtils/Android.mk
index 42b57dd..4b1b05e 100644
--- a/emulator/opengl/shared/OpenglOsUtils/Android.mk
+++ b/emulator/opengl/shared/OpenglOsUtils/Android.mk
@@ -15,12 +15,12 @@ host_common_INCLUDES := $(LOCAL_PATH)
ifeq ($(HOST_OS),windows)
host_common_SRC_FILES += \
osProcessWin.cpp \
- osThreadWin.cpp
+
host_common_LDLIBS += -lws2_32 -lpsapi
else
host_common_SRC_FILES += \
osProcessUnix.cpp \
- osThreadUnix.cpp
+
host_common_LDLIBS += -ldl
endif
diff --git a/emulator/opengl/shared/OpenglOsUtils/osThread.h b/emulator/opengl/shared/OpenglOsUtils/osThread.h
deleted file mode 100644
index 970396d..0000000
--- a/emulator/opengl/shared/OpenglOsUtils/osThread.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
-* Copyright (C) 2011 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 _OSUTILS_THREAD_H
-#define _OSUTILS_THREAD_H
-
-#ifdef _WIN32
-#include <windows.h>
-#else // !WIN32
-#include <pthread.h>
-#endif
-
-namespace osUtils {
-
-class Thread
-{
-public:
- Thread();
- virtual ~Thread();
-
- virtual int Main() = 0;
-
- bool start();
- bool wait(int *exitStatus);
- bool trywait(int *exitStatus);
-
-private:
-#ifdef _WIN32
- static DWORD WINAPI thread_main(void *p_arg);
-#else // !WIN32
- static void* thread_main(void *p_arg);
-#endif
-
-private:
-#ifdef _WIN32
- HANDLE m_thread;
- DWORD m_threadId;
-#else // !WIN32
- pthread_t m_thread;
- int m_exitStatus;
- pthread_mutex_t m_lock;
-#endif
- bool m_isRunning;
-};
-
-} // of namespace osUtils
-
-#endif
diff --git a/emulator/opengl/shared/OpenglOsUtils/osThreadUnix.cpp b/emulator/opengl/shared/OpenglOsUtils/osThreadUnix.cpp
deleted file mode 100644
index ef2bebc..0000000
--- a/emulator/opengl/shared/OpenglOsUtils/osThreadUnix.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
-* Copyright (C) 2011 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 "osThread.h"
-
-#include "emugl/common/thread_store.h"
-
-#include <stdint.h>
-
-namespace osUtils {
-
-Thread::Thread() :
- m_thread((pthread_t)NULL),
- m_exitStatus(0),
- m_isRunning(false)
-{
- pthread_mutex_init(&m_lock, NULL);
-}
-
-Thread::~Thread()
-{
- pthread_mutex_destroy(&m_lock);
-}
-
-bool
-Thread::start()
-{
- pthread_mutex_lock(&m_lock);
- m_isRunning = true;
- int ret = pthread_create(&m_thread, NULL, Thread::thread_main, this);
- if(ret) {
- m_isRunning = false;
- }
- pthread_mutex_unlock(&m_lock);
- return m_isRunning;
-}
-
-bool
-Thread::wait(int *exitStatus)
-{
- if (!m_isRunning) {
- return false;
- }
-
- void *retval;
- if (pthread_join(m_thread,&retval)) {
- return false;
- }
-
- if (exitStatus) {
- *exitStatus = (int)(uintptr_t)retval;
- }
- return true;
-}
-
-bool
-Thread::trywait(int *exitStatus)
-{
- bool ret = false;
-
- pthread_mutex_lock(&m_lock);
- if (!m_isRunning) {
- *exitStatus = m_exitStatus;
- ret = true;
- }
- pthread_mutex_unlock(&m_lock);
- return ret;
-}
-
-void *
-Thread::thread_main(void *p_arg)
-{
- Thread *self = (Thread *)p_arg;
- int ret = self->Main();
-
- pthread_mutex_lock(&self->m_lock);
- self->m_isRunning = false;
- self->m_exitStatus = ret;
- pthread_mutex_unlock(&self->m_lock);
-
- ::emugl::ThreadStore::OnThreadExit();
- return (void*)(uintptr_t)ret;
-}
-
-} // of namespace osUtils
-
diff --git a/emulator/opengl/shared/OpenglOsUtils/osThreadWin.cpp b/emulator/opengl/shared/OpenglOsUtils/osThreadWin.cpp
deleted file mode 100644
index 1a30b97..0000000
--- a/emulator/opengl/shared/OpenglOsUtils/osThreadWin.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
-* Copyright (C) 2011 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 "osThread.h"
-
-#include "emugl/common/thread_store.h"
-
-namespace osUtils {
-
-Thread::Thread() :
- m_thread(NULL),
- m_threadId(0),
- m_isRunning(false)
-{
-}
-
-Thread::~Thread()
-{
- if(m_thread) {
- CloseHandle(m_thread);
- }
-}
-
-bool
-Thread::start()
-{
- m_isRunning = true;
- m_thread = CreateThread(NULL, 0, &Thread::thread_main, this, 0, &m_threadId);
- if(!m_thread) {
- m_isRunning = false;
- }
- return m_isRunning;
-}
-
-bool
-Thread::wait(int *exitStatus)
-{
- if (!m_isRunning) {
- return false;
- }
-
- if(WaitForSingleObject(m_thread, INFINITE) == WAIT_FAILED) {
- return false;
- }
-
- DWORD retval;
- if (!GetExitCodeThread(m_thread,&retval)) {
- return false;
- }
-
- m_isRunning = 0;
-
- if (exitStatus) {
- *exitStatus = retval;
- }
- return true;
-}
-
-bool
-Thread::trywait(int *exitStatus)
-{
- if (!m_isRunning) {
- return false;
- }
-
- if(WaitForSingleObject(m_thread, 0) == WAIT_OBJECT_0) {
-
- DWORD retval;
- if (!GetExitCodeThread(m_thread,&retval)) {
- return true;
- }
-
- if (exitStatus) {
- *exitStatus = retval;
- }
- return true;
- }
-
- return false;
-}
-
-DWORD WINAPI
-Thread::thread_main(void *p_arg)
-{
- Thread *self = (Thread *)p_arg;
- int ret = self->Main();
- self->m_isRunning = false;
- ::emugl::ThreadStore::OnThreadExit();
- return ret;
-}
-
-} // of namespace osUtils
diff --git a/emulator/opengl/shared/emugl/common/Android.mk b/emulator/opengl/shared/emugl/common/Android.mk
index e75f887..fb9ab57 100644
--- a/emulator/opengl/shared/emugl/common/Android.mk
+++ b/emulator/opengl/shared/emugl/common/Android.mk
@@ -17,8 +17,12 @@ commonSources := \
host_commonSources := $(commonSources)
host_commonLdLibs := -lstdc++
+
ifneq (windows,$(HOST_OS))
- host_commonLdLibs += -ldl
+ host_commonSources += thread_pthread.cpp
+ host_commonLdLibs += -ldl -lpthread
+else
+ host_commonSources += thread_win32.cpp
endif
$(call emugl-begin-host-static-library,libemugl_common)
@@ -47,6 +51,7 @@ host_commonSources := \
shared_library_unittest.cpp \
smart_ptr_unittest.cpp \
thread_store_unittest.cpp \
+ thread_unittest.cpp \
$(call emugl-begin-host-executable,emugl_common_host_unittests)
LOCAL_SRC_FILES := $(host_commonSources)
diff --git a/emulator/opengl/shared/emugl/common/thread.h b/emulator/opengl/shared/emugl/common/thread.h
new file mode 100644
index 0000000..d0f7d20
--- /dev/null
+++ b/emulator/opengl/shared/emugl/common/thread.h
@@ -0,0 +1,102 @@
+// 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_H
+#define EMUGL_COMMON_THREAD_H
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <pthread.h>
+#endif
+
+#include <stdint.h>
+
+namespace emugl {
+
+// Wrapper class for platform-specific threads.
+// To create your own thread, define a sub-class of emugl::Thread
+// and override its main() method.
+//
+// For example:
+//
+// class MyThread : public emugl::Thread {
+// public:
+// MyThread() : Thread() {}
+//
+// virtual intptr_t main() {
+// ... main thread loop implementation
+// return 0;
+// }
+// };
+//
+// ...
+//
+// // Create new instance, but does not start it.
+// MyThread* thread = new MyThread();
+//
+// // Start the thread.
+// thread->start();
+//
+// // Wait for thread completion, and gets result into |exitStatus|.
+// int exitStatus;
+// thread->wait(&exitStatus);
+//
+class Thread {
+public:
+ // Public constructor.
+ Thread();
+
+ // Virtual destructor.
+ virtual ~Thread();
+
+ // Override this method in your own thread sub-classes. This will
+ // be called when start() is invoked on the Thread instance.
+ virtual intptr_t main() = 0;
+
+ // Start a thread instance. Return true on success, false otherwise
+ // (e.g. if the thread was already started or terminated).
+ bool start();
+
+ // Wait for thread termination and retrieve exist status into
+ // |*exitStatus|. Return true on success, false otherwise.
+ // NOTE: |exitStatus| can be NULL.
+ bool wait(intptr_t *exitStatus);
+
+ // Check whether a thread has terminated. On success, return true
+ // and sets |*exitStatus|. On failure, return false.
+ // NOTE: |exitStatus| can be NULL.
+ bool tryWait(intptr_t *exitStatus);
+
+private:
+#ifdef _WIN32
+ static DWORD WINAPI thread_main(void* arg);
+
+ HANDLE mThread;
+ DWORD mThreadId;
+ CRITICAL_SECTION mLock;
+#else // !WIN32
+ static void* thread_main(void* arg);
+
+ pthread_t mThread;
+ pthread_mutex_t mLock;
+#endif
+ intptr_t mExitStatus;
+ bool mIsRunning;
+};
+
+} // namespace emugl
+
+#endif // EMUGL_COMMON_THREAD_H
+
diff --git a/emulator/opengl/shared/emugl/common/thread_pthread.cpp b/emulator/opengl/shared/emugl/common/thread_pthread.cpp
new file mode 100644
index 0000000..5dbc3ab
--- /dev/null
+++ b/emulator/opengl/shared/emugl/common/thread_pthread.cpp
@@ -0,0 +1,114 @@
+// 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.h"
+
+#include "emugl/common/thread_store.h"
+
+#include <stdio.h>
+
+namespace emugl {
+
+namespace {
+
+class ScopedLocker {
+public:
+ ScopedLocker(pthread_mutex_t* mutex) : mMutex(mutex) {
+ pthread_mutex_lock(mMutex);
+ }
+
+ ~ScopedLocker() {
+ pthread_mutex_unlock(mMutex);
+ }
+private:
+ pthread_mutex_t* mMutex;
+};
+
+} // namespace
+
+Thread::Thread() :
+ mThread((pthread_t)NULL),
+ mExitStatus(0),
+ mIsRunning(false) {
+ pthread_mutex_init(&mLock, NULL);
+}
+
+Thread::~Thread() {
+ pthread_mutex_destroy(&mLock);
+}
+
+bool Thread::start() {
+ bool ret = true;
+ pthread_mutex_lock(&mLock);
+ mIsRunning = true;
+ if (pthread_create(&mThread, NULL, thread_main, this)) {
+ ret = false;
+ mIsRunning = false;
+ }
+ pthread_mutex_unlock(&mLock);
+ return ret;
+}
+
+bool Thread::wait(intptr_t *exitStatus) {
+ {
+ ScopedLocker locker(&mLock);
+ if (!mIsRunning) {
+ // Thread already stopped.
+ if (exitStatus) {
+ *exitStatus = mExitStatus;
+ }
+ return true;
+ }
+ }
+
+ // NOTE: Do not hold the lock when waiting for the thread to ensure
+ // it can update mIsRunning and mExitStatus properly in thread_main
+ // without blocking.
+ void *retval;
+ if (pthread_join(mThread, &retval)) {
+ return false;
+ }
+ if (exitStatus) {
+ *exitStatus = (intptr_t)retval;
+ }
+ return true;
+}
+
+bool Thread::tryWait(intptr_t *exitStatus) {
+ ScopedLocker locker(&mLock);
+ if (!mIsRunning) {
+ return false;
+ }
+ if (exitStatus) {
+ *exitStatus = mExitStatus;
+ }
+ return true;
+}
+
+// static
+void* Thread::thread_main(void *arg) {
+ Thread* self = reinterpret_cast<Thread*>(arg);
+ intptr_t ret = self->main();
+
+ pthread_mutex_lock(&self->mLock);
+ self->mIsRunning = false;
+ self->mExitStatus = ret;
+ pthread_mutex_unlock(&self->mLock);
+
+ ::emugl::ThreadStore::OnThreadExit();
+
+ return (void*)ret;
+}
+
+} // namespace emugl
diff --git a/emulator/opengl/shared/emugl/common/thread_unittest.cpp b/emulator/opengl/shared/emugl/common/thread_unittest.cpp
new file mode 100644
index 0000000..db1931f
--- /dev/null
+++ b/emulator/opengl/shared/emugl/common/thread_unittest.cpp
@@ -0,0 +1,109 @@
+// 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.h"
+
+#include "emugl/common/mutex.h"
+
+#include <gtest/gtest.h>
+
+namespace emugl {
+
+namespace {
+
+// A simple thread instance that does nothing at all and exits immediately.
+class EmptyThread : public ::emugl::Thread {
+public:
+ intptr_t main() { return 42; }
+};
+
+class CountingThread : public ::emugl::Thread {
+public:
+ class State {
+ public:
+ State() : mLock(), mCount(0) {}
+ ~State() {}
+
+ void increment() {
+ mLock.lock();
+ mCount++;
+ mLock.unlock();
+ }
+
+ int count() const {
+ int ret;
+ mLock.lock();
+ ret = mCount;
+ mLock.unlock();
+ return ret;
+ }
+
+ private:
+ mutable Mutex mLock;
+ int mCount;
+ };
+
+ CountingThread(State* state) : mState(state) {}
+
+ intptr_t main() {
+ mState->increment();
+ return 0;
+ }
+
+private:
+ State* mState;
+};
+
+} // namespace
+
+TEST(ThreadTest, SimpleThread) {
+ Thread* thread = new EmptyThread();
+ EXPECT_TRUE(thread);
+ EXPECT_TRUE(thread->start());
+ intptr_t status;
+ EXPECT_TRUE(thread->wait(&status));
+ EXPECT_EQ(42, status);
+}
+
+TEST(ThreadTest, MultipleThreads) {
+ CountingThread::State state;
+ const size_t kMaxThreads = 100;
+ Thread* threads[kMaxThreads];
+
+ // Create all threads.
+ for (size_t n = 0; n < kMaxThreads; ++n) {
+ threads[n] = new CountingThread(&state);
+ EXPECT_TRUE(threads[n]) << "thread " << n;
+ }
+
+ // Start them all.
+ for (size_t n = 0; n < kMaxThreads; ++n) {
+ EXPECT_TRUE(threads[n]->start()) << "thread " << n;
+ }
+
+ // Wait for them all.
+ for (size_t n = 0; n < kMaxThreads; ++n) {
+ EXPECT_TRUE(threads[n]->wait(NULL)) << "thread " << n;
+ }
+
+ // Check state.
+ EXPECT_EQ((int)kMaxThreads, state.count());
+
+ // Delete them all.
+ for (size_t n = 0; n < kMaxThreads; ++n) {
+ delete threads[n];
+ }
+}
+
+} // namespace emugl
diff --git a/emulator/opengl/shared/emugl/common/thread_win32.cpp b/emulator/opengl/shared/emugl/common/thread_win32.cpp
new file mode 100644
index 0000000..f348ca8
--- /dev/null
+++ b/emulator/opengl/shared/emugl/common/thread_win32.cpp
@@ -0,0 +1,122 @@
+// 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.h"
+
+#include "emugl/common/thread_store.h"
+
+namespace emugl {
+
+namespace {
+
+class ScopedLocker {
+public:
+ ScopedLocker(CRITICAL_SECTION* section) : mSection(section) {
+ EnterCriticalSection(mSection);
+ }
+
+ ~ScopedLocker() {
+ LeaveCriticalSection(mSection);
+ }
+private:
+ CRITICAL_SECTION* mSection;
+};
+
+} // namespace
+
+Thread::Thread() :
+ mThread(INVALID_HANDLE_VALUE),
+ mThreadId(0),
+ mExitStatus(0),
+ mIsRunning(false) {
+ InitializeCriticalSection(&mLock);
+}
+
+Thread::~Thread() {
+ if(mThread != INVALID_HANDLE_VALUE) {
+ CloseHandle(mThread);
+ }
+ DeleteCriticalSection(&mLock);
+}
+
+bool Thread::start() {
+ ScopedLocker locker(&mLock);
+
+ bool ret = true;
+ mIsRunning = true;
+ mThread = CreateThread(NULL, 0, &Thread::thread_main, this, 0, &mThreadId);
+ if (!mThread) {
+ ret = false;
+ mIsRunning = false;
+ }
+ return ret;
+}
+
+bool Thread::wait(intptr_t* exitStatus) {
+ {
+ ScopedLocker locker(&mLock);
+ if (!mIsRunning) {
+ // Thread already stopped.
+ if (exitStatus) {
+ *exitStatus = mExitStatus;
+ }
+ return true;
+ }
+ }
+
+ // NOTE: Do not hold lock during wait to aloow thread_main to
+ // properly update mIsRunning and mExitStatus on thread exit.
+ if (WaitForSingleObject(mThread, INFINITE) == WAIT_FAILED) {
+ return false;
+ }
+
+ if (exitStatus) {
+ ScopedLocker locker(&mLock);
+ *exitStatus = mExitStatus;
+ }
+ return true;
+}
+
+bool Thread::tryWait(intptr_t* exitStatus) {
+ ScopedLocker locker(&mLock);
+
+ if (!mIsRunning ||
+ WaitForSingleObject(mThread, 0) != WAIT_OBJECT_0) {
+ return false;
+ }
+
+ if (exitStatus) {
+ *exitStatus = mExitStatus;
+ }
+ return true;
+}
+
+// static
+DWORD WINAPI Thread::thread_main(void *arg)
+{
+ Thread* self = reinterpret_cast<Thread*>(arg);
+ intptr_t ret = self->main();
+
+ EnterCriticalSection(&self->mLock);
+ self->mIsRunning = false;
+ self->mExitStatus = ret;
+ LeaveCriticalSection(&self->mLock);
+
+ // Ensure all thread-local values are released for this thread.
+ ::emugl::ThreadStore::OnThreadExit();
+
+ return static_cast<DWORD>(ret);
+}
+
+} // namespace emugl