Bug 867089 - Validate the playbackRate before using it. r=ehsan
[gecko.git] / netwerk / cache / nsDiskCacheBinding.cpp
blob2467994c7d71714f9bb06fa3f8647c3ed195579f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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 "nsCache.h"
8 #include <limits.h>
10 #include "nscore.h"
11 #include "nsDiskCacheBinding.h"
12 #include "nsCacheService.h"
16 /******************************************************************************
17 * static hash table callback functions
19 *****************************************************************************/
20 struct HashTableEntry : PLDHashEntryHdr {
21 nsDiskCacheBinding * mBinding;
25 static PLDHashNumber
26 HashKey( PLDHashTable *table, const void *key)
28 return (PLDHashNumber) NS_PTR_TO_INT32(key);
32 static bool
33 MatchEntry(PLDHashTable * /* table */,
34 const PLDHashEntryHdr * header,
35 const void * key)
37 HashTableEntry * hashEntry = (HashTableEntry *) header;
38 return (hashEntry->mBinding->mRecord.HashNumber() == (PLDHashNumber) NS_PTR_TO_INT32(key));
41 static void
42 MoveEntry(PLDHashTable * /* table */,
43 const PLDHashEntryHdr * src,
44 PLDHashEntryHdr * dst)
46 ((HashTableEntry *)dst)->mBinding = ((HashTableEntry *)src)->mBinding;
50 static void
51 ClearEntry(PLDHashTable * /* table */,
52 PLDHashEntryHdr * header)
54 ((HashTableEntry *)header)->mBinding = nullptr;
58 /******************************************************************************
59 * Utility Functions
60 *****************************************************************************/
61 nsDiskCacheBinding *
62 GetCacheEntryBinding(nsCacheEntry * entry)
64 return (nsDiskCacheBinding *) entry->Data();
68 /******************************************************************************
69 * nsDiskCacheBinding
70 *****************************************************************************/
72 NS_IMPL_THREADSAFE_ISUPPORTS0(nsDiskCacheBinding)
74 nsDiskCacheBinding::nsDiskCacheBinding(nsCacheEntry* entry, nsDiskCacheRecord * record)
75 : mCacheEntry(entry)
76 , mStreamIO(nullptr)
77 , mDeactivateEvent(nullptr)
79 NS_ASSERTION(record->ValidRecord(), "bad record");
80 PR_INIT_CLIST(this);
81 mRecord = *record;
82 mDoomed = entry->IsDoomed();
83 mGeneration = record->Generation(); // 0 == uninitialized, or data & meta using block files
86 nsDiskCacheBinding::~nsDiskCacheBinding()
88 // Grab the cache lock since the binding is stored in nsCacheEntry::mData
89 // and it is released using nsCacheService::ReleaseObject_Locked() which
90 // releases the object outside the cache lock.
91 nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHEBINDING_DESTRUCTOR));
93 NS_ASSERTION(PR_CLIST_IS_EMPTY(this), "binding deleted while still on list");
94 if (!PR_CLIST_IS_EMPTY(this))
95 PR_REMOVE_LINK(this); // XXX why are we still on a list?
97 // sever streamIO/binding link
98 if (mStreamIO) {
99 if (NS_FAILED(mStreamIO->ClearBinding()))
100 nsCacheService::DoomEntry(mCacheEntry);
101 NS_RELEASE(mStreamIO);
105 nsresult
106 nsDiskCacheBinding::EnsureStreamIO()
108 if (!mStreamIO) {
109 mStreamIO = new nsDiskCacheStreamIO(this);
110 if (!mStreamIO) return NS_ERROR_OUT_OF_MEMORY;
111 NS_ADDREF(mStreamIO);
113 return NS_OK;
117 /******************************************************************************
118 * nsDiskCacheBindery
120 * Keeps track of bound disk cache entries to detect for collisions.
122 *****************************************************************************/
124 PLDHashTableOps nsDiskCacheBindery::ops =
126 PL_DHashAllocTable,
127 PL_DHashFreeTable,
128 HashKey,
129 MatchEntry,
130 MoveEntry,
131 ClearEntry,
132 PL_DHashFinalizeStub
136 nsDiskCacheBindery::nsDiskCacheBindery()
137 : initialized(false)
142 nsDiskCacheBindery::~nsDiskCacheBindery()
144 Reset();
148 nsresult
149 nsDiskCacheBindery::Init()
151 nsresult rv = NS_OK;
152 initialized = PL_DHashTableInit(&table, &ops, nullptr, sizeof(HashTableEntry), 0);
154 if (!initialized) rv = NS_ERROR_OUT_OF_MEMORY;
156 return rv;
159 void
160 nsDiskCacheBindery::Reset()
162 if (initialized) {
163 PL_DHashTableFinish(&table);
164 initialized = false;
169 nsDiskCacheBinding *
170 nsDiskCacheBindery::CreateBinding(nsCacheEntry * entry,
171 nsDiskCacheRecord * record)
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;
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;
193 return binding;
198 * FindActiveEntry : to find active colliding entry so we can doom it
200 nsDiskCacheBinding *
201 nsDiskCacheBindery::FindActiveBinding(uint32_t hashNumber)
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;
219 return binding;
224 * AddBinding
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
230 * Called from BindEntry()
231 * - a generation number needs to be assigned
233 nsresult
234 nsDiskCacheBindery::AddBinding(nsDiskCacheBinding * binding)
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;
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;
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;
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;
286 PR_INSERT_BEFORE(binding, hashEntry->mBinding);
287 break;
290 return NS_OK;
295 * RemoveBinding : remove binding from collision detection on deactivation
297 void
298 nsDiskCacheBindery::RemoveBinding(nsDiskCacheBinding * binding)
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;
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);
327 PR_REMOVE_AND_INIT_LINK(binding);
332 * ActiveBinding : PLDHashTable enumerate function to verify active bindings
335 PLDHashOperator
336 ActiveBinding(PLDHashTable * table,
337 PLDHashEntryHdr * hdr,
338 uint32_t number,
339 void * arg)
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;
351 binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
352 } while (binding != head);
354 return PL_DHASH_NEXT;
359 * ActiveBindings : return true if any bindings have open descriptors
361 bool
362 nsDiskCacheBindery::ActiveBindings()
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;
373 struct AccumulatorArg {
374 size_t mUsage;
375 nsMallocSizeOfFun mMallocSizeOf;
378 PLDHashOperator
379 AccumulateHeapUsage(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number,
380 void *arg)
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);
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);
401 binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
402 } while (binding != head);
404 return PL_DHASH_NEXT;
408 * SizeOfExcludingThis: return the amount of heap memory (bytes) being used by the bindery
410 size_t
411 nsDiskCacheBindery::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf)
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;