Merge "Fix deadlock when cleaning objects in eglTerminate"

This commit is contained in:
Jesse Hall 2012-04-18 09:04:35 -07:00 committed by Android (Google) Code Review
commit aa1667f006
2 changed files with 79 additions and 29 deletions

View File

@ -70,8 +70,7 @@ extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS]; egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
egl_display_t::egl_display_t() : egl_display_t::egl_display_t() :
magic('_dpy'), finishOnSwap(false), traceGpuCompletion(false), refs(0), magic('_dpy'), finishOnSwap(false), traceGpuCompletion(false), refs(0) {
mWakeCount(0), mHibernating(false), mAttemptHibernation(false) {
} }
egl_display_t::~egl_display_t() { egl_display_t::~egl_display_t() {
@ -253,6 +252,9 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) {
*major = VERSION_MAJOR; *major = VERSION_MAJOR;
if (minor != NULL) if (minor != NULL)
*minor = VERSION_MINOR; *minor = VERSION_MINOR;
mHibernation.setDisplayValid(true);
return EGL_TRUE; return EGL_TRUE;
} }
@ -282,6 +284,8 @@ EGLBoolean egl_display_t::terminate() {
res = EGL_TRUE; res = EGL_TRUE;
} }
mHibernation.setDisplayValid(false);
// Mark all objects remaining in the list as terminated, unless // Mark all objects remaining in the list as terminated, unless
// there are no reference to them, it which case, we're free to // there are no reference to them, it which case, we're free to
// delete them. // delete them.
@ -351,8 +355,7 @@ EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c,
if (result == EGL_TRUE) { if (result == EGL_TRUE) {
c->onMakeCurrent(draw, read); c->onMakeCurrent(draw, read);
if (!cur_c) { if (!cur_c) {
mWakeCount++; mHibernation.incWakeCount(HibernationMachine::STRONG);
mAttemptHibernation = false;
} }
} }
} else { } else {
@ -360,8 +363,7 @@ EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c,
disp.dpy, impl_draw, impl_read, impl_ctx); disp.dpy, impl_draw, impl_read, impl_ctx);
if (result == EGL_TRUE) { if (result == EGL_TRUE) {
cur_c->onLooseCurrent(); cur_c->onLooseCurrent();
mWakeCount--; mHibernation.decWakeCount(HibernationMachine::STRONG);
mAttemptHibernation = true;
} }
} }
} }
@ -378,14 +380,26 @@ EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c,
return result; return result;
} }
bool egl_display_t::enter() { // ----------------------------------------------------------------------------
Mutex::Autolock _l(lock);
bool egl_display_t::HibernationMachine::incWakeCount(WakeRefStrength strength) {
Mutex::Autolock _l(mLock);
ALOGE_IF(mWakeCount < 0 || mWakeCount == INT32_MAX, ALOGE_IF(mWakeCount < 0 || mWakeCount == INT32_MAX,
"Invalid WakeCount (%d) on enter\n", mWakeCount); "Invalid WakeCount (%d) on enter\n", mWakeCount);
mWakeCount++; mWakeCount++;
if (strength == STRONG)
mAttemptHibernation = false;
if (CC_UNLIKELY(mHibernating)) { if (CC_UNLIKELY(mHibernating)) {
ALOGV("Awakening\n"); ALOGV("Awakening\n");
egl_connection_t* const cnx = &gEGLImpl; egl_connection_t* const cnx = &gEGLImpl;
// These conditions should be guaranteed before entering hibernation;
// we don't want to get into a state where we can't wake up.
ALOGD_IF(!mDpyValid || !cnx->egl.eglAwakenProcessIMG,
"Invalid hibernation state, unable to awaken\n");
if (!cnx->egl.eglAwakenProcessIMG()) { if (!cnx->egl.eglAwakenProcessIMG()) {
ALOGE("Failed to awaken EGL implementation\n"); ALOGE("Failed to awaken EGL implementation\n");
return false; return false;
@ -395,13 +409,20 @@ bool egl_display_t::enter() {
return true; return true;
} }
void egl_display_t::leave() { void egl_display_t::HibernationMachine::decWakeCount(WakeRefStrength strength) {
Mutex::Autolock _l(lock); Mutex::Autolock _l(mLock);
ALOGE_IF(mWakeCount <= 0, "Invalid WakeCount (%d) on leave\n", mWakeCount); ALOGE_IF(mWakeCount <= 0, "Invalid WakeCount (%d) on leave\n", mWakeCount);
if (--mWakeCount == 0 && CC_UNLIKELY(mAttemptHibernation)) {
mWakeCount--;
if (strength == STRONG)
mAttemptHibernation = true;
if (mWakeCount == 0 && CC_UNLIKELY(mAttemptHibernation)) {
egl_connection_t* const cnx = &gEGLImpl; egl_connection_t* const cnx = &gEGLImpl;
mAttemptHibernation = false; mAttemptHibernation = false;
if (cnx->egl.eglHibernateProcessIMG && cnx->egl.eglAwakenProcessIMG) { if (mDpyValid &&
cnx->egl.eglHibernateProcessIMG &&
cnx->egl.eglAwakenProcessIMG) {
ALOGV("Hibernating\n"); ALOGV("Hibernating\n");
if (!cnx->egl.eglHibernateProcessIMG()) { if (!cnx->egl.eglHibernateProcessIMG()) {
ALOGE("Failed to hibernate EGL implementation\n"); ALOGE("Failed to hibernate EGL implementation\n");
@ -412,16 +433,9 @@ void egl_display_t::leave() {
} }
} }
void egl_display_t::onWindowSurfaceCreated() { void egl_display_t::HibernationMachine::setDisplayValid(bool valid) {
Mutex::Autolock _l(lock); Mutex::Autolock _l(mLock);
mWakeCount++; mDpyValid = valid;
mAttemptHibernation = false;
}
void egl_display_t::onWindowSurfaceDestroyed() {
Mutex::Autolock _l(lock);
mWakeCount--;
mAttemptHibernation = true;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -77,8 +77,12 @@ public:
// proper hibernate/wakeup sequencing. If a surface destruction triggers // proper hibernate/wakeup sequencing. If a surface destruction triggers
// hibernation, hibernation will be delayed at least until the calling // hibernation, hibernation will be delayed at least until the calling
// thread's egl_display_ptr is destroyed. // thread's egl_display_ptr is destroyed.
void onWindowSurfaceCreated(); void onWindowSurfaceCreated() {
void onWindowSurfaceDestroyed(); mHibernation.incWakeCount(HibernationMachine::STRONG);
}
void onWindowSurfaceDestroyed() {
mHibernation.decWakeCount(HibernationMachine::STRONG);
}
static egl_display_t* get(EGLDisplay dpy); static egl_display_t* get(EGLDisplay dpy);
static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp); static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp);
@ -123,8 +127,8 @@ public:
private: private:
friend class egl_display_ptr; friend class egl_display_ptr;
bool enter(); bool enter() { return mHibernation.incWakeCount(HibernationMachine::WEAK); }
void leave(); void leave() { return mHibernation.decWakeCount(HibernationMachine::WEAK); }
uint32_t refs; uint32_t refs;
mutable Mutex lock; mutable Mutex lock;
@ -133,9 +137,41 @@ private:
String8 mVersionString; String8 mVersionString;
String8 mClientApiString; String8 mClientApiString;
String8 mExtensionString; String8 mExtensionString;
int32_t mWakeCount;
bool mHibernating; // HibernationMachine uses its own internal mutex to protect its own data.
bool mAttemptHibernation; // The owning egl_display_t's lock may be but is not required to be held
// when calling HibernationMachine methods. As a result, nothing in this
// class may call back up to egl_display_t directly or indirectly.
class HibernationMachine {
public:
// STRONG refs cancel (inc) or initiate (dec) a hibernation attempt
// the next time the wakecount reaches zero. WEAK refs don't affect
// whether a hibernation attempt will be made. Use STRONG refs only
// for infrequent/heavy changes that are likely to indicate the
// EGLDisplay is entering or leaving a long-term idle state.
enum WakeRefStrength {
WEAK = 0,
STRONG = 1,
};
HibernationMachine(): mWakeCount(0), mHibernating(false),
mAttemptHibernation(false), mDpyValid(false)
{}
~HibernationMachine() {}
bool incWakeCount(WakeRefStrength strenth);
void decWakeCount(WakeRefStrength strenth);
void setDisplayValid(bool valid);
private:
Mutex mLock;
int32_t mWakeCount;
bool mHibernating;
bool mAttemptHibernation;
bool mDpyValid;
};
HibernationMachine mHibernation;
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------