blob: f864bacaeb899fd07f0c56d090ab4a346327e7af [file] [log] [blame]
/*
* 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;
mDH = NULL;
#if RS_OBJECT_DEBUG
mDH = new DebugHelper();
#endif
rsAssert(rsc);
add();
//ALOGV("ObjectBase %p con", this);
}
ObjectBase::~ObjectBase() {
//ALOGV("~ObjectBase %p ref %i,%i", this, mUserRefCount, mSysRefCount);
#if RS_OBJECT_DEBUG
mDH->dump();
delete mDH;
mDH = NULL;
#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 {
__sync_fetch_and_add(&mUserRefCount, 1);
//ALOGV("ObjectBase %p incU ref %i, %i", this, mUserRefCount, mSysRefCount);
}
void ObjectBase::incSysRef() const {
__sync_fetch_and_add(&mSysRefCount, 1);
//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) {
mDH->dump();
}
#endif
if ((__sync_fetch_and_sub(&mUserRefCount, 1) <= 1)) {
__sync_synchronize();
if (mSysRefCount <= 0) {
return checkDelete(this);
}
}
return false;
}
bool ObjectBase::zeroUserRef() const {
//ALOGV("ObjectBase %p zeroU ref %i, %i", this, mUserRefCount, mSysRefCount);
__sync_and_and_fetch(&mUserRefCount, 0);
if (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 ((__sync_fetch_and_sub(&mSysRefCount, 1) <= 1)) {
__sync_synchronize();
if (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;
}