diff options
Diffstat (limited to 'db/mork/src/morkNode.cpp')
-rw-r--r-- | db/mork/src/morkNode.cpp | 592 |
1 files changed, 592 insertions, 0 deletions
diff --git a/db/mork/src/morkNode.cpp b/db/mork/src/morkNode.cpp new file mode 100644 index 000000000..823ce475b --- /dev/null +++ b/db/mork/src/morkNode.cpp @@ -0,0 +1,592 @@ +/* -*- 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<nsresult>(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 explictly 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 |