Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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<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 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