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:
parent
f1cc94960a
commit
7e453a51a5
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user