Bug 3362814 Fix SMP race in access to mRequestExit

Also fix an unlikely SMP race in access to mHoldSelf on entry to _threadLoop.

Change-Id: I6cbc0b94739c7dd5e77e8a5ba0da22cdc0b1a4db
This commit is contained in:
Glenn Kasten 2011-02-01 11:32:29 -08:00
parent f1cc94960a
commit 7e453a51a5
2 changed files with 25 additions and 5 deletions

View File

@ -527,9 +527,10 @@ private:
static int _threadLoop(void* user); static int _threadLoop(void* user);
const bool mCanCallJava; const bool mCanCallJava;
thread_id_t mThread; thread_id_t mThread;
Mutex mLock; mutable Mutex mLock;
Condition mThreadExitedCondition; Condition mThreadExitedCondition;
status_t mStatus; status_t mStatus;
// note that all accesses of mExitPending and mRunning need to hold mLock
volatile bool mExitPending; volatile bool mExitPending;
volatile bool mRunning; volatile bool mRunning;
sp<Thread> mHoldSelf; sp<Thread> mHoldSelf;

View File

@ -675,6 +675,9 @@ Thread::Thread(bool canCallJava)
mLock("Thread::mLock"), mLock("Thread::mLock"),
mStatus(NO_ERROR), mStatus(NO_ERROR),
mExitPending(false), mRunning(false) mExitPending(false), mRunning(false)
#ifdef HAVE_ANDROID_OS
, mTid(-1)
#endif
{ {
} }
@ -715,6 +718,7 @@ status_t Thread::run(const char* name, int32_t priority, size_t stack)
res = androidCreateRawThreadEtc(_threadLoop, res = androidCreateRawThreadEtc(_threadLoop,
this, name, priority, stack, &mThread); this, name, priority, stack, &mThread);
} }
// The new thread wakes up at _threadLoop, but immediately blocks on mLock
if (res == false) { if (res == false) {
mStatus = UNKNOWN_ERROR; // something happened! mStatus = UNKNOWN_ERROR; // something happened!
@ -730,11 +734,19 @@ status_t Thread::run(const char* name, int32_t priority, size_t stack)
// here merely indicates successfully starting the thread and does not // here merely indicates successfully starting the thread and does not
// imply successful termination/execution. // imply successful termination/execution.
return NO_ERROR; return NO_ERROR;
// Exiting scope of mLock is a memory barrier and allows new thread to run
} }
int Thread::_threadLoop(void* user) int Thread::_threadLoop(void* user)
{ {
Thread* const self = static_cast<Thread*>(user); Thread* const self = static_cast<Thread*>(user);
// force a memory barrier before reading any fields, in particular mHoldSelf
{
Mutex::Autolock _l(self->mLock);
}
sp<Thread> strong(self->mHoldSelf); sp<Thread> strong(self->mHoldSelf);
wp<Thread> weak(strong); wp<Thread> weak(strong);
self->mHoldSelf.clear(); self->mHoldSelf.clear();
@ -753,7 +765,7 @@ int Thread::_threadLoop(void* user)
self->mStatus = self->readyToRun(); self->mStatus = self->readyToRun();
result = (self->mStatus == NO_ERROR); result = (self->mStatus == NO_ERROR);
if (result && !self->mExitPending) { if (result && !self->exitPending()) {
// Binder threads (and maybe others) rely on threadLoop // Binder threads (and maybe others) rely on threadLoop
// running at least once after a successful ::readyToRun() // running at least once after a successful ::readyToRun()
// (unless, of course, the thread has already been asked to exit // (unless, of course, the thread has already been asked to exit
@ -770,18 +782,21 @@ int Thread::_threadLoop(void* user)
result = self->threadLoop(); result = self->threadLoop();
} }
// establish a scope for mLock
{
Mutex::Autolock _l(self->mLock);
if (result == false || self->mExitPending) { if (result == false || self->mExitPending) {
self->mExitPending = true; self->mExitPending = true;
self->mLock.lock();
self->mRunning = false; self->mRunning = false;
// clear thread ID so that requestExitAndWait() does not exit if // clear thread ID so that requestExitAndWait() does not exit if
// called by a new thread using the same thread ID as this one. // called by a new thread using the same thread ID as this one.
self->mThread = thread_id_t(-1); 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(); self->mThreadExitedCondition.broadcast();
self->mThread = thread_id_t(-1); // thread id could be reused
self->mLock.unlock();
break; break;
} }
}
// Release our strong reference, to let a chance to the thread // Release our strong reference, to let a chance to the thread
// to die a peaceful death. // to die a peaceful death.
@ -795,6 +810,7 @@ int Thread::_threadLoop(void* user)
void Thread::requestExit() void Thread::requestExit()
{ {
Mutex::Autolock _l(mLock);
mExitPending = true; mExitPending = true;
} }
@ -815,6 +831,8 @@ status_t Thread::requestExitAndWait()
while (mRunning == true) { while (mRunning == true) {
mThreadExitedCondition.wait(mLock); 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; mExitPending = false;
return mStatus; return mStatus;
@ -822,6 +840,7 @@ status_t Thread::requestExitAndWait()
bool Thread::exitPending() const bool Thread::exitPending() const
{ {
Mutex::Autolock _l(mLock);
return mExitPending; return mExitPending;
} }