1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #ifndef _MDB_
7 # include "mdb.h"
8 #endif
9
10 #ifndef _MORK_
11 # include "mork.h"
12 #endif
13
14 #ifndef _MORKNODE_
15 # include "morkNode.h"
16 #endif
17
18 #ifndef _MORKPOOL_
19 # include "morkPool.h"
20 #endif
21
22 #ifndef _MORKENV_
23 # include "morkEnv.h"
24 #endif
25
26 #ifndef _MORKHANDLE_
27 # include "morkHandle.h"
28 #endif
29
30 /*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/
31
32 /* ===== ===== ===== ===== morkUsage ===== ===== ===== ===== */
33
34 static morkUsage morkUsage_gHeap; // ensure EnsureReadyStaticUsage()
35 const morkUsage& morkUsage::kHeap = morkUsage_gHeap;
36
37 static morkUsage morkUsage_gStack; // ensure EnsureReadyStaticUsage()
38 const morkUsage& morkUsage::kStack = morkUsage_gStack;
39
40 static morkUsage morkUsage_gMember; // ensure EnsureReadyStaticUsage()
41 const morkUsage& morkUsage::kMember = morkUsage_gMember;
42
43 static morkUsage morkUsage_gGlobal; // ensure EnsureReadyStaticUsage()
44 const morkUsage& morkUsage::kGlobal = morkUsage_gGlobal;
45
46 static morkUsage morkUsage_gPool; // ensure EnsureReadyStaticUsage()
47 const morkUsage& morkUsage::kPool = morkUsage_gPool;
48
49 static morkUsage morkUsage_gNone; // ensure EnsureReadyStaticUsage()
50 const morkUsage& morkUsage::kNone = morkUsage_gNone;
51
52 // This must be structured to allow for non-zero values in global variables
53 // just before static init time. We can only safely check for whether a
54 // global has the address of some other global. Please, do not initialize
55 // either of the variables below to zero, because this could break when a zero
56 // is assigned at static init time, but after EnsureReadyStaticUsage() runs.
57
58 static mork_u4 morkUsage_g_static_init_target; // only address of this matters
59 static mork_u4* morkUsage_g_static_init_done; // is address of target above?
60
61 #define morkUsage_do_static_init() \
62 (morkUsage_g_static_init_done = &morkUsage_g_static_init_target)
63
64 #define morkUsage_need_static_init() \
65 (morkUsage_g_static_init_done != &morkUsage_g_static_init_target)
66
67 /*static*/
EnsureReadyStaticUsage()68 void morkUsage::EnsureReadyStaticUsage() {
69 if (morkUsage_need_static_init()) {
70 morkUsage_do_static_init();
71
72 morkUsage_gHeap.InitUsage(morkUsage_kHeap);
73 morkUsage_gStack.InitUsage(morkUsage_kStack);
74 morkUsage_gMember.InitUsage(morkUsage_kMember);
75 morkUsage_gGlobal.InitUsage(morkUsage_kGlobal);
76 morkUsage_gPool.InitUsage(morkUsage_kPool);
77 morkUsage_gNone.InitUsage(morkUsage_kNone);
78 }
79 }
80
81 /*static*/
GetHeap()82 const morkUsage& morkUsage::GetHeap() // kHeap safe at static init time
83 {
84 EnsureReadyStaticUsage();
85 return morkUsage_gHeap;
86 }
87
88 /*static*/
GetStack()89 const morkUsage& morkUsage::GetStack() // kStack safe at static init time
90 {
91 EnsureReadyStaticUsage();
92 return morkUsage_gStack;
93 }
94
95 /*static*/
GetMember()96 const morkUsage& morkUsage::GetMember() // kMember safe at static init time
97 {
98 EnsureReadyStaticUsage();
99 return morkUsage_gMember;
100 }
101
102 /*static*/
GetGlobal()103 const morkUsage& morkUsage::GetGlobal() // kGlobal safe at static init time
104 {
105 EnsureReadyStaticUsage();
106 return morkUsage_gGlobal;
107 }
108
109 /*static*/
GetPool()110 const morkUsage& morkUsage::GetPool() // kPool safe at static init time
111 {
112 EnsureReadyStaticUsage();
113 return morkUsage_gPool;
114 }
115
116 /*static*/
GetNone()117 const morkUsage& morkUsage::GetNone() // kNone safe at static init time
118 {
119 EnsureReadyStaticUsage();
120 return morkUsage_gNone;
121 }
122
morkUsage()123 morkUsage::morkUsage() {
124 if (morkUsage_need_static_init()) {
125 morkUsage::EnsureReadyStaticUsage();
126 }
127 }
128
morkUsage(mork_usage code)129 morkUsage::morkUsage(mork_usage code) : mUsage_Code(code) {
130 if (morkUsage_need_static_init()) {
131 morkUsage::EnsureReadyStaticUsage();
132 }
133 }
134
135 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
136
MakeNew(size_t inSize,nsIMdbHeap & ioHeap,morkEnv * ev)137 /*static*/ void* morkNode::MakeNew(size_t inSize, nsIMdbHeap& ioHeap,
138 morkEnv* ev) {
139 void* node = 0;
140 ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void**)&node);
141 if (!node) ev->OutOfMemoryError();
142
143 return node;
144 }
145
ZapOld(morkEnv * ev,nsIMdbHeap * ioHeap)146 /*public non-poly*/ void morkNode::ZapOld(morkEnv* ev, nsIMdbHeap* ioHeap) {
147 if (this->IsNode()) {
148 mork_usage usage = mNode_Usage; // mNode_Usage before ~morkNode
149 this->morkNode::~morkNode(); // first call polymorphic destructor
150 if (ioHeap) // was this node heap allocated?
151 ioHeap->Free(ev->AsMdbEnv(), this);
152 else if (usage == morkUsage_kPool) // mNode_Usage before ~morkNode
153 {
154 morkHandle* h = (morkHandle*)this;
155 if (h->IsHandle() && h->GoodHandleTag()) {
156 if (h->mHandle_Face) {
157 if (ev->mEnv_HandlePool)
158 ev->mEnv_HandlePool->ZapHandle(ev, h->mHandle_Face);
159 else if (h->mHandle_Env && h->mHandle_Env->mEnv_HandlePool)
160 h->mHandle_Env->mEnv_HandlePool->ZapHandle(ev, h->mHandle_Face);
161 } else
162 ev->NilPointerError();
163 }
164 }
165 } else
166 this->NonNodeError(ev);
167 }
168
CloseMorkNode(morkEnv * ev)169 /*public virtual*/ void morkNode::CloseMorkNode(
170 morkEnv* ev) // CloseNode() only if open
171 {
172 if (this->IsOpenNode()) {
173 this->MarkClosing();
174 this->CloseNode(ev);
175 this->MarkShut();
176 }
177 }
178 NS_IMETHODIMP
CloseMdbObject(nsIMdbEnv * mev)179 morkNode::CloseMdbObject(nsIMdbEnv* mev) {
180 return morkNode::CloseMdbObject((morkEnv*)mev);
181 }
182
CloseMdbObject(morkEnv * ev)183 nsresult morkNode::CloseMdbObject(morkEnv* ev) {
184 // if only one ref, Handle_CutStrongRef will clean up better.
185 if (mNode_Uses == 1)
186 // XXX Casting mork_uses to nsresult
187 return static_cast<nsresult>(CutStrongRef(ev));
188
189 nsresult outErr = NS_OK;
190
191 if (IsNode() && IsOpenNode()) {
192 if (ev) {
193 CloseMorkNode(ev);
194 outErr = ev->AsErr();
195 }
196 }
197 return outErr;
198 }
199
200 /*public virtual*/
~morkNode()201 morkNode::~morkNode() // assert that CloseNode() executed earlier
202 {
203 MORK_ASSERT(this->IsShutNode() ||
204 IsDeadNode()); // sometimes we call destructor explicitly w/o
205 // freeing object.
206 mNode_Access = morkAccess_kDead;
207 mNode_Usage = morkUsage_kNone;
208 }
209
210 /*public virtual*/
211 // void CloseMorkNode(morkEnv* ev) = 0; // CloseNode() only if open
212 // CloseMorkNode() is the polymorphic close method called when uses==0,
213 // which must do NOTHING at all when IsOpenNode() is not true. Otherwise,
214 // CloseMorkNode() should call a static close method specific to an object.
215 // Each such static close method should either call inherited static close
216 // methods, or else perform the consolidated effect of calling them, where
217 // subclasses should closely track any changes in base classes with care.
218
219 /*public non-poly*/
morkNode(mork_usage inCode)220 morkNode::morkNode(mork_usage inCode)
221 : mNode_Heap(0),
222 mNode_Base(morkBase_kNode),
223 mNode_Derived(0) // until subclass sets appropriately
224 ,
225 mNode_Access(morkAccess_kOpen),
226 mNode_Usage(inCode),
227 mNode_Mutable(morkAble_kEnabled),
228 mNode_Load(morkLoad_kClean),
229 mNode_Uses(1),
230 mNode_Refs(1) {}
231
232 /*public non-poly*/
morkNode(const morkUsage & inUsage,nsIMdbHeap * ioHeap)233 morkNode::morkNode(const morkUsage& inUsage, nsIMdbHeap* ioHeap)
234 : mNode_Heap(ioHeap),
235 mNode_Base(morkBase_kNode),
236 mNode_Derived(0) // until subclass sets appropriately
237 ,
238 mNode_Access(morkAccess_kOpen),
239 mNode_Usage(inUsage.Code()),
240 mNode_Mutable(morkAble_kEnabled),
241 mNode_Load(morkLoad_kClean),
242 mNode_Uses(1),
243 mNode_Refs(1) {
244 if (!ioHeap && mNode_Usage == morkUsage_kHeap) MORK_ASSERT(ioHeap);
245 }
246
247 /*public non-poly*/
morkNode(morkEnv * ev,const morkUsage & inUsage,nsIMdbHeap * ioHeap)248 morkNode::morkNode(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap)
249 : mNode_Heap(ioHeap),
250 mNode_Base(morkBase_kNode),
251 mNode_Derived(0) // until subclass sets appropriately
252 ,
253 mNode_Access(morkAccess_kOpen),
254 mNode_Usage(inUsage.Code()),
255 mNode_Mutable(morkAble_kEnabled),
256 mNode_Load(morkLoad_kClean),
257 mNode_Uses(1),
258 mNode_Refs(1) {
259 if (!ioHeap && mNode_Usage == morkUsage_kHeap) {
260 this->NilHeapError(ev);
261 }
262 }
263
RefsUnderUsesWarning(morkEnv * ev) const264 /*protected non-poly*/ void morkNode::RefsUnderUsesWarning(morkEnv* ev) const {
265 ev->NewError("mNode_Refs < mNode_Uses");
266 }
267
NonNodeError(morkEnv * ev) const268 /*protected non-poly*/ void morkNode::NonNodeError(
269 morkEnv* ev) const // called when IsNode() is false
270 {
271 ev->NewError("non-morkNode");
272 }
273
NonOpenNodeError(morkEnv * ev) const274 /*protected non-poly*/ void morkNode::NonOpenNodeError(
275 morkEnv* ev) const // when IsOpenNode() is false
276 {
277 ev->NewError("non-open-morkNode");
278 }
279
NonMutableNodeError(morkEnv * ev) const280 /*protected non-poly*/ void morkNode::NonMutableNodeError(
281 morkEnv* ev) const // when IsMutable() is false
282 {
283 ev->NewError("non-mutable-morkNode");
284 }
285
NilHeapError(morkEnv * ev) const286 /*protected non-poly*/ void morkNode::NilHeapError(
287 morkEnv* ev) const // zero mNode_Heap w/ kHeap usage
288 {
289 ev->NewError("nil mNode_Heap");
290 }
291
RefsOverflowWarning(morkEnv * ev) const292 /*protected non-poly*/ void morkNode::RefsOverflowWarning(
293 morkEnv* ev) const // mNode_Refs overflow
294 {
295 ev->NewWarning("mNode_Refs overflow");
296 }
297
UsesOverflowWarning(morkEnv * ev) const298 /*protected non-poly*/ void morkNode::UsesOverflowWarning(
299 morkEnv* ev) const // mNode_Uses overflow
300 {
301 ev->NewWarning("mNode_Uses overflow");
302 }
303
RefsUnderflowWarning(morkEnv * ev) const304 /*protected non-poly*/ void morkNode::RefsUnderflowWarning(
305 morkEnv* ev) const // mNode_Refs underflow
306 {
307 ev->NewWarning("mNode_Refs underflow");
308 }
309
UsesUnderflowWarning(morkEnv * ev) const310 /*protected non-poly*/ void morkNode::UsesUnderflowWarning(
311 morkEnv* ev) const // mNode_Uses underflow
312 {
313 ev->NewWarning("mNode_Uses underflow");
314 }
315
CloseNode(morkEnv * ev)316 /*public non-poly*/ void morkNode::CloseNode(
317 morkEnv* ev) // called by CloseMorkNode();
318 {
319 if (this->IsNode())
320 this->MarkShut();
321 else
322 this->NonNodeError(ev);
323 }
324
325 extern void // utility method very similar to morkNode::SlotStrongNode():
nsIMdbFile_SlotStrongFile(nsIMdbFile * self,morkEnv * ev,nsIMdbFile ** ioSlot)326 nsIMdbFile_SlotStrongFile(nsIMdbFile* self, morkEnv* ev, nsIMdbFile** ioSlot)
327 // If *ioSlot is non-nil, that file is released by CutStrongRef() and
328 // then zeroed out. Then if self is non-nil, this is acquired by
329 // calling AddStrongRef(), and if the return value shows success,
330 // then self is put into slot *ioSlot. Note self can be nil, so we take
331 // expression 'nsIMdbFile_SlotStrongFile(0, ev, &slot)'.
332 {
333 nsIMdbFile* file = *ioSlot;
334 if (self != file) {
335 if (file) {
336 *ioSlot = 0;
337 NS_RELEASE(file);
338 }
339 if (self && ev->Good()) NS_ADDREF(*ioSlot = self);
340 }
341 }
342
343 void // utility method very similar to morkNode::SlotStrongNode():
nsIMdbHeap_SlotStrongHeap(nsIMdbHeap * self,morkEnv * ev,nsIMdbHeap ** ioSlot)344 nsIMdbHeap_SlotStrongHeap(nsIMdbHeap* self, morkEnv* ev, nsIMdbHeap** ioSlot)
345 // If *ioSlot is non-nil, that heap is released by CutStrongRef() and
346 // then zeroed out. Then if self is non-nil, self is acquired by
347 // calling AddStrongRef(), and if the return value shows success,
348 // then self is put into slot *ioSlot. Note self can be nil, so we
349 // permit expression 'nsIMdbHeap_SlotStrongHeap(0, ev, &slot)'.
350 {
351 nsIMdbHeap* heap = *ioSlot;
352 if (self != heap) {
353 if (heap) *ioSlot = 0;
354
355 if (self && ev->Good()) *ioSlot = self;
356 }
357 }
358
SlotStrongNode(morkNode * me,morkEnv * ev,morkNode ** ioSlot)359 /*public static*/ void morkNode::SlotStrongNode(morkNode* me, morkEnv* ev,
360 morkNode** ioSlot)
361 // If *ioSlot is non-nil, that node is released by CutStrongRef() and
362 // then zeroed out. Then if me is non-nil, this is acquired by
363 // calling AddStrongRef(), and if positive is returned to show success,
364 // then me is put into slot *ioSlot. Note me can be nil, so we take
365 // expression 'morkNode::SlotStrongNode((morkNode*) 0, ev, &slot)'.
366 {
367 morkNode* node = *ioSlot;
368 if (me != node) {
369 if (node) {
370 // what if this nulls out the ev and causes asserts?
371 // can we move this after the CutStrongRef()?
372 *ioSlot = 0;
373 node->CutStrongRef(ev);
374 }
375 if (me && me->AddStrongRef(ev)) *ioSlot = me;
376 }
377 }
378
SlotWeakNode(morkNode * me,morkEnv * ev,morkNode ** ioSlot)379 /*public static*/ void morkNode::SlotWeakNode(morkNode* me, morkEnv* ev,
380 morkNode** ioSlot)
381 // If *ioSlot is non-nil, that node is released by CutWeakRef() and
382 // then zeroed out. Then if me is non-nil, this is acquired by
383 // calling AddWeakRef(), and if positive is returned to show success,
384 // then me is put into slot *ioSlot. Note me can be nil, so we
385 // expression 'morkNode::SlotWeakNode((morkNode*) 0, ev, &slot)'.
386 {
387 morkNode* node = *ioSlot;
388 if (me != node) {
389 if (node) {
390 *ioSlot = 0;
391 node->CutWeakRef(ev);
392 }
393 if (me && me->AddWeakRef(ev)) *ioSlot = me;
394 }
395 }
396
AddStrongRef(morkEnv * ev)397 /*public non-poly*/ mork_uses morkNode::AddStrongRef(morkEnv* ev) {
398 mork_uses outUses = 0;
399 if (this->IsNode()) {
400 mork_uses uses = mNode_Uses;
401 mork_refs refs = mNode_Refs;
402 if (refs < uses) // need to fix broken refs/uses relation?
403 {
404 this->RefsUnderUsesWarning(ev);
405 mNode_Refs = mNode_Uses = refs = uses;
406 }
407 if (refs < morkNode_kMaxRefCount) // not too great?
408 {
409 mNode_Refs = ++refs;
410 mNode_Uses = ++uses;
411 } else
412 this->RefsOverflowWarning(ev);
413
414 outUses = uses;
415 } else
416 this->NonNodeError(ev);
417 return outUses;
418 }
419
cut_use_count(morkEnv * ev)420 /*private non-poly*/ mork_bool morkNode::cut_use_count(
421 morkEnv* ev) // just one part of CutStrongRef()
422 {
423 mork_bool didCut = morkBool_kFalse;
424 if (this->IsNode()) {
425 mork_uses uses = mNode_Uses;
426 if (uses) // not yet zero?
427 mNode_Uses = --uses;
428 else
429 this->UsesUnderflowWarning(ev);
430
431 didCut = morkBool_kTrue;
432 if (!mNode_Uses) // last use gone? time to close node?
433 {
434 if (this->IsOpenNode()) {
435 if (!mNode_Refs) // no outstanding reference?
436 {
437 this->RefsUnderflowWarning(ev);
438 ++mNode_Refs; // prevent potential crash during close
439 }
440 this->CloseMorkNode(ev); // polymorphic self close
441 // (Note CutNode() is not polymorphic -- so don't call that.)
442 }
443 }
444 } else
445 this->NonNodeError(ev);
446 return didCut;
447 }
448
CutStrongRef(morkEnv * ev)449 /*public non-poly*/ mork_uses morkNode::CutStrongRef(morkEnv* ev) {
450 mork_refs outRefs = 0;
451 if (this->IsNode()) {
452 if (this->cut_use_count(ev)) outRefs = this->CutWeakRef(ev);
453 } else
454 this->NonNodeError(ev);
455
456 return outRefs;
457 }
458
AddWeakRef(morkEnv * ev)459 /*public non-poly*/ mork_refs morkNode::AddWeakRef(morkEnv* ev) {
460 mork_refs outRefs = 0;
461 if (this->IsNode()) {
462 mork_refs refs = mNode_Refs;
463 if (refs < morkNode_kMaxRefCount) // not too great?
464 mNode_Refs = ++refs;
465 else
466 this->RefsOverflowWarning(ev);
467
468 outRefs = refs;
469 } else
470 this->NonNodeError(ev);
471
472 return outRefs;
473 }
474
CutWeakRef(morkEnv * ev)475 /*public non-poly*/ mork_refs morkNode::CutWeakRef(morkEnv* ev) {
476 mork_refs outRefs = 0;
477 if (this->IsNode()) {
478 mork_uses uses = mNode_Uses;
479 mork_refs refs = mNode_Refs;
480 if (refs) // not yet zero?
481 mNode_Refs = --refs;
482 else
483 this->RefsUnderflowWarning(ev);
484
485 if (refs < uses) // need to fix broken refs/uses relation?
486 {
487 this->RefsUnderUsesWarning(ev);
488 mNode_Refs = mNode_Uses = refs = uses;
489 }
490
491 outRefs = refs;
492 if (!refs) // last reference gone? time to destroy node?
493 this->ZapOld(ev, mNode_Heap); // self destroy, use this no longer
494 } else
495 this->NonNodeError(ev);
496
497 return outRefs;
498 }
499
500 static const char morkNode_kBroken[] = "broken";
501
GetNodeAccessAsString() const502 /*public non-poly*/ const char* morkNode::GetNodeAccessAsString()
503 const // e.g. "open", "shut", etc.
504 {
505 const char* outString = morkNode_kBroken;
506 switch (mNode_Access) {
507 case morkAccess_kOpen:
508 outString = "open";
509 break;
510 case morkAccess_kClosing:
511 outString = "closing";
512 break;
513 case morkAccess_kShut:
514 outString = "shut";
515 break;
516 case morkAccess_kDead:
517 outString = "dead";
518 break;
519 }
520 return outString;
521 }
522
GetNodeUsageAsString() const523 /*public non-poly*/ const char* morkNode::GetNodeUsageAsString()
524 const // e.g. "heap", "stack", etc.
525 {
526 const char* outString = morkNode_kBroken;
527 switch (mNode_Usage) {
528 case morkUsage_kHeap:
529 outString = "heap";
530 break;
531 case morkUsage_kStack:
532 outString = "stack";
533 break;
534 case morkUsage_kMember:
535 outString = "member";
536 break;
537 case morkUsage_kGlobal:
538 outString = "global";
539 break;
540 case morkUsage_kPool:
541 outString = "pool";
542 break;
543 case morkUsage_kNone:
544 outString = "none";
545 break;
546 }
547 return outString;
548 }
549
550 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
551