1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/cache/nsDiskCacheBinding.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,423 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "mozilla/MemoryReporting.h" 1.11 +#include "nsCache.h" 1.12 +#include <limits.h> 1.13 + 1.14 +#include "nscore.h" 1.15 +#include "nsDiskCacheBinding.h" 1.16 +#include "nsCacheService.h" 1.17 + 1.18 + 1.19 + 1.20 +/****************************************************************************** 1.21 + * static hash table callback functions 1.22 + * 1.23 + *****************************************************************************/ 1.24 +struct HashTableEntry : PLDHashEntryHdr { 1.25 + nsDiskCacheBinding * mBinding; 1.26 +}; 1.27 + 1.28 + 1.29 +static PLDHashNumber 1.30 +HashKey( PLDHashTable *table, const void *key) 1.31 +{ 1.32 + return (PLDHashNumber) NS_PTR_TO_INT32(key); 1.33 +} 1.34 + 1.35 + 1.36 +static bool 1.37 +MatchEntry(PLDHashTable * /* table */, 1.38 + const PLDHashEntryHdr * header, 1.39 + const void * key) 1.40 +{ 1.41 + HashTableEntry * hashEntry = (HashTableEntry *) header; 1.42 + return (hashEntry->mBinding->mRecord.HashNumber() == (PLDHashNumber) NS_PTR_TO_INT32(key)); 1.43 +} 1.44 + 1.45 +static void 1.46 +MoveEntry(PLDHashTable * /* table */, 1.47 + const PLDHashEntryHdr * src, 1.48 + PLDHashEntryHdr * dst) 1.49 +{ 1.50 + ((HashTableEntry *)dst)->mBinding = ((HashTableEntry *)src)->mBinding; 1.51 +} 1.52 + 1.53 + 1.54 +static void 1.55 +ClearEntry(PLDHashTable * /* table */, 1.56 + PLDHashEntryHdr * header) 1.57 +{ 1.58 + ((HashTableEntry *)header)->mBinding = nullptr; 1.59 +} 1.60 + 1.61 + 1.62 +/****************************************************************************** 1.63 + * Utility Functions 1.64 + *****************************************************************************/ 1.65 +nsDiskCacheBinding * 1.66 +GetCacheEntryBinding(nsCacheEntry * entry) 1.67 +{ 1.68 + return (nsDiskCacheBinding *) entry->Data(); 1.69 +} 1.70 + 1.71 + 1.72 +/****************************************************************************** 1.73 + * nsDiskCacheBinding 1.74 + *****************************************************************************/ 1.75 + 1.76 +NS_IMPL_ISUPPORTS0(nsDiskCacheBinding) 1.77 + 1.78 +nsDiskCacheBinding::nsDiskCacheBinding(nsCacheEntry* entry, nsDiskCacheRecord * record) 1.79 + : mCacheEntry(entry) 1.80 + , mStreamIO(nullptr) 1.81 + , mDeactivateEvent(nullptr) 1.82 +{ 1.83 + NS_ASSERTION(record->ValidRecord(), "bad record"); 1.84 + PR_INIT_CLIST(this); 1.85 + mRecord = *record; 1.86 + mDoomed = entry->IsDoomed(); 1.87 + mGeneration = record->Generation(); // 0 == uninitialized, or data & meta using block files 1.88 +} 1.89 + 1.90 +nsDiskCacheBinding::~nsDiskCacheBinding() 1.91 +{ 1.92 + // Grab the cache lock since the binding is stored in nsCacheEntry::mData 1.93 + // and it is released using nsCacheService::ReleaseObject_Locked() which 1.94 + // releases the object outside the cache lock. 1.95 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHEBINDING_DESTRUCTOR)); 1.96 + 1.97 + NS_ASSERTION(PR_CLIST_IS_EMPTY(this), "binding deleted while still on list"); 1.98 + if (!PR_CLIST_IS_EMPTY(this)) 1.99 + PR_REMOVE_LINK(this); // XXX why are we still on a list? 1.100 + 1.101 + // sever streamIO/binding link 1.102 + if (mStreamIO) { 1.103 + if (NS_FAILED(mStreamIO->ClearBinding())) 1.104 + nsCacheService::DoomEntry(mCacheEntry); 1.105 + NS_RELEASE(mStreamIO); 1.106 + } 1.107 +} 1.108 + 1.109 +nsresult 1.110 +nsDiskCacheBinding::EnsureStreamIO() 1.111 +{ 1.112 + if (!mStreamIO) { 1.113 + mStreamIO = new nsDiskCacheStreamIO(this); 1.114 + if (!mStreamIO) return NS_ERROR_OUT_OF_MEMORY; 1.115 + NS_ADDREF(mStreamIO); 1.116 + } 1.117 + return NS_OK; 1.118 +} 1.119 + 1.120 + 1.121 +/****************************************************************************** 1.122 + * nsDiskCacheBindery 1.123 + * 1.124 + * Keeps track of bound disk cache entries to detect for collisions. 1.125 + * 1.126 + *****************************************************************************/ 1.127 + 1.128 +const PLDHashTableOps nsDiskCacheBindery::ops = 1.129 +{ 1.130 + PL_DHashAllocTable, 1.131 + PL_DHashFreeTable, 1.132 + HashKey, 1.133 + MatchEntry, 1.134 + MoveEntry, 1.135 + ClearEntry, 1.136 + PL_DHashFinalizeStub 1.137 +}; 1.138 + 1.139 + 1.140 +nsDiskCacheBindery::nsDiskCacheBindery() 1.141 + : initialized(false) 1.142 +{ 1.143 +} 1.144 + 1.145 + 1.146 +nsDiskCacheBindery::~nsDiskCacheBindery() 1.147 +{ 1.148 + Reset(); 1.149 +} 1.150 + 1.151 + 1.152 +nsresult 1.153 +nsDiskCacheBindery::Init() 1.154 +{ 1.155 + nsresult rv = NS_OK; 1.156 + PL_DHashTableInit(&table, &ops, nullptr, sizeof(HashTableEntry), 0); 1.157 + initialized = true; 1.158 + 1.159 + return rv; 1.160 +} 1.161 + 1.162 +void 1.163 +nsDiskCacheBindery::Reset() 1.164 +{ 1.165 + if (initialized) { 1.166 + PL_DHashTableFinish(&table); 1.167 + initialized = false; 1.168 + } 1.169 +} 1.170 + 1.171 + 1.172 +nsDiskCacheBinding * 1.173 +nsDiskCacheBindery::CreateBinding(nsCacheEntry * entry, 1.174 + nsDiskCacheRecord * record) 1.175 +{ 1.176 + NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized"); 1.177 + nsCOMPtr<nsISupports> data = entry->Data(); 1.178 + if (data) { 1.179 + NS_ERROR("cache entry already has bind data"); 1.180 + return nullptr; 1.181 + } 1.182 + 1.183 + nsDiskCacheBinding * binding = new nsDiskCacheBinding(entry, record); 1.184 + if (!binding) return nullptr; 1.185 + 1.186 + // give ownership of the binding to the entry 1.187 + entry->SetData(binding); 1.188 + 1.189 + // add binding to collision detection system 1.190 + nsresult rv = AddBinding(binding); 1.191 + if (NS_FAILED(rv)) { 1.192 + entry->SetData(nullptr); 1.193 + return nullptr; 1.194 + } 1.195 + 1.196 + return binding; 1.197 +} 1.198 + 1.199 + 1.200 +/** 1.201 + * FindActiveEntry : to find active colliding entry so we can doom it 1.202 + */ 1.203 +nsDiskCacheBinding * 1.204 +nsDiskCacheBindery::FindActiveBinding(uint32_t hashNumber) 1.205 +{ 1.206 + NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized"); 1.207 + // find hash entry for key 1.208 + HashTableEntry * hashEntry; 1.209 + hashEntry = 1.210 + (HashTableEntry *) PL_DHashTableOperate(&table, 1.211 + (void*)(uintptr_t) hashNumber, 1.212 + PL_DHASH_LOOKUP); 1.213 + if (PL_DHASH_ENTRY_IS_FREE(hashEntry)) return nullptr; 1.214 + 1.215 + // walk list looking for active entry 1.216 + NS_ASSERTION(hashEntry->mBinding, "hash entry left with no binding"); 1.217 + nsDiskCacheBinding * binding = hashEntry->mBinding; 1.218 + while (binding->mCacheEntry->IsDoomed()) { 1.219 + binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding); 1.220 + if (binding == hashEntry->mBinding) return nullptr; 1.221 + } 1.222 + return binding; 1.223 +} 1.224 + 1.225 + 1.226 +/** 1.227 + * AddBinding 1.228 + * 1.229 + * Called from FindEntry() if we read an entry off of disk 1.230 + * - it may already have a generation number 1.231 + * - a generation number conflict is an error 1.232 + * 1.233 + * Called from BindEntry() 1.234 + * - a generation number needs to be assigned 1.235 + */ 1.236 +nsresult 1.237 +nsDiskCacheBindery::AddBinding(nsDiskCacheBinding * binding) 1.238 +{ 1.239 + NS_ENSURE_ARG_POINTER(binding); 1.240 + NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized"); 1.241 + 1.242 + // find hash entry for key 1.243 + HashTableEntry * hashEntry; 1.244 + hashEntry = (HashTableEntry *) 1.245 + PL_DHashTableOperate(&table, 1.246 + (void *)(uintptr_t) binding->mRecord.HashNumber(), 1.247 + PL_DHASH_ADD); 1.248 + if (!hashEntry) return NS_ERROR_OUT_OF_MEMORY; 1.249 + 1.250 + if (hashEntry->mBinding == nullptr) { 1.251 + hashEntry->mBinding = binding; 1.252 + if (binding->mGeneration == 0) 1.253 + binding->mGeneration = 1; // if generation uninitialized, set it to 1 1.254 + 1.255 + return NS_OK; 1.256 + } 1.257 + 1.258 + 1.259 + // insert binding in generation order 1.260 + nsDiskCacheBinding * p = hashEntry->mBinding; 1.261 + bool calcGeneration = (binding->mGeneration == 0); // do we need to calculate generation? 1.262 + if (calcGeneration) binding->mGeneration = 1; // initialize to 1 if uninitialized 1.263 + while (1) { 1.264 + 1.265 + if (binding->mGeneration < p->mGeneration) { 1.266 + // here we are 1.267 + PR_INSERT_BEFORE(binding, p); 1.268 + if (hashEntry->mBinding == p) 1.269 + hashEntry->mBinding = binding; 1.270 + break; 1.271 + } 1.272 + 1.273 + if (binding->mGeneration == p->mGeneration) { 1.274 + if (calcGeneration) ++binding->mGeneration; // try the next generation 1.275 + else { 1.276 + NS_ERROR("### disk cache: generations collide!"); 1.277 + return NS_ERROR_UNEXPECTED; 1.278 + } 1.279 + } 1.280 + 1.281 + p = (nsDiskCacheBinding *)PR_NEXT_LINK(p); 1.282 + if (p == hashEntry->mBinding) { 1.283 + // end of line: insert here or die 1.284 + p = (nsDiskCacheBinding *)PR_PREV_LINK(p); // back up and check generation 1.285 + if (p->mGeneration == 255) { 1.286 + NS_WARNING("### disk cache: generation capacity at full"); 1.287 + return NS_ERROR_UNEXPECTED; 1.288 + } 1.289 + PR_INSERT_BEFORE(binding, hashEntry->mBinding); 1.290 + break; 1.291 + } 1.292 + } 1.293 + return NS_OK; 1.294 +} 1.295 + 1.296 + 1.297 +/** 1.298 + * RemoveBinding : remove binding from collision detection on deactivation 1.299 + */ 1.300 +void 1.301 +nsDiskCacheBindery::RemoveBinding(nsDiskCacheBinding * binding) 1.302 +{ 1.303 + NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized"); 1.304 + if (!initialized) return; 1.305 + 1.306 + HashTableEntry * hashEntry; 1.307 + void * key = (void *)(uintptr_t)binding->mRecord.HashNumber(); 1.308 + 1.309 + hashEntry = (HashTableEntry*) PL_DHashTableOperate(&table, 1.310 + (void*)(uintptr_t) key, 1.311 + PL_DHASH_LOOKUP); 1.312 + if (!PL_DHASH_ENTRY_IS_BUSY(hashEntry)) { 1.313 + NS_WARNING("### disk cache: binding not in hashtable!"); 1.314 + return; 1.315 + } 1.316 + 1.317 + if (binding == hashEntry->mBinding) { 1.318 + if (PR_CLIST_IS_EMPTY(binding)) { 1.319 + // remove this hash entry 1.320 + PL_DHashTableOperate(&table, 1.321 + (void*)(uintptr_t) binding->mRecord.HashNumber(), 1.322 + PL_DHASH_REMOVE); 1.323 + return; 1.324 + 1.325 + } else { 1.326 + // promote next binding to head, and unlink this binding 1.327 + hashEntry->mBinding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding); 1.328 + } 1.329 + } 1.330 + PR_REMOVE_AND_INIT_LINK(binding); 1.331 +} 1.332 + 1.333 + 1.334 +/** 1.335 + * ActiveBinding : PLDHashTable enumerate function to verify active bindings 1.336 + */ 1.337 + 1.338 +PLDHashOperator 1.339 +ActiveBinding(PLDHashTable * table, 1.340 + PLDHashEntryHdr * hdr, 1.341 + uint32_t number, 1.342 + void * arg) 1.343 +{ 1.344 + nsDiskCacheBinding * binding = ((HashTableEntry *)hdr)->mBinding; 1.345 + NS_ASSERTION(binding, "### disk cache binding = nullptr!"); 1.346 + 1.347 + nsDiskCacheBinding * head = binding; 1.348 + do { 1.349 + if (binding->IsActive()) { 1.350 + *((bool *)arg) = true; 1.351 + return PL_DHASH_STOP; 1.352 + } 1.353 + 1.354 + binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding); 1.355 + } while (binding != head); 1.356 + 1.357 + return PL_DHASH_NEXT; 1.358 +} 1.359 + 1.360 + 1.361 +/** 1.362 + * ActiveBindings : return true if any bindings have open descriptors 1.363 + */ 1.364 +bool 1.365 +nsDiskCacheBindery::ActiveBindings() 1.366 +{ 1.367 + NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized"); 1.368 + if (!initialized) return false; 1.369 + 1.370 + bool activeBinding = false; 1.371 + PL_DHashTableEnumerate(&table, ActiveBinding, &activeBinding); 1.372 + 1.373 + return activeBinding; 1.374 +} 1.375 + 1.376 +struct AccumulatorArg { 1.377 + size_t mUsage; 1.378 + mozilla::MallocSizeOf mMallocSizeOf; 1.379 +}; 1.380 + 1.381 +PLDHashOperator 1.382 +AccumulateHeapUsage(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number, 1.383 + void *arg) 1.384 +{ 1.385 + nsDiskCacheBinding *binding = ((HashTableEntry *)hdr)->mBinding; 1.386 + NS_ASSERTION(binding, "### disk cache binding = nsnull!"); 1.387 + 1.388 + AccumulatorArg *acc = (AccumulatorArg *)arg; 1.389 + 1.390 + nsDiskCacheBinding *head = binding; 1.391 + do { 1.392 + acc->mUsage += acc->mMallocSizeOf(binding); 1.393 + 1.394 + if (binding->mStreamIO) { 1.395 + acc->mUsage += binding->mStreamIO->SizeOfIncludingThis(acc->mMallocSizeOf); 1.396 + } 1.397 + 1.398 + /* No good way to get at mDeactivateEvent internals for proper size, so 1.399 + we use this as an estimate. */ 1.400 + if (binding->mDeactivateEvent) { 1.401 + acc->mUsage += acc->mMallocSizeOf(binding->mDeactivateEvent); 1.402 + } 1.403 + 1.404 + binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding); 1.405 + } while (binding != head); 1.406 + 1.407 + return PL_DHASH_NEXT; 1.408 +} 1.409 + 1.410 +/** 1.411 + * SizeOfExcludingThis: return the amount of heap memory (bytes) being used by the bindery 1.412 + */ 1.413 +size_t 1.414 +nsDiskCacheBindery::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) 1.415 +{ 1.416 + NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized"); 1.417 + if (!initialized) return 0; 1.418 + 1.419 + AccumulatorArg arg; 1.420 + arg.mUsage = 0; 1.421 + arg.mMallocSizeOf = aMallocSizeOf; 1.422 + 1.423 + PL_DHashTableEnumerate(&table, AccumulateHeapUsage, &arg); 1.424 + 1.425 + return arg.mUsage; 1.426 +}