summaryrefslogtreecommitdiffstats
path: root/db/mork/src/morkNode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'db/mork/src/morkNode.cpp')
-rw-r--r--db/mork/src/morkNode.cpp592
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