1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
16 * The Original Code is nsCacheEntry.cpp, released
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.
25 * Gordon Sheridan <gordon@netscape.com>
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.
39 * ***** END LICENSE BLOCK ***** */
43 #include "nsCacheEntry.h"
44 #include "nsCacheEntryDescriptor.h"
45 #include "nsCacheMetaData.h"
46 #include "nsCacheRequest.h"
47 #include "nsThreadUtils.h"
49 #include "nsICacheService.h"
51 #include "nsCacheService.h"
52 #include "nsCacheDevice.h"
56 nsCacheEntry::nsCacheEntry(nsCString
* key
,
58 nsCacheStoragePolicy storagePolicy
)
63 mExpirationTime(NO_EXPIRATION_TIME
),
69 MOZ_COUNT_CTOR(nsCacheEntry
);
71 PR_INIT_CLIST(&mRequestQ
);
72 PR_INIT_CLIST(&mDescriptorQ
);
74 if (streamBased
) MarkStreamBased();
75 SetStoragePolicy(storagePolicy
);
79 nsCacheEntry::~nsCacheEntry()
81 MOZ_COUNT_DTOR(nsCacheEntry
);
85 nsCacheService::ReleaseObject_Locked(mData
, mThread
);
90 nsCacheEntry::Create( const char * key
,
92 nsCacheStoragePolicy storagePolicy
,
93 nsCacheDevice
* device
,
94 nsCacheEntry
** result
)
96 nsCString
* newKey
= new nsCString(key
);
97 if (!newKey
) return NS_ERROR_OUT_OF_MEMORY
;
99 nsCacheEntry
* entry
= new nsCacheEntry(newKey
, streamBased
, storagePolicy
);
100 if (!entry
) { delete newKey
; return NS_ERROR_OUT_OF_MEMORY
; }
102 entry
->SetCacheDevice(device
);
110 nsCacheEntry::Fetched()
112 mLastFetched
= SecondsFromPRTime(PR_Now());
119 nsCacheEntry::GetDeviceID()
121 if (mCacheDevice
) return mCacheDevice
->GetDeviceID();
127 nsCacheEntry::TouchData()
129 mLastModified
= SecondsFromPRTime(PR_Now());
135 nsCacheEntry::SetData(nsISupports
* data
)
138 nsCacheService::ReleaseObject_Locked(mData
, mThread
);
143 NS_ADDREF(mData
= data
);
144 mThread
= do_GetCurrentThread();
150 nsCacheEntry::TouchMetaData()
152 mLastModified
= SecondsFromPRTime(PR_Now());
159 * 0 descriptors (new entry)
160 * 0 descriptors (existing, bound entry)
161 * n descriptors (existing, bound entry) valid
162 * n descriptors (existing, bound entry) not valid (wait until valid or doomed)
166 nsCacheEntry::RequestAccess(nsCacheRequest
* request
, nsCacheAccessMode
*accessGranted
)
170 if (!IsInitialized()) {
171 // brand new, unbound entry
172 request
->mKey
= nsnull
; // steal ownership of the key string
173 if (request
->IsStreamBased()) MarkStreamBased();
176 *accessGranted
= request
->AccessRequested() & nsICache::ACCESS_WRITE
;
177 NS_ASSERTION(*accessGranted
, "new cache entry for READ-ONLY request");
178 PR_APPEND_LINK(request
, &mRequestQ
);
182 if (IsDoomed()) return NS_ERROR_CACHE_ENTRY_DOOMED
;
184 if (IsStreamData() != request
->IsStreamBased()) {
185 *accessGranted
= nsICache::ACCESS_NONE
;
186 return request
->IsStreamBased() ?
187 NS_ERROR_CACHE_DATA_IS_NOT_STREAM
: NS_ERROR_CACHE_DATA_IS_STREAM
;
190 if (PR_CLIST_IS_EMPTY(&mDescriptorQ
)) {
191 // 1st descriptor for existing bound entry
192 *accessGranted
= request
->AccessRequested();
193 if (*accessGranted
& nsICache::ACCESS_WRITE
) {
199 // nth request for existing, bound entry
200 *accessGranted
= request
->AccessRequested() & ~nsICache::ACCESS_WRITE
;
202 rv
= NS_ERROR_CACHE_WAIT_FOR_VALIDATION
;
204 PR_APPEND_LINK(request
,&mRequestQ
);
211 nsCacheEntry::CreateDescriptor(nsCacheRequest
* request
,
212 nsCacheAccessMode accessGranted
,
213 nsICacheEntryDescriptor
** result
)
215 NS_ENSURE_ARG_POINTER(request
&& result
);
217 nsCacheEntryDescriptor
* descriptor
=
218 new nsCacheEntryDescriptor(this, accessGranted
);
220 // XXX check request is on q
221 PR_REMOVE_AND_INIT_LINK(request
); // remove request regardless of success
223 if (descriptor
== nsnull
)
224 return NS_ERROR_OUT_OF_MEMORY
;
226 PR_APPEND_LINK(descriptor
, &mDescriptorQ
);
228 NS_ADDREF(*result
= descriptor
);
234 nsCacheEntry::RemoveRequest(nsCacheRequest
* request
)
236 // XXX if debug: verify this request belongs to this entry
237 PR_REMOVE_AND_INIT_LINK(request
);
239 // return true if this entry should stay active
240 return !((PR_CLIST_IS_EMPTY(&mRequestQ
)) &&
241 (PR_CLIST_IS_EMPTY(&mDescriptorQ
)));
246 nsCacheEntry::RemoveDescriptor(nsCacheEntryDescriptor
* descriptor
)
248 NS_ASSERTION(descriptor
->CacheEntry() == this, "### Wrong cache entry!!");
249 PR_REMOVE_AND_INIT_LINK(descriptor
);
250 descriptor
->ClearCacheEntry();
252 if (!PR_CLIST_IS_EMPTY(&mDescriptorQ
))
253 return PR_TRUE
; // stay active if we still have open descriptors
255 if (PR_CLIST_IS_EMPTY(&mRequestQ
))
256 return PR_FALSE
; // no descriptors or requests, we can deactivate
258 return PR_TRUE
; // find next best request to give a descriptor to
263 nsCacheEntry::DetachDescriptors(void)
265 nsCacheEntryDescriptor
* descriptor
=
266 (nsCacheEntryDescriptor
*)PR_LIST_HEAD(&mDescriptorQ
);
268 while (descriptor
!= &mDescriptorQ
) {
269 nsCacheEntryDescriptor
* nextDescriptor
=
270 (nsCacheEntryDescriptor
*)PR_NEXT_LINK(descriptor
);
272 descriptor
->ClearCacheEntry();
273 PR_REMOVE_AND_INIT_LINK(descriptor
);
274 descriptor
= nextDescriptor
;
279 /******************************************************************************
280 * nsCacheEntryInfo - for implementing about:cache
281 *****************************************************************************/
283 NS_IMPL_ISUPPORTS1(nsCacheEntryInfo
, nsICacheEntryInfo
)
287 nsCacheEntryInfo::GetClientID(char ** clientID
)
289 NS_ENSURE_ARG_POINTER(clientID
);
290 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
292 return ClientIDFromCacheKey(*mCacheEntry
->Key(), clientID
);
297 nsCacheEntryInfo::GetDeviceID(char ** deviceID
)
299 NS_ENSURE_ARG_POINTER(deviceID
);
300 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
302 *deviceID
= NS_strdup(mCacheEntry
->GetDeviceID());
303 return *deviceID
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
308 nsCacheEntryInfo::GetKey(nsACString
&key
)
310 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
312 return ClientKeyFromCacheKey(*mCacheEntry
->Key(), key
);
317 nsCacheEntryInfo::GetFetchCount(PRInt32
* fetchCount
)
319 NS_ENSURE_ARG_POINTER(fetchCount
);
320 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
322 *fetchCount
= mCacheEntry
->FetchCount();
328 nsCacheEntryInfo::GetLastFetched(PRUint32
* lastFetched
)
330 NS_ENSURE_ARG_POINTER(lastFetched
);
331 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
333 *lastFetched
= mCacheEntry
->LastFetched();
339 nsCacheEntryInfo::GetLastModified(PRUint32
* lastModified
)
341 NS_ENSURE_ARG_POINTER(lastModified
);
342 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
344 *lastModified
= mCacheEntry
->LastModified();
350 nsCacheEntryInfo::GetExpirationTime(PRUint32
* expirationTime
)
352 NS_ENSURE_ARG_POINTER(expirationTime
);
353 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
355 *expirationTime
= mCacheEntry
->ExpirationTime();
361 nsCacheEntryInfo::GetDataSize(PRUint32
* dataSize
)
363 NS_ENSURE_ARG_POINTER(dataSize
);
364 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
366 *dataSize
= mCacheEntry
->DataSize();
372 nsCacheEntryInfo::IsStreamBased(PRBool
* result
)
374 NS_ENSURE_ARG_POINTER(result
);
375 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
377 *result
= mCacheEntry
->IsStreamData();
382 /******************************************************************************
383 * nsCacheEntryHashTable
384 *****************************************************************************/
387 nsCacheEntryHashTable::ops
=
399 nsCacheEntryHashTable::nsCacheEntryHashTable()
400 : initialized(PR_FALSE
)
402 MOZ_COUNT_CTOR(nsCacheEntryHashTable
);
406 nsCacheEntryHashTable::~nsCacheEntryHashTable()
408 MOZ_COUNT_DTOR(nsCacheEntryHashTable
);
415 nsCacheEntryHashTable::Init()
418 initialized
= PL_DHashTableInit(&table
, &ops
, nsnull
,
419 sizeof(nsCacheEntryHashTableEntry
), 512);
421 if (!initialized
) rv
= NS_ERROR_OUT_OF_MEMORY
;
427 nsCacheEntryHashTable::Shutdown()
430 PL_DHashTableFinish(&table
);
431 initialized
= PR_FALSE
;
437 nsCacheEntryHashTable::GetEntry( const nsCString
* key
)
439 PLDHashEntryHdr
*hashEntry
;
440 nsCacheEntry
*result
= nsnull
;
442 NS_ASSERTION(initialized
, "nsCacheEntryHashTable not initialized");
443 if (!initialized
) return nsnull
;
445 hashEntry
= PL_DHashTableOperate(&table
, key
, PL_DHASH_LOOKUP
);
446 if (PL_DHASH_ENTRY_IS_BUSY(hashEntry
)) {
447 result
= ((nsCacheEntryHashTableEntry
*)hashEntry
)->cacheEntry
;
454 nsCacheEntryHashTable::AddEntry( nsCacheEntry
*cacheEntry
)
456 PLDHashEntryHdr
*hashEntry
;
458 NS_ASSERTION(initialized
, "nsCacheEntryHashTable not initialized");
459 if (!initialized
) return NS_ERROR_NOT_INITIALIZED
;
460 if (!cacheEntry
) return NS_ERROR_NULL_POINTER
;
462 hashEntry
= PL_DHashTableOperate(&table
, cacheEntry
->mKey
, PL_DHASH_ADD
);
464 NS_ASSERTION(((nsCacheEntryHashTableEntry
*)hashEntry
)->cacheEntry
== 0,
465 "### nsCacheEntryHashTable::AddEntry - entry already used");
467 ((nsCacheEntryHashTableEntry
*)hashEntry
)->cacheEntry
= cacheEntry
;
474 nsCacheEntryHashTable::RemoveEntry( nsCacheEntry
*cacheEntry
)
476 NS_ASSERTION(initialized
, "nsCacheEntryHashTable not initialized");
477 NS_ASSERTION(cacheEntry
, "### cacheEntry == nsnull");
479 if (!initialized
) return; // NS_ERROR_NOT_INITIALIZED
482 // XXX debug code to make sure we have the entry we're trying to remove
483 nsCacheEntry
*check
= GetEntry(cacheEntry
->mKey
);
484 NS_ASSERTION(check
== cacheEntry
, "### Attempting to remove unknown cache entry!!!");
486 (void) PL_DHashTableOperate(&table
, cacheEntry
->mKey
, PL_DHASH_REMOVE
);
491 nsCacheEntryHashTable::VisitEntries( PLDHashEnumerator etor
, void *arg
)
493 NS_ASSERTION(initialized
, "nsCacheEntryHashTable not initialized");
494 if (!initialized
) return; // NS_ERROR_NOT_INITIALIZED
495 PL_DHashTableEnumerate(&table
, etor
, arg
);
500 * hash table operation callback functions
504 nsCacheEntryHashTable::HashKey( PLDHashTable
*table
, const void *key
)
506 return PL_DHashStringKey(table
,((nsCString
*)key
)->get());
510 nsCacheEntryHashTable::MatchEntry(PLDHashTable
* /* table */,
511 const PLDHashEntryHdr
* hashEntry
,
514 NS_ASSERTION(key
!= nsnull
, "### nsCacheEntryHashTable::MatchEntry : null key");
515 nsCacheEntry
*cacheEntry
= ((nsCacheEntryHashTableEntry
*)hashEntry
)->cacheEntry
;
517 return cacheEntry
->mKey
->Equals(*(nsCString
*)key
);
522 nsCacheEntryHashTable::MoveEntry(PLDHashTable
* /* table */,
523 const PLDHashEntryHdr
*from
,
526 ((nsCacheEntryHashTableEntry
*)to
)->cacheEntry
=
527 ((nsCacheEntryHashTableEntry
*)from
)->cacheEntry
;
532 nsCacheEntryHashTable::ClearEntry(PLDHashTable
* /* table */,
533 PLDHashEntryHdr
* hashEntry
)
535 ((nsCacheEntryHashTableEntry
*)hashEntry
)->cacheEntry
= 0;