netwerk/cache/nsCacheEntry.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:31ede13fc8d5
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
8 #include "nsCache.h"
9 #include "nspr.h"
10 #include "nsCacheEntry.h"
11 #include "nsCacheEntryDescriptor.h"
12 #include "nsCacheMetaData.h"
13 #include "nsCacheRequest.h"
14 #include "nsThreadUtils.h"
15 #include "nsError.h"
16 #include "nsICacheService.h"
17 #include "nsCacheService.h"
18 #include "nsCacheDevice.h"
19 #include "nsHashKeys.h"
20 #include "mozilla/VisualEventTracer.h"
21
22 using namespace mozilla;
23
24 nsCacheEntry::nsCacheEntry(const nsACString & key,
25 bool streamBased,
26 nsCacheStoragePolicy storagePolicy)
27 : mKey(key),
28 mFetchCount(0),
29 mLastFetched(0),
30 mLastModified(0),
31 mExpirationTime(nsICache::NO_EXPIRATION_TIME),
32 mFlags(0),
33 mPredictedDataSize(-1),
34 mDataSize(0),
35 mCacheDevice(nullptr),
36 mCustomDevice(nullptr),
37 mData(nullptr)
38 {
39 MOZ_COUNT_CTOR(nsCacheEntry);
40 PR_INIT_CLIST(this);
41 PR_INIT_CLIST(&mRequestQ);
42 PR_INIT_CLIST(&mDescriptorQ);
43
44 if (streamBased) MarkStreamBased();
45 SetStoragePolicy(storagePolicy);
46
47 MarkPublic();
48
49 MOZ_EVENT_TRACER_NAME_OBJECT(this, key.BeginReading());
50 }
51
52
53 nsCacheEntry::~nsCacheEntry()
54 {
55 MOZ_COUNT_DTOR(nsCacheEntry);
56
57 if (mData)
58 nsCacheService::ReleaseObject_Locked(mData, mThread);
59 }
60
61
62 nsresult
63 nsCacheEntry::Create( const char * key,
64 bool streamBased,
65 nsCacheStoragePolicy storagePolicy,
66 nsCacheDevice * device,
67 nsCacheEntry ** result)
68 {
69 nsCacheEntry* entry = new nsCacheEntry(nsCString(key),
70 streamBased,
71 storagePolicy);
72 entry->SetCacheDevice(device);
73 *result = entry;
74 return NS_OK;
75 }
76
77
78 void
79 nsCacheEntry::Fetched()
80 {
81 mLastFetched = SecondsFromPRTime(PR_Now());
82 ++mFetchCount;
83 MarkEntryDirty();
84 }
85
86
87 const char *
88 nsCacheEntry::GetDeviceID()
89 {
90 if (mCacheDevice) return mCacheDevice->GetDeviceID();
91 return nullptr;
92 }
93
94
95 void
96 nsCacheEntry::TouchData()
97 {
98 mLastModified = SecondsFromPRTime(PR_Now());
99 MarkDataDirty();
100 }
101
102
103 void
104 nsCacheEntry::SetData(nsISupports * data)
105 {
106 if (mData) {
107 nsCacheService::ReleaseObject_Locked(mData, mThread);
108 mData = nullptr;
109 }
110
111 if (data) {
112 NS_ADDREF(mData = data);
113 mThread = do_GetCurrentThread();
114 }
115 }
116
117
118 void
119 nsCacheEntry::TouchMetaData()
120 {
121 mLastModified = SecondsFromPRTime(PR_Now());
122 MarkMetaDataDirty();
123 }
124
125
126 /**
127 * cache entry states
128 * 0 descriptors (new entry)
129 * 0 descriptors (existing, bound entry)
130 * n descriptors (existing, bound entry) valid
131 * n descriptors (existing, bound entry) not valid (wait until valid or doomed)
132 */
133
134 nsresult
135 nsCacheEntry::RequestAccess(nsCacheRequest * request, nsCacheAccessMode *accessGranted)
136 {
137 nsresult rv = NS_OK;
138
139 if (IsDoomed()) return NS_ERROR_CACHE_ENTRY_DOOMED;
140
141 if (!IsInitialized()) {
142 // brand new, unbound entry
143 if (request->IsStreamBased()) MarkStreamBased();
144 MarkInitialized();
145
146 *accessGranted = request->AccessRequested() & nsICache::ACCESS_WRITE;
147 NS_ASSERTION(*accessGranted, "new cache entry for READ-ONLY request");
148 PR_APPEND_LINK(request, &mRequestQ);
149 return rv;
150 }
151
152 if (IsStreamData() != request->IsStreamBased()) {
153 *accessGranted = nsICache::ACCESS_NONE;
154 return request->IsStreamBased() ?
155 NS_ERROR_CACHE_DATA_IS_NOT_STREAM : NS_ERROR_CACHE_DATA_IS_STREAM;
156 }
157
158 if (PR_CLIST_IS_EMPTY(&mDescriptorQ)) {
159 // 1st descriptor for existing bound entry
160 *accessGranted = request->AccessRequested();
161 if (*accessGranted & nsICache::ACCESS_WRITE) {
162 MarkInvalid();
163 } else {
164 MarkValid();
165 }
166 } else {
167 // nth request for existing, bound entry
168 *accessGranted = request->AccessRequested() & ~nsICache::ACCESS_WRITE;
169 if (!IsValid())
170 rv = NS_ERROR_CACHE_WAIT_FOR_VALIDATION;
171 }
172 PR_APPEND_LINK(request,&mRequestQ);
173
174 return rv;
175 }
176
177
178 nsresult
179 nsCacheEntry::CreateDescriptor(nsCacheRequest * request,
180 nsCacheAccessMode accessGranted,
181 nsICacheEntryDescriptor ** result)
182 {
183 NS_ENSURE_ARG_POINTER(request && result);
184
185 nsCacheEntryDescriptor * descriptor =
186 new nsCacheEntryDescriptor(this, accessGranted);
187
188 // XXX check request is on q
189 PR_REMOVE_AND_INIT_LINK(request); // remove request regardless of success
190
191 if (descriptor == nullptr)
192 return NS_ERROR_OUT_OF_MEMORY;
193
194 PR_APPEND_LINK(descriptor, &mDescriptorQ);
195
196 CACHE_LOG_DEBUG((" descriptor %p created for request %p on entry %p\n",
197 descriptor, request, this));
198
199 NS_ADDREF(*result = descriptor);
200 return NS_OK;
201 }
202
203
204 bool
205 nsCacheEntry::RemoveRequest(nsCacheRequest * request)
206 {
207 // XXX if debug: verify this request belongs to this entry
208 PR_REMOVE_AND_INIT_LINK(request);
209
210 // return true if this entry should stay active
211 return !((PR_CLIST_IS_EMPTY(&mRequestQ)) &&
212 (PR_CLIST_IS_EMPTY(&mDescriptorQ)));
213 }
214
215
216 bool
217 nsCacheEntry::RemoveDescriptor(nsCacheEntryDescriptor * descriptor,
218 bool * doomEntry)
219 {
220 NS_ASSERTION(descriptor->CacheEntry() == this, "### Wrong cache entry!!");
221
222 *doomEntry = descriptor->ClearCacheEntry();
223
224 PR_REMOVE_AND_INIT_LINK(descriptor);
225
226 if (!PR_CLIST_IS_EMPTY(&mDescriptorQ))
227 return true; // stay active if we still have open descriptors
228
229 if (PR_CLIST_IS_EMPTY(&mRequestQ))
230 return false; // no descriptors or requests, we can deactivate
231
232 return true; // find next best request to give a descriptor to
233 }
234
235
236 void
237 nsCacheEntry::DetachDescriptors()
238 {
239 nsCacheEntryDescriptor * descriptor =
240 (nsCacheEntryDescriptor *)PR_LIST_HEAD(&mDescriptorQ);
241
242 while (descriptor != &mDescriptorQ) {
243 nsCacheEntryDescriptor * nextDescriptor =
244 (nsCacheEntryDescriptor *)PR_NEXT_LINK(descriptor);
245
246 descriptor->ClearCacheEntry();
247 PR_REMOVE_AND_INIT_LINK(descriptor);
248 descriptor = nextDescriptor;
249 }
250 }
251
252
253 void
254 nsCacheEntry::GetDescriptors(
255 nsTArray<nsRefPtr<nsCacheEntryDescriptor> > &outDescriptors)
256 {
257 nsCacheEntryDescriptor * descriptor =
258 (nsCacheEntryDescriptor *)PR_LIST_HEAD(&mDescriptorQ);
259
260 while (descriptor != &mDescriptorQ) {
261 nsCacheEntryDescriptor * nextDescriptor =
262 (nsCacheEntryDescriptor *)PR_NEXT_LINK(descriptor);
263
264 outDescriptors.AppendElement(descriptor);
265 descriptor = nextDescriptor;
266 }
267 }
268
269
270 /******************************************************************************
271 * nsCacheEntryInfo - for implementing about:cache
272 *****************************************************************************/
273
274 NS_IMPL_ISUPPORTS(nsCacheEntryInfo, nsICacheEntryInfo)
275
276
277 NS_IMETHODIMP
278 nsCacheEntryInfo::GetClientID(char ** clientID)
279 {
280 NS_ENSURE_ARG_POINTER(clientID);
281 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
282
283 return ClientIDFromCacheKey(*mCacheEntry->Key(), clientID);
284 }
285
286
287 NS_IMETHODIMP
288 nsCacheEntryInfo::GetDeviceID(char ** deviceID)
289 {
290 NS_ENSURE_ARG_POINTER(deviceID);
291 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
292
293 *deviceID = NS_strdup(mCacheEntry->GetDeviceID());
294 return *deviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
295 }
296
297
298 NS_IMETHODIMP
299 nsCacheEntryInfo::GetKey(nsACString &key)
300 {
301 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
302
303 return ClientKeyFromCacheKey(*mCacheEntry->Key(), key);
304 }
305
306
307 NS_IMETHODIMP
308 nsCacheEntryInfo::GetFetchCount(int32_t * fetchCount)
309 {
310 NS_ENSURE_ARG_POINTER(fetchCount);
311 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
312
313 *fetchCount = mCacheEntry->FetchCount();
314 return NS_OK;
315 }
316
317
318 NS_IMETHODIMP
319 nsCacheEntryInfo::GetLastFetched(uint32_t * lastFetched)
320 {
321 NS_ENSURE_ARG_POINTER(lastFetched);
322 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
323
324 *lastFetched = mCacheEntry->LastFetched();
325 return NS_OK;
326 }
327
328
329 NS_IMETHODIMP
330 nsCacheEntryInfo::GetLastModified(uint32_t * lastModified)
331 {
332 NS_ENSURE_ARG_POINTER(lastModified);
333 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
334
335 *lastModified = mCacheEntry->LastModified();
336 return NS_OK;
337 }
338
339
340 NS_IMETHODIMP
341 nsCacheEntryInfo::GetExpirationTime(uint32_t * expirationTime)
342 {
343 NS_ENSURE_ARG_POINTER(expirationTime);
344 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
345
346 *expirationTime = mCacheEntry->ExpirationTime();
347 return NS_OK;
348 }
349
350
351 NS_IMETHODIMP
352 nsCacheEntryInfo::GetDataSize(uint32_t * dataSize)
353 {
354 NS_ENSURE_ARG_POINTER(dataSize);
355 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
356
357 *dataSize = mCacheEntry->DataSize();
358 return NS_OK;
359 }
360
361
362 NS_IMETHODIMP
363 nsCacheEntryInfo::IsStreamBased(bool * result)
364 {
365 NS_ENSURE_ARG_POINTER(result);
366 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
367
368 *result = mCacheEntry->IsStreamData();
369 return NS_OK;
370 }
371
372
373 /******************************************************************************
374 * nsCacheEntryHashTable
375 *****************************************************************************/
376
377 const PLDHashTableOps
378 nsCacheEntryHashTable::ops =
379 {
380 PL_DHashAllocTable,
381 PL_DHashFreeTable,
382 HashKey,
383 MatchEntry,
384 MoveEntry,
385 ClearEntry,
386 PL_DHashFinalizeStub
387 };
388
389
390 nsCacheEntryHashTable::nsCacheEntryHashTable()
391 : initialized(false)
392 {
393 MOZ_COUNT_CTOR(nsCacheEntryHashTable);
394 }
395
396
397 nsCacheEntryHashTable::~nsCacheEntryHashTable()
398 {
399 MOZ_COUNT_DTOR(nsCacheEntryHashTable);
400 if (initialized)
401 Shutdown();
402 }
403
404
405 nsresult
406 nsCacheEntryHashTable::Init()
407 {
408 nsresult rv = NS_OK;
409 initialized = PL_DHashTableInit(&table, &ops, nullptr,
410 sizeof(nsCacheEntryHashTableEntry),
411 512, fallible_t());
412
413 if (!initialized) rv = NS_ERROR_OUT_OF_MEMORY;
414
415 return rv;
416 }
417
418 void
419 nsCacheEntryHashTable::Shutdown()
420 {
421 if (initialized) {
422 PL_DHashTableFinish(&table);
423 initialized = false;
424 }
425 }
426
427
428 nsCacheEntry *
429 nsCacheEntryHashTable::GetEntry( const nsCString * key)
430 {
431 PLDHashEntryHdr *hashEntry;
432 nsCacheEntry *result = nullptr;
433
434 NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
435 if (!initialized) return nullptr;
436
437 hashEntry = PL_DHashTableOperate(&table, key, PL_DHASH_LOOKUP);
438 if (PL_DHASH_ENTRY_IS_BUSY(hashEntry)) {
439 result = ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry;
440 }
441 return result;
442 }
443
444
445 nsresult
446 nsCacheEntryHashTable::AddEntry( nsCacheEntry *cacheEntry)
447 {
448 PLDHashEntryHdr *hashEntry;
449
450 NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
451 if (!initialized) return NS_ERROR_NOT_INITIALIZED;
452 if (!cacheEntry) return NS_ERROR_NULL_POINTER;
453
454 hashEntry = PL_DHashTableOperate(&table, &(cacheEntry->mKey), PL_DHASH_ADD);
455 #ifndef DEBUG_dougt
456 NS_ASSERTION(((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry == 0,
457 "### nsCacheEntryHashTable::AddEntry - entry already used");
458 #endif
459 ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = cacheEntry;
460
461 return NS_OK;
462 }
463
464
465 void
466 nsCacheEntryHashTable::RemoveEntry( nsCacheEntry *cacheEntry)
467 {
468 NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
469 NS_ASSERTION(cacheEntry, "### cacheEntry == nullptr");
470
471 if (!initialized) return; // NS_ERROR_NOT_INITIALIZED
472
473 #if DEBUG
474 // XXX debug code to make sure we have the entry we're trying to remove
475 nsCacheEntry *check = GetEntry(&(cacheEntry->mKey));
476 NS_ASSERTION(check == cacheEntry, "### Attempting to remove unknown cache entry!!!");
477 #endif
478 (void) PL_DHashTableOperate(&table, &(cacheEntry->mKey), PL_DHASH_REMOVE);
479 }
480
481
482 void
483 nsCacheEntryHashTable::VisitEntries( PLDHashEnumerator etor, void *arg)
484 {
485 NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
486 if (!initialized) return; // NS_ERROR_NOT_INITIALIZED
487 PL_DHashTableEnumerate(&table, etor, arg);
488 }
489
490
491 /**
492 * hash table operation callback functions
493 */
494
495 PLDHashNumber
496 nsCacheEntryHashTable::HashKey( PLDHashTable *table, const void *key)
497 {
498 return HashString(*static_cast<const nsCString *>(key));
499 }
500
501 bool
502 nsCacheEntryHashTable::MatchEntry(PLDHashTable * /* table */,
503 const PLDHashEntryHdr * hashEntry,
504 const void * key)
505 {
506 NS_ASSERTION(key != nullptr, "### nsCacheEntryHashTable::MatchEntry : null key");
507 nsCacheEntry *cacheEntry = ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry;
508
509 return cacheEntry->mKey.Equals(*(nsCString *)key);
510 }
511
512
513 void
514 nsCacheEntryHashTable::MoveEntry(PLDHashTable * /* table */,
515 const PLDHashEntryHdr *from,
516 PLDHashEntryHdr *to)
517 {
518 ((nsCacheEntryHashTableEntry *)to)->cacheEntry =
519 ((nsCacheEntryHashTableEntry *)from)->cacheEntry;
520 }
521
522
523 void
524 nsCacheEntryHashTable::ClearEntry(PLDHashTable * /* table */,
525 PLDHashEntryHdr * hashEntry)
526 {
527 ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = 0;
528 }

mercurial