Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

VPKFile.C

Go to the documentation of this file.
00001 // Copyright (C) 2001, Compaq Computer Corporation
00002 // 
00003 // This file is part of Vesta.
00004 // 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 // 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 // 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 // Last modified on Fri Apr 29 19:43:15 EDT 2005 by ken@xorian.net
00020 //      modified on Thu Feb 10 12:40:09 PST 2000 by heydon
00021 
00022 // vesta/basics
00023 #include <Basics.H>
00024 #include <IntKey.H>
00025 #include <FS.H>
00026 #include <Sequence.H>
00027 #include <Generics.H>
00028 
00029 // vesta/srpc
00030 #include <SRPC.H>
00031 
00032 // vesta/fp
00033 #include <FP.H>
00034 
00035 // vesta/cache-common
00036 #include <PKPrefix.H>
00037 #include <BitVector.H>
00038 #include <CacheIntf.H>
00039 #include <Debug.H>
00040 #include <CacheState.H>
00041 #include <Model.H>
00042 #include <PKEpoch.H>
00043 #include <CacheIndex.H>
00044 #include <FV.H>
00045 #include <VestaVal.H>
00046 
00047 // vesta/cache-server
00048 #include "CacheConfigServer.H"
00049 #include "IntIntTblLR.H"
00050 #include "Combine.H"
00051 #include "CacheEntry.H"
00052 #include "VPKFileChkPt.H"
00053 #include "SPKFile.H"
00054 #include "VPKFile.H"
00055 #include "SMultiPKFile.H"
00056 
00057 using std::streampos;
00058 using std::ifstream;
00059 using std::cerr;
00060 using std::cout;
00061 using std::endl;
00062 
00063 VPKFile::VPKFile(const FP::Tag &pk,
00064   PKFile::Epoch newPKEpoch, FV::Epoch newNamesEpoch) throw (FS::Failure)
00065   : SPKFile(pk), newUncommon((CE::List *)NULL), freePKFileEpoch(-1),
00066     evicted(false)
00067 {
00068     try {
00069         // read header information from disk
00070         this->ReadPKFHeaderTail(pk);
00071 
00072         // fill in the extra fields of a VPKFile
00073         for (int i = 0; i < this->allNames.len; i++) {
00074             bool inMap = this->nameMap.Put(this->allNames.name[i], i);
00075             assert(!inMap);
00076         }
00077         this->isEmpty = false;
00078     }
00079     catch (SMultiPKFileRep::NotFound) {
00080         // fields initialized to empty VPKFile; set "pkEpoch"
00081         this->pkEpoch = newPKEpoch;
00082         this->namesEpoch = newNamesEpoch;
00083         this->isEmpty = true;
00084     }
00085 }
00086 
00087 void VPKFile::ReadPKFHeaderTail(const FP::Tag &pk)
00088   throw (FS::Failure, SMultiPKFileRep::NotFound)
00089 {
00090     PKPrefix::T pfx(pk);
00091     try {
00092         // seek to PKFile on disk
00093         ifstream ifs;
00094         SMultiPKFile::OpenRead(pfx, /*OUT*/ ifs);
00095         int version;
00096         try {
00097             streampos origin =
00098               SMultiPKFile::SeekToPKFile(ifs, pk, /*OUT*/ version);
00099 
00100             // read its <PKFHeaderTail>
00101             SPKFileRep::Header *dummy;
00102             this->Read(ifs, origin, version,
00103               /*OUT*/ /*hdr=*/ dummy, /*readEntries=*/ false);
00104         } catch (...) { FS::Close(ifs); throw; }
00105         FS::Close(ifs);
00106     }
00107     catch (FS::EndOfFile) {
00108         // indicates a programming error
00109         cerr << "Fatal error: premature EOF in MultiPKFile " << pfx
00110              << "; crashing..." << endl;
00111         assert(false);
00112     }
00113 }
00114 
00115 bool VPKFile::IsEmpty() throw ()
00116 /* REQUIRES Sup(LL) < SELF.mu */
00117 {
00118     bool res;
00119     this->mu.lock();
00120     res = ((this->oldEntries.Size() == 0) && (this->newCommon.Size() == 0)
00121            && (this->newUncommon == (CE::List *)NULL));
00122     this->mu.unlock();
00123     return res;
00124 }
00125 
00126 static void Etp_CacheS_FreeVariables(int num_names, int sz) throw ()
00127 /* This procedure is a no-op. It's for etp(1) logging purposes only. It
00128    reflects the size of the result returned by the CacheS::FreeVariables
00129    method. "num_names" is the number of free variables, and "sz" is
00130    a lower bound on the size of those names (in bytes). */
00131 { /* SKIP */ }
00132 
00133 void VPKFile::SendAllNames(SRPC *srpc, bool debug)
00134   throw (SRPC::failure, PrefixTbl::Overflow)
00135 /* REQUIRES Sup(LL) < SELF.mu */
00136 {
00137     this->mu.lock();
00138 
00139     // post debugging
00140     if (debug) {
00141         Debug::Lock();
00142         cout << Debug::Timestamp() << "RETURNED -- FreeVariables" << endl;
00143         cout << "  epoch = " << this->namesEpoch << endl;
00144         cout << "  isEmpty = " << BoolName[this->isEmpty] << endl;
00145         cout << "  names =" << endl; this->allNames.Print(cout, /*indent=*/ 4);
00146         cout << endl;
00147         Debug::Unlock();
00148     }
00149 
00150     // You might think that this assertion should hold true:
00151 
00152     //    assert(!this->isEmpty || (this->allNames.len == 0));
00153 
00154     // But you'd be wrong.  Adding a cache entry can fail after
00155     // this->allNames has been augmented if there was a duplicate free
00156     // variable provided by the caller.  This can leave isEmpty set to
00157     // true and allNames non-empty.
00158 
00159     Etp_CacheS_FreeVariables(this->allNames.len, this->allNames.CheapSize());
00160 
00161     // send values
00162     try {
00163         srpc->send_int((int)this->isEmpty);
00164         this->allNames.Send(*srpc);
00165         srpc->send_int((int)this->namesEpoch);
00166     } catch (...) { this->mu.unlock(); throw; }
00167     this->mu.unlock();
00168 }
00169 
00170 CacheIntf::LookupRes VPKFile::Lookup(FV::Epoch id, const FP::List &fps,
00171   /*OUT*/ CacheEntry::Index &ci, /*OUT*/ const VestaVal::T* &value,
00172   /*OUT*/ CacheIntf::LookupOutcome &outcome) throw (FS::Failure)
00173 /* REQUIRES Sup(LL) = SELF.mu */
00174 {
00175     CacheIntf::LookupRes res = CacheIntf::Miss;
00176     CE::T *ce =  (CE::T *)NULL;  // non-NULL => cache hit
00177 
00178     // initialize OUT parameters
00179     outcome = CacheIntf::AllMisses;
00180 
00181     // check that "id" is the correct epoch
00182     if (namesEpoch != id) {
00183         // "namesEpoch < id" indicates a bug in the client
00184         res = (namesEpoch < id)
00185           ? CacheIntf::BadLookupArgs
00186           : CacheIntf::FVMismatch;
00187         outcome = CacheIntf::NoOutcome;
00188     } else if (fps.len != allNames.len) {
00189         // an "fps" of the wrong length is a bug in the client
00190         outcome = CacheIntf::NoOutcome;
00191         res = CacheIntf::BadLookupArgs;
00192     } else {
00193         // check for a hit ...
00194         CE::List *list;
00195         Combine::FPTag commonTag(fps, this->commonNames);
00196         // ... vs. new common entries
00197         if (this->newCommon.Get(commonTag, /*OUT*/ list)
00198             && ((ce = LookupInList(list, fps)) != NULL)) {
00199             outcome = CacheIntf::NewHits;
00200             res = CacheIntf::Hit;
00201         }
00202         // ... vs. new uncommon entries
00203         else if ((ce = LookupInList(this->newUncommon, fps)) != NULL) {
00204             outcome = CacheIntf::NewHits;
00205             res = CacheIntf::Hit;
00206         }
00207         // ... vs. paged-in entries
00208         else if (this->oldEntries.Get(commonTag, /*OUT*/ list)
00209                  && ((ce = LookupInList(list, fps)) != NULL)) {
00210             outcome = CacheIntf::WarmHits;
00211             res = CacheIntf::Hit;
00212         }
00213         // ... vs. entries on disk
00214         else {
00215             PKPrefix::T pfx(pk);
00216             try {
00217                 // lookup the entry in the SMultiPKFile on disk
00218                 ifstream ifs;
00219                 SMultiPKFile::OpenRead(pfx, /*OUT*/ ifs);
00220                 int version;
00221                 try {
00222                     streampos origin =
00223                       SMultiPKFile::SeekToPKFile(ifs, pk, /*OUT*/ version);
00224                     ce = SPKFile::Lookup(ifs, origin, version, commonTag, fps);
00225                 } catch (...) { FS::Close(ifs); throw; }
00226                 FS::Close(ifs);
00227 
00228                 if (ce != (CE::T *)NULL) {
00229                     outcome = CacheIntf::DiskHits;
00230                     res = CacheIntf::Hit;
00231 
00232                     // in the event of a hit, add the entry to "oldEntries"
00233                     this->AddEntryToTable(/*INOUT*/ this->oldEntries,
00234                       commonTag, ce);
00235                 }
00236             }
00237             catch (SMultiPKFileRep::NotFound) {
00238                 /* SKIP -- do nothing; if there is no file, then
00239                    we should report a miss. */
00240             }
00241             catch (FS::EndOfFile) {
00242                 // indicates a programming error
00243                 cerr << "Fatal error: premature EOF in MultiPKFile " << pfx
00244                      << "; crashing..." << endl;
00245                 assert(false);
00246             }
00247         }
00248         if (res == CacheIntf::Hit) {
00249             // extract relevant fields
00250             assert(ce != (CE::T *)NULL);
00251             ci = ce->CI();
00252             value = ce->Value();
00253         }
00254     }
00255 
00256     return res;
00257 }
00258 
00259 CE::T* VPKFile::LookupInList(const CE::List *entries, const FP::List &fps)
00260   throw ()
00261 /* REQUIRES Sup(LL) = SELF.mu AND ListLock(*entries) IN LL */
00262 {
00263     for (const CE::List *curr = entries; curr != NULL; curr = curr->Tail()) {
00264         CE::T *ce = curr->Head();
00265         if (ce->FPMatch(fps)) {
00266             return ce;
00267         }
00268     }
00269     return (CE::T *)NULL;
00270 }
00271 
00272 CE::T* VPKFile::NewEntry(CacheEntry::Index ci, FV::List* names, FP::List *fps,
00273   VestaVal::T* value, Model::T model, CacheEntry::Indices* kids,
00274   /*OUT*/ FP::Tag* &commonFP)
00275   throw (DuplicateNames)
00276 /* REQUIRES Sup(LL) = SELF.mu */
00277 {
00278     assert(names->len == fps->len);
00279     bool newNames = false;
00280 
00281     // the set of names for this entry corresponding to "names"
00282     BitVector *namesBV = NEW_CONSTR(BitVector,
00283                                     (/*sizeHint=*/ this->allNames.len));
00284 
00285     // (partial) map of index in "allNames" to index in "names" (and hence,
00286     // in "fps"); the domain of the map is the set of indices of the set
00287     // bits in "namesBV". Invariant: "imap.Get(i,j) => allNames[i]==names[j]".
00288     IntIntTblLR *imap = (IntIntTblLR *)NULL;
00289 
00290     /* For each name in "names", we look up the name in "this->nameMap" to see
00291        if the name is new for this "VPKFile" or not. If it is not new, we
00292        get the index of the name from the table. If it is new, we append the
00293        name to "allNames" and add it to "this->nameMap". In either case, we
00294        set the appropriate bit in "namesBV", and record the mapping from bit
00295        vector index to "names/fps" index in "imap". */
00296     for (int i = 0; i < names->len; i++) {
00297         int index;              // index of "names->name[i]" in "allNames"
00298         FV::T *name = &(names->name[i]);
00299         if (!(this->nameMap.Get(*name, /*OUT*/ index))) {
00300             // name not in "allNames"
00301             index = this->allNames.Append(*name);
00302             newNames = true;
00303             bool inMap = this->nameMap.Put(*name, index); assert(!inMap);
00304         }
00305         if (namesBV->Set(index)) throw DuplicateNames();
00306         if ((imap == (IntIntTblLR *)NULL) && (index != i)) {
00307           // no longer the identity map
00308           imap = NEW_CONSTR(IntIntTblLR, (names->len));
00309           for (short k = 0; k < i; k++) {
00310             bool inMap = imap->Put(k, k); assert(!inMap);
00311           }
00312         }
00313         if (imap != (IntIntTblLR *)NULL) {
00314             bool inMap = imap->Put(index, i); assert(!inMap);
00315         }
00316     }
00317 
00318     // increment the epoch if any new names were added
00319     if (newNames) {
00320         this->namesEpoch++;
00321     }
00322 
00323     // delete arg(s) not incorporated into entry
00324     delete names;
00325 
00326     // initialize "uncommonNames" and "commonFP"
00327     BitVector *uncommonNames = NEW_CONSTR(BitVector, (namesBV));
00328     commonFP = (FP::Tag *)NULL;
00329     if (this->IsCommon(*uncommonNames)) {
00330         *uncommonNames -= this->commonNames;
00331         commonFP = NEW_PTRFREE_CONSTR(Combine::FPTag, (*fps, this->commonNames, imap));
00332     }
00333 
00334     // create and return the new cache entry
00335     return NEW_CONSTR(CE::T,
00336                       (ci, uncommonNames, fps, imap, value, kids, model));
00337 } // NewEntry
00338 
00339 void VPKFile::AddEntry(const Text* sourceFunc, CE::T* ce, FP::Tag* commonFP,
00340   PKFile::Epoch newPKEpoch) throw ()
00341 /* REQUIRES Sup(LL) = SELF.mu */
00342 {
00343     // increase "this->pkEpoch" if a larger "newPKEpoch" was given
00344     if (newPKEpoch != -1) {
00345         assert(newPKEpoch >= this->pkEpoch);
00346         this->pkEpoch = newPKEpoch;
00347     }
00348 
00349     // update this->sourceFunc if necessary
00350     if (this->sourceFunc == (Text *)NULL) {
00351         this->sourceFunc = sourceFunc;
00352     }
00353 
00354     // add entry to proper "new" bin
00355     if (commonFP == (FP::Tag*)NULL) {
00356       this->newUncommon = NEW_CONSTR(CE::List, (ce, this->newUncommon));
00357     } else {
00358         this->AddEntryToTable(/*INOUT*/ this->newCommon, *commonFP, ce);
00359     }
00360 
00361     // This VPKFile is now non-empty
00362     this->isEmpty = false;
00363 }
00364 
00365 static CE::List* VPKFile_ChkptEntryList(CE::List *entries) throw ()
00366 /* Return a copy of the list "entries". */
00367 {
00368     CE::List *res = (CE::List *)NULL;
00369     for (CE::List *curr = entries; curr != NULL; curr = curr->Tail()) {
00370       CE::T *newCE = NEW_CONSTR(CE::T, (curr->Head()));
00371       res = NEW_CONSTR(CE::List, (newCE, res));
00372     }
00373     return res;
00374 }
00375 
00376 static void VPKFile_ChkptEntryTbl(const SPKFile::CFPEntryMap &inTbl,
00377   /*OUT*/ SPKFile::CFPEntryMap &outTbl) throw ()
00378 {
00379     FP::Tag cfp;
00380     CE::List *oldEntries;
00381     SPKFile::CFPEntryIter it(&inTbl);
00382     while (it.Next(/*OUT*/ cfp, /*OUT*/ oldEntries)) {
00383         CE::List *newEntries = VPKFile_ChkptEntryList(oldEntries);
00384         bool inTbl = outTbl.Put(cfp, newEntries, /*resize=*/ false);
00385         assert(!inTbl);
00386     }
00387     outTbl.Resize();
00388 }
00389 
00390 VPKFileChkPt *VPKFile::CheckPoint() throw ()
00391 /* REQUIRES Sup(LL) < SELF.mu */
00392 {
00393     this->mu.lock();
00394     // copy SPKFile fields
00395     VPKFileChkPt *res = SPKFile::CheckPoint();
00396 
00397     // copy remaining VPKFile fields
00398     res->newUncommon = VPKFile_ChkptEntryList(this->newUncommon);
00399     if (this->newCommon.Size() > 0) {
00400         VPKFile_ChkptEntryTbl(this->newCommon, /*OUT*/ res->newCommon);
00401     }
00402     res->newUncommonHead = this->newUncommon;
00403     res->newCommonHeads = this->newCommon; // tbl assignment; copies ptrs only
00404     res->hasNewEntries = this->HasNewEntries();
00405 
00406     // increment the PK epoch so entries created after this
00407     // checkpoint has been taken will have a new epoch
00408     this->pkEpoch++;
00409     this->mu.unlock();
00410     return res;
00411 }
00412 
00413 // Update Methods -------------------------------------------------------------
00414 
00415 void VPKFile::Update(const SPKFile &pf,
00416   const VPKFileChkPt &chkpt, const BitVector *toDelete,
00417   const BitVector *exCommonNames, const BitVector *exUncommonNames,
00418   BitVector *packMask, IntIntTblLR *reMap, bool becameEmpty,
00419   /*INOUT*/ EntryState &state) throw ()
00420 /* REQUIRES sup(LL) = SELF.mu */
00421 /* This method updates the "VPKFile" to put it in sync with the
00422    corresponding "SPKFile" "pf". The arguments "exCommonNames",
00423    "exUncommonNames", "packMask", "reMap", and "becameEmpty" reflect
00424    how the VPKFile should be changed.
00425 
00426    The method must consider three kinds of entries in the VPKFile:
00427 
00428      1) New entries that have arrived since the VPKFile was checkpointed.
00429         These entries were not written to the SPKFile.
00430      2) New entries that existed at the time the VPKFile was checkpointed,
00431         and that have since been written to the SPKFile. Hence, the updated
00432         versions of these entries are stored in "pf.oldEntries".
00433      3) Old entries that were already written to the SPKFile before the
00434         VPKFile was ever checkpointed. Updated versions of these entries
00435         are also stored in "pf.oldEntries".
00436 
00437    The entries of type (1) need not be updated; they will remain new entries.
00438    It is a matter of policy as to how the entries of type (2) and (3) should
00439    be handled. For each catagory, the question is whether or not the entries
00440    should be saved in memory in the table "this->oldEntries". The two Boolean
00441    configuration values "KeepNewOnFlush" and "KeepOldOnFlush" determine which
00442    entries are kept in memory.
00443 
00444    In more detail, here is what the method does:
00445 
00446    First, those new entries in this "VPKFile" that are also in the "chkpt" are
00447    deleted from "this->newCommon" and "this->newUncommon".
00448 
00449    Second, the "packMask" and "reMap" values are updated to account for the
00450    fact that some new names may have been added to the VPKFile since it was
00451    checkpointed.
00452 
00453    Third, "this->oldEntries" is set according to the policy determined by the
00454    configuration variables "KeepNewOnFlush" and "KeepOldOnFlush". This is
00455    done by forming a list of entries to keep (by CI) according to the
00456    policy, erasing the current "this->oldEntries" table, and then copying
00457    the kept entries from "pf.oldEntries" to "this->oldEntries". Hence, these
00458    entries are not explictly updated here. Instead, the entries are simply
00459    copied from "pf" (the SPKFile), where they were already updated.
00460 
00461    Fourth, if the set of common names have changed or any entries are being
00462    deleted, any new entries of type (1) must be processed. We do not expect
00463    there to be many new entries, so the current implementation simply moves
00464    any "newCommon" entries to the "newUncommon" list.
00465 
00466    When the new entries on the "newUncommon" list are processed, there is one
00467    other potential complication. These entries may contain names that have
00468    been deleted from the SPKFile's "allNames" list because entries containing
00469    those names have been deleted. If any entries in the "newUncommon" list
00470    contain such deleted names, the names are re-appended to this VPKFile's
00471    "allNames" list and the "packMask" and "reMap" values are updated
00472    accordingly. See "VPKFile::UpdateUncommonList" and
00473    "VPKFile::CycleDeletedNamesInList" for details.
00474 
00475    Fifth, if the stable PKFile became empty by having all its entries
00476    deleted ("becameEmpty" is true), and we have no new entries added
00477    since the checkpoint was taken, then we return to the "fresh" state
00478    (by setting "isEmpty" to true). */
00479 {
00481     // remove checkpointed entries from "newCommon" and "newUncommon"
00482     if (chkpt.hasNewEntries) {
00483         DeleteCheckpointedEntries(chkpt, /*INOUT*/ state);
00484     }
00485 
00487     /* Between the time that the VPKFile was checkpointed and now, some new
00488        entries may have been added. In the process, some new names may have
00489        been added to "allNames". The "packMask" and "reMap" arguments need
00490        to be augmented to include these new names. */
00491     if (packMask != (BitVector *)NULL) {
00492         // the new names are those with indices in the half-open interval
00493         // "[chkpt.allNamesLen, this->allNames.len)"
00494         if (this->allNames.len > chkpt.allNamesLen) {
00495             // add new names to "packMask"
00496             int lastBx = packMask->MSB();  // first, record index of old MSB
00497             packMask->SetInterval(chkpt.allNamesLen, this->allNames.len-1);
00498 
00499             // add corresponding entries to "reMap"
00500             assert(reMap != (IntIntTblLR *)NULL);
00501             short nextBx = -1;
00502             (void) reMap->Get(lastBx, /*OUT*/ nextBx); nextBx++;
00503             for (int i = chkpt.allNamesLen; i < this->allNames.len; i++) {
00504                 bool inTbl = reMap->Put(i, nextBx++); assert(!inTbl);
00505             }
00506         }
00507     }
00508 
00510     // determine which entries to keep in "this->oldEntries"
00511     /* We have not yet updated any of the entries in "this->oldEntries",
00512        but we have updated all of the entries in "pf.OldEntries()". Hence,
00513        we record the CIs of any entries we want to store in
00514        "this->oldEntries" (in "keepCIs"), erase "this->oldEntries", and
00515        then copy those entries in "pf.OldEntries()" whose CIs are in
00516        "keepCIs" to "this->oldEntries". Note that any entries deleted
00517        according to "toDelete" will not be stored in "pf.OldEntries", so
00518        they will not be saved in "this->oldEntries". */
00519     IntIntTbl keepCIs(/*sizeHint=*/ 0, /*useGC=*/ true);
00520 
00521     // Save new entries (type (2)) if the policy so dictates
00522     if (Config_KeepNewOnFlush && chkpt.hasNewEntries) {
00523         RecordCIsFromList(chkpt.newUncommon, /*INOUT*/ keepCIs);
00524         RecordCIsFromTbl(chkpt.newCommon, /*INOUT*/ keepCIs);
00525     }
00526 
00527     // Save existing ``warm'' entries (type (3)) if the policy so dictates
00528     if (Config_KeepOldOnFlush) {
00529         RecordCIsFromTbl(this->oldEntries, /*INOUT*/ keepCIs);
00530     }
00531 
00532     /* copy entries from "pf.OldEntries()" with CIs in "keepCIs"
00533        to "this->oldEntries" */
00534     int sizeHint = max(this->oldEntries.Size(), keepCIs.Size());
00535     this->DeleteOldEntries(sizeHint, /*INOUT*/ state);
00536     if (keepCIs.Size() > 0) {
00537         this->CopyByCIs(pf.OldEntries(), /*INOUT*/ keepCIs, /*INOUT*/ state);
00538     }
00539 
00541     // recompute if common names have changed or there are entries to delete
00542     bool commonNamesChanged = ((exCommonNames != (BitVector *)NULL)
00543       || (exUncommonNames != (BitVector *)NULL));
00544     if (commonNamesChanged || (toDelete != (BitVector *)NULL)) {
00545 
00546         /* At this point, any entries in "newCommon" and "newUncommon" are new
00547            entries that were added to this VPKFile since graphlog was flushed
00548            at the start of the weed. Hence, even if "toDelete" is non-NULL,
00549            the graphlog cannot name any of the entries in "newCommon" or
00550            "newUncommon".
00551 
00552            However, if the set of common names as been augmented at all (i.e.,
00553            if "exUncommonNames" is non-NULL), the entries in the "newCommon"
00554            table may no longer possess all the common names. If the set of
00555            common names has decreased, it may also be the case that some
00556            entries in "newUncommon" may *have* all common names, and so may be
00557            shifted to the "newCommon" table. However, we do not expect many
00558            new entries to have been created since the checkpoint, so for
00559            now we just move all such entries to the "newUncommon" list.
00560            
00561            Furthermore, if "packMask" is non-NULL, the set of "allNames" has
00562            changed, so any new entries need to be packed according to
00563            "packMask" and "reMap". */
00564         if (this->newCommon.Size() > 0) {
00565             // move the "newCommon" entries -> "newUncommon"
00566             /* NOTE: This must be done before "commonNames" has changed
00567                according to "exCommonNames" and "exUncommonNames"! */
00568             MoveCommonToUncommon(this->commonNames);
00569         }
00570 
00571         if (commonNamesChanged) {
00572             // set new value for common names
00573             if (exCommonNames != (BitVector *)NULL)
00574                 this->commonNames ^= *exCommonNames;
00575             if (exUncommonNames != (BitVector *)NULL)
00576                 this->commonNames ^= *exUncommonNames;
00577         }
00578         if (packMask != (BitVector *)NULL) {
00579             // check that no names in "this->commonNames" are being deleted
00580             BitVector temp(&(this->commonNames)); temp -= *packMask;
00581             assert(temp.IsEmpty());
00582         }
00583 
00584         /* Even if the common names have not changed, we still have to
00585            process the new entries because their names and bit vectors
00586            may need packing. */
00587 
00588         // go over the new entries now on the "newUncommon" list
00589         if (this->newUncommon != (CE::List *)NULL) {
00590             UpdateUncommonList(chkpt.sourceFunc, /*INOUT*/ packMask,
00591               /*INOUT*/ reMap, /*unlazyTag=*/ false);
00592         }
00593 
00594         // pack the common names (this is a no-op if "packMask == NULL")
00595         this->commonNames.Pack(packMask);
00596         assert(this->commonNames == pf.CommonNames());
00597 
00598         // pack "this->allNames" if necessary
00599         if (packMask != (BitVector *)NULL) {
00600             this->allNames.Pack(*packMask);
00601             this->namesEpoch++;
00602 
00603             // recompute "nameMap"
00604             this->nameMap.Init(/*sizeHint=*/ this->allNames.len);
00605             for (int i = 0; i < this->allNames.len; i++) {
00606                 bool inTbl = nameMap.Put(this->allNames.name[i], i);
00607                 assert(!inTbl);
00608             }
00609         }
00610     }
00611 
00613     // If all entries were deleted from the stable PKFile and we have
00614     // no new entries, this is now an empty VPKFile.
00615     if(becameEmpty && !this->HasNewEntries())
00616       {
00617         // We are now an empty VPKFile.
00618         this->isEmpty = true;
00619 
00620         // We must have been deleting.
00621         assert(toDelete != (BitVector *)NULL);
00622 
00623         // We shouldn't have kept any old entries.
00624         assert(this->oldEntries.Size() == 0);
00625       }
00626 
00627 } // Update
00628 
00629 static void VPKFile_TruncateList(/*INOUT*/ CE::List* &head,
00630   const CE::List *tail, /*INOUT*/ EntryState &state) throw ()
00631 /* REQUIRES ListLock(head) IN LL */
00632 /* REQUIRES (tail != NULL) => (head != NULL) */
00633 /* If "tail" is NULL, do nothing. Otherwise, this requires that "head" is
00634    non-NULL and that "tail" points into the list starting at "head". The list
00635    is truncated to the longest list that no longer includes "*tail" (and any
00636    elements reachable from it. */
00637 {
00638     if (tail != (CE::List *)NULL) {
00639         // find element pointing to "tail"
00640         assert(head != (CE::List *)NULL);
00641         CE::List *curr, *prev = (CE::List *)NULL;
00642         for (curr = head; curr != tail; curr = curr->Tail()) {
00643             prev = curr;
00644         }
00645 
00646         // set previous element to NULL
00647         if (prev == (CE::List *)NULL) {
00648             assert(head == tail);
00649             head = (CE::List *)NULL;
00650         } else {
00651             prev->SetTail((CE::List *)NULL);
00652         }
00653 
00654         // update "state"
00655         for (; curr != (CE::List *)NULL; curr = curr->Tail()) {
00656             state.newEntryCnt--;
00657             state.newPklSize -= curr->Head()->Value()->len;
00658         }
00659     }
00660 }
00661 
00662 void VPKFile::DeleteCheckpointedEntries(const VPKFileChkPt &chkpt,
00663   /*INOUT*/ EntryState &state) throw ()
00664 /* REQUIRES Sup(LL) = SELF.mu */
00665 {
00666     // delete all entries in "chkpt.newUncommonHead".
00667     VPKFile_TruncateList(this->newUncommon,
00668       chkpt.newUncommonHead, /*INOUT*/ state);
00669 
00670     // delete all entries pointed to from entries in "chkpt.newCommonHeads"
00671     if (chkpt.newCommonHeads.Size() > 0) {
00672         /* Truncating the entries in "this->newCommon" may leave some of
00673            them set to the empty list. We delete those lists from the
00674            table. */
00675         CFPEntryIter iter(&(chkpt.newCommonHeads));
00676         FP::Tag key; CE::List *tail;
00677         while (iter.Next(/*OUT*/ key, /*OUT*/ tail)) {
00678             CE::List *head;
00679             bool inTbl = this->newCommon.Get(key, /*OUT*/ head);
00680             assert(inTbl && head != (CE::List *)NULL);
00681             VPKFile_TruncateList(/*INOUT*/ head, tail, /*INOUT*/ state);
00682             if (head == (CE::List *)NULL) {
00683                 this->newCommon.Delete(key, /*OUT*/ tail, /*resize=*/ false);
00684                 assert(inTbl);
00685             }
00686         }
00687         this->newCommon.Resize();
00688     }
00689 }
00690 
00691 void VPKFile::DeleteOldEntries(int sizeHint, /*INOUT*/ EntryState &state)
00692   throw ()
00693 /* REQUIRES sup(LL) = SELF.mu */
00694 {
00695     // update "state" to account for all entries in "oldEntries"
00696     CFPEntryIter iter(&(this->oldEntries));
00697     FP::Tag key; CE::List *curr;
00698     while (iter.Next(/*OUT*/ key, /*OUT*/ curr)) {
00699         for (; curr != (CE::List *)NULL; curr = curr->Tail()) {
00700             state.oldEntryCnt--;
00701             state.oldPklSize -= curr->Head()->Value()->len;
00702         }
00703     }
00704 
00705     // reset the "oldEntries" table
00706     this->oldEntries.Init(sizeHint);
00707 }
00708 
00709 void VPKFile::MoveCommonToUncommon(const BitVector &oldCommon) throw ()
00710 /* REQUIRES this->newCommon.Size() > 0 */
00711 {
00712     // move each list in the table
00713     assert(this->newCommon.Size() > 0);
00714     SPKFile::CFPEntryIter it(&(this->newCommon));
00715     FP::Tag cfp; CE::List *head;
00716     while (it.Next(/*OUT*/ cfp, /*OUT*/ head)) {
00717         MoveCommonListToUncommon(head, oldCommon);
00718     }
00719 
00720     // clear the table
00721     newCommon.Init(/*sizeHint =*/ newCommon.Size());
00722 }
00723 
00724 void VPKFile::MoveCommonListToUncommon(CE::List *head,
00725   const BitVector &oldCommon) throw () 
00726 /* REQUIRES SELF.mu, ListLock(*head) IN LL */
00727 /* REQUIRES head != (CE::List *)NULL */
00728 {
00729     assert(head != (CE::List *)NULL);
00730     CE::List *curr, *prev;
00731     for (curr = head; curr != (CE::List *)NULL;
00732          prev = curr, curr = curr->Tail()) {
00733         CE::T *ce = curr->Head();
00734         ce->XorUncommonNames(oldCommon);
00735     }
00736     prev->SetTail(this->newUncommon);
00737     this->newUncommon = head;
00738 }
00739 
00740 void VPKFile::UpdateUncommonList(const Text* sourceFunc,
00741   /*INOUT*/ BitVector *packMask, /*INOUT*/ IntIntTblLR *reMap,
00742   bool unlazyTag) throw ()
00743 /* REQUIRES sup(LL) = SELF.mu */
00744 {
00745     CE::List *curr = this->newUncommon;
00746     this->newUncommon = (CE::List *)NULL;
00747     
00748     if (packMask != (BitVector *)NULL) {
00749         assert(reMap != (IntIntTblLR *)NULL);
00750         CycleDeletedNamesInList(curr, *packMask, *reMap);
00751     }
00752     
00753     for (/*SKIP*/; curr != (CE::List *)NULL; curr = curr->Tail()) {
00754         CE::T *ce = curr->Head();
00755         
00756         // make the entry common if necessary
00757         FP::Tag *commonFP = (FP::Tag *)NULL;
00758         if (this->IsCommon(ce->UncommonNames())) {
00759             ce->XorUncommonNames(commonNames);
00760             commonFP = CommonFP(*ce);
00761         }
00762         
00763         // add the entry to the cache
00764         /* Note: this must be done before the entry is packed, since this
00765             uses the unpacked version of "commonNames" to compute the max
00766                 common timestamp for the entry in the event that the entry is
00767                     common (i.e., "commonFP != NULL"). */
00768         AddEntry(sourceFunc, ce, commonFP);
00769         
00770         // pack the entry's names, bit vectors
00771         ce->Pack(packMask, reMap);
00772         
00773         // unlazy the cache entry's uncommon tag (if necessary)
00774         if (unlazyTag) ce->UnlazyUncommonFP();
00775     }
00776 } // UpdateUncommonList
00777 
00778 void VPKFile::RecordCIsFromList(const CE::List *ents,
00779   /*INOUT*/ IntIntTbl& keep) const throw ()
00780 /* REQUIRES ListLock(*ents) IN LL */
00781 {
00782     for (const CE::List *curr = ents; curr != NULL; curr = curr->Tail()) {
00783         IntKey key(curr->Head()->CI());
00784         bool inTbl = keep.Put(IntKey(key), 0); assert(!inTbl);
00785     }
00786 }
00787 
00788 void VPKFile::RecordCIsFromTbl(const CFPEntryMap& tbl,
00789   /*INOUT*/ IntIntTbl& keep) const throw ()
00790 {
00791     if (tbl.Size() > 0) {
00792         CFPEntryIter it(&tbl);
00793         FP::Tag cfp; CE::List *head;
00794         while (it.Next(/*OUT*/ cfp, /*OUT*/ head)) {
00795             RecordCIsFromList(head, keep);
00796         }
00797     }
00798 }
00799 
00800 void VPKFile::CopyByCIs(const CFPEntryMap& source,
00801   /*INOUT*/ IntIntTbl& cis, /*INOUT*/ EntryState & state) throw ()
00802 /* REQUIRES SELF.mu IN LL */
00803 {
00804     if (source.Size() > 0 && cis.Size() > 0) {
00805         CFPEntryIter it(&source);
00806         FP::Tag cfp; CE::List *head, *curr;
00807         while (it.Next(/*OUT*/ cfp, /*OUT*/ head)) {
00808             for (curr = head; curr != NULL; curr = curr->Tail()) {
00809                 CE::T *ce = curr->Head();
00810                 int cnt;
00811                 if (cis.Get(ce->CI(), /*OUT*/ cnt)) {
00812                     // make sure same CI is not selected twice
00813                     assert(cnt == 0);
00814                     (void) cis.Put(ce->CI(), cnt+1);
00815 
00816                     // add this entry to "this->oldEntries"
00817                     this->AddEntryToTable(this->oldEntries, cfp, ce);
00818 
00819                     // update "state"
00820                     state.oldEntryCnt++;
00821                     state.oldPklSize += ce->Value()->len;
00822                 }
00823             }
00824         }
00825     }
00826 }
00827 
00828 void VPKFile::CycleDeletedNamesInList(CE::List *curr,
00829   /*INOUT*/ BitVector &packMask, /*INOUT*/ IntIntTblLR &reMap) throw ()
00830 /* REQUIRES sup(LL) = SELF.mu AND ListLock(*curr) IN LL */
00831 {
00832     // compute the index of where any new names get packed to
00833     short lastBx = packMask.MSB(), nextBx = -1;
00834     (void) reMap.Get(lastBx, /*OUT*/ nextBx);
00835     nextBx++;
00836 
00837     // keep track of new indices of deleted names
00838     // this maps old indices of deleted names to their new indices
00839     IntIntTblLR delMap(reMap.ArraySize());
00840 
00841     for (/*SKIP*/; curr != (CE::List *)NULL; curr = curr->Tail()) {
00842         CE::T *ce = curr->Head();
00843         BitVector delBV(ce->UncommonNames());
00844         delBV -= packMask;
00845         if (!(delBV.IsEmpty())) {
00846             // update "VPKFile" values, "packMask", "reMap"
00847             BVIter it(&delBV); int oldBx; short newBx;
00848             while (it.Next(/*OUT*/ oldBx)) {
00849                 if (!delMap.Get(oldBx, /*OUT*/ newBx)) {
00850                     // add the name to "this->allNames" (if necessary)
00851                     FV::T *name = &(this->allNames.name[oldBx]); // alias
00852                     newBx = this->allNames.Append(*name);
00853                     bool inTbl = delMap.Put(oldBx, newBx); assert(!inTbl);
00854 
00855                     // update "this->nameMap"
00856                     inTbl = this->nameMap.Put(*name, newBx); assert(inTbl);
00857 
00858                     // update "packMask"
00859                     bool set = packMask.Set(newBx); assert(!set);
00860 
00861                     // update "reMap"
00862                     inTbl = reMap.Put(newBx, nextBx++);
00863                     assert(!inTbl);
00864                 }
00865             }
00866 
00867             // update the entry "ce"
00868             ce->CycleNames(delBV, delMap);
00869         }
00870     }
00871 }
00872 
00873 bool VPKFile::ReadyForPurgeWarm(int latestEpoch) const throw()
00874   /* REQUIRES Sup(LL) = SELF.mu */
00875 {
00876   // We're ready to have our warm entries purged from memory iff:
00877 
00878   // - Enough time has passed since we were last used (configurable by
00879   // PurgeWarmPeriodCnt)
00880 
00881   // - We have no new entries
00882 
00883   // - We have one or more old (warm) entries
00884   return ((this->freePKFileEpoch <= (latestEpoch -
00885                                      Config_PurgeWarmPeriodCnt)) &&
00886           !this->HasNewEntries() &&
00887           (this->OldEntries()->Size() > 0));
00888 }
00889 
00890 bool VPKFile::ReadyForEviction(int latestEpoch) const throw()
00891   /* REQUIRES Sup(LL) = SELF.mu */
00892 {
00893   // We're ready to be evicted from the cache iff:
00894 
00895   // - Enough time has passed since we were last used (configurable by
00896   // EvictPeriodCnt)
00897 
00898   // - We have no new entries
00899 
00900   // - We have no old (warm) entries
00901   return ((this->freePKFileEpoch <= (latestEpoch -
00902                                      Config_EvictPeriodCnt)) &&
00903           !this->HasNewEntries() &&
00904           (this->OldEntries()->Size() == 0));
00905 }

Generated on Mon May 8 00:48:36 2006 for Vesta by  doxygen 1.4.2