/* -*- 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(); } } // 456789_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; } // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789