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/. */
11 #include "nsDiskCacheBinding.h"
12 #include "nsCacheService.h"
16 /******************************************************************************
17 * static hash table callback functions
19 *****************************************************************************/
20 struct HashTableEntry
: PLDHashEntryHdr
{
21 nsDiskCacheBinding
* mBinding
;
26 HashKey( PLDHashTable
*table
, const void *key
)
28 return (PLDHashNumber
) NS_PTR_TO_INT32(key
);
33 MatchEntry(PLDHashTable
* /* table */,
34 const PLDHashEntryHdr
* header
,
37 HashTableEntry
* hashEntry
= (HashTableEntry
*) header
;
38 return (hashEntry
->mBinding
->mRecord
.HashNumber() == (PLDHashNumber
) NS_PTR_TO_INT32(key
));
42 MoveEntry(PLDHashTable
* /* table */,
43 const PLDHashEntryHdr
* src
,
44 PLDHashEntryHdr
* dst
)
46 ((HashTableEntry
*)dst
)->mBinding
= ((HashTableEntry
*)src
)->mBinding
;
51 ClearEntry(PLDHashTable
* /* table */,
52 PLDHashEntryHdr
* header
)
54 ((HashTableEntry
*)header
)->mBinding
= nullptr;
58 /******************************************************************************
60 *****************************************************************************/
62 GetCacheEntryBinding(nsCacheEntry
* entry
)
64 return (nsDiskCacheBinding
*) entry
->Data();
68 /******************************************************************************
70 *****************************************************************************/
72 NS_IMPL_THREADSAFE_ISUPPORTS0(nsDiskCacheBinding
)
74 nsDiskCacheBinding::nsDiskCacheBinding(nsCacheEntry
* entry
, nsDiskCacheRecord
* record
)
77 , mDeactivateEvent(nullptr)
79 NS_ASSERTION(record
->ValidRecord(), "bad 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
99 if (NS_FAILED(mStreamIO
->ClearBinding()))
100 nsCacheService::DoomEntry(mCacheEntry
);
101 NS_RELEASE(mStreamIO
);
106 nsDiskCacheBinding::EnsureStreamIO()
109 mStreamIO
= new nsDiskCacheStreamIO(this);
110 if (!mStreamIO
) return NS_ERROR_OUT_OF_MEMORY
;
111 NS_ADDREF(mStreamIO
);
117 /******************************************************************************
120 * Keeps track of bound disk cache entries to detect for collisions.
122 *****************************************************************************/
124 PLDHashTableOps
nsDiskCacheBindery::ops
=
136 nsDiskCacheBindery::nsDiskCacheBindery()
142 nsDiskCacheBindery::~nsDiskCacheBindery()
149 nsDiskCacheBindery::Init()
152 initialized
= PL_DHashTableInit(&table
, &ops
, nullptr, sizeof(HashTableEntry
), 0);
154 if (!initialized
) rv
= NS_ERROR_OUT_OF_MEMORY
;
160 nsDiskCacheBindery::Reset()
163 PL_DHashTableFinish(&table
);
170 nsDiskCacheBindery::CreateBinding(nsCacheEntry
* entry
,
171 nsDiskCacheRecord
* record
)
173 NS_ASSERTION(initialized
, "nsDiskCacheBindery not initialized");
174 nsCOMPtr
<nsISupports
> data
= entry
->Data();
176 NS_ERROR("cache entry already has bind data");
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
);
189 entry
->SetData(nullptr);
198 * FindActiveEntry : to find active colliding entry so we can doom it
201 nsDiskCacheBindery::FindActiveBinding(uint32_t hashNumber
)
203 NS_ASSERTION(initialized
, "nsDiskCacheBindery not initialized");
204 // find hash entry for key
205 HashTableEntry
* hashEntry
;
207 (HashTableEntry
*) PL_DHashTableOperate(&table
,
208 (void*)(uintptr_t) hashNumber
,
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;
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
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(),
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
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
262 if (binding
->mGeneration
< p
->mGeneration
) {
264 PR_INSERT_BEFORE(binding
, p
);
265 if (hashEntry
->mBinding
== p
)
266 hashEntry
->mBinding
= binding
;
270 if (binding
->mGeneration
== p
->mGeneration
) {
271 if (calcGeneration
) ++binding
->mGeneration
; // try the next generation
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
);
295 * RemoveBinding : remove binding from collision detection on deactivation
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
,
309 if (!PL_DHASH_ENTRY_IS_BUSY(hashEntry
)) {
310 NS_WARNING("### disk cache: binding not in hashtable!");
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(),
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
336 ActiveBinding(PLDHashTable
* table
,
337 PLDHashEntryHdr
* hdr
,
341 nsDiskCacheBinding
* binding
= ((HashTableEntry
*)hdr
)->mBinding
;
342 NS_ASSERTION(binding
, "### disk cache binding = nullptr!");
344 nsDiskCacheBinding
* head
= binding
;
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
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
{
375 nsMallocSizeOfFun mMallocSizeOf
;
379 AccumulateHeapUsage(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
, uint32_t number
,
382 nsDiskCacheBinding
*binding
= ((HashTableEntry
*)hdr
)->mBinding
;
383 NS_ASSERTION(binding
, "### disk cache binding = nsnull!");
385 AccumulatorArg
*acc
= (AccumulatorArg
*)arg
;
387 nsDiskCacheBinding
*head
= binding
;
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
411 nsDiskCacheBindery::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf
)
413 NS_ASSERTION(initialized
, "nsDiskCacheBindery not initialized");
414 if (!initialized
) return 0;
418 arg
.mMallocSizeOf
= aMallocSizeOf
;
420 PL_DHashTableEnumerate(&table
, AccumulateHeapUsage
, &arg
);