netwerk/cache/nsDiskCacheBinding.cpp

branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
equal deleted inserted replaced
-1:000000000000 0:c493bf83f77e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
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/. */
6
7 #include "mozilla/MemoryReporting.h"
8 #include "nsCache.h"
9 #include <limits.h>
10
11 #include "nscore.h"
12 #include "nsDiskCacheBinding.h"
13 #include "nsCacheService.h"
14
15
16
17 /******************************************************************************
18 * static hash table callback functions
19 *
20 *****************************************************************************/
21 struct HashTableEntry : PLDHashEntryHdr {
22 nsDiskCacheBinding * mBinding;
23 };
24
25
26 static PLDHashNumber
27 HashKey( PLDHashTable *table, const void *key)
28 {
29 return (PLDHashNumber) NS_PTR_TO_INT32(key);
30 }
31
32
33 static bool
34 MatchEntry(PLDHashTable * /* table */,
35 const PLDHashEntryHdr * header,
36 const void * key)
37 {
38 HashTableEntry * hashEntry = (HashTableEntry *) header;
39 return (hashEntry->mBinding->mRecord.HashNumber() == (PLDHashNumber) NS_PTR_TO_INT32(key));
40 }
41
42 static void
43 MoveEntry(PLDHashTable * /* table */,
44 const PLDHashEntryHdr * src,
45 PLDHashEntryHdr * dst)
46 {
47 ((HashTableEntry *)dst)->mBinding = ((HashTableEntry *)src)->mBinding;
48 }
49
50
51 static void
52 ClearEntry(PLDHashTable * /* table */,
53 PLDHashEntryHdr * header)
54 {
55 ((HashTableEntry *)header)->mBinding = nullptr;
56 }
57
58
59 /******************************************************************************
60 * Utility Functions
61 *****************************************************************************/
62 nsDiskCacheBinding *
63 GetCacheEntryBinding(nsCacheEntry * entry)
64 {
65 return (nsDiskCacheBinding *) entry->Data();
66 }
67
68
69 /******************************************************************************
70 * nsDiskCacheBinding
71 *****************************************************************************/
72
73 NS_IMPL_ISUPPORTS0(nsDiskCacheBinding)
74
75 nsDiskCacheBinding::nsDiskCacheBinding(nsCacheEntry* entry, nsDiskCacheRecord * record)
76 : mCacheEntry(entry)
77 , mStreamIO(nullptr)
78 , mDeactivateEvent(nullptr)
79 {
80 NS_ASSERTION(record->ValidRecord(), "bad record");
81 PR_INIT_CLIST(this);
82 mRecord = *record;
83 mDoomed = entry->IsDoomed();
84 mGeneration = record->Generation(); // 0 == uninitialized, or data & meta using block files
85 }
86
87 nsDiskCacheBinding::~nsDiskCacheBinding()
88 {
89 // Grab the cache lock since the binding is stored in nsCacheEntry::mData
90 // and it is released using nsCacheService::ReleaseObject_Locked() which
91 // releases the object outside the cache lock.
92 nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHEBINDING_DESTRUCTOR));
93
94 NS_ASSERTION(PR_CLIST_IS_EMPTY(this), "binding deleted while still on list");
95 if (!PR_CLIST_IS_EMPTY(this))
96 PR_REMOVE_LINK(this); // XXX why are we still on a list?
97
98 // sever streamIO/binding link
99 if (mStreamIO) {
100 if (NS_FAILED(mStreamIO->ClearBinding()))
101 nsCacheService::DoomEntry(mCacheEntry);
102 NS_RELEASE(mStreamIO);
103 }
104 }
105
106 nsresult
107 nsDiskCacheBinding::EnsureStreamIO()
108 {
109 if (!mStreamIO) {
110 mStreamIO = new nsDiskCacheStreamIO(this);
111 if (!mStreamIO) return NS_ERROR_OUT_OF_MEMORY;
112 NS_ADDREF(mStreamIO);
113 }
114 return NS_OK;
115 }
116
117
118 /******************************************************************************
119 * nsDiskCacheBindery
120 *
121 * Keeps track of bound disk cache entries to detect for collisions.
122 *
123 *****************************************************************************/
124
125 const PLDHashTableOps nsDiskCacheBindery::ops =
126 {
127 PL_DHashAllocTable,
128 PL_DHashFreeTable,
129 HashKey,
130 MatchEntry,
131 MoveEntry,
132 ClearEntry,
133 PL_DHashFinalizeStub
134 };
135
136
137 nsDiskCacheBindery::nsDiskCacheBindery()
138 : initialized(false)
139 {
140 }
141
142
143 nsDiskCacheBindery::~nsDiskCacheBindery()
144 {
145 Reset();
146 }
147
148
149 nsresult
150 nsDiskCacheBindery::Init()
151 {
152 nsresult rv = NS_OK;
153 PL_DHashTableInit(&table, &ops, nullptr, sizeof(HashTableEntry), 0);
154 initialized = true;
155
156 return rv;
157 }
158
159 void
160 nsDiskCacheBindery::Reset()
161 {
162 if (initialized) {
163 PL_DHashTableFinish(&table);
164 initialized = false;
165 }
166 }
167
168
169 nsDiskCacheBinding *
170 nsDiskCacheBindery::CreateBinding(nsCacheEntry * entry,
171 nsDiskCacheRecord * record)
172 {
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;
178 }
179
180 nsDiskCacheBinding * binding = new nsDiskCacheBinding(entry, record);
181 if (!binding) return nullptr;
182
183 // give ownership of the binding to the entry
184 entry->SetData(binding);
185
186 // add binding to collision detection system
187 nsresult rv = AddBinding(binding);
188 if (NS_FAILED(rv)) {
189 entry->SetData(nullptr);
190 return nullptr;
191 }
192
193 return binding;
194 }
195
196
197 /**
198 * FindActiveEntry : to find active colliding entry so we can doom it
199 */
200 nsDiskCacheBinding *
201 nsDiskCacheBindery::FindActiveBinding(uint32_t hashNumber)
202 {
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;
211
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;
218 }
219 return binding;
220 }
221
222
223 /**
224 * AddBinding
225 *
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
229 *
230 * Called from BindEntry()
231 * - a generation number needs to be assigned
232 */
233 nsresult
234 nsDiskCacheBindery::AddBinding(nsDiskCacheBinding * binding)
235 {
236 NS_ENSURE_ARG_POINTER(binding);
237 NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
238
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;
246
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
251
252 return NS_OK;
253 }
254
255
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) {
261
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;
268 }
269
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;
275 }
276 }
277
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;
285 }
286 PR_INSERT_BEFORE(binding, hashEntry->mBinding);
287 break;
288 }
289 }
290 return NS_OK;
291 }
292
293
294 /**
295 * RemoveBinding : remove binding from collision detection on deactivation
296 */
297 void
298 nsDiskCacheBindery::RemoveBinding(nsDiskCacheBinding * binding)
299 {
300 NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
301 if (!initialized) return;
302
303 HashTableEntry * hashEntry;
304 void * key = (void *)(uintptr_t)binding->mRecord.HashNumber();
305
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;
312 }
313
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;
321
322 } else {
323 // promote next binding to head, and unlink this binding
324 hashEntry->mBinding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
325 }
326 }
327 PR_REMOVE_AND_INIT_LINK(binding);
328 }
329
330
331 /**
332 * ActiveBinding : PLDHashTable enumerate function to verify active bindings
333 */
334
335 PLDHashOperator
336 ActiveBinding(PLDHashTable * table,
337 PLDHashEntryHdr * hdr,
338 uint32_t number,
339 void * arg)
340 {
341 nsDiskCacheBinding * binding = ((HashTableEntry *)hdr)->mBinding;
342 NS_ASSERTION(binding, "### disk cache binding = nullptr!");
343
344 nsDiskCacheBinding * head = binding;
345 do {
346 if (binding->IsActive()) {
347 *((bool *)arg) = true;
348 return PL_DHASH_STOP;
349 }
350
351 binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
352 } while (binding != head);
353
354 return PL_DHASH_NEXT;
355 }
356
357
358 /**
359 * ActiveBindings : return true if any bindings have open descriptors
360 */
361 bool
362 nsDiskCacheBindery::ActiveBindings()
363 {
364 NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
365 if (!initialized) return false;
366
367 bool activeBinding = false;
368 PL_DHashTableEnumerate(&table, ActiveBinding, &activeBinding);
369
370 return activeBinding;
371 }
372
373 struct AccumulatorArg {
374 size_t mUsage;
375 mozilla::MallocSizeOf mMallocSizeOf;
376 };
377
378 PLDHashOperator
379 AccumulateHeapUsage(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number,
380 void *arg)
381 {
382 nsDiskCacheBinding *binding = ((HashTableEntry *)hdr)->mBinding;
383 NS_ASSERTION(binding, "### disk cache binding = nsnull!");
384
385 AccumulatorArg *acc = (AccumulatorArg *)arg;
386
387 nsDiskCacheBinding *head = binding;
388 do {
389 acc->mUsage += acc->mMallocSizeOf(binding);
390
391 if (binding->mStreamIO) {
392 acc->mUsage += binding->mStreamIO->SizeOfIncludingThis(acc->mMallocSizeOf);
393 }
394
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);
399 }
400
401 binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
402 } while (binding != head);
403
404 return PL_DHASH_NEXT;
405 }
406
407 /**
408 * SizeOfExcludingThis: return the amount of heap memory (bytes) being used by the bindery
409 */
410 size_t
411 nsDiskCacheBindery::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
412 {
413 NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
414 if (!initialized) return 0;
415
416 AccumulatorArg arg;
417 arg.mUsage = 0;
418 arg.mMallocSizeOf = aMallocSizeOf;
419
420 PL_DHashTableEnumerate(&table, AccumulateHeapUsage, &arg);
421
422 return arg.mUsage;
423 }

mercurial