// @(#)root/proof:$Name: $:$Id: TObjectCache.h,v 1.3 2006/07/01 11:39:37 rdm Exp $ // Author: M. Biskup 2/4/06 /************************************************************************* * Copyright (C) 1995-2006, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ #ifndef ROOT_TObjectCache #define ROOT_TObjectCache ////////////////////////////////////////////////////////////////////////// // // // TObjectCache // // // // Manage small caches for TDirectories, TFiles or TTrees using LRU // // strategy. // // // ////////////////////////////////////////////////////////////////////////// #ifndef ROOT_TObject #include "TObject.h" #endif #ifndef ROOT_TString #include "TString.h" #endif #ifndef ROOT_TError #include "TError.h" #endif #ifndef ROOT_TList #include "TList.h" #endif #include #include class TFile; class TDirectory; class TTree; // Template base class for a cache. ObjKey is a class used to identify objects and // Obj class are the objects themselves. The cache uses LRU strategy and removes objects // if the number of objects in the cache is too big. The lookup in the cache takes O(n) // operations, where n is the cache size. // Both ObjKey and Obj classes should implement ordering operators (equal, less then). template class TObjectCache { public: typedef ObjKey TCacheKey; typedef Obj TCacheObject; typedef std::pair ObjectAndBool_t; private: // A list element. Helper structure for the implementation of the object cache. class TCacheElem : public TObject { public: TCacheObject fObj; TCacheKey fKey; Int_t fCount; TCacheElem(const TCacheObject &obj, const TCacheKey &objKey) : fObj(obj), fKey(objKey), fCount(0) { } }; Int_t fMaxSize; // The maximum size of the cache. If the size exceeds this // value the least recently used object are removed, // until the cache size drops below this threshold or there // all other objects are in use. Int_t fSize; // The number of objects currently stored in the cache. TList fMRU; // Most recently used list element (but not used right now) TList fInUse; // A list of all the objects in use. std::map fObjMap; // a map from objects (any stored in the cache) // to list links std::map fKeyMap; // a map from object keys (any stored in the cache) // to list links TObjLink* FindByKey(const TCacheKey &k) { // Returns TObjLink entry storing a given object key. If no object with this // key is stored in the cache, 0 is returned. typename std::map::iterator i = fKeyMap.find(k); if (i != fKeyMap.end()) return i->second; return 0; } TObjLink* FindByObj(const TCacheObject &obj) { // Returns TObjLink entry storing a given object. If this object is not stored // in the cache, 0 is returned. typename std::map::iterator i = fObjMap.find(obj); if (i != fObjMap.end()) return i->second; else return 0; } void RemoveOld() { // Removes unused elements from the cache. // Uses LRU strategy. while (fMRU.GetSize() > 0 && fSize > fMaxSize) { TCacheElem *elem = dynamic_cast(fMRU.Remove(fMRU.LastLink())); fObjMap.erase(elem->fObj); fKeyMap.erase(elem->fKey); Unload(elem->fObj); delete elem; fSize--; } } protected: // The load function loads an objects given its key. Should be defined in subclasses. // The first element of the pair returned is the loaded object. The second tells wether // or not the loading was successfull (kTRUE - success, kFALSE - error). Only if the value // is kTRUE the object will be stored in the cache. virtual ObjectAndBool_t Load(const TCacheKey &k) = 0; // Called when removing the object from the cache. Can, e.g., delete the object (if it's a pointer). virtual void Unload(TCacheObject &/* o */) {} public: TObjectCache() : fMaxSize(10), fSize(0) { } virtual ~TObjectCache() { // Destructor. Calls Unload for all the objects cached. // The existence of objects which haven't been released is considered // as an error. if (fInUse.GetSize() > 0) { ::Error("TEventIterTree::TObjectCache::~TObjectCache()", "deleting the cache while some object haven't been released"); } TIter next(&fInUse); while (TCacheElem* elem = dynamic_cast(next())) { Unload(elem->fObj); } fInUse.SetOwner(); // ~fInUse will delete all the elements TIter next2(&fMRU); while (TCacheElem* elem = dynamic_cast(next2())) { Unload(elem->fObj); } fMRU.SetOwner(); // ~fMRU will delete all the elements } TCacheObject Acquire(const TCacheKey &k) { // Returns an object given it's key. The object should be later released. // The same object can be acquired several times (returning the same, cached object) // The number of Release() calls should be equal to the number of // Acquire() calls. TObjLink *link = FindByKey(k); TCacheElem* elem; if (link == 0) { // create a new element ObjectAndBool_t loadResult = Load(k); if (!loadResult.second) return loadResult.first; // this value musn't be cached elem = new TCacheElem(loadResult.first, k); fInUse.AddFirst(elem); link = fInUse.FirstLink(); fObjMap[loadResult.first] = link; fKeyMap[k] = link; fSize++; } else { elem = dynamic_cast(link->GetObject()); if (elem->fCount == 0) { // bring the element to the InUse list fMRU.Remove(link); fInUse.AddFirst(elem); link = fInUse.FirstLink(); fObjMap[elem->fObj] = link; fKeyMap[elem->fKey] = link; } } elem->fCount++; RemoveOld(); return elem->fObj; } TCacheObject Acquire(const TString& fileName, const TString& dirName) { return Acquire(std::make_pair(fileName, dirName)); } TCacheObject Acquire(const TString& fileName, const TString& dirName, const TString& treeName) { return Acquire(std::make_pair(fileName, std::make_pair(dirName, treeName))); } void Release(TCacheObject &obj) { // Releases the element acquired using Acquire() function. The object is not // deleted imediatelly but is kept in the cache. The next Acquire call with // the key of this object will return the same obj value. Objects are deleted // from the cache when its size is too big. TObjLink *link = FindByObj(obj); if (link == 0) ::Error("TEventIterTree::TObjectCache::Release()", "Releasing an object not present in the cache"); else { TCacheElem * elem = dynamic_cast(link->GetObject()); elem->fCount--; if (elem->fCount < 0) { ::Error("TEventIterTree::TObjectCache::Release()", "Releasing an object more times than it has been acquired"); elem->fCount = 0; } else if (elem->fCount == 0) { // move it to the MRU list fInUse.Remove(link); fMRU.AddFirst(elem); link = fMRU.FirstLink(); fObjMap[elem->fObj] = link; fKeyMap[elem->fKey] = link; RemoveOld(); } } } Int_t GetMaxSize() const { return fMaxSize; } // returns the maximum size of the cache void SetMaxSize(Int_t size) { fMaxSize = size; } // sets the maximum size of the cache }; // A File Cache. Uses a singleton pattern with Instance() method. class TFileCache : public TObjectCache { private: static TFileCache *fgInstance; TFileCache() {} public: ObjectAndBool_t Load(const TString &fileName); virtual void Unload(TFile* &f); static TFileCache *Instance(); }; // A Directory Cache. Uses TFileCache for opening files. class TDirectoryCache : public TObjectCache, TDirectory*> { private: std::map fDirectoryFiles; // an association Directory <-> its File static TDirectoryCache *fgInstance; TDirectoryCache() {} public: ObjectAndBool_t Load(const TCacheKey &k); virtual void Unload(TDirectory* &dir); static TDirectoryCache *Instance(); }; // A Tree Cache. Uses TFileCache for opening files. class TTreeFileCache : public TObjectCache >, TTree*> { private: std::map fTreeDirectories; // an association Tree <-> its File public: ObjectAndBool_t Load(const TCacheKey &k); virtual void Unload(TTree* &tree); }; #endif