|
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 } |