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

FdCache.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 //
00020 // FdCache.C
00021 // Last modified on Mon May 23 21:59:59 EDT 2005 by ken@xorian.net  
00022 //      modified on Thu Jul 19 15:36:18 PDT 2001 by mann  
00023 //      modified on Mon Apr 28 17:30:28 PDT 1997 by heydon
00024 //
00025 // Cache of open file descriptors for files named by ShortId
00026 //
00027 
00028 #include "Basics.H"
00029 #include "FdCache.H"
00030 #include "ShortIdKey.H"
00031 #include "logging.H"
00032 
00033 #include <Thread.H>
00034 #include <fcntl.h>
00035 
00036 #include "timing.H"
00037 
00038 namespace FdCache
00039 {
00040   // Types
00041   struct FdInfo {
00042     int fd;             // Open file descriptor number
00043     FdCache::OFlag ofl; // Open for rw or ro
00044     time_t lastUse;     // Time when last used
00045     // LRU list links
00046     FdInfo* prev;
00047     FdInfo* next;
00048     // Links to more descriptors for the same file
00049     FdInfo* moreprev;
00050     FdInfo* more;
00051     // Need to store key redundantly for removal via LRU
00052     ShortIdKey key;
00053   };
00054   typedef ::Table<ShortIdKey, FdInfo*>::Default FdTable;
00055   typedef ::Table<ShortIdKey, FdInfo*>::Iterator FdIter;
00056 
00057   
00058   class PartialCache
00059   {
00060   private:
00061     Basics::mutex mu;
00062     FdTable table;
00063     FdInfo lruListHead;
00064     unsigned int nCached;
00065 
00066     Basics::uint64 hit_count, open_miss_count, try_miss_count,
00067       evict_count, expire_count;
00068 
00069     void fdiDeleteNL(FdInfo* fdi, char* where);
00070 
00071     // Lookup an entry.  Caller must lock/unlock mu.
00072     int lookup(ShortIdKey &key, FdCache::OFlag ofl,
00073                /*OUT*/ FdCache::OFlag &oflout)
00074       throw ();
00075 
00076   public:
00077     PartialCache()
00078       : nCached(0),
00079         hit_count(0), open_miss_count(0), try_miss_count(0),
00080         evict_count(0), expire_count(0)
00081     {
00082       this->lruListHead.prev = this->lruListHead.next = &(this->lruListHead);
00083     }
00084 
00085     int open(ShortId sid, FdCache::OFlag ofl, FdCache::OFlag* oflout) throw ();
00086     int tryopen(ShortId sid, FdCache::OFlag ofl, FdCache::OFlag* oflOut =NULL)
00087     throw();
00088     void close(ShortId sid, int fd, OFlag ofl) throw();
00089     void flush(ShortId sid, OFlag ofl) throw();
00090 
00091     void janitor_sweep(time_t now) throw();
00092 
00093     void accumulateStats(/*OUT*/ Basics::uint32 &n_in_cache,
00094                          /*OUT*/ Basics::uint64 &hits,
00095                          /*OUT*/ Basics::uint64 &open_misses,
00096                          /*OUT*/ Basics::uint64 &try_misses,
00097                          /*OUT*/ Basics::uint64 &evictions,
00098                          /*OUT*/ Basics::uint64 &expirations);
00099   };
00100 }
00101 
00102 // Module tuning parameters
00103 static const int JANITOR_SLEEP = 5*60;  // how often janitor cleans up
00104 static const int JANITOR_TOO_OLD = 2*JANITOR_SLEEP;  // how old is too old
00105 
00106 // Module globals
00107 static pthread_once_t once = PTHREAD_ONCE_INIT;
00108 static unsigned int maxCached;
00109 static Basics::thread janitor; // closes unused files that are too old
00110 
00111 static unsigned int fdCache_split_bits = 2, fdCache_count;
00112 static Bit32 fdCache_split_mask;
00113 static FdCache::PartialCache *fdCaches;
00114 
00115 // Common routine for deletion
00116 void FdCache::PartialCache::fdiDeleteNL(FdCache::FdInfo* fdi, char* where)
00117 {
00118     FdCache::FdInfo* deleted;
00119     if (fdi->moreprev == NULL) {
00120         bool ok = this->table.Delete(fdi->key, deleted, false);
00121         assert(ok);
00122         assert(deleted == fdi);
00123         if (fdi->more != NULL) {
00124             this->table.Put(fdi->key, fdi->more);
00125         }
00126     }
00127     fdi->prev->next = fdi->next;
00128     fdi->next->prev = fdi->prev;
00129     if (fdi->moreprev) fdi->moreprev->more = fdi->more;
00130     if (fdi->more) fdi->more->moreprev = fdi->moreprev;
00131 
00132     ::close(fdi->fd);
00133     Repos::dprintf(DBG_FDCACHE, "FdCache::%s deleted old sid 0x%08x, fd %d, ofl %d\n",
00134                    where, fdi->key.sid, fdi->fd, fdi->ofl);
00135     delete fdi;
00136 }
00137 
00138 void FdCache::PartialCache::janitor_sweep(time_t now) throw()
00139 {
00140   this->mu.lock();
00141   while (this->nCached > 0 && 
00142          (now - this->lruListHead.next->lastUse) >= JANITOR_TOO_OLD) {
00143     this->fdiDeleteNL(this->lruListHead.next, "JanitorThread");
00144     this->nCached--;
00145     this->expire_count++;
00146   }
00147   this->mu.unlock();
00148 }
00149 
00150 void *
00151 JanitorThread(void *arg)
00152 {
00153     signal(SIGPIPE, SIG_IGN);
00154     signal(SIGQUIT, SIG_DFL);
00155     signal(SIGSEGV, SIG_DFL);
00156     signal(SIGABRT, SIG_DFL);
00157     signal(SIGBUS, SIG_DFL);
00158     signal(SIGILL, SIG_DFL);
00159 
00160     for (;;) {
00161         time_t now = time(NULL);
00162         // Loop over the file descriptor caches, and have each one
00163         // discard old file descriptors.
00164         for(unsigned int i = 0; i < fdCache_count; i++)
00165           {
00166             fdCaches[i].janitor_sweep(now);
00167           }
00168         sleep(JANITOR_SLEEP);
00169     }
00170 
00171     //return (void *)NULL;    // Not reached
00172 }
00173 
00174 extern "C"
00175 {
00176   static void
00177   FdCache_init()
00178   {
00179     maxCached = getdtablesize() / 2;
00180     assert(maxCached > 0);
00181 
00182     // Get the number of bits we'll be using to split between
00183     // different file descriptor caches.
00184     Text value;
00185     if (VestaConfig::get("Repository", "fdCache_split_bits", value)) {
00186         fdCache_split_bits = atoi(value.cchars());
00187     }
00188 
00189     // Allocate the required number of file descriptor caches.
00190     fdCache_count = (1 << fdCache_split_bits);
00191     fdCaches = NEW_ARRAY(FdCache::PartialCache, fdCache_count);
00192 
00193     // Generate a mask to use when finding which file descriptor cache
00194     // a given ShortId belongs in.
00195     fdCache_split_mask = 0;
00196     for(unsigned int i = 0; i < fdCache_split_bits; i++)
00197       {
00198         fdCache_split_mask |= (1 << i);
00199       }
00200     assert(fdCache_split_mask < fdCache_count);
00201 
00202     Basics::thread_attr janitor_attr;
00203 #if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) && !defined(__linux__)
00204     // Linux only allows the superuser to use SCHED_RR
00205     janitor_attr.set_schedpolicy(SCHED_RR);
00206     janitor_attr.set_inheritsched(PTHREAD_EXPLICIT_SCHED);
00207     janitor_attr.set_sched_priority(sched_get_priority_min(SCHED_RR));
00208 #endif
00209 
00210     // Start the janitor thread.
00211     janitor.fork(JanitorThread, 0, janitor_attr);
00212   }
00213 }
00214 
00215 static unsigned int fdCacheIndex(ShortId sid)
00216 {
00217   unsigned int result = sid & fdCache_split_mask;
00218   assert(result < fdCache_count);
00219   return result;
00220 }
00221 
00222 int
00223 FdCache::PartialCache::lookup(ShortIdKey &key, FdCache::OFlag ofl,
00224                               /*OUT*/ FdCache::OFlag &oflout)
00225   throw ()
00226 {
00227   FdCache::FdInfo* fdihead = 0;
00228   int fd = -1;
00229   FdCache::OFlag oflused = FdCache::any;
00230 
00231   if (this->table.Delete(key, fdihead, false)) {
00232     // Chain found in cache; look for one with correct ofl
00233     FdCache::FdInfo* fdi = fdihead;
00234     do {
00235       FdCache::FdInfo* more = fdi->more;
00236       if (ofl == FdCache::any || ofl == fdi->ofl) {
00237         fd = fdi->fd;
00238         assert(fd != -1);
00239         oflout = fdi->ofl;
00240         fdi->prev->next = fdi->next;
00241         fdi->next->prev = fdi->prev;
00242         if (fdi->moreprev != NULL) {
00243           fdi->moreprev->more = fdi->more;
00244         }
00245         if (fdi->more != NULL) {
00246           fdi->more->moreprev = fdi->moreprev;
00247         }
00248         if (fdihead == fdi) {
00249           fdihead = fdi->more;
00250         }
00251         delete fdi;
00252         this->nCached--;
00253         break;
00254       }
00255       fdi = more;
00256     } while (fdi);
00257     if (fdihead != NULL) {
00258       this->table.Put(key, fdihead);
00259     }
00260   }
00261 
00262   return fd;
00263 }
00264 
00265 int
00266 FdCache::open(ShortId sid, FdCache::OFlag ofl, FdCache::OFlag* oflout) throw ()
00267 {
00268   pthread_once(&once, FdCache_init);
00269   unsigned int index = fdCacheIndex(sid);
00270   assert(fdCaches != NULL);
00271   return fdCaches[index].open(sid, ofl, oflout);
00272 }
00273 
00274 int
00275 FdCache::PartialCache::open(ShortId sid, FdCache::OFlag ofl, FdCache::OFlag* oflout) throw ()
00276 {
00277   // First search the cache for an existing file descriptor.
00278   FdCache::OFlag oflused = FdCache::any;
00279   ShortIdKey key(sid);
00280 
00281   RECORD_TIME_POINT;
00282   this->mu.lock();
00283   RECORD_TIME_POINT;
00284 
00285   int fd = this->lookup(key, ofl, oflused);
00286 
00287   // Update hit/miss statistics
00288   if(fd != -1)
00289     this->hit_count++;
00290   else
00291     this->open_miss_count++;
00292 
00293   this->mu.unlock();
00294   RECORD_TIME_POINT;
00295 
00296   if (fd == -1)
00297     {
00298       // Not found in cache: open a new file descriptor
00299       if (ofl == FdCache::any) {
00300         oflused = FdCache::ro; 
00301       } else {
00302         oflused = ofl;
00303       }
00304       RECORD_TIME_POINT;
00305       fd = SourceOrDerived::fdopen(sid, oflused == FdCache::ro ?
00306                                    O_RDONLY : O_RDWR);
00307       RECORD_TIME_POINT;
00308       Repos::dprintf(DBG_FDCACHE, "FdCache::open 0x%08x opened fd %d ofl %d=>%d\n",
00309                      sid, fd, ofl, oflused);
00310     }
00311   else
00312     {
00313       Repos::dprintf(DBG_FDCACHE,
00314                      "FdCache::open 0x%08x found fd %d ofl %d=>%d\n",
00315                      sid, fd, ofl, oflused);
00316     }
00317   assert(oflused != FdCache::any);
00318   if (oflout) *oflout = oflused;
00319   return fd;
00320 }
00321 
00322 int FdCache::tryopen(ShortId sid, FdCache::OFlag ofl, FdCache::OFlag* oflout)
00323   throw ()
00324 {
00325   pthread_once(&once, FdCache_init);
00326   unsigned int index = fdCacheIndex(sid);
00327   assert(fdCaches != NULL);
00328   return fdCaches[index].tryopen(sid, ofl, oflout);
00329 }
00330 
00331 int
00332 FdCache::PartialCache::tryopen(ShortId sid, FdCache::OFlag ofl, FdCache::OFlag* oflout)
00333   throw ()
00334 {
00335   // First search the cache for an existing file descriptor.
00336   FdCache::OFlag oflused = FdCache::any;
00337   ShortIdKey key(sid);
00338 
00339   RECORD_TIME_POINT;
00340   this->mu.lock();
00341   RECORD_TIME_POINT;
00342 
00343   int fd = this->lookup(key, ofl, oflused);
00344 
00345   // Update hit/miss statistics
00346   if(fd != -1)
00347     this->hit_count++;
00348   else
00349     this->try_miss_count++;
00350 
00351   this->mu.unlock();
00352   RECORD_TIME_POINT;
00353 
00354   if(fd != -1)
00355     {
00356       assert(oflused != FdCache::any);
00357       if (oflout) *oflout = oflused;
00358       Repos::dprintf(DBG_FDCACHE,
00359                      "FdCache::tryopen 0x%08x found fd %d ofl %d=>%d\n",
00360                      sid, fd, ofl, oflused);
00361     }
00362   return fd;
00363 }
00364 
00365 void
00366 FdCache::close(ShortId sid, int fd, FdCache::OFlag ofl) throw ()
00367 {
00368   pthread_once(&once, FdCache_init);
00369   unsigned int index = fdCacheIndex(sid);
00370   assert(fdCaches != NULL);
00371   fdCaches[index].close(sid, fd, ofl);
00372 }
00373 
00374 void
00375 FdCache::PartialCache::close(ShortId sid, int fd, FdCache::OFlag ofl) throw ()
00376 {
00377   RECORD_TIME_POINT;
00378   this->mu.lock();
00379   RECORD_TIME_POINT;
00380   assert(fd != -1);
00381   ShortIdKey key(sid);
00382   FdCache::FdInfo* fdi;
00383   FdCache::FdInfo* oldfdi;
00384   if (!this->table.Delete(key, oldfdi, false)) {
00385     oldfdi = NULL;
00386   }
00387   fdi = NEW(FdCache::FdInfo);
00388   fdi->fd = fd;
00389   assert(ofl != FdCache::any);
00390   fdi->ofl = ofl;
00391   fdi->lastUse = time(NULL);
00392   fdi->next = &this->lruListHead;
00393   fdi->prev = this->lruListHead.prev;
00394   fdi->prev->next = fdi;
00395   fdi->next->prev = fdi;
00396   fdi->key = key;
00397   fdi->more = oldfdi;
00398   fdi->moreprev = NULL;
00399   if (oldfdi) oldfdi->moreprev = fdi;
00400   this->table.Put(key, fdi);
00401   Repos::dprintf(DBG_FDCACHE, "FdCache::close 0x%08x cached fd %d %s\n",
00402                  sid, fd, oldfdi ? "(dupe)" : "");
00403   this->nCached++;
00404   if (this->nCached > (maxCached / fdCache_count)) {
00405     fdiDeleteNL(lruListHead.next, "close");
00406     this->nCached--;
00407     this->evict_count++;
00408   }
00409   this->mu.unlock();
00410   RECORD_TIME_POINT;
00411 }
00412 
00413 void
00414 FdCache::flush(ShortId sid, FdCache::OFlag ofl) throw ()
00415 {
00416   pthread_once(&once, FdCache_init);
00417   unsigned int index = fdCacheIndex(sid);
00418   assert(fdCaches != NULL);
00419   fdCaches[index].flush(sid, ofl);
00420 }
00421 
00422 void
00423 FdCache::PartialCache::flush(ShortId sid, FdCache::OFlag ofl) throw ()
00424 {
00425   this->mu.lock();
00426   ShortIdKey key(sid);
00427   FdCache::FdInfo* fdihead;
00428   if (this->table.Delete(key, fdihead, false)) {
00429     FdCache::FdInfo* fdi = fdihead;
00430     do {
00431       FdCache::FdInfo* more = fdi->more;
00432       if (ofl == FdCache::any || ofl == fdi->ofl) {
00433         fdi->prev->next = fdi->next;
00434         fdi->next->prev = fdi->prev;
00435         if (fdi->moreprev != NULL) {
00436           fdi->moreprev->more = fdi->more;
00437         }
00438         if (fdi->more != NULL) {
00439           fdi->more->moreprev = fdi->moreprev;
00440         }
00441         if (fdihead == fdi) {
00442           fdihead = fdi->more;
00443         }
00444         Repos::dprintf(DBG_FDCACHE,
00445                        "FdCache::flush 0x%08x deleted fd %d ofl %d\n",
00446                        sid, fdi->fd, fdi->ofl);
00447 	::close(fdi->fd);
00448         delete fdi;
00449         this->nCached--;
00450       }
00451       fdi = more;
00452     } while (fdi);
00453     if (fdihead != NULL) {
00454       this->table.Put(key, fdihead);
00455     }
00456   }
00457   this->mu.unlock();
00458 }
00459 
00460 void
00461 FdCache::getStats(/*OUT*/ Basics::uint32 &n_in_cache,
00462                   /*OUT*/ Basics::uint64 &hits,
00463                   /*OUT*/ Basics::uint64 &open_misses,
00464                   /*OUT*/ Basics::uint64 &try_misses,
00465                   /*OUT*/ Basics::uint64 &evictions,
00466                   /*OUT*/ Basics::uint64 &expirations)
00467 {
00468   // First zero the output parameters, as each PartialCache will add
00469   // its statistics to them.
00470   n_in_cache = 0;
00471   hits = 0;
00472   open_misses = 0;
00473   try_misses = 0;
00474   evictions = 0;
00475   expirations = 0;
00476 
00477   // Loop over the file descriptor caches, and gather statistics from
00478   // each one.
00479   for(unsigned int i = 0; i < fdCache_count; i++)
00480     {
00481       fdCaches[i].accumulateStats(n_in_cache,
00482                                   hits, open_misses, try_misses,
00483                                   evictions, expirations);
00484     }
00485 }
00486 
00487 void
00488 FdCache::PartialCache::accumulateStats(/*OUT*/ Basics::uint32 &n_in_cache,
00489                                        /*OUT*/ Basics::uint64 &hits,
00490                                        /*OUT*/ Basics::uint64 &open_misses,
00491                                        /*OUT*/ Basics::uint64 &try_misses,
00492                                        /*OUT*/ Basics::uint64 &evictions,
00493                                        /*OUT*/ Basics::uint64 &expirations)
00494 {
00495   this->mu.lock();
00496   n_in_cache += this->nCached;
00497   hits += this->hit_count;
00498   open_misses += this->open_miss_count;
00499   try_misses += this->try_miss_count;
00500   evictions += this->evict_count;
00501   expirations += this->expire_count;
00502   this->mu.unlock();
00503 }

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