Back to index

lightning-sunbird  0.9+nobinonly
morkZone.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #ifndef _MDB_
00039 #include "mdb.h"
00040 #endif
00041 
00042 #ifndef _MORK_
00043 #include "mork.h"
00044 #endif
00045 
00046 #ifndef _MORKNODE_
00047 #include "morkNode.h"
00048 #endif
00049 
00050 #ifndef _MORKZONE_
00051 #include "morkZone.h"
00052 #endif
00053 
00054 #ifndef _MORKENV_
00055 #include "morkEnv.h"
00056 #endif
00057 
00058 //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
00059 
00060 // { ===== begin morkNode interface =====
00061 // public: // morkNode virtual methods
00062 void morkZone::CloseMorkNode(morkEnv* ev) // CloseZone() only if open
00063 {
00064   if ( this->IsOpenNode() )
00065   {
00066     this->MarkClosing();
00067     this->CloseZone(ev);
00068     this->MarkShut();
00069   }
00070 }
00071 
00072 morkZone::~morkZone() // assert that CloseZone() executed earlier
00073 {
00074   MORK_ASSERT(this->IsShutNode());
00075 }
00076 
00077 // public: // morkMap construction & destruction
00078 morkZone::morkZone(morkEnv* ev, const morkUsage& inUsage,
00079   nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioZoneHeap)
00080 : morkNode(ev, inUsage, ioNodeHeap)
00081 , mZone_Heap( 0 )
00082 , mZone_HeapVolume( 0 )
00083 , mZone_BlockVolume( 0 )
00084 , mZone_RunVolume( 0 )
00085 , mZone_ChipVolume( 0 )
00086   
00087 , mZone_FreeOldRunVolume( 0 )
00088   
00089 , mZone_HunkCount( 0 )
00090 , mZone_FreeOldRunCount( 0 )
00091 
00092 , mZone_HunkList( 0 )
00093 , mZone_FreeOldRunList( 0 )
00094   
00095 , mZone_At( 0 )
00096 , mZone_AtSize( 0 )
00097     
00098   // morkRun*     mZone_FreeRuns[ morkZone_kBuckets + 1 ];
00099 {
00100 
00101   morkRun** runs = mZone_FreeRuns;
00102   morkRun** end = runs + (morkZone_kBuckets + 1); // one past last slot
00103   --runs; // prepare for preincrement
00104   while ( ++runs < end ) // another slot in array?
00105     *runs = 0; // clear all the slots
00106   
00107   if ( ev->Good() )
00108   {
00109     if ( ioZoneHeap )
00110     {
00111       nsIMdbHeap_SlotStrongHeap(ioZoneHeap, ev, &mZone_Heap);
00112       if ( ev->Good() )
00113         mNode_Derived = morkDerived_kZone;
00114     }
00115     else
00116       ev->NilPointerError();
00117   }
00118 }
00119 
00120 void morkZone::CloseZone(morkEnv* ev) // called by CloseMorkNode()
00121 {
00122   if ( this )
00123   {
00124     if ( this->IsNode() )
00125     {
00126       nsIMdbHeap* heap = mZone_Heap;
00127       if ( heap )
00128       {
00129         morkHunk* hunk = 0;
00130         nsIMdbEnv* mev = ev->AsMdbEnv();
00131         
00132         morkHunk* next = mZone_HunkList;
00133         while ( ( hunk = next ) != 0 )
00134         {
00135 #ifdef morkHunk_USE_TAG_SLOT
00136           if ( !hunk->HunkGoodTag()  )
00137             hunk->BadHunkTagWarning(ev);
00138 #endif /* morkHunk_USE_TAG_SLOT */
00139 
00140           next = hunk->HunkNext();
00141           heap->Free(mev, hunk);
00142         }
00143       }
00144       nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mZone_Heap);
00145       this->MarkShut();
00146     }
00147     else
00148       this->NonNodeError(ev);
00149   }
00150   else
00151     ev->NilPointerError();
00152 }
00153 
00154 // } ===== end morkNode methods =====
00155 
00156 /*static*/ void
00157 morkZone::NonZoneTypeError(morkEnv* ev)
00158 {
00159   ev->NewError("non morkZone");
00160 }
00161 
00162 /*static*/ void
00163 morkZone::NilZoneHeapError(morkEnv* ev)
00164 {
00165   ev->NewError("nil mZone_Heap");
00166 }
00167 
00168 /*static*/ void
00169 morkHunk::BadHunkTagWarning(morkEnv* ev)
00170 {
00171   ev->NewWarning("bad mHunk_Tag");
00172 }
00173 
00174 /*static*/ void
00175 morkRun::BadRunTagError(morkEnv* ev)
00176 {
00177   ev->NewError("bad mRun_Tag");
00178 }
00179 
00180 /*static*/ void
00181 morkRun::RunSizeAlignError(morkEnv* ev)
00182 {
00183   ev->NewError("bad RunSize() alignment");
00184 }
00185 
00186 // { ===== begin morkZone methods =====
00187 
00188 
00189 mork_size morkZone::zone_grow_at(morkEnv* ev, mork_size inNeededSize)
00190 {
00191   mZone_At = 0;       // remove any ref to current hunk
00192   mZone_AtSize = 0;   // zero available bytes in current hunk
00193   
00194   mork_size runSize = 0; // actual size of a particular run
00195   
00196   // try to find a run in old run list with at least inNeededSize bytes:
00197   morkRun* run = mZone_FreeOldRunList; // cursor in list scan
00198   morkRun* prev = 0; // the node before run in the list scan
00199  
00200   while ( run ) // another run in list to check?
00201   {
00202     morkOldRun* oldRun = (morkOldRun*) run;
00203     mork_size oldSize = oldRun->OldSize();
00204     if ( oldSize >= inNeededSize ) // found one big enough?
00205     {
00206       runSize = oldSize;
00207       break; // end while loop early
00208     }
00209     prev = run; // remember last position in singly linked list
00210     run = run->RunNext(); // advance cursor to next node in list
00211   }
00212   if ( runSize && run ) // found a usable old run?
00213   {
00214     morkRun* next = run->RunNext();
00215     if ( prev ) // another node in free list precedes run?
00216       prev->RunSetNext(next); // unlink run
00217     else
00218       mZone_FreeOldRunList = next; // unlink run from head of list
00219       
00220     morkOldRun *oldRun = (morkOldRun *) run;
00221     oldRun->OldSetSize(runSize);
00222     mZone_At = (mork_u1*) run;
00223     mZone_AtSize = runSize;
00224 
00225 #ifdef morkZone_CONFIG_DEBUG
00226 #ifdef morkZone_CONFIG_ALIGN_8
00227     mork_ip lowThree = ((mork_ip) mZone_At) & 7;
00228     if ( lowThree ) // not 8 byte aligned?
00229 #else /*morkZone_CONFIG_ALIGN_8*/
00230     mork_ip lowTwo = ((mork_ip) mZone_At) & 3;
00231     if ( lowTwo ) // not 4 byte aligned?
00232 #endif /*morkZone_CONFIG_ALIGN_8*/
00233       ev->NewWarning("mZone_At not aligned");
00234 #endif /*morkZone_CONFIG_DEBUG*/
00235   }
00236   else // need to allocate a brand new run
00237   {
00238     inNeededSize += 7; // allow for possible alignment padding
00239     mork_size newSize = ( inNeededSize > morkZone_kNewHunkSize )?
00240       inNeededSize : morkZone_kNewHunkSize;
00241       
00242     morkHunk* hunk = this->zone_new_hunk(ev, newSize);
00243     if ( hunk )
00244     {
00245       morkRun* hunkRun = hunk->HunkRun();
00246       mork_u1* at = (mork_u1*) hunkRun->RunAsBlock();
00247       mork_ip lowBits = ((mork_ip) at) & 7;
00248       if ( lowBits ) // not 8 byte aligned?
00249       {
00250         mork_ip skip = (8 - lowBits); // skip the complement to align
00251         at += skip;
00252         newSize -= skip;
00253       }
00254       mZone_At = at;
00255       mZone_AtSize = newSize;
00256     }
00257   }
00258   
00259   return mZone_AtSize;
00260 }
00261 
00262 morkHunk* morkZone::zone_new_hunk(morkEnv* ev, mdb_size inSize) // alloc  
00263 {
00264   mdb_size hunkSize = inSize + sizeof(morkHunk);
00265   void* outBlock = 0; // we are going straight to the heap:
00266   mZone_Heap->Alloc(ev->AsMdbEnv(), hunkSize, &outBlock);
00267   if ( outBlock )
00268   {
00269 #ifdef morkZone_CONFIG_VOL_STATS
00270     mZone_HeapVolume += hunkSize; // track all heap allocations
00271 #endif /* morkZone_CONFIG_VOL_STATS */
00272   
00273     morkHunk* hunk = (morkHunk*) outBlock;
00274 #ifdef morkHunk_USE_TAG_SLOT
00275     hunk->HunkInitTag();
00276 #endif /* morkHunk_USE_TAG_SLOT */
00277   
00278     hunk->HunkSetNext(mZone_HunkList);
00279     mZone_HunkList = hunk;
00280     ++mZone_HunkCount;
00281     
00282     morkRun* run = hunk->HunkRun();
00283     run->RunSetSize(inSize);
00284 #ifdef morkRun_USE_TAG_SLOT
00285     run->RunInitTag();
00286 #endif /* morkRun_USE_TAG_SLOT */
00287     
00288     return hunk;
00289   }
00290   if ( ev->Good() ) // got this far without any error reported yet?
00291     ev->OutOfMemoryError();
00292   return (morkHunk*) 0;
00293 }
00294 
00295 void* morkZone::zone_new_chip(morkEnv* ev, mdb_size inSize) // alloc  
00296 {
00297 #ifdef morkZone_CONFIG_VOL_STATS
00298   mZone_BlockVolume += inSize; // sum sizes of both chips and runs
00299 #endif /* morkZone_CONFIG_VOL_STATS */
00300   
00301   mork_u1* at = mZone_At;
00302   mork_size atSize = mZone_AtSize; // available bytes in current hunk
00303   if ( atSize >= inSize ) // current hunk can satisfy request?
00304   {
00305     mZone_At = at + inSize;
00306     mZone_AtSize = atSize - inSize;
00307     return at;
00308   }
00309   else if ( atSize > morkZone_kMaxHunkWaste ) // over max waste allowed?
00310   {
00311     morkHunk* hunk = this->zone_new_hunk(ev, inSize);
00312     if ( hunk )
00313       return hunk->HunkRun();
00314       
00315     return (void*) 0; // show allocation has failed
00316   }
00317   else // get ourselves a new hunk for suballocation:
00318   {
00319     atSize = this->zone_grow_at(ev, inSize); // get a new hunk
00320   }
00321 
00322   if ( atSize >= inSize ) // current hunk can satisfy request?
00323   {
00324     at = mZone_At;
00325     mZone_At = at + inSize;
00326     mZone_AtSize = atSize - inSize;
00327     return at;
00328   }
00329   
00330   if ( ev->Good() ) // got this far without any error reported yet?
00331     ev->OutOfMemoryError();
00332     
00333   return (void*) 0; // show allocation has failed
00334 }
00335 
00336 void* morkZone::ZoneNewChip(morkEnv* ev, mdb_size inSize) // alloc  
00337 {
00338 #ifdef morkZone_CONFIG_ARENA
00339 
00340 #ifdef morkZone_CONFIG_DEBUG
00341   if ( !this->IsZone() )
00342     this->NonZoneTypeError(ev);
00343   else if ( !mZone_Heap )
00344     this->NilZoneHeapError(ev);
00345 #endif /*morkZone_CONFIG_DEBUG*/
00346 
00347 #ifdef morkZone_CONFIG_ALIGN_8
00348   inSize += 7;
00349   inSize &= ~((mork_ip) 7); // force to multiple of 8 bytes
00350 #else /*morkZone_CONFIG_ALIGN_8*/
00351   inSize += 3;
00352   inSize &= ~((mork_ip) 3); // force to multiple of 4 bytes
00353 #endif /*morkZone_CONFIG_ALIGN_8*/
00354 
00355 #ifdef morkZone_CONFIG_VOL_STATS
00356   mZone_ChipVolume += inSize; // sum sizes of chips only
00357 #endif /* morkZone_CONFIG_VOL_STATS */
00358 
00359   return this->zone_new_chip(ev, inSize);
00360 
00361 #else /*morkZone_CONFIG_ARENA*/
00362   void* outBlock = 0;
00363   mZone_Heap->Alloc(ev->AsMdbEnv(), inSize, &outBlock);
00364   return outBlock;
00365 #endif /*morkZone_CONFIG_ARENA*/
00366   
00367 }
00368   
00369 // public: // ...but runs do indeed know how big they are
00370 void* morkZone::ZoneNewRun(morkEnv* ev, mdb_size inSize) // alloc  
00371 {
00372 #ifdef morkZone_CONFIG_ARENA
00373 
00374 #ifdef morkZone_CONFIG_DEBUG
00375   if ( !this->IsZone() )
00376     this->NonZoneTypeError(ev);
00377   else if ( !mZone_Heap )
00378     this->NilZoneHeapError(ev);
00379 #endif /*morkZone_CONFIG_DEBUG*/
00380 
00381   inSize += morkZone_kRoundAdd;
00382   inSize &= morkZone_kRoundMask;
00383   if ( inSize <= morkZone_kMaxCachedRun )
00384   {
00385     morkRun** bucket = mZone_FreeRuns + (inSize >> morkZone_kRoundBits);
00386     morkRun* hit = *bucket;
00387     if ( hit ) // cache hit?
00388     {
00389       *bucket = hit->RunNext();
00390       hit->RunSetSize(inSize);
00391       return hit->RunAsBlock();
00392     }
00393   }
00394   mdb_size blockSize = inSize + sizeof(morkRun); // plus run overhead
00395 #ifdef morkZone_CONFIG_VOL_STATS
00396   mZone_RunVolume += blockSize; // sum sizes of runs only
00397 #endif /* morkZone_CONFIG_VOL_STATS */
00398   morkRun* run = (morkRun*) this->zone_new_chip(ev, blockSize);
00399   if ( run )
00400   {
00401     run->RunSetSize(inSize);
00402 #ifdef morkRun_USE_TAG_SLOT
00403     run->RunInitTag();
00404 #endif /* morkRun_USE_TAG_SLOT */
00405     return run->RunAsBlock();
00406   }
00407   
00408   if ( ev->Good() ) // got this far without any error reported yet?
00409     ev->OutOfMemoryError();
00410   
00411   return (void*) 0; // indicate failed allocation
00412 
00413 #else /*morkZone_CONFIG_ARENA*/
00414   void* outBlock = 0;
00415   mZone_Heap->Alloc(ev->AsMdbEnv(), inSize, &outBlock);
00416   return outBlock;
00417 #endif /*morkZone_CONFIG_ARENA*/
00418 }
00419 
00420 void morkZone::ZoneZapRun(morkEnv* ev, void* ioRunBlock) // free
00421 {
00422 #ifdef morkZone_CONFIG_ARENA
00423 
00424   morkRun* run = morkRun::BlockAsRun(ioRunBlock);
00425   mdb_size runSize = run->RunSize();
00426 #ifdef morkZone_CONFIG_VOL_STATS
00427   mZone_BlockVolume -= runSize; // tracking sizes of both chips and runs
00428 #endif /* morkZone_CONFIG_VOL_STATS */
00429 
00430 #ifdef morkZone_CONFIG_DEBUG
00431   if ( !this->IsZone() )
00432     this->NonZoneTypeError(ev);
00433   else if ( !mZone_Heap )
00434     this->NilZoneHeapError(ev);
00435   else if ( !ioRunBlock )
00436     ev->NilPointerError();
00437   else if ( runSize & morkZone_kRoundAdd )
00438     run->RunSizeAlignError(ev);
00439 #ifdef morkRun_USE_TAG_SLOT
00440   else if ( !run->RunGoodTag() )
00441     run->BadRunTagError(ev);
00442 #endif /* morkRun_USE_TAG_SLOT */
00443 #endif /*morkZone_CONFIG_DEBUG*/
00444 
00445   if ( runSize <= morkZone_kMaxCachedRun ) // goes into free run list?
00446   {
00447     morkRun** bucket = mZone_FreeRuns + (runSize >> morkZone_kRoundBits);
00448     run->RunSetNext(*bucket); // push onto free run list
00449     *bucket = run;
00450   }
00451   else // free old run list
00452   {
00453     run->RunSetNext(mZone_FreeOldRunList); // push onto free old run list
00454     mZone_FreeOldRunList = run;
00455     ++mZone_FreeOldRunCount;
00456 #ifdef morkZone_CONFIG_VOL_STATS
00457     mZone_FreeOldRunVolume += runSize;
00458 #endif /* morkZone_CONFIG_VOL_STATS */
00459 
00460     morkOldRun* oldRun = (morkOldRun*) run; // to access extra size slot
00461     oldRun->OldSetSize(runSize); // so we know how big this is later
00462   }
00463 
00464 #else /*morkZone_CONFIG_ARENA*/
00465   mZone_Heap->Free(ev->AsMdbEnv(), ioRunBlock);
00466 #endif /*morkZone_CONFIG_ARENA*/
00467 }
00468 
00469 void* morkZone::ZoneGrowRun(morkEnv* ev, void* ioRunBlock, mdb_size inSize)
00470 {
00471 #ifdef morkZone_CONFIG_ARENA
00472 
00473   morkRun* run = morkRun::BlockAsRun(ioRunBlock);
00474   mdb_size runSize = run->RunSize();
00475 
00476 #ifdef morkZone_CONFIG_DEBUG
00477   if ( !this->IsZone() )
00478     this->NonZoneTypeError(ev);
00479   else if ( !mZone_Heap )
00480     this->NilZoneHeapError(ev);
00481 #endif /*morkZone_CONFIG_DEBUG*/
00482 
00483 #ifdef morkZone_CONFIG_ALIGN_8
00484   inSize += 7;
00485   inSize &= ~((mork_ip) 7); // force to multiple of 8 bytes
00486 #else /*morkZone_CONFIG_ALIGN_8*/
00487   inSize += 3;
00488   inSize &= ~((mork_ip) 3); // force to multiple of 4 bytes
00489 #endif /*morkZone_CONFIG_ALIGN_8*/
00490 
00491   if ( inSize > runSize )
00492   {
00493     void* newBuf = this->ZoneNewRun(ev, inSize);
00494     if ( newBuf )
00495     {
00496       MORK_MEMCPY(newBuf, ioRunBlock, runSize);
00497       this->ZoneZapRun(ev, ioRunBlock);
00498       
00499       return newBuf;
00500     }
00501   }
00502   else
00503     return ioRunBlock; // old size is big enough
00504   
00505   if ( ev->Good() ) // got this far without any error reported yet?
00506     ev->OutOfMemoryError();
00507   
00508   return (void*) 0; // indicate failed allocation
00509 
00510 #else /*morkZone_CONFIG_ARENA*/
00511   void* outBlock = 0;
00512   mZone_Heap->Free(ev->AsMdbEnv(), ioRunBlock);
00513   return outBlock;
00514 #endif /*morkZone_CONFIG_ARENA*/
00515 }
00516 
00517 //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
00518 
00519 // { ===== begin nsIMdbHeap methods =====
00520 /*virtual*/ mdb_err
00521 morkZone::Alloc(nsIMdbEnv* mev, // allocate a piece of memory
00522   mdb_size inSize,   // requested size of new memory block 
00523   void** outBlock)  // memory block of inSize bytes, or nil
00524 {
00525   mdb_err outErr = 0;
00526   void* block = 0;
00527   morkEnv* ev = morkEnv::FromMdbEnv(mev);
00528   if ( ev )
00529   {
00530     block = this->ZoneNewRun(ev, inSize);
00531     outErr = ev->AsErr();
00532   }
00533   else
00534     outErr = 1;
00535     
00536   if ( outBlock )
00537     *outBlock = block;
00538     
00539   return outErr;
00540 }
00541   
00542 /*virtual*/ mdb_err
00543 morkZone::Free(nsIMdbEnv* mev, // free block allocated earlier by Alloc()
00544   void* inBlock)
00545 {
00546   mdb_err outErr = 0;
00547   if ( inBlock )
00548   {
00549     morkEnv* ev = morkEnv::FromMdbEnv(mev);
00550     if ( ev )
00551     {
00552       this->ZoneZapRun(ev, inBlock);
00553       outErr = ev->AsErr();
00554     }
00555     else
00556       outErr = 1;
00557   }
00558     
00559   return outErr;
00560 }
00561 
00562 /*virtual*/ mdb_err
00563 morkZone::HeapAddStrongRef(nsIMdbEnv* mev) // does nothing
00564 {
00565   MORK_USED_1(mev);
00566   return 0;
00567 }
00568 
00569 /*virtual*/ mdb_err
00570 morkZone::HeapCutStrongRef(nsIMdbEnv* mev) // does nothing
00571 {
00572   MORK_USED_1(mev);
00573   return 0;
00574 }
00575 
00576 // } ===== end nsIMdbHeap methods =====
00577