/* * Copyright (C) 2009 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 "rsObjectBase.h" #include "rsContext.h" using namespace android; using namespace android::renderscript; pthread_mutex_t ObjectBase::gObjectInitMutex = PTHREAD_MUTEX_INITIALIZER; ObjectBase::ObjectBase(Context *rsc) { mUserRefCount = 0; mSysRefCount = 0; mRSC = rsc; mNext = NULL; mPrev = NULL; #if RS_OBJECT_DEBUG mStack.update(2); #endif rsAssert(rsc); add(); //ALOGV("ObjectBase %p con", this); } ObjectBase::~ObjectBase() { //ALOGV("~ObjectBase %p ref %i,%i", this, mUserRefCount, mSysRefCount); #if RS_OBJECT_DEBUG mStack.dump(); #endif if (mPrev || mNext) { // While the normal practice is to call remove before we call // delete. Its possible for objects without a re-use list // for avoiding duplication to be created on the stack. In those // cases we need to remove ourself here. asyncLock(); remove(); asyncUnlock(); } rsAssert(!mUserRefCount); rsAssert(!mSysRefCount); } void ObjectBase::dumpLOGV(const char *op) const { if (mName.size()) { ALOGV("%s RSobj %p, name %s, refs %i,%i links %p,%p,%p", op, this, mName.string(), mUserRefCount, mSysRefCount, mNext, mPrev, mRSC); } else { ALOGV("%s RSobj %p, no-name, refs %i,%i links %p,%p,%p", op, this, mUserRefCount, mSysRefCount, mNext, mPrev, mRSC); } } void ObjectBase::incUserRef() const { android_atomic_inc(&mUserRefCount); //ALOGV("ObjectBase %p incU ref %i, %i", this, mUserRefCount, mSysRefCount); } void ObjectBase::incSysRef() const { android_atomic_inc(&mSysRefCount); //ALOGV("ObjectBase %p incS ref %i, %i", this, mUserRefCount, mSysRefCount); } void ObjectBase::preDestroy() const { } bool ObjectBase::freeChildren() { return false; } bool ObjectBase::checkDelete(const ObjectBase *ref) { if (!ref) { return false; } asyncLock(); // This lock protects us against the non-RS threads changing // the ref counts. At this point we should be the only thread // working on them. if (ref->mUserRefCount || ref->mSysRefCount) { asyncUnlock(); return false; } ref->remove(); // At this point we can unlock because there should be no possible way // for another thread to reference this object. ref->preDestroy(); asyncUnlock(); delete ref; return true; } bool ObjectBase::decUserRef() const { rsAssert(mUserRefCount > 0); #if RS_OBJECT_DEBUG ALOGV("ObjectBase %p decU ref %i, %i", this, mUserRefCount, mSysRefCount); if (mUserRefCount <= 0) { mStack.dump(); } #endif if ((android_atomic_dec(&mUserRefCount) <= 1) && (android_atomic_acquire_load(&mSysRefCount) <= 0)) { return checkDelete(this); } return false; } bool ObjectBase::zeroUserRef() const { //ALOGV("ObjectBase %p zeroU ref %i, %i", this, mUserRefCount, mSysRefCount); android_atomic_acquire_store(0, &mUserRefCount); if (android_atomic_acquire_load(&mSysRefCount) <= 0) { return checkDelete(this); } return false; } bool ObjectBase::decSysRef() const { //ALOGV("ObjectBase %p decS ref %i, %i", this, mUserRefCount, mSysRefCount); rsAssert(mSysRefCount > 0); if ((android_atomic_dec(&mSysRefCount) <= 1) && (android_atomic_acquire_load(&mUserRefCount) <= 0)) { return checkDelete(this); } return false; } void ObjectBase::setName(const char *name) { mName.setTo(name); } void ObjectBase::setName(const char *name, uint32_t len) { mName.setTo(name, len); } void ObjectBase::asyncLock() { pthread_mutex_lock(&gObjectInitMutex); } void ObjectBase::asyncUnlock() { pthread_mutex_unlock(&gObjectInitMutex); } void ObjectBase::add() const { asyncLock(); rsAssert(!mNext); rsAssert(!mPrev); //ALOGV("calling add rsc %p", mRSC); mNext = mRSC->mObjHead; if (mRSC->mObjHead) { mRSC->mObjHead->mPrev = this; } mRSC->mObjHead = this; asyncUnlock(); } void ObjectBase::remove() const { //ALOGV("calling remove rsc %p", mRSC); if (!mRSC) { rsAssert(!mPrev); rsAssert(!mNext); return; } if (mRSC->mObjHead == this) { mRSC->mObjHead = mNext; } if (mPrev) { mPrev->mNext = mNext; } if (mNext) { mNext->mPrev = mPrev; } mPrev = NULL; mNext = NULL; } void ObjectBase::zeroAllUserRef(Context *rsc) { if (rsc->props.mLogObjects) { ALOGV("Forcing release of all outstanding user refs."); } // This operation can be slow, only to be called during context cleanup. const ObjectBase * o = rsc->mObjHead; while (o) { //ALOGE("o %p", o); if (o->zeroUserRef()) { // deleted the object and possibly others, restart from head. o = rsc->mObjHead; //ALOGE("o head %p", o); } else { o = o->mNext; //ALOGE("o next %p", o); } } if (rsc->props.mLogObjects) { ALOGV("Objects remaining."); dumpAll(rsc); } } void ObjectBase::freeAllChildren(Context *rsc) { if (rsc->props.mLogObjects) { ALOGV("Forcing release of all child objects."); } // This operation can be slow, only to be called during context cleanup. ObjectBase * o = (ObjectBase *)rsc->mObjHead; while (o) { if (o->freeChildren()) { // deleted ref to self and possibly others, restart from head. o = (ObjectBase *)rsc->mObjHead; } else { o = (ObjectBase *)o->mNext; } } if (rsc->props.mLogObjects) { ALOGV("Objects remaining."); dumpAll(rsc); } } void ObjectBase::dumpAll(Context *rsc) { asyncLock(); ALOGV("Dumping all objects"); const ObjectBase * o = rsc->mObjHead; while (o) { ALOGV(" Object %p", o); o->dumpLOGV(" "); o = o->mNext; } asyncUnlock(); } bool ObjectBase::isValid(const Context *rsc, const ObjectBase *obj) { asyncLock(); const ObjectBase * o = rsc->mObjHead; while (o) { if (o == obj) { asyncUnlock(); return true; } o = o->mNext; } asyncUnlock(); return false; }