1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is nsDiskCacheBinding.cpp, released
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 2001
22 * the Initial Developer. All Rights Reserved.
25 * Patrick C. Beard <beard@netscape.com>
26 * Gordon Sheridan <gordon@netscape.com>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either the GNU General Public License Version 2 or later (the "GPL"), or
30 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
45 #include "nsDiskCacheBinding.h"
49 /******************************************************************************
50 * static hash table callback functions
52 *****************************************************************************/
55 #pragma mark HASHTABLE CALLBACKS
58 struct HashTableEntry
: PLDHashEntryHdr
{
59 nsDiskCacheBinding
* mBinding
;
64 HashKey( PLDHashTable
*table
, const void *key
)
66 return (PLDHashNumber
) NS_PTR_TO_INT32(key
);
71 MatchEntry(PLDHashTable
* /* table */,
72 const PLDHashEntryHdr
* header
,
75 HashTableEntry
* hashEntry
= (HashTableEntry
*) header
;
76 return (hashEntry
->mBinding
->mRecord
.HashNumber() == (PLDHashNumber
) NS_PTR_TO_INT32(key
));
80 MoveEntry(PLDHashTable
* /* table */,
81 const PLDHashEntryHdr
* src
,
82 PLDHashEntryHdr
* dst
)
84 ((HashTableEntry
*)dst
)->mBinding
= ((HashTableEntry
*)src
)->mBinding
;
89 ClearEntry(PLDHashTable
* /* table */,
90 PLDHashEntryHdr
* header
)
92 ((HashTableEntry
*)header
)->mBinding
= nsnull
;
96 /******************************************************************************
98 *****************************************************************************/
101 #pragma mark DISK CACHE BINDERY
105 GetCacheEntryBinding(nsCacheEntry
* entry
)
107 return (nsDiskCacheBinding
*) entry
->Data();
111 /******************************************************************************
113 *****************************************************************************/
115 NS_IMPL_THREADSAFE_ISUPPORTS0(nsDiskCacheBinding
)
117 nsDiskCacheBinding::nsDiskCacheBinding(nsCacheEntry
* entry
, nsDiskCacheRecord
* record
)
121 NS_ASSERTION(record
->ValidRecord(), "bad record");
124 mDoomed
= entry
->IsDoomed();
125 mGeneration
= record
->Generation(); // 0 == uninitialized, or data & meta using block files
128 nsDiskCacheBinding::~nsDiskCacheBinding()
130 NS_ASSERTION(PR_CLIST_IS_EMPTY(this), "binding deleted while still on list");
131 if (!PR_CLIST_IS_EMPTY(this))
132 PR_REMOVE_LINK(this); // XXX why are we still on a list?
134 // sever streamIO/binding link
136 mStreamIO
->ClearBinding();
137 NS_RELEASE(mStreamIO
);
142 nsDiskCacheBinding::EnsureStreamIO()
145 mStreamIO
= new nsDiskCacheStreamIO(this);
146 if (!mStreamIO
) return NS_ERROR_OUT_OF_MEMORY
;
147 NS_ADDREF(mStreamIO
);
153 /******************************************************************************
156 * Keeps track of bound disk cache entries to detect for collisions.
158 *****************************************************************************/
160 PLDHashTableOps
nsDiskCacheBindery::ops
=
172 nsDiskCacheBindery::nsDiskCacheBindery()
173 : initialized(PR_FALSE
)
178 nsDiskCacheBindery::~nsDiskCacheBindery()
185 nsDiskCacheBindery::Init()
188 initialized
= PL_DHashTableInit(&table
, &ops
, nsnull
, sizeof(HashTableEntry
), 0);
190 if (!initialized
) rv
= NS_ERROR_OUT_OF_MEMORY
;
196 nsDiskCacheBindery::Reset()
199 PL_DHashTableFinish(&table
);
200 initialized
= PR_FALSE
;
206 nsDiskCacheBindery::CreateBinding(nsCacheEntry
* entry
,
207 nsDiskCacheRecord
* record
)
209 NS_ASSERTION(initialized
, "nsDiskCacheBindery not initialized");
210 nsCOMPtr
<nsISupports
> data
= entry
->Data();
212 NS_ERROR("cache entry already has bind data");
216 nsDiskCacheBinding
* binding
= new nsDiskCacheBinding(entry
, record
);
217 if (!binding
) return nsnull
;
219 // give ownership of the binding to the entry
220 entry
->SetData(binding
);
222 // add binding to collision detection system
223 nsresult rv
= AddBinding(binding
);
225 entry
->SetData(nsnull
);
234 * FindActiveEntry : to find active colliding entry so we can doom it
237 nsDiskCacheBindery::FindActiveBinding(PRUint32 hashNumber
)
239 NS_ASSERTION(initialized
, "nsDiskCacheBindery not initialized");
240 // find hash entry for key
241 HashTableEntry
* hashEntry
;
242 hashEntry
= (HashTableEntry
*) PL_DHashTableOperate(&table
, (void*) hashNumber
, PL_DHASH_LOOKUP
);
243 if (PL_DHASH_ENTRY_IS_FREE(hashEntry
)) return nsnull
;
245 // walk list looking for active entry
246 NS_ASSERTION(hashEntry
->mBinding
, "hash entry left with no binding");
247 nsDiskCacheBinding
* binding
= hashEntry
->mBinding
;
248 while (binding
->mCacheEntry
->IsDoomed()) {
249 binding
= (nsDiskCacheBinding
*)PR_NEXT_LINK(binding
);
250 if (binding
== hashEntry
->mBinding
) return nsnull
;
259 * Called from FindEntry() if we read an entry off of disk
260 * - it may already have a generation number
261 * - a generation number conflict is an error
263 * Called from BindEntry()
264 * - a generation number needs to be assigned
267 nsDiskCacheBindery::AddBinding(nsDiskCacheBinding
* binding
)
269 NS_ENSURE_ARG_POINTER(binding
);
270 NS_ASSERTION(initialized
, "nsDiskCacheBindery not initialized");
272 // find hash entry for key
273 HashTableEntry
* hashEntry
;
274 hashEntry
= (HashTableEntry
*) PL_DHashTableOperate(&table
,
275 (void*) binding
->mRecord
.HashNumber(),
277 if (!hashEntry
) return NS_ERROR_OUT_OF_MEMORY
;
279 if (hashEntry
->mBinding
== nsnull
) {
280 hashEntry
->mBinding
= binding
;
281 if (binding
->mGeneration
== 0)
282 binding
->mGeneration
= 1; // if generation uninitialized, set it to 1
288 // insert binding in generation order
289 nsDiskCacheBinding
* p
= hashEntry
->mBinding
;
290 PRBool calcGeneration
= (binding
->mGeneration
== 0); // do we need to calculate generation?
291 if (calcGeneration
) binding
->mGeneration
= 1; // initialize to 1 if uninitialized
294 if (binding
->mGeneration
< p
->mGeneration
) {
296 PR_INSERT_BEFORE(binding
, p
);
297 if (hashEntry
->mBinding
== p
)
298 hashEntry
->mBinding
= binding
;
302 if (binding
->mGeneration
== p
->mGeneration
) {
303 if (calcGeneration
) ++binding
->mGeneration
; // try the next generation
305 NS_ERROR("### disk cache: generations collide!");
306 return NS_ERROR_UNEXPECTED
;
310 p
= (nsDiskCacheBinding
*)PR_NEXT_LINK(p
);
311 if (p
== hashEntry
->mBinding
) {
312 // end of line: insert here or die
313 p
= (nsDiskCacheBinding
*)PR_PREV_LINK(p
); // back up and check generation
314 if (p
->mGeneration
== 255) {
315 NS_WARNING("### disk cache: generation capacity at full");
316 return NS_ERROR_UNEXPECTED
;
318 PR_INSERT_BEFORE(binding
, hashEntry
->mBinding
);
327 * RemoveBinding : remove binding from collision detection on deactivation
330 nsDiskCacheBindery::RemoveBinding(nsDiskCacheBinding
* binding
)
332 NS_ASSERTION(initialized
, "nsDiskCacheBindery not initialized");
333 if (!initialized
) return;
335 HashTableEntry
* hashEntry
;
336 void * key
= (void *)binding
->mRecord
.HashNumber();
338 hashEntry
= (HashTableEntry
*) PL_DHashTableOperate(&table
,
341 if (!PL_DHASH_ENTRY_IS_BUSY(hashEntry
)) {
342 NS_WARNING("### disk cache: binding not in hashtable!");
346 if (binding
== hashEntry
->mBinding
) {
347 if (PR_CLIST_IS_EMPTY(binding
)) {
348 // remove this hash entry
349 (void) PL_DHashTableOperate(&table
,
350 (void*) binding
->mRecord
.HashNumber(),
355 // promote next binding to head, and unlink this binding
356 hashEntry
->mBinding
= (nsDiskCacheBinding
*)PR_NEXT_LINK(binding
);
359 PR_REMOVE_AND_INIT_LINK(binding
);
364 * ActiveBinding : PLDHashTable enumerate function to verify active bindings
368 ActiveBinding(PLDHashTable
* table
,
369 PLDHashEntryHdr
* hdr
,
373 nsDiskCacheBinding
* binding
= ((HashTableEntry
*)hdr
)->mBinding
;
374 NS_ASSERTION(binding
, "### disk cache binding = nsnull!");
376 nsDiskCacheBinding
* head
= binding
;
378 if (binding
->IsActive()) {
379 *((PRBool
*)arg
) = PR_TRUE
;
380 return PL_DHASH_STOP
;
383 binding
= (nsDiskCacheBinding
*)PR_NEXT_LINK(binding
);
384 } while (binding
!= head
);
386 return PL_DHASH_NEXT
;
391 * ActiveBindings : return PR_TRUE if any bindings have open descriptors
394 nsDiskCacheBindery::ActiveBindings()
396 NS_ASSERTION(initialized
, "nsDiskCacheBindery not initialized");
397 if (!initialized
) return PR_FALSE
;
399 PRBool activeBinding
= PR_FALSE
;
400 PL_DHashTableEnumerate(&table
, ActiveBinding
, &activeBinding
);
402 return activeBinding
;