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