netwerk/cache/nsDiskCacheBinding.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  *
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "mozilla/MemoryReporting.h"
     8 #include "nsCache.h"
     9 #include <limits.h>
    11 #include "nscore.h"
    12 #include "nsDiskCacheBinding.h"
    13 #include "nsCacheService.h"
    17 /******************************************************************************
    18  *  static hash table callback functions
    19  *
    20  *****************************************************************************/
    21 struct HashTableEntry : PLDHashEntryHdr {
    22     nsDiskCacheBinding *  mBinding;
    23 };
    26 static PLDHashNumber
    27 HashKey( PLDHashTable *table, const void *key)
    28 {
    29     return (PLDHashNumber) NS_PTR_TO_INT32(key);
    30 }
    33 static bool
    34 MatchEntry(PLDHashTable *              /* table */,
    35             const PLDHashEntryHdr *       header,
    36             const void *                  key)
    37 {
    38     HashTableEntry * hashEntry = (HashTableEntry *) header;
    39     return (hashEntry->mBinding->mRecord.HashNumber() == (PLDHashNumber) NS_PTR_TO_INT32(key));
    40 }
    42 static void
    43 MoveEntry(PLDHashTable *           /* table */,
    44           const PLDHashEntryHdr *     src,
    45           PLDHashEntryHdr       *     dst)
    46 {
    47     ((HashTableEntry *)dst)->mBinding = ((HashTableEntry *)src)->mBinding;
    48 }
    51 static void
    52 ClearEntry(PLDHashTable *      /* table */,
    53            PLDHashEntryHdr *      header)
    54 {
    55     ((HashTableEntry *)header)->mBinding = nullptr;
    56 }
    59 /******************************************************************************
    60  *  Utility Functions
    61  *****************************************************************************/
    62 nsDiskCacheBinding *
    63 GetCacheEntryBinding(nsCacheEntry * entry)
    64 {
    65     return (nsDiskCacheBinding *) entry->Data();
    66 }
    69 /******************************************************************************
    70  *  nsDiskCacheBinding
    71  *****************************************************************************/
    73 NS_IMPL_ISUPPORTS0(nsDiskCacheBinding)
    75 nsDiskCacheBinding::nsDiskCacheBinding(nsCacheEntry* entry, nsDiskCacheRecord * record)
    76     :   mCacheEntry(entry)
    77     ,   mStreamIO(nullptr)
    78     ,   mDeactivateEvent(nullptr)
    79 {
    80     NS_ASSERTION(record->ValidRecord(), "bad record");
    81     PR_INIT_CLIST(this);
    82     mRecord     = *record;
    83     mDoomed     = entry->IsDoomed();
    84     mGeneration = record->Generation();    // 0 == uninitialized, or data & meta using block files
    85 }
    87 nsDiskCacheBinding::~nsDiskCacheBinding()
    88 {
    89     // Grab the cache lock since the binding is stored in nsCacheEntry::mData
    90     // and it is released using nsCacheService::ReleaseObject_Locked() which
    91     // releases the object outside the cache lock.
    92     nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHEBINDING_DESTRUCTOR));
    94     NS_ASSERTION(PR_CLIST_IS_EMPTY(this), "binding deleted while still on list");
    95     if (!PR_CLIST_IS_EMPTY(this))
    96         PR_REMOVE_LINK(this);       // XXX why are we still on a list?
    98     // sever streamIO/binding link
    99     if (mStreamIO) {
   100         if (NS_FAILED(mStreamIO->ClearBinding()))
   101             nsCacheService::DoomEntry(mCacheEntry);
   102         NS_RELEASE(mStreamIO);
   103     }
   104 }
   106 nsresult
   107 nsDiskCacheBinding::EnsureStreamIO()
   108 {
   109     if (!mStreamIO) {
   110         mStreamIO = new nsDiskCacheStreamIO(this);
   111         if (!mStreamIO)  return NS_ERROR_OUT_OF_MEMORY;
   112         NS_ADDREF(mStreamIO);
   113     }
   114     return NS_OK;
   115 }
   118 /******************************************************************************
   119  *  nsDiskCacheBindery
   120  *
   121  *  Keeps track of bound disk cache entries to detect for collisions.
   122  *
   123  *****************************************************************************/
   125 const PLDHashTableOps nsDiskCacheBindery::ops =
   126 {
   127     PL_DHashAllocTable,
   128     PL_DHashFreeTable,
   129     HashKey,
   130     MatchEntry,
   131     MoveEntry,
   132     ClearEntry,
   133     PL_DHashFinalizeStub
   134 };
   137 nsDiskCacheBindery::nsDiskCacheBindery()
   138     : initialized(false)
   139 {
   140 }
   143 nsDiskCacheBindery::~nsDiskCacheBindery()
   144 {
   145     Reset();
   146 }
   149 nsresult
   150 nsDiskCacheBindery::Init()
   151 {
   152     nsresult rv = NS_OK;
   153     PL_DHashTableInit(&table, &ops, nullptr, sizeof(HashTableEntry), 0);
   154     initialized = true;
   156     return rv;
   157 }
   159 void
   160 nsDiskCacheBindery::Reset()
   161 {
   162     if (initialized) {
   163         PL_DHashTableFinish(&table);
   164         initialized = false;
   165     }
   166 }
   169 nsDiskCacheBinding *
   170 nsDiskCacheBindery::CreateBinding(nsCacheEntry *       entry,
   171                                   nsDiskCacheRecord *  record)
   172 {
   173     NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
   174     nsCOMPtr<nsISupports> data = entry->Data();
   175     if (data) {
   176         NS_ERROR("cache entry already has bind data");
   177         return nullptr;
   178     }
   180     nsDiskCacheBinding * binding = new nsDiskCacheBinding(entry, record);
   181     if (!binding)  return nullptr;
   183     // give ownership of the binding to the entry
   184     entry->SetData(binding);
   186     // add binding to collision detection system
   187     nsresult rv = AddBinding(binding);
   188     if (NS_FAILED(rv)) {
   189         entry->SetData(nullptr);
   190         return nullptr;
   191     }
   193     return binding;
   194 }
   197 /**
   198  *  FindActiveEntry :  to find active colliding entry so we can doom it
   199  */
   200 nsDiskCacheBinding *
   201 nsDiskCacheBindery::FindActiveBinding(uint32_t  hashNumber)
   202 {
   203     NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
   204     // find hash entry for key
   205     HashTableEntry * hashEntry;
   206     hashEntry =
   207       (HashTableEntry *) PL_DHashTableOperate(&table,
   208                                               (void*)(uintptr_t) hashNumber,
   209                                               PL_DHASH_LOOKUP);
   210     if (PL_DHASH_ENTRY_IS_FREE(hashEntry)) return nullptr;
   212     // walk list looking for active entry
   213     NS_ASSERTION(hashEntry->mBinding, "hash entry left with no binding");
   214     nsDiskCacheBinding * binding = hashEntry->mBinding;    
   215     while (binding->mCacheEntry->IsDoomed()) {
   216         binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
   217         if (binding == hashEntry->mBinding)  return nullptr;
   218     }
   219     return binding;
   220 }
   223 /**
   224  *  AddBinding
   225  *
   226  *  Called from FindEntry() if we read an entry off of disk
   227  *      - it may already have a generation number
   228  *      - a generation number conflict is an error
   229  *
   230  *  Called from BindEntry()
   231  *      - a generation number needs to be assigned
   232  */
   233 nsresult
   234 nsDiskCacheBindery::AddBinding(nsDiskCacheBinding * binding)
   235 {
   236     NS_ENSURE_ARG_POINTER(binding);
   237     NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
   239     // find hash entry for key
   240     HashTableEntry * hashEntry;
   241     hashEntry = (HashTableEntry *)
   242       PL_DHashTableOperate(&table,
   243                            (void *)(uintptr_t) binding->mRecord.HashNumber(),
   244                            PL_DHASH_ADD);
   245     if (!hashEntry) return NS_ERROR_OUT_OF_MEMORY;
   247     if (hashEntry->mBinding == nullptr) {
   248         hashEntry->mBinding = binding;
   249         if (binding->mGeneration == 0)
   250             binding->mGeneration = 1;   // if generation uninitialized, set it to 1
   252         return NS_OK;
   253     }
   256     // insert binding in generation order
   257     nsDiskCacheBinding * p  = hashEntry->mBinding;
   258     bool     calcGeneration = (binding->mGeneration == 0);  // do we need to calculate generation?
   259     if (calcGeneration)  binding->mGeneration = 1;          // initialize to 1 if uninitialized
   260     while (1) {
   262         if (binding->mGeneration < p->mGeneration) {
   263             // here we are
   264             PR_INSERT_BEFORE(binding, p);
   265             if (hashEntry->mBinding == p)
   266                 hashEntry->mBinding = binding;
   267             break;
   268         }
   270         if (binding->mGeneration == p->mGeneration) {
   271             if (calcGeneration)  ++binding->mGeneration;    // try the next generation
   272             else {
   273                 NS_ERROR("### disk cache: generations collide!");
   274                 return NS_ERROR_UNEXPECTED;
   275             }
   276         }
   278         p = (nsDiskCacheBinding *)PR_NEXT_LINK(p);
   279         if (p == hashEntry->mBinding) {
   280             // end of line: insert here or die
   281             p = (nsDiskCacheBinding *)PR_PREV_LINK(p);  // back up and check generation
   282             if (p->mGeneration == 255) {
   283                 NS_WARNING("### disk cache: generation capacity at full");
   284                 return NS_ERROR_UNEXPECTED;
   285             }
   286             PR_INSERT_BEFORE(binding, hashEntry->mBinding);
   287             break;
   288         }
   289     }
   290     return NS_OK;
   291 }
   294 /**
   295  *  RemoveBinding :  remove binding from collision detection on deactivation
   296  */
   297 void
   298 nsDiskCacheBindery::RemoveBinding(nsDiskCacheBinding * binding)
   299 {
   300     NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
   301     if (!initialized)   return;
   303     HashTableEntry * hashEntry;
   304     void           * key = (void *)(uintptr_t)binding->mRecord.HashNumber();
   306     hashEntry = (HashTableEntry*) PL_DHashTableOperate(&table,
   307                                                        (void*)(uintptr_t) key,
   308                                                        PL_DHASH_LOOKUP);
   309     if (!PL_DHASH_ENTRY_IS_BUSY(hashEntry)) {
   310         NS_WARNING("### disk cache: binding not in hashtable!");
   311         return;
   312     }
   314     if (binding == hashEntry->mBinding) {
   315         if (PR_CLIST_IS_EMPTY(binding)) {
   316             // remove this hash entry
   317             PL_DHashTableOperate(&table,
   318                                  (void*)(uintptr_t) binding->mRecord.HashNumber(),
   319                                  PL_DHASH_REMOVE);
   320             return;
   322         } else {
   323             // promote next binding to head, and unlink this binding
   324             hashEntry->mBinding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
   325         }
   326     }
   327     PR_REMOVE_AND_INIT_LINK(binding);
   328 }
   331 /**
   332  *  ActiveBinding : PLDHashTable enumerate function to verify active bindings
   333  */
   335 PLDHashOperator
   336 ActiveBinding(PLDHashTable *    table,
   337               PLDHashEntryHdr * hdr,
   338               uint32_t          number,
   339               void *            arg)
   340 {
   341     nsDiskCacheBinding * binding = ((HashTableEntry *)hdr)->mBinding;
   342     NS_ASSERTION(binding, "### disk cache binding = nullptr!");
   344     nsDiskCacheBinding * head = binding;
   345     do {   
   346         if (binding->IsActive()) {
   347            *((bool *)arg) = true;
   348             return PL_DHASH_STOP;
   349         }
   351         binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
   352     } while (binding != head);
   354     return PL_DHASH_NEXT;
   355 }
   358 /**
   359  *  ActiveBindings : return true if any bindings have open descriptors
   360  */
   361 bool
   362 nsDiskCacheBindery::ActiveBindings()
   363 {
   364     NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
   365     if (!initialized) return false;
   367     bool    activeBinding = false;
   368     PL_DHashTableEnumerate(&table, ActiveBinding, &activeBinding);
   370     return activeBinding;
   371 }
   373 struct AccumulatorArg {
   374     size_t mUsage;
   375     mozilla::MallocSizeOf mMallocSizeOf;
   376 };
   378 PLDHashOperator
   379 AccumulateHeapUsage(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number,
   380                     void *arg)
   381 {
   382     nsDiskCacheBinding *binding = ((HashTableEntry *)hdr)->mBinding;
   383     NS_ASSERTION(binding, "### disk cache binding = nsnull!");
   385     AccumulatorArg *acc = (AccumulatorArg *)arg;
   387     nsDiskCacheBinding *head = binding;
   388     do {
   389         acc->mUsage += acc->mMallocSizeOf(binding);
   391         if (binding->mStreamIO) {
   392             acc->mUsage += binding->mStreamIO->SizeOfIncludingThis(acc->mMallocSizeOf);
   393         }
   395         /* No good way to get at mDeactivateEvent internals for proper size, so
   396            we use this as an estimate. */
   397         if (binding->mDeactivateEvent) {
   398             acc->mUsage += acc->mMallocSizeOf(binding->mDeactivateEvent);
   399         }
   401         binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
   402     } while (binding != head);
   404     return PL_DHASH_NEXT;
   405 }
   407 /**
   408  * SizeOfExcludingThis: return the amount of heap memory (bytes) being used by the bindery
   409  */
   410 size_t
   411 nsDiskCacheBindery::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
   412 {
   413     NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
   414     if (!initialized) return 0;
   416     AccumulatorArg arg;
   417     arg.mUsage = 0;
   418     arg.mMallocSizeOf = aMallocSizeOf;
   420     PL_DHashTableEnumerate(&table, AccumulateHeapUsage, &arg);
   422     return arg.mUsage;
   423 }

mercurial