diff options
author | Alex Ray <aray@google.com> | 2013-08-02 14:40:08 -0700 |
---|---|---|
committer | Alex Ray <aray@google.com> | 2013-08-02 14:40:08 -0700 |
commit | d98e07fdf9c338589f263c47ce5c844ed43efad5 (patch) | |
tree | d4ff9849df225df1e4c46386fdabe30407ba5513 /libutils/Threads.cpp | |
parent | be06210c508d5878dcc7d185e5613f4c7e38dfe8 (diff) | |
download | system_core-d98e07fdf9c338589f263c47ce5c844ed43efad5.zip system_core-d98e07fdf9c338589f263c47ce5c844ed43efad5.tar.gz system_core-d98e07fdf9c338589f263c47ce5c844ed43efad5.tar.bz2 |
move libs/utils to libutils
Change-Id: I6cf4268599460791414882f91eeb88a992fbd29d
Diffstat (limited to 'libutils/Threads.cpp')
-rw-r--r-- | libutils/Threads.cpp | 878 |
1 files changed, 878 insertions, 0 deletions
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp new file mode 100644 index 0000000..ff74914 --- /dev/null +++ b/libutils/Threads.cpp @@ -0,0 +1,878 @@ +/* + * Copyright (C) 2007 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. + */ + +// #define LOG_NDEBUG 0 +#define LOG_TAG "libutils.threads" + +#include <utils/threads.h> +#include <utils/Log.h> + +#include <cutils/sched_policy.h> + +#include <stdio.h> +#include <stdlib.h> +#include <memory.h> +#include <errno.h> +#include <assert.h> +#include <unistd.h> + +#if defined(HAVE_PTHREADS) +# include <pthread.h> +# include <sched.h> +# include <sys/resource.h> +#ifdef HAVE_ANDROID_OS +# include <bionic_pthread.h> +#endif +#elif defined(HAVE_WIN32_THREADS) +# include <windows.h> +# include <stdint.h> +# include <process.h> +# define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW +#endif + +#if defined(HAVE_PRCTL) +#include <sys/prctl.h> +#endif + +/* + * =========================================================================== + * Thread wrappers + * =========================================================================== + */ + +using namespace android; + +// ---------------------------------------------------------------------------- +#if defined(HAVE_PTHREADS) +// ---------------------------------------------------------------------------- + +/* + * Create and run a new thread. + * + * We create it "detached", so it cleans up after itself. + */ + +typedef void* (*android_pthread_entry)(void*); + +struct thread_data_t { + thread_func_t entryFunction; + void* userData; + int priority; + char * threadName; + + // we use this trampoline when we need to set the priority with + // nice/setpriority, and name with prctl. + static int trampoline(const thread_data_t* t) { + thread_func_t f = t->entryFunction; + void* u = t->userData; + int prio = t->priority; + char * name = t->threadName; + delete t; + setpriority(PRIO_PROCESS, 0, prio); + if (prio >= ANDROID_PRIORITY_BACKGROUND) { + set_sched_policy(0, SP_BACKGROUND); + } else { + set_sched_policy(0, SP_FOREGROUND); + } + + if (name) { + androidSetThreadName(name); + free(name); + } + return f(u); + } +}; + +void androidSetThreadName(const char* name) { +#if defined(HAVE_PRCTL) + // Mac OS doesn't have this, and we build libutil for the host too + int hasAt = 0; + int hasDot = 0; + const char *s = name; + while (*s) { + if (*s == '.') hasDot = 1; + else if (*s == '@') hasAt = 1; + s++; + } + int len = s - name; + if (len < 15 || hasAt || !hasDot) { + s = name; + } else { + s = name + len - 15; + } + prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0); +#endif +} + +int androidCreateRawThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId) +{ + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + +#ifdef HAVE_ANDROID_OS /* valgrind is rejecting RT-priority create reqs */ + if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) { + // Now that the pthread_t has a method to find the associated + // android_thread_id_t (pid) from pthread_t, it would be possible to avoid + // this trampoline in some cases as the parent could set the properties + // for the child. However, there would be a race condition because the + // child becomes ready immediately, and it doesn't work for the name. + // prctl(PR_SET_NAME) only works for self; prctl(PR_SET_THREAD_NAME) was + // proposed but not yet accepted. + thread_data_t* t = new thread_data_t; + t->priority = threadPriority; + t->threadName = threadName ? strdup(threadName) : NULL; + t->entryFunction = entryFunction; + t->userData = userData; + entryFunction = (android_thread_func_t)&thread_data_t::trampoline; + userData = t; + } +#endif + + if (threadStackSize) { + pthread_attr_setstacksize(&attr, threadStackSize); + } + + errno = 0; + pthread_t thread; + int result = pthread_create(&thread, &attr, + (android_pthread_entry)entryFunction, userData); + pthread_attr_destroy(&attr); + if (result != 0) { + ALOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n" + "(android threadPriority=%d)", + entryFunction, result, errno, threadPriority); + return 0; + } + + // Note that *threadID is directly available to the parent only, as it is + // assigned after the child starts. Use memory barrier / lock if the child + // or other threads also need access. + if (threadId != NULL) { + *threadId = (android_thread_id_t)thread; // XXX: this is not portable + } + return 1; +} + +#ifdef HAVE_ANDROID_OS +static pthread_t android_thread_id_t_to_pthread(android_thread_id_t thread) +{ + return (pthread_t) thread; +} +#endif + +android_thread_id_t androidGetThreadId() +{ + return (android_thread_id_t)pthread_self(); +} + +// ---------------------------------------------------------------------------- +#elif defined(HAVE_WIN32_THREADS) +// ---------------------------------------------------------------------------- + +/* + * Trampoline to make us __stdcall-compliant. + * + * We're expected to delete "vDetails" when we're done. + */ +struct threadDetails { + int (*func)(void*); + void* arg; +}; +static __stdcall unsigned int threadIntermediary(void* vDetails) +{ + struct threadDetails* pDetails = (struct threadDetails*) vDetails; + int result; + + result = (*(pDetails->func))(pDetails->arg); + + delete pDetails; + + ALOG(LOG_VERBOSE, "thread", "thread exiting\n"); + return (unsigned int) result; +} + +/* + * Create and run a new thread. + */ +static bool doCreateThread(android_thread_func_t fn, void* arg, android_thread_id_t *id) +{ + HANDLE hThread; + struct threadDetails* pDetails = new threadDetails; // must be on heap + unsigned int thrdaddr; + + pDetails->func = fn; + pDetails->arg = arg; + +#if defined(HAVE__BEGINTHREADEX) + hThread = (HANDLE) _beginthreadex(NULL, 0, threadIntermediary, pDetails, 0, + &thrdaddr); + if (hThread == 0) +#elif defined(HAVE_CREATETHREAD) + hThread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE) threadIntermediary, + (void*) pDetails, 0, (DWORD*) &thrdaddr); + if (hThread == NULL) +#endif + { + ALOG(LOG_WARN, "thread", "WARNING: thread create failed\n"); + return false; + } + +#if defined(HAVE_CREATETHREAD) + /* close the management handle */ + CloseHandle(hThread); +#endif + + if (id != NULL) { + *id = (android_thread_id_t)thrdaddr; + } + + return true; +} + +int androidCreateRawThreadEtc(android_thread_func_t fn, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId) +{ + return doCreateThread( fn, userData, threadId); +} + +android_thread_id_t androidGetThreadId() +{ + return (android_thread_id_t)GetCurrentThreadId(); +} + +// ---------------------------------------------------------------------------- +#else +#error "Threads not supported" +#endif + +// ---------------------------------------------------------------------------- + +int androidCreateThread(android_thread_func_t fn, void* arg) +{ + return createThreadEtc(fn, arg); +} + +int androidCreateThreadGetID(android_thread_func_t fn, void *arg, android_thread_id_t *id) +{ + return createThreadEtc(fn, arg, "android:unnamed_thread", + PRIORITY_DEFAULT, 0, id); +} + +static android_create_thread_fn gCreateThreadFn = androidCreateRawThreadEtc; + +int androidCreateThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId) +{ + return gCreateThreadFn(entryFunction, userData, threadName, + threadPriority, threadStackSize, threadId); +} + +void androidSetCreateThreadFunc(android_create_thread_fn func) +{ + gCreateThreadFn = func; +} + +pid_t androidGetTid() +{ +#ifdef HAVE_GETTID + return gettid(); +#else + return getpid(); +#endif +} + +#ifdef HAVE_ANDROID_OS +int androidSetThreadPriority(pid_t tid, int pri) +{ + int rc = 0; + +#if defined(HAVE_PTHREADS) + int lasterr = 0; + + if (pri >= ANDROID_PRIORITY_BACKGROUND) { + rc = set_sched_policy(tid, SP_BACKGROUND); + } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) { + rc = set_sched_policy(tid, SP_FOREGROUND); + } + + if (rc) { + lasterr = errno; + } + + if (setpriority(PRIO_PROCESS, tid, pri) < 0) { + rc = INVALID_OPERATION; + } else { + errno = lasterr; + } +#endif + + return rc; +} + +int androidGetThreadPriority(pid_t tid) { +#if defined(HAVE_PTHREADS) + return getpriority(PRIO_PROCESS, tid); +#else + return ANDROID_PRIORITY_NORMAL; +#endif +} + +#endif + +namespace android { + +/* + * =========================================================================== + * Mutex class + * =========================================================================== + */ + +#if defined(HAVE_PTHREADS) +// implemented as inlines in threads.h +#elif defined(HAVE_WIN32_THREADS) + +Mutex::Mutex() +{ + HANDLE hMutex; + + assert(sizeof(hMutex) == sizeof(mState)); + + hMutex = CreateMutex(NULL, FALSE, NULL); + mState = (void*) hMutex; +} + +Mutex::Mutex(const char* name) +{ + // XXX: name not used for now + HANDLE hMutex; + + assert(sizeof(hMutex) == sizeof(mState)); + + hMutex = CreateMutex(NULL, FALSE, NULL); + mState = (void*) hMutex; +} + +Mutex::Mutex(int type, const char* name) +{ + // XXX: type and name not used for now + HANDLE hMutex; + + assert(sizeof(hMutex) == sizeof(mState)); + + hMutex = CreateMutex(NULL, FALSE, NULL); + mState = (void*) hMutex; +} + +Mutex::~Mutex() +{ + CloseHandle((HANDLE) mState); +} + +status_t Mutex::lock() +{ + DWORD dwWaitResult; + dwWaitResult = WaitForSingleObject((HANDLE) mState, INFINITE); + return dwWaitResult != WAIT_OBJECT_0 ? -1 : NO_ERROR; +} + +void Mutex::unlock() +{ + if (!ReleaseMutex((HANDLE) mState)) + ALOG(LOG_WARN, "thread", "WARNING: bad result from unlocking mutex\n"); +} + +status_t Mutex::tryLock() +{ + DWORD dwWaitResult; + + dwWaitResult = WaitForSingleObject((HANDLE) mState, 0); + if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT) + ALOG(LOG_WARN, "thread", "WARNING: bad result from try-locking mutex\n"); + return (dwWaitResult == WAIT_OBJECT_0) ? 0 : -1; +} + +#else +#error "Somebody forgot to implement threads for this platform." +#endif + + +/* + * =========================================================================== + * Condition class + * =========================================================================== + */ + +#if defined(HAVE_PTHREADS) +// implemented as inlines in threads.h +#elif defined(HAVE_WIN32_THREADS) + +/* + * Windows doesn't have a condition variable solution. It's possible + * to create one, but it's easy to get it wrong. For a discussion, and + * the origin of this implementation, see: + * + * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html + * + * The implementation shown on the page does NOT follow POSIX semantics. + * As an optimization they require acquiring the external mutex before + * calling signal() and broadcast(), whereas POSIX only requires grabbing + * it before calling wait(). The implementation here has been un-optimized + * to have the correct behavior. + */ +typedef struct WinCondition { + // Number of waiting threads. + int waitersCount; + + // Serialize access to waitersCount. + CRITICAL_SECTION waitersCountLock; + + // Semaphore used to queue up threads waiting for the condition to + // become signaled. + HANDLE sema; + + // An auto-reset event used by the broadcast/signal thread to wait + // for all the waiting thread(s) to wake up and be released from + // the semaphore. + HANDLE waitersDone; + + // This mutex wouldn't be necessary if we required that the caller + // lock the external mutex before calling signal() and broadcast(). + // I'm trying to mimic pthread semantics though. + HANDLE internalMutex; + + // Keeps track of whether we were broadcasting or signaling. This + // allows us to optimize the code if we're just signaling. + bool wasBroadcast; + + status_t wait(WinCondition* condState, HANDLE hMutex, nsecs_t* abstime) + { + // Increment the wait count, avoiding race conditions. + EnterCriticalSection(&condState->waitersCountLock); + condState->waitersCount++; + //printf("+++ wait: incr waitersCount to %d (tid=%ld)\n", + // condState->waitersCount, getThreadId()); + LeaveCriticalSection(&condState->waitersCountLock); + + DWORD timeout = INFINITE; + if (abstime) { + nsecs_t reltime = *abstime - systemTime(); + if (reltime < 0) + reltime = 0; + timeout = reltime/1000000; + } + + // Atomically release the external mutex and wait on the semaphore. + DWORD res = + SignalObjectAndWait(hMutex, condState->sema, timeout, FALSE); + + //printf("+++ wait: awake (tid=%ld)\n", getThreadId()); + + // Reacquire lock to avoid race conditions. + EnterCriticalSection(&condState->waitersCountLock); + + // No longer waiting. + condState->waitersCount--; + + // Check to see if we're the last waiter after a broadcast. + bool lastWaiter = (condState->wasBroadcast && condState->waitersCount == 0); + + //printf("+++ wait: lastWaiter=%d (wasBc=%d wc=%d)\n", + // lastWaiter, condState->wasBroadcast, condState->waitersCount); + + LeaveCriticalSection(&condState->waitersCountLock); + + // If we're the last waiter thread during this particular broadcast + // then signal broadcast() that we're all awake. It'll drop the + // internal mutex. + if (lastWaiter) { + // Atomically signal the "waitersDone" event and wait until we + // can acquire the internal mutex. We want to do this in one step + // because it ensures that everybody is in the mutex FIFO before + // any thread has a chance to run. Without it, another thread + // could wake up, do work, and hop back in ahead of us. + SignalObjectAndWait(condState->waitersDone, condState->internalMutex, + INFINITE, FALSE); + } else { + // Grab the internal mutex. + WaitForSingleObject(condState->internalMutex, INFINITE); + } + + // Release the internal and grab the external. + ReleaseMutex(condState->internalMutex); + WaitForSingleObject(hMutex, INFINITE); + + return res == WAIT_OBJECT_0 ? NO_ERROR : -1; + } +} WinCondition; + +/* + * Constructor. Set up the WinCondition stuff. + */ +Condition::Condition() +{ + WinCondition* condState = new WinCondition; + + condState->waitersCount = 0; + condState->wasBroadcast = false; + // semaphore: no security, initial value of 0 + condState->sema = CreateSemaphore(NULL, 0, 0x7fffffff, NULL); + InitializeCriticalSection(&condState->waitersCountLock); + // auto-reset event, not signaled initially + condState->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL); + // used so we don't have to lock external mutex on signal/broadcast + condState->internalMutex = CreateMutex(NULL, FALSE, NULL); + + mState = condState; +} + +/* + * Destructor. Free Windows resources as well as our allocated storage. + */ +Condition::~Condition() +{ + WinCondition* condState = (WinCondition*) mState; + if (condState != NULL) { + CloseHandle(condState->sema); + CloseHandle(condState->waitersDone); + delete condState; + } +} + + +status_t Condition::wait(Mutex& mutex) +{ + WinCondition* condState = (WinCondition*) mState; + HANDLE hMutex = (HANDLE) mutex.mState; + + return ((WinCondition*)mState)->wait(condState, hMutex, NULL); +} + +status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) +{ + WinCondition* condState = (WinCondition*) mState; + HANDLE hMutex = (HANDLE) mutex.mState; + nsecs_t absTime = systemTime()+reltime; + + return ((WinCondition*)mState)->wait(condState, hMutex, &absTime); +} + +/* + * Signal the condition variable, allowing one thread to continue. + */ +void Condition::signal() +{ + WinCondition* condState = (WinCondition*) mState; + + // Lock the internal mutex. This ensures that we don't clash with + // broadcast(). + WaitForSingleObject(condState->internalMutex, INFINITE); + + EnterCriticalSection(&condState->waitersCountLock); + bool haveWaiters = (condState->waitersCount > 0); + LeaveCriticalSection(&condState->waitersCountLock); + + // If no waiters, then this is a no-op. Otherwise, knock the semaphore + // down a notch. + if (haveWaiters) + ReleaseSemaphore(condState->sema, 1, 0); + + // Release internal mutex. + ReleaseMutex(condState->internalMutex); +} + +/* + * Signal the condition variable, allowing all threads to continue. + * + * First we have to wake up all threads waiting on the semaphore, then + * we wait until all of the threads have actually been woken before + * releasing the internal mutex. This ensures that all threads are woken. + */ +void Condition::broadcast() +{ + WinCondition* condState = (WinCondition*) mState; + + // Lock the internal mutex. This keeps the guys we're waking up + // from getting too far. + WaitForSingleObject(condState->internalMutex, INFINITE); + + EnterCriticalSection(&condState->waitersCountLock); + bool haveWaiters = false; + + if (condState->waitersCount > 0) { + haveWaiters = true; + condState->wasBroadcast = true; + } + + if (haveWaiters) { + // Wake up all the waiters. + ReleaseSemaphore(condState->sema, condState->waitersCount, 0); + + LeaveCriticalSection(&condState->waitersCountLock); + + // Wait for all awakened threads to acquire the counting semaphore. + // The last guy who was waiting sets this. + WaitForSingleObject(condState->waitersDone, INFINITE); + + // Reset wasBroadcast. (No crit section needed because nobody + // else can wake up to poke at it.) + condState->wasBroadcast = 0; + } else { + // nothing to do + LeaveCriticalSection(&condState->waitersCountLock); + } + + // Release internal mutex. + ReleaseMutex(condState->internalMutex); +} + +#else +#error "condition variables not supported on this platform" +#endif + +// ---------------------------------------------------------------------------- + +/* + * This is our thread object! + */ + +Thread::Thread(bool canCallJava) + : mCanCallJava(canCallJava), + mThread(thread_id_t(-1)), + mLock("Thread::mLock"), + mStatus(NO_ERROR), + mExitPending(false), mRunning(false) +#ifdef HAVE_ANDROID_OS + , mTid(-1) +#endif +{ +} + +Thread::~Thread() +{ +} + +status_t Thread::readyToRun() +{ + return NO_ERROR; +} + +status_t Thread::run(const char* name, int32_t priority, size_t stack) +{ + Mutex::Autolock _l(mLock); + + if (mRunning) { + // thread already started + return INVALID_OPERATION; + } + + // reset status and exitPending to their default value, so we can + // try again after an error happened (either below, or in readyToRun()) + mStatus = NO_ERROR; + mExitPending = false; + mThread = thread_id_t(-1); + + // hold a strong reference on ourself + mHoldSelf = this; + + mRunning = true; + + bool res; + if (mCanCallJava) { + res = createThreadEtc(_threadLoop, + this, name, priority, stack, &mThread); + } else { + res = androidCreateRawThreadEtc(_threadLoop, + this, name, priority, stack, &mThread); + } + + if (res == false) { + mStatus = UNKNOWN_ERROR; // something happened! + mRunning = false; + mThread = thread_id_t(-1); + mHoldSelf.clear(); // "this" may have gone away after this. + + return UNKNOWN_ERROR; + } + + // Do not refer to mStatus here: The thread is already running (may, in fact + // already have exited with a valid mStatus result). The NO_ERROR indication + // here merely indicates successfully starting the thread and does not + // imply successful termination/execution. + return NO_ERROR; + + // Exiting scope of mLock is a memory barrier and allows new thread to run +} + +int Thread::_threadLoop(void* user) +{ + Thread* const self = static_cast<Thread*>(user); + + sp<Thread> strong(self->mHoldSelf); + wp<Thread> weak(strong); + self->mHoldSelf.clear(); + +#ifdef HAVE_ANDROID_OS + // this is very useful for debugging with gdb + self->mTid = gettid(); +#endif + + bool first = true; + + do { + bool result; + if (first) { + first = false; + self->mStatus = self->readyToRun(); + result = (self->mStatus == NO_ERROR); + + if (result && !self->exitPending()) { + // Binder threads (and maybe others) rely on threadLoop + // running at least once after a successful ::readyToRun() + // (unless, of course, the thread has already been asked to exit + // at that point). + // This is because threads are essentially used like this: + // (new ThreadSubclass())->run(); + // The caller therefore does not retain a strong reference to + // the thread and the thread would simply disappear after the + // successful ::readyToRun() call instead of entering the + // threadLoop at least once. + result = self->threadLoop(); + } + } else { + result = self->threadLoop(); + } + + // establish a scope for mLock + { + Mutex::Autolock _l(self->mLock); + if (result == false || self->mExitPending) { + self->mExitPending = true; + self->mRunning = false; + // clear thread ID so that requestExitAndWait() does not exit if + // called by a new thread using the same thread ID as this one. + self->mThread = thread_id_t(-1); + // note that interested observers blocked in requestExitAndWait are + // awoken by broadcast, but blocked on mLock until break exits scope + self->mThreadExitedCondition.broadcast(); + break; + } + } + + // Release our strong reference, to let a chance to the thread + // to die a peaceful death. + strong.clear(); + // And immediately, re-acquire a strong reference for the next loop + strong = weak.promote(); + } while(strong != 0); + + return 0; +} + +void Thread::requestExit() +{ + Mutex::Autolock _l(mLock); + mExitPending = true; +} + +status_t Thread::requestExitAndWait() +{ + Mutex::Autolock _l(mLock); + if (mThread == getThreadId()) { + ALOGW( + "Thread (this=%p): don't call waitForExit() from this " + "Thread object's thread. It's a guaranteed deadlock!", + this); + + return WOULD_BLOCK; + } + + mExitPending = true; + + while (mRunning == true) { + mThreadExitedCondition.wait(mLock); + } + // This next line is probably not needed any more, but is being left for + // historical reference. Note that each interested party will clear flag. + mExitPending = false; + + return mStatus; +} + +status_t Thread::join() +{ + Mutex::Autolock _l(mLock); + if (mThread == getThreadId()) { + ALOGW( + "Thread (this=%p): don't call join() from this " + "Thread object's thread. It's a guaranteed deadlock!", + this); + + return WOULD_BLOCK; + } + + while (mRunning == true) { + mThreadExitedCondition.wait(mLock); + } + + return mStatus; +} + +bool Thread::isRunning() const { + Mutex::Autolock _l(mLock); + return mRunning; +} + +#ifdef HAVE_ANDROID_OS +pid_t Thread::getTid() const +{ + // mTid is not defined until the child initializes it, and the caller may need it earlier + Mutex::Autolock _l(mLock); + pid_t tid; + if (mRunning) { + pthread_t pthread = android_thread_id_t_to_pthread(mThread); + tid = __pthread_gettid(pthread); + } else { + ALOGW("Thread (this=%p): getTid() is undefined before run()", this); + tid = -1; + } + return tid; +} +#endif + +bool Thread::exitPending() const +{ + Mutex::Autolock _l(mLock); + return mExitPending; +} + + + +}; // namespace android |