/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef _MDB_ #include "mdb.h" #endif #ifndef _MORK_ #include "mork.h" #endif #ifndef _MORKNODE_ #include "morkNode.h" #endif #ifndef _MORKPOOL_ #include "morkPool.h" #endif #ifndef _MORKENV_ #include "morkEnv.h" #endif #ifndef _MORKHANDLE_ #include "morkHandle.h" #endif /*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/ /* ===== ===== ===== ===== morkUsage ===== ===== ===== ===== */ static morkUsage morkUsage_gHeap; // ensure EnsureReadyStaticUsage() const morkUsage& morkUsage::kHeap = morkUsage_gHeap; static morkUsage morkUsage_gStack; // ensure EnsureReadyStaticUsage() const morkUsage& morkUsage::kStack = morkUsage_gStack; static morkUsage morkUsage_gMember; // ensure EnsureReadyStaticUsage() const morkUsage& morkUsage::kMember = morkUsage_gMember; static morkUsage morkUsage_gGlobal; // ensure EnsureReadyStaticUsage() const morkUsage& morkUsage::kGlobal = morkUsage_gGlobal; static morkUsage morkUsage_gPool; // ensure EnsureReadyStaticUsage() const morkUsage& morkUsage::kPool = morkUsage_gPool; static morkUsage morkUsage_gNone; // ensure EnsureReadyStaticUsage() const morkUsage& morkUsage::kNone = morkUsage_gNone; // This must be structured to allow for non-zero values in global variables // just before static init time. We can only safely check for whether a // global has the address of some other global. Please, do not initialize // either of the variables below to zero, because this could break when a zero // is assigned at static init time, but after EnsureReadyStaticUsage() runs. static mork_u4 morkUsage_g_static_init_target; // only address of this matters static mork_u4* morkUsage_g_static_init_done; // is address of target above? #define morkUsage_do_static_init() \ ( morkUsage_g_static_init_done = &morkUsage_g_static_init_target ) #define morkUsage_need_static_init() \ ( morkUsage_g_static_init_done != &morkUsage_g_static_init_target ) /*static*/ void morkUsage::EnsureReadyStaticUsage() { if ( morkUsage_need_static_init() ) { morkUsage_do_static_init(); morkUsage_gHeap.InitUsage(morkUsage_kHeap); morkUsage_gStack.InitUsage(morkUsage_kStack); morkUsage_gMember.InitUsage(morkUsage_kMember); morkUsage_gGlobal.InitUsage(morkUsage_kGlobal); morkUsage_gPool.InitUsage(morkUsage_kPool); morkUsage_gNone.InitUsage(morkUsage_kNone); } } /*static*/ const morkUsage& morkUsage::GetHeap() // kHeap safe at static init time { EnsureReadyStaticUsage(); return morkUsage_gHeap; } /*static*/ const morkUsage& morkUsage::GetStack() // kStack safe at static init time { EnsureReadyStaticUsage(); return morkUsage_gStack; } /*static*/ const morkUsage& morkUsage::GetMember() // kMember safe at static init time { EnsureReadyStaticUsage(); return morkUsage_gMember; } /*static*/ const morkUsage& morkUsage::GetGlobal() // kGlobal safe at static init time { EnsureReadyStaticUsage(); return morkUsage_gGlobal; } /*static*/ const morkUsage& morkUsage::GetPool() // kPool safe at static init time { EnsureReadyStaticUsage(); return morkUsage_gPool; } /*static*/ const morkUsage& morkUsage::GetNone() // kNone safe at static init time { EnsureReadyStaticUsage(); return morkUsage_gNone; } morkUsage::morkUsage() { if ( morkUsage_need_static_init() ) { morkUsage::EnsureReadyStaticUsage(); } } morkUsage::morkUsage(mork_usage code) : mUsage_Code(code) { if ( morkUsage_need_static_init() ) { morkUsage::EnsureReadyStaticUsage(); } } //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 /*static*/ void* morkNode::MakeNew(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) { void* node = 0; ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void **) &node); if ( !node ) ev->OutOfMemoryError(); return node; } /*public non-poly*/ void morkNode::ZapOld(morkEnv* ev, nsIMdbHeap* ioHeap) { if ( this->IsNode() ) { mork_usage usage = mNode_Usage; // mNode_Usage before ~morkNode this->morkNode::~morkNode(); // first call polymorphic destructor if ( ioHeap ) // was this node heap allocated? ioHeap->Free(ev->AsMdbEnv(), this); else if ( usage == morkUsage_kPool ) // mNode_Usage before ~morkNode { morkHandle* h = (morkHandle*) this; if ( h->IsHandle() && h->GoodHandleTag() ) { if ( h->mHandle_Face ) { if (ev->mEnv_HandlePool) ev->mEnv_HandlePool->ZapHandle(ev, h->mHandle_Face); else if (h->mHandle_Env && h->mHandle_Env->mEnv_HandlePool) h->mHandle_Env->mEnv_HandlePool->ZapHandle(ev, h->mHandle_Face); } else ev->NilPointerError(); } } } else this->NonNodeError(ev); } /*public virtual*/ void morkNode::CloseMorkNode(morkEnv* ev) // CloseNode() only if open { if ( this->IsOpenNode() ) { this->MarkClosing(); this->CloseNode(ev); this->MarkShut(); } } NS_IMETHODIMP morkNode::CloseMdbObject(nsIMdbEnv* mev) { return morkNode::CloseMdbObject((morkEnv *) mev); } nsresult morkNode::CloseMdbObject(morkEnv *ev) { // if only one ref, Handle_CutStrongRef will clean up better. if (mNode_Uses == 1) // XXX Casting mork_uses to nsresult return static_cast(CutStrongRef(ev)); nsresult outErr = NS_OK; if ( IsNode() && IsOpenNode() ) { if ( ev ) { CloseMorkNode(ev); outErr = ev->AsErr(); } } return outErr; } /*public virtual*/ morkNode::~morkNode() // assert that CloseNode() executed earlier { MORK_ASSERT(this->IsShutNode() || IsDeadNode()); // sometimes we call destructor explicitly w/o freeing object. mNode_Access = morkAccess_kDead; mNode_Usage = morkUsage_kNone; } /*public virtual*/ // void CloseMorkNode(morkEnv* ev) = 0; // CloseNode() only if open // CloseMorkNode() is the polymorphic close method called when uses==0, // which must do NOTHING at all when IsOpenNode() is not true. Otherwise, // CloseMorkNode() should call a static close method specific to an object. // Each such static close method should either call inherited static close // methods, or else perform the consolidated effect of calling them, where // subclasses should closely track any changes in base classes with care. /*public non-poly*/ morkNode::morkNode( mork_usage inCode ) : mNode_Heap( 0 ) , mNode_Base( morkBase_kNode ) , mNode_Derived ( 0 ) // until subclass sets appropriately , mNode_Access( morkAccess_kOpen ) , mNode_Usage( inCode ) , mNode_Mutable( morkAble_kEnabled ) , mNode_Load( morkLoad_kClean ) , mNode_Uses( 1 ) , mNode_Refs( 1 ) { } /*public non-poly*/ morkNode::morkNode(const morkUsage& inUsage, nsIMdbHeap* ioHeap) : mNode_Heap( ioHeap ) , mNode_Base( morkBase_kNode ) , mNode_Derived ( 0 ) // until subclass sets appropriately , mNode_Access( morkAccess_kOpen ) , mNode_Usage( inUsage.Code() ) , mNode_Mutable( morkAble_kEnabled ) , mNode_Load( morkLoad_kClean ) , mNode_Uses( 1 ) , mNode_Refs( 1 ) { if ( !ioHeap && mNode_Usage == morkUsage_kHeap ) MORK_ASSERT(ioHeap); } /*public non-poly*/ morkNode::morkNode(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap) : mNode_Heap( ioHeap ) , mNode_Base( morkBase_kNode ) , mNode_Derived ( 0 ) // until subclass sets appropriately , mNode_Access( morkAccess_kOpen ) , mNode_Usage( inUsage.Code() ) , mNode_Mutable( morkAble_kEnabled ) , mNode_Load( morkLoad_kClean ) , mNode_Uses( 1 ) , mNode_Refs( 1 ) { if ( !ioHeap && mNode_Usage == morkUsage_kHeap ) { this->NilHeapError(ev); } } /*protected non-poly*/ void morkNode::RefsUnderUsesWarning(morkEnv* ev) const { ev->NewError("mNode_Refs < mNode_Uses"); } /*protected non-poly*/ void morkNode::NonNodeError(morkEnv* ev) const // called when IsNode() is false { ev->NewError("non-morkNode"); } /*protected non-poly*/ void morkNode::NonOpenNodeError(morkEnv* ev) const // when IsOpenNode() is false { ev->NewError("non-open-morkNode"); } /*protected non-poly*/ void morkNode::NonMutableNodeError(morkEnv* ev) const // when IsMutable() is false { ev->NewError("non-mutable-morkNode"); } /*protected non-poly*/ void morkNode::NilHeapError(morkEnv* ev) const // zero mNode_Heap w/ kHeap usage { ev->NewError("nil mNode_Heap"); } /*protected non-poly*/ void morkNode::RefsOverflowWarning(morkEnv* ev) const // mNode_Refs overflow { ev->NewWarning("mNode_Refs overflow"); } /*protected non-poly*/ void morkNode::UsesOverflowWarning(morkEnv* ev) const // mNode_Uses overflow { ev->NewWarning("mNode_Uses overflow"); } /*protected non-poly*/ void morkNode::RefsUnderflowWarning(morkEnv* ev) const // mNode_Refs underflow { ev->NewWarning("mNode_Refs underflow"); } /*protected non-poly*/ void morkNode::UsesUnderflowWarning(morkEnv* ev) const // mNode_Uses underflow { ev->NewWarning("mNode_Uses underflow"); } /*public non-poly*/ void morkNode::CloseNode(morkEnv* ev) // called by CloseMorkNode(); { if ( this->IsNode() ) this->MarkShut(); else this->NonNodeError(ev); } extern void // utility method very similar to morkNode::SlotStrongNode(): nsIMdbFile_SlotStrongFile(nsIMdbFile* self, morkEnv* ev, nsIMdbFile** ioSlot) // If *ioSlot is non-nil, that file is released by CutStrongRef() and // then zeroed out. Then if self is non-nil, this is acquired by // calling AddStrongRef(), and if the return value shows success, // then self is put into slot *ioSlot. Note self can be nil, so we take // expression 'nsIMdbFile_SlotStrongFile(0, ev, &slot)'. { nsIMdbFile* file = *ioSlot; if ( self != file ) { if ( file ) { *ioSlot = 0; NS_RELEASE(file); } if (self && ev->Good()) NS_ADDREF(*ioSlot = self); } } void // utility method very similar to morkNode::SlotStrongNode(): nsIMdbHeap_SlotStrongHeap(nsIMdbHeap* self, morkEnv* ev, nsIMdbHeap** ioSlot) // If *ioSlot is non-nil, that heap is released by CutStrongRef() and // then zeroed out. Then if self is non-nil, self is acquired by // calling AddStrongRef(), and if the return value shows success, // then self is put into slot *ioSlot. Note self can be nil, so we // permit expression 'nsIMdbHeap_SlotStrongHeap(0, ev, &slot)'. { nsIMdbHeap* heap = *ioSlot; if ( self != heap ) { if ( heap ) *ioSlot = 0; if (self && ev->Good()) *ioSlot = self; } } /*public static*/ void morkNode::SlotStrongNode(morkNode* me, morkEnv* ev, morkNode** ioSlot) // If *ioSlot is non-nil, that node is released by CutStrongRef() and // then zeroed out. Then if me is non-nil, this is acquired by // calling AddStrongRef(), and if positive is returned to show success, // then me is put into slot *ioSlot. Note me can be nil, so we take // expression 'morkNode::SlotStrongNode((morkNode*) 0, ev, &slot)'. { morkNode* node = *ioSlot; if ( me != node ) { if ( node ) { // what if this nulls out the ev and causes asserts? // can we move this after the CutStrongRef()? *ioSlot = 0; node->CutStrongRef(ev); } if ( me && me->AddStrongRef(ev) ) *ioSlot = me; } } /*public static*/ void morkNode::SlotWeakNode(morkNode* me, morkEnv* ev, morkNode** ioSlot) // If *ioSlot is non-nil, that node is released by CutWeakRef() and // then zeroed out. Then if me is non-nil, this is acquired by // calling AddWeakRef(), and if positive is returned to show success, // then me is put into slot *ioSlot. Note me can be nil, so we // expression 'morkNode::SlotWeakNode((morkNode*) 0, ev, &slot)'. { morkNode* node = *ioSlot; if ( me != node ) { if ( node ) { *ioSlot = 0; node->CutWeakRef(ev); } if ( me && me->AddWeakRef(ev) ) *ioSlot = me; } } /*public non-poly*/ mork_uses morkNode::AddStrongRef(morkEnv* ev) { mork_uses outUses = 0; if ( this->IsNode() ) { mork_uses uses = mNode_Uses; mork_refs refs = mNode_Refs; if ( refs < uses ) // need to fix broken refs/uses relation? { this->RefsUnderUsesWarning(ev); mNode_Refs = mNode_Uses = refs = uses; } if ( refs < morkNode_kMaxRefCount ) // not too great? { mNode_Refs = ++refs; mNode_Uses = ++uses; } else this->RefsOverflowWarning(ev); outUses = uses; } else this->NonNodeError(ev); return outUses; } /*private non-poly*/ mork_bool morkNode::cut_use_count(morkEnv* ev) // just one part of CutStrongRef() { mork_bool didCut = morkBool_kFalse; if ( this->IsNode() ) { mork_uses uses = mNode_Uses; if ( uses ) // not yet zero? mNode_Uses = --uses; else this->UsesUnderflowWarning(ev); didCut = morkBool_kTrue; if ( !mNode_Uses ) // last use gone? time to close node? { if ( this->IsOpenNode() ) { if ( !mNode_Refs ) // no outstanding reference? { this->RefsUnderflowWarning(ev); ++mNode_Refs; // prevent potential crash during close } this->CloseMorkNode(ev); // polymorphic self close // (Note CutNode() is not polymorphic -- so don't call that.) } } } else this->NonNodeError(ev); return didCut; } /*public non-poly*/ mork_uses morkNode::CutStrongRef(morkEnv* ev) { mork_refs outRefs = 0; if ( this->IsNode() ) { if ( this->cut_use_count(ev) ) outRefs = this->CutWeakRef(ev); } else this->NonNodeError(ev); return outRefs; } /*public non-poly*/ mork_refs morkNode::AddWeakRef(morkEnv* ev) { mork_refs outRefs = 0; if ( this->IsNode() ) { mork_refs refs = mNode_Refs; if ( refs < morkNode_kMaxRefCount ) // not too great? mNode_Refs = ++refs; else this->RefsOverflowWarning(ev); outRefs = refs; } else this->NonNodeError(ev); return outRefs; } /*public non-poly*/ mork_refs morkNode::CutWeakRef(morkEnv* ev) { mork_refs outRefs = 0; if ( this->IsNode() ) { mork_uses uses = mNode_Uses; mork_refs refs = mNode_Refs; if ( refs ) // not yet zero? mNode_Refs = --refs; else this->RefsUnderflowWarning(ev); if ( refs < uses ) // need to fix broken refs/uses relation? { this->RefsUnderUsesWarning(ev); mNode_Refs = mNode_Uses = refs = uses; } outRefs = refs; if ( !refs ) // last reference gone? time to destroy node? this->ZapOld(ev, mNode_Heap); // self destroy, use this no longer } else this->NonNodeError(ev); return outRefs; } static const char morkNode_kBroken[] = "broken"; /*public non-poly*/ const char* morkNode::GetNodeAccessAsString() const // e.g. "open", "shut", etc. { const char* outString = morkNode_kBroken; switch( mNode_Access ) { case morkAccess_kOpen: outString = "open"; break; case morkAccess_kClosing: outString = "closing"; break; case morkAccess_kShut: outString = "shut"; break; case morkAccess_kDead: outString = "dead"; break; } return outString; } /*public non-poly*/ const char* morkNode::GetNodeUsageAsString() const // e.g. "heap", "stack", etc. { const char* outString = morkNode_kBroken; switch( mNode_Usage ) { case morkUsage_kHeap: outString = "heap"; break; case morkUsage_kStack: outString = "stack"; break; case morkUsage_kMember: outString = "member"; break; case morkUsage_kGlobal: outString = "global"; break; case morkUsage_kPool: outString = "pool"; break; case morkUsage_kNone: outString = "none"; break; } return outString; } //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789