1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
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/
10 : *
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
14 : * License.
15 : *
16 : * The Original Code is nsDiskCacheBinding.cpp, released
17 : * May 10, 2001.
18 : *
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.
23 : *
24 : * Contributor(s):
25 : * Patrick C. Beard <beard@netscape.com>
26 : * Gordon Sheridan <gordon@netscape.com>
27 : *
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.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include <limits.h>
43 :
44 : #include "nscore.h"
45 : #include "nsDiskCacheBinding.h"
46 : #include "nsCacheService.h"
47 :
48 :
49 :
50 : /******************************************************************************
51 : * static hash table callback functions
52 : *
53 : *****************************************************************************/
54 : struct HashTableEntry : PLDHashEntryHdr {
55 : nsDiskCacheBinding * mBinding;
56 : };
57 :
58 :
59 : static PLDHashNumber
60 4470 : HashKey( PLDHashTable *table, const void *key)
61 : {
62 4470 : return (PLDHashNumber) NS_PTR_TO_INT32(key);
63 : }
64 :
65 :
66 : static bool
67 1661 : MatchEntry(PLDHashTable * /* table */,
68 : const PLDHashEntryHdr * header,
69 : const void * key)
70 : {
71 1661 : HashTableEntry * hashEntry = (HashTableEntry *) header;
72 1661 : return (hashEntry->mBinding->mRecord.HashNumber() == (PLDHashNumber) NS_PTR_TO_INT32(key));
73 : }
74 :
75 : static void
76 0 : MoveEntry(PLDHashTable * /* table */,
77 : const PLDHashEntryHdr * src,
78 : PLDHashEntryHdr * dst)
79 : {
80 0 : ((HashTableEntry *)dst)->mBinding = ((HashTableEntry *)src)->mBinding;
81 0 : }
82 :
83 :
84 : static void
85 830 : ClearEntry(PLDHashTable * /* table */,
86 : PLDHashEntryHdr * header)
87 : {
88 830 : ((HashTableEntry *)header)->mBinding = nsnull;
89 830 : }
90 :
91 :
92 : /******************************************************************************
93 : * Utility Functions
94 : *****************************************************************************/
95 : nsDiskCacheBinding *
96 3038 : GetCacheEntryBinding(nsCacheEntry * entry)
97 : {
98 3038 : return (nsDiskCacheBinding *) entry->Data();
99 : }
100 :
101 :
102 : /******************************************************************************
103 : * nsDiskCacheBinding
104 : *****************************************************************************/
105 :
106 1660 : NS_IMPL_THREADSAFE_ISUPPORTS0(nsDiskCacheBinding)
107 :
108 830 : nsDiskCacheBinding::nsDiskCacheBinding(nsCacheEntry* entry, nsDiskCacheRecord * record)
109 : : mCacheEntry(entry)
110 : , mStreamIO(nsnull)
111 830 : , mDeactivateEvent(nsnull)
112 : {
113 830 : NS_ASSERTION(record->ValidRecord(), "bad record");
114 830 : PR_INIT_CLIST(this);
115 830 : mRecord = *record;
116 830 : mDoomed = entry->IsDoomed();
117 830 : mGeneration = record->Generation(); // 0 == uninitialized, or data & meta using block files
118 830 : }
119 :
120 1660 : nsDiskCacheBinding::~nsDiskCacheBinding()
121 : {
122 830 : NS_ASSERTION(PR_CLIST_IS_EMPTY(this), "binding deleted while still on list");
123 830 : if (!PR_CLIST_IS_EMPTY(this))
124 0 : PR_REMOVE_LINK(this); // XXX why are we still on a list?
125 :
126 : // sever streamIO/binding link
127 830 : if (mStreamIO) {
128 710 : if (NS_FAILED(mStreamIO->ClearBinding()))
129 0 : nsCacheService::DoomEntry(mCacheEntry);
130 710 : NS_RELEASE(mStreamIO);
131 : }
132 3320 : }
133 :
134 : nsresult
135 729 : nsDiskCacheBinding::EnsureStreamIO()
136 : {
137 729 : if (!mStreamIO) {
138 710 : mStreamIO = new nsDiskCacheStreamIO(this);
139 710 : if (!mStreamIO) return NS_ERROR_OUT_OF_MEMORY;
140 710 : NS_ADDREF(mStreamIO);
141 : }
142 729 : return NS_OK;
143 : }
144 :
145 :
146 : /******************************************************************************
147 : * nsDiskCacheBindery
148 : *
149 : * Keeps track of bound disk cache entries to detect for collisions.
150 : *
151 : *****************************************************************************/
152 :
153 : PLDHashTableOps nsDiskCacheBindery::ops =
154 : {
155 : PL_DHashAllocTable,
156 : PL_DHashFreeTable,
157 : HashKey,
158 : MatchEntry,
159 : MoveEntry,
160 : ClearEntry,
161 : PL_DHashFinalizeStub
162 : };
163 :
164 :
165 175 : nsDiskCacheBindery::nsDiskCacheBindery()
166 175 : : initialized(false)
167 : {
168 175 : }
169 :
170 :
171 175 : nsDiskCacheBindery::~nsDiskCacheBindery()
172 : {
173 175 : Reset();
174 175 : }
175 :
176 :
177 : nsresult
178 236 : nsDiskCacheBindery::Init()
179 : {
180 236 : nsresult rv = NS_OK;
181 236 : initialized = PL_DHashTableInit(&table, &ops, nsnull, sizeof(HashTableEntry), 0);
182 :
183 236 : if (!initialized) rv = NS_ERROR_OUT_OF_MEMORY;
184 :
185 236 : return rv;
186 : }
187 :
188 : void
189 411 : nsDiskCacheBindery::Reset()
190 : {
191 411 : if (initialized) {
192 236 : PL_DHashTableFinish(&table);
193 236 : initialized = false;
194 : }
195 411 : }
196 :
197 :
198 : nsDiskCacheBinding *
199 830 : nsDiskCacheBindery::CreateBinding(nsCacheEntry * entry,
200 : nsDiskCacheRecord * record)
201 : {
202 830 : NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
203 1660 : nsCOMPtr<nsISupports> data = entry->Data();
204 830 : if (data) {
205 0 : NS_ERROR("cache entry already has bind data");
206 0 : return nsnull;
207 : }
208 :
209 830 : nsDiskCacheBinding * binding = new nsDiskCacheBinding(entry, record);
210 830 : if (!binding) return nsnull;
211 :
212 : // give ownership of the binding to the entry
213 830 : entry->SetData(binding);
214 :
215 : // add binding to collision detection system
216 830 : nsresult rv = AddBinding(binding);
217 830 : if (NS_FAILED(rv)) {
218 0 : entry->SetData(nsnull);
219 0 : return nsnull;
220 : }
221 :
222 830 : return binding;
223 : }
224 :
225 :
226 : /**
227 : * FindActiveEntry : to find active colliding entry so we can doom it
228 : */
229 : nsDiskCacheBinding *
230 1980 : nsDiskCacheBindery::FindActiveBinding(PRUint32 hashNumber)
231 : {
232 1980 : NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
233 : // find hash entry for key
234 : HashTableEntry * hashEntry;
235 1980 : hashEntry = (HashTableEntry *) PL_DHashTableOperate(&table, (void*) hashNumber, PL_DHASH_LOOKUP);
236 1980 : if (PL_DHASH_ENTRY_IS_FREE(hashEntry)) return nsnull;
237 :
238 : // walk list looking for active entry
239 1 : NS_ASSERTION(hashEntry->mBinding, "hash entry left with no binding");
240 1 : nsDiskCacheBinding * binding = hashEntry->mBinding;
241 2 : while (binding->mCacheEntry->IsDoomed()) {
242 0 : binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
243 0 : if (binding == hashEntry->mBinding) return nsnull;
244 : }
245 1 : return binding;
246 : }
247 :
248 :
249 : /**
250 : * AddBinding
251 : *
252 : * Called from FindEntry() if we read an entry off of disk
253 : * - it may already have a generation number
254 : * - a generation number conflict is an error
255 : *
256 : * Called from BindEntry()
257 : * - a generation number needs to be assigned
258 : */
259 : nsresult
260 830 : nsDiskCacheBindery::AddBinding(nsDiskCacheBinding * binding)
261 : {
262 830 : NS_ENSURE_ARG_POINTER(binding);
263 830 : NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
264 :
265 : // find hash entry for key
266 : HashTableEntry * hashEntry;
267 : hashEntry = (HashTableEntry *) PL_DHashTableOperate(&table,
268 830 : (void*) binding->mRecord.HashNumber(),
269 830 : PL_DHASH_ADD);
270 830 : if (!hashEntry) return NS_ERROR_OUT_OF_MEMORY;
271 :
272 830 : if (hashEntry->mBinding == nsnull) {
273 830 : hashEntry->mBinding = binding;
274 830 : if (binding->mGeneration == 0)
275 827 : binding->mGeneration = 1; // if generation uninitialized, set it to 1
276 :
277 830 : return NS_OK;
278 : }
279 :
280 :
281 : // insert binding in generation order
282 0 : nsDiskCacheBinding * p = hashEntry->mBinding;
283 0 : bool calcGeneration = (binding->mGeneration == 0); // do we need to calculate generation?
284 0 : if (calcGeneration) binding->mGeneration = 1; // initialize to 1 if uninitialized
285 0 : while (1) {
286 :
287 0 : if (binding->mGeneration < p->mGeneration) {
288 : // here we are
289 0 : PR_INSERT_BEFORE(binding, p);
290 0 : if (hashEntry->mBinding == p)
291 0 : hashEntry->mBinding = binding;
292 0 : break;
293 : }
294 :
295 0 : if (binding->mGeneration == p->mGeneration) {
296 0 : if (calcGeneration) ++binding->mGeneration; // try the next generation
297 : else {
298 0 : NS_ERROR("### disk cache: generations collide!");
299 0 : return NS_ERROR_UNEXPECTED;
300 : }
301 : }
302 :
303 0 : p = (nsDiskCacheBinding *)PR_NEXT_LINK(p);
304 0 : if (p == hashEntry->mBinding) {
305 : // end of line: insert here or die
306 0 : p = (nsDiskCacheBinding *)PR_PREV_LINK(p); // back up and check generation
307 0 : if (p->mGeneration == 255) {
308 0 : NS_WARNING("### disk cache: generation capacity at full");
309 0 : return NS_ERROR_UNEXPECTED;
310 : }
311 0 : PR_INSERT_BEFORE(binding, hashEntry->mBinding);
312 0 : break;
313 : }
314 : }
315 0 : return NS_OK;
316 : }
317 :
318 :
319 : /**
320 : * RemoveBinding : remove binding from collision detection on deactivation
321 : */
322 : void
323 830 : nsDiskCacheBindery::RemoveBinding(nsDiskCacheBinding * binding)
324 : {
325 830 : NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
326 830 : if (!initialized) return;
327 :
328 : HashTableEntry * hashEntry;
329 830 : void * key = (void *)binding->mRecord.HashNumber();
330 :
331 : hashEntry = (HashTableEntry*) PL_DHashTableOperate(&table,
332 : (void*) key,
333 830 : PL_DHASH_LOOKUP);
334 830 : if (!PL_DHASH_ENTRY_IS_BUSY(hashEntry)) {
335 0 : NS_WARNING("### disk cache: binding not in hashtable!");
336 0 : return;
337 : }
338 :
339 830 : if (binding == hashEntry->mBinding) {
340 830 : if (PR_CLIST_IS_EMPTY(binding)) {
341 : // remove this hash entry
342 : (void) PL_DHashTableOperate(&table,
343 830 : (void*) binding->mRecord.HashNumber(),
344 830 : PL_DHASH_REMOVE);
345 830 : return;
346 :
347 : } else {
348 : // promote next binding to head, and unlink this binding
349 0 : hashEntry->mBinding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
350 : }
351 : }
352 0 : PR_REMOVE_AND_INIT_LINK(binding);
353 : }
354 :
355 :
356 : /**
357 : * ActiveBinding : PLDHashTable enumerate function to verify active bindings
358 : */
359 :
360 : PLDHashOperator
361 0 : ActiveBinding(PLDHashTable * table,
362 : PLDHashEntryHdr * hdr,
363 : PRUint32 number,
364 : void * arg)
365 : {
366 0 : nsDiskCacheBinding * binding = ((HashTableEntry *)hdr)->mBinding;
367 0 : NS_ASSERTION(binding, "### disk cache binding = nsnull!");
368 :
369 0 : nsDiskCacheBinding * head = binding;
370 0 : do {
371 0 : if (binding->IsActive()) {
372 0 : *((bool *)arg) = true;
373 0 : return PL_DHASH_STOP;
374 : }
375 :
376 0 : binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
377 : } while (binding != head);
378 :
379 0 : return PL_DHASH_NEXT;
380 : }
381 :
382 :
383 : /**
384 : * ActiveBindings : return true if any bindings have open descriptors
385 : */
386 : bool
387 61 : nsDiskCacheBindery::ActiveBindings()
388 : {
389 61 : NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
390 61 : if (!initialized) return false;
391 :
392 61 : bool activeBinding = false;
393 61 : PL_DHashTableEnumerate(&table, ActiveBinding, &activeBinding);
394 :
395 61 : return activeBinding;
396 : }
|