/*
* 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"
#include "rsDebugHelper.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 = nullptr;
mPrev = nullptr;
mDH = nullptr;
mName = nullptr;
if (gDebugStacks || gDebugReferences || gDebugLeaks) {
mDH = new DebugHelper();
}
rsAssert(rsc);
add();
if (gDebugLifetime || gDebugReferences) {
ALOGV("ObjectBase constructed %p", this);
}
}
ObjectBase::~ObjectBase() {
if (gDebugLifetime || gDebugReferences) {
ALOGV("ObjectBase destroyed %p refs %i %i", this, mUserRefCount, mSysRefCount);
}
if (gDebugStacks || gDebugReferences || gDebugLeaks) {
if (gDebugStacks || gDebugReferences) {
mDH->dump();
}
delete mDH;
mDH = nullptr;
}
free(const_cast<char *>(mName));
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) {
ALOGV("%s RSobj %p, name %s, refs %i,%i links %p,%p,%p",
op, this, mName, 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);
if (gDebugReferences) {
ALOGV("ObjectBase %p incU ref %i, %i", this, mUserRefCount, mSysRefCount);
}
}
void ObjectBase::incSysRef() const {
__sync_fetch_and_add(&mSysRefCount, 1);
if (gDebugReferences) {
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 (gDebugReferences) {
ALOGV("ObjectBase %p decU ref %i, %i", this, mUserRefCount, mSysRefCount);
if (mUserRefCount <= 0) {
mDH->dump();
}
}
if ((__sync_fetch_and_sub(&mUserRefCount, 1) <= 1)) {
__sync_synchronize();
if (mSysRefCount <= 0) {
return checkDelete(this);
}
}
return false;
}
bool ObjectBase::zeroUserRef() const {
if (gDebugReferences) {
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 {
if (gDebugReferences) {
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 = strdup(name);
}
void ObjectBase::setName(const char *name, uint32_t len) {
char *c = (char*)calloc(len + 1, sizeof(char));
rsAssert(c);
memcpy(c, name, len);
mName = c;
}
void ObjectBase::asyncLock() {
pthread_mutex_lock(&gObjectInitMutex);
}
void ObjectBase::asyncUnlock() {
pthread_mutex_unlock(&gObjectInitMutex);
}
void ObjectBase::add() const {
asyncLock();
rsAssert(!mNext);
rsAssert(!mPrev);
mNext = mRSC->mObjHead;
if (mRSC->mObjHead) {
mRSC->mObjHead->mPrev = this;
}
mRSC->mObjHead = this;
asyncUnlock();
}
void ObjectBase::remove() const {
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 = nullptr;
mNext = nullptr;
}
void ObjectBase::zeroAllUserRef(Context *rsc) {
if (gDebugReferences || gDebugLeaks) {
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 (gDebugReferences || gDebugLeaks) {
ALOGV("Objects remaining.");
dumpAll(rsc);
}
}
void ObjectBase::freeAllChildren(Context *rsc) {
if (gDebugReferences) {
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 (gDebugReferences) {
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(" ");
if (o->mDH != nullptr) {
o->mDH->dump();
}
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;
}
void ObjectBase::callUpdateCacheObject(const Context *rsc, void *dstObj) const {
//ALOGE("ObjectBase::callUpdateCacheObject %p %p", this, dstObj);
*((const void **)dstObj) = this;
}