Back to index

lightning-sunbird  0.9+nobinonly
nsDiskCacheBinding.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is nsDiskCacheBinding.cpp, released
00017  * May 10, 2001.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 2001
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   Patrick C. Beard <beard@netscape.com>
00026  *   Gordon Sheridan  <gordon@netscape.com>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either the GNU General Public License Version 2 or later (the "GPL"), or
00030  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include <limits.h>
00043 
00044 #include "nscore.h"
00045 #include "nsDiskCacheBinding.h"
00046 
00047 
00048 
00049 /******************************************************************************
00050  *  static hash table callback functions
00051  *
00052  *****************************************************************************/
00053 #ifdef XP_MAC
00054 #pragma mark -
00055 #pragma mark HASHTABLE CALLBACKS
00056 #endif
00057 
00058 struct HashTableEntry : PLDHashEntryHdr {
00059     nsDiskCacheBinding *  mBinding;
00060 };
00061 
00062 
00063 static const void * PR_CALLBACK
00064 GetKey(PLDHashTable * /*table*/, PLDHashEntryHdr * header)
00065 {
00066     return (void*) ((HashTableEntry *)header)->mBinding->mRecord.HashNumber();
00067 }
00068 
00069 
00070 static PLDHashNumber PR_CALLBACK
00071 HashKey( PLDHashTable *table, const void *key)
00072 {
00073     return (PLDHashNumber) NS_PTR_TO_INT32(key);
00074 }
00075 
00076 
00077 static PRBool PR_CALLBACK
00078 MatchEntry(PLDHashTable *              /* table */,
00079             const PLDHashEntryHdr *       header,
00080             const void *                  key)
00081 {
00082     HashTableEntry * hashEntry = (HashTableEntry *) header;
00083     return (hashEntry->mBinding->mRecord.HashNumber() == (PLDHashNumber) NS_PTR_TO_INT32(key));
00084 }
00085 
00086 static void PR_CALLBACK
00087 MoveEntry(PLDHashTable *           /* table */,
00088           const PLDHashEntryHdr *     src,
00089           PLDHashEntryHdr       *     dst)
00090 {
00091     ((HashTableEntry *)dst)->mBinding = ((HashTableEntry *)src)->mBinding;
00092 }
00093 
00094 
00095 static void PR_CALLBACK
00096 ClearEntry(PLDHashTable *      /* table */,
00097            PLDHashEntryHdr *      header)
00098 {
00099     ((HashTableEntry *)header)->mBinding = nsnull;
00100 }
00101 
00102 
00103 /******************************************************************************
00104  *  Utility Functions
00105  *****************************************************************************/
00106 #ifdef XP_MAC
00107 #pragma mark -
00108 #pragma mark DISK CACHE BINDERY
00109 #endif
00110 
00111 nsDiskCacheBinding *
00112 GetCacheEntryBinding(nsCacheEntry * entry)
00113 {
00114     return (nsDiskCacheBinding *) entry->Data();
00115 }
00116 
00117 
00118 /******************************************************************************
00119  *  nsDiskCacheBinding
00120  *****************************************************************************/
00121 
00122 NS_IMPL_THREADSAFE_ISUPPORTS0(nsDiskCacheBinding)
00123 
00124 nsDiskCacheBinding::nsDiskCacheBinding(nsCacheEntry* entry, nsDiskCacheRecord * record)
00125     :   mCacheEntry(entry)
00126     ,   mStreamIO(nsnull)
00127 {
00128     NS_ASSERTION(record->ValidRecord(), "bad record");
00129     PR_INIT_CLIST(this);
00130     mRecord     = *record;
00131     mDoomed     = entry->IsDoomed();
00132     mGeneration = record->Generation();    // 0 == uninitialized, or data & meta using block files
00133 }
00134 
00135 nsDiskCacheBinding::~nsDiskCacheBinding()
00136 {
00137     NS_ASSERTION(PR_CLIST_IS_EMPTY(this), "binding deleted while still on list");
00138     if (!PR_CLIST_IS_EMPTY(this))
00139         PR_REMOVE_LINK(this);       // XXX why are we still on a list?
00140     
00141     // sever streamIO/binding link
00142     if (mStreamIO) {
00143         mStreamIO->ClearBinding();
00144         NS_RELEASE(mStreamIO);
00145     }
00146 }
00147 
00148 nsresult
00149 nsDiskCacheBinding::EnsureStreamIO()
00150 {
00151     if (!mStreamIO) {
00152         mStreamIO = new nsDiskCacheStreamIO(this);
00153         if (!mStreamIO)  return NS_ERROR_OUT_OF_MEMORY;
00154         NS_ADDREF(mStreamIO);
00155     }
00156     return NS_OK;
00157 }
00158 
00159 
00160 /******************************************************************************
00161  *  nsDiskCacheBindery
00162  *
00163  *  Keeps track of bound disk cache entries to detect for collisions.
00164  *
00165  *****************************************************************************/
00166 
00167 PLDHashTableOps nsDiskCacheBindery::ops =
00168 {
00169     PL_DHashAllocTable,
00170     PL_DHashFreeTable,
00171     GetKey,
00172     HashKey,
00173     MatchEntry,
00174     MoveEntry,
00175     ClearEntry,
00176     PL_DHashFinalizeStub
00177 };
00178 
00179 
00180 nsDiskCacheBindery::nsDiskCacheBindery()
00181     : initialized(PR_FALSE)
00182 {
00183 }
00184 
00185 
00186 nsDiskCacheBindery::~nsDiskCacheBindery()
00187 {
00188     Reset();
00189 }
00190 
00191 
00192 nsresult
00193 nsDiskCacheBindery::Init()
00194 {
00195     nsresult rv = NS_OK;
00196     initialized = PL_DHashTableInit(&table, &ops, nsnull, sizeof(HashTableEntry), 0);
00197 
00198     if (!initialized) rv = NS_ERROR_OUT_OF_MEMORY;
00199     
00200     return rv;
00201 }
00202 
00203 void
00204 nsDiskCacheBindery::Reset()
00205 {
00206     if (initialized) {
00207         PL_DHashTableFinish(&table);
00208         initialized = PR_FALSE;
00209     }
00210 }
00211 
00212 
00213 nsDiskCacheBinding *
00214 nsDiskCacheBindery::CreateBinding(nsCacheEntry *       entry,
00215                                   nsDiskCacheRecord *  record)
00216 {
00217     NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
00218     nsCOMPtr<nsISupports> data = entry->Data();
00219     if (data) {
00220         NS_ERROR("cache entry already has bind data");
00221         return nsnull;
00222     }
00223     
00224     nsDiskCacheBinding * binding = new nsDiskCacheBinding(entry, record);
00225     if (!binding)  return nsnull;
00226         
00227     // give ownership of the binding to the entry
00228     entry->SetData(binding);
00229     
00230     // add binding to collision detection system
00231     nsresult rv = AddBinding(binding);
00232     if (NS_FAILED(rv)) {
00233         entry->SetData(nsnull);
00234         return nsnull;
00235     }
00236 
00237     return binding;
00238 }
00239 
00240 
00244 nsDiskCacheBinding *
00245 nsDiskCacheBindery::FindActiveBinding(PRUint32  hashNumber)
00246 {
00247     NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
00248     // find hash entry for key
00249     HashTableEntry * hashEntry;
00250     hashEntry = (HashTableEntry *) PL_DHashTableOperate(&table, (void*) hashNumber, PL_DHASH_LOOKUP);
00251     if (PL_DHASH_ENTRY_IS_FREE(hashEntry)) return nsnull;
00252     
00253     // walk list looking for active entry
00254     NS_ASSERTION(hashEntry->mBinding, "hash entry left with no binding");
00255     nsDiskCacheBinding * binding = hashEntry->mBinding;    
00256     while (binding->mCacheEntry->IsDoomed()) {
00257         binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
00258         if (binding == hashEntry->mBinding)  return nsnull;
00259     }
00260     return binding;
00261 }
00262 
00263 
00274 nsresult
00275 nsDiskCacheBindery::AddBinding(nsDiskCacheBinding * binding)
00276 {
00277     NS_ENSURE_ARG_POINTER(binding);
00278     NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
00279 
00280     // find hash entry for key
00281     HashTableEntry * hashEntry;
00282     hashEntry = (HashTableEntry *) PL_DHashTableOperate(&table,
00283                                                         (void*) binding->mRecord.HashNumber(),
00284                                                         PL_DHASH_ADD);
00285     if (!hashEntry) return NS_ERROR_OUT_OF_MEMORY;
00286     
00287     if (hashEntry->mBinding == nsnull) {
00288         hashEntry->mBinding = binding;
00289         if (binding->mGeneration == 0)
00290             binding->mGeneration = 1;   // if generation uninitialized, set it to 1
00291             
00292         return NS_OK;
00293     }
00294     
00295     
00296     // insert binding in generation order
00297     nsDiskCacheBinding * p  = hashEntry->mBinding;
00298     PRBool   calcGeneration = (binding->mGeneration == 0);  // do we need to calculate generation?
00299     if (calcGeneration)  binding->mGeneration = 1;          // initialize to 1 if uninitialized
00300     while (1) {
00301     
00302         if (binding->mGeneration < p->mGeneration) {
00303             // here we are
00304             PR_INSERT_BEFORE(binding, p);
00305             if (hashEntry->mBinding == p)
00306                 hashEntry->mBinding = binding;
00307             break;
00308         }
00309         
00310         if (binding->mGeneration == p->mGeneration) {
00311             if (calcGeneration)  ++binding->mGeneration;    // try the next generation
00312             else {
00313                 NS_ERROR("### disk cache: generations collide!");
00314                 return NS_ERROR_UNEXPECTED;
00315             }
00316         }
00317 
00318         p = (nsDiskCacheBinding *)PR_NEXT_LINK(p);
00319         if (p == hashEntry->mBinding) {
00320             // end of line: insert here or die
00321             p = (nsDiskCacheBinding *)PR_PREV_LINK(p);  // back up and check generation
00322             if (p->mGeneration == 255) {
00323                 NS_WARNING("### disk cache: generation capacity at full");
00324                 return NS_ERROR_UNEXPECTED;
00325             }
00326             PR_INSERT_BEFORE(binding, hashEntry->mBinding);
00327             break;
00328         }
00329     }
00330     return NS_OK;
00331 }
00332 
00333 
00337 void
00338 nsDiskCacheBindery::RemoveBinding(nsDiskCacheBinding * binding)
00339 {
00340     NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
00341     if (!initialized)   return;
00342     
00343     HashTableEntry * hashEntry;
00344     void *           key = (void *)binding->mRecord.HashNumber();
00345 
00346     hashEntry = (HashTableEntry*) PL_DHashTableOperate(&table,
00347                                                        (void*) key,
00348                                                        PL_DHASH_LOOKUP);
00349     if (!PL_DHASH_ENTRY_IS_BUSY(hashEntry)) {
00350         NS_WARNING("### disk cache: binding not in hashtable!");
00351         return;
00352     }
00353     
00354     if (binding == hashEntry->mBinding) {
00355         if (PR_CLIST_IS_EMPTY(binding)) {
00356             // remove this hash entry
00357             (void) PL_DHashTableOperate(&table,
00358                                         (void*) binding->mRecord.HashNumber(),
00359                                         PL_DHASH_REMOVE);
00360             return;
00361             
00362         } else {
00363             // promote next binding to head, and unlink this binding
00364             hashEntry->mBinding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
00365         }
00366     }
00367     PR_REMOVE_AND_INIT_LINK(binding);
00368 }
00369 
00370 
00375 PLDHashOperator PR_CALLBACK
00376 ActiveBinding(PLDHashTable *    table,
00377               PLDHashEntryHdr * hdr,
00378               PRUint32          number,
00379               void *            arg)
00380 {
00381     nsDiskCacheBinding * binding = ((HashTableEntry *)hdr)->mBinding;
00382     NS_ASSERTION(binding, "### disk cache binding = nsnull!");
00383     
00384     nsDiskCacheBinding * head = binding;
00385     do {   
00386         if (binding->IsActive()) {
00387            *((PRBool *)arg) = PR_TRUE;
00388             return PL_DHASH_STOP;
00389         }
00390 
00391         binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
00392     } while (binding != head);
00393 
00394     return PL_DHASH_NEXT;
00395 }
00396 
00397 
00401 PRBool
00402 nsDiskCacheBindery::ActiveBindings()
00403 {
00404     NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
00405     if (!initialized) return PR_FALSE;
00406 
00407     PRBool  activeBinding = PR_FALSE;
00408     PL_DHashTableEnumerate(&table, ActiveBinding, &activeBinding);
00409 
00410     return activeBinding;
00411 }