1 //------------------------------------------------------------------------------
2 // <copyright file="CacheEntry.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
10 * Copyright (c) 1998-1999, Microsoft Corporation
14 namespace System
.Web
.Caching
{
16 using System
.Threading
;
18 using System
.Web
.Util
;
19 using System
.Collections
;
20 using System
.Web
.Management
;
21 using System
.Web
.Hosting
;
22 using System
.Globalization
;
24 internal class CacheKey
{
25 protected const byte BitPublic
= 0x20;
26 protected const byte BitOutputCache
= 0x40;
28 protected string _key
; /* key to the item */
29 protected byte _bits
; /* cache lifetime state and public property */
32 internal CacheKey(String key
, bool isPublic
) {
34 throw new ArgumentNullException("key");
41 else if (key
[0] == CacheInternal
.PrefixOutputCache
[0]) {
42 _bits
|= BitOutputCache
;
47 Debug
.Assert(CacheInternal
.PrefixFIRST
[0] <= key
[0] && key
[0] <= CacheInternal
.PrefixLAST
[0],
48 "CacheInternal.PrefixFIRST[0] <= key[0] && key[0] <= CacheInternal.PrefixLAST[0], key=" + key
);
57 internal bool IsOutputCache
{
58 get { return (_bits & BitOutputCache) != 0; }
61 internal bool IsPublic
{
62 get { return (_bits & BitPublic) != 0; }
65 public override int GetHashCode() {
67 _hashCode
= _key
.GetHashCode();
74 public override string ToString() {
75 return (IsPublic
? "P:" : "I:") + _key
;
81 * An entry in the cache.
82 * Overhead is 68 bytes + object header.
84 internal sealed class CacheEntry
: CacheKey
{
85 const CacheItemPriority CacheItemPriorityMin
= CacheItemPriority
.Low
;
86 const CacheItemPriority CacheItemPriorityMax
= CacheItemPriority
.NotRemovable
;
87 static readonly TimeSpan OneYear
= new TimeSpan(365, 0, 0, 0);
90 internal enum EntryState
: byte {
91 NotInCache
= 0x00, // Created but not in hashtable
92 AddingToCache
= 0x01, // In hashtable only
93 AddedToCache
= 0x02, // In hashtable + expires + usage
94 RemovingFromCache
= 0x04, // Removed from hashtable only
95 RemovedFromCache
= 0x08, // Removed from hashtable & expires & usage
99 const byte EntryStateMask
= 0x1f;
100 // protected const byte BitPublic = 0x20;
103 object _value
; /* value */
104 DateTime _utcCreated
; /* creation date */
108 DateTime _utcExpires
; /* when this item expires */
109 TimeSpan _slidingExpiration
; /* expiration interval */
110 byte _expiresBucket
; /* index of the expiration list (bucket) */
111 ExpiresEntryRef _expiresEntryRef
; /* ref into the expiration list */
114 byte _usageBucket
; /* index of the usage list (== priority-1) */
115 UsageEntryRef _usageEntryRef
; /* ref into the usage list */
116 DateTime _utcLastUpdate
; /* time we last updated usage */
117 CacheInternal _cache
;
120 CacheDependency _dependency
; /* dependencies this item has */
121 object _onRemovedTargets
; /* targets of OnRemove notification */
130 CacheDependency dependency
,
131 CacheItemRemovedCallback onRemovedHandler
,
132 DateTime utcAbsoluteExpiration
,
133 TimeSpan slidingExpiration
,
134 CacheItemPriority priority
,
136 CacheInternal cache
) :
138 base(key
, isPublic
) {
141 throw new ArgumentNullException("value");
144 if (slidingExpiration
< TimeSpan
.Zero
|| OneYear
< slidingExpiration
) {
145 throw new ArgumentOutOfRangeException("slidingExpiration");
148 if (utcAbsoluteExpiration
!= Cache
.NoAbsoluteExpiration
&& slidingExpiration
!= Cache
.NoSlidingExpiration
) {
149 throw new ArgumentException(SR
.GetString(SR
.Invalid_expiration_combination
));
152 if (priority
< CacheItemPriorityMin
|| CacheItemPriorityMax
< priority
) {
153 throw new ArgumentOutOfRangeException("priority");
157 _dependency
= dependency
;
158 _onRemovedTargets
= onRemovedHandler
;
160 _utcCreated
= DateTime
.UtcNow
;
161 _slidingExpiration
= slidingExpiration
;
162 if (_slidingExpiration
> TimeSpan
.Zero
) {
163 _utcExpires
= _utcCreated
+ _slidingExpiration
;
166 _utcExpires
= utcAbsoluteExpiration
;
169 _expiresEntryRef
= ExpiresEntryRef
.INVALID
;
170 _expiresBucket
= 0xff;
172 _usageEntryRef
= UsageEntryRef
.INVALID
;
173 if (priority
== CacheItemPriority
.NotRemovable
) {
177 _usageBucket
= (byte) (priority
- 1);
183 internal Object Value
{
187 internal DateTime UtcCreated
{
188 get {return _utcCreated;}
191 internal EntryState State
{
192 get { return (EntryState) (_bits & EntryStateMask); }
193 set { _bits = (byte) (((uint) _bits & ~(uint)EntryStateMask) | (uint) value); }
196 internal DateTime UtcExpires
{
197 get {return _utcExpires;}
198 set {_utcExpires = value;}
201 internal TimeSpan SlidingExpiration
{
202 get {return _slidingExpiration;}
205 internal byte ExpiresBucket
{
206 get {return _expiresBucket;}
207 set {_expiresBucket = value;}
210 internal ExpiresEntryRef ExpiresEntryRef
{
211 get {return _expiresEntryRef;}
212 set {_expiresEntryRef = value;}
215 internal bool HasExpiration() {
216 return _utcExpires
< DateTime
.MaxValue
;
219 internal bool InExpires() {
220 return !_expiresEntryRef
.IsInvalid
;
223 internal byte UsageBucket
{
224 get {return _usageBucket;}
227 internal UsageEntryRef UsageEntryRef
{
228 get {return _usageEntryRef;}
229 set {_usageEntryRef = value;}
232 internal DateTime UtcLastUsageUpdate
{
233 get {return _utcLastUpdate;}
234 set {_utcLastUpdate = value;}
237 internal bool HasUsage() {
238 return _usageBucket
!= 0xff;
241 internal bool InUsage() {
242 return !_usageEntryRef
.IsInvalid
;
245 internal CacheDependency Dependency
{
246 get {return _dependency;}
249 internal void MonitorDependencyChanges() {
250 // need to protect against the item being closed
251 CacheDependency dependency
= _dependency
;
252 if (dependency
!= null && State
== EntryState
.AddedToCache
) {
253 if (!dependency
.TakeOwnership()) {
254 throw new InvalidOperationException(
255 SR
.GetString(SR
.Cache_dependency_used_more_that_once
));
258 dependency
.SetCacheDependencyChanged((Object sender
, EventArgs args
) => {
259 DependencyChanged(sender
, args
);
265 * The entry has changed, so remove ourselves from the cache.
267 void DependencyChanged(Object sender
, EventArgs e
) {
268 if (State
== EntryState
.AddedToCache
) {
269 _cache
.Remove(this, CacheItemRemovedReason
.DependencyChanged
);
274 * Helper to call the on-remove callback
277 private void CallCacheItemRemovedCallback(CacheItemRemovedCallback callback
, CacheItemRemovedReason reason
) {
280 // for public need to impersonate if called outside of request context
281 if (HttpContext
.Current
== null) {
282 using (new ApplicationImpersonationContext()) {
283 callback(_key
, _value
, reason
);
287 callback(_key
, _value
, reason
);
290 catch (Exception e
) {
291 // for public need to report application error
292 HttpApplicationFactory
.RaiseError(e
);
295 WebBaseEvent
.RaiseRuntimeError(e
, this);
302 // for private items just make the call and eat any exceptions
304 using (new ApplicationImpersonationContext()) {
305 callback(_key
, _value
, reason
);
314 * Close the item to complete its removal from cache.
316 * @param reason The reason the item is removed.
318 internal void Close(CacheItemRemovedReason reason
) {
319 Debug
.Assert(State
== EntryState
.RemovedFromCache
, "State == EntryState.RemovedFromCache");
320 State
= EntryState
.Closed
;
322 object onRemovedTargets
= null;
323 object[] targets
= null;
326 if (_onRemovedTargets
!= null) {
327 onRemovedTargets
= _onRemovedTargets
;
328 if (onRemovedTargets
is Hashtable
) {
329 ICollection col
= ((Hashtable
) onRemovedTargets
).Keys
;
330 targets
= new object[col
.Count
];
331 col
.CopyTo(targets
, 0);
336 if (onRemovedTargets
!= null) {
337 if (targets
!= null) {
338 foreach (object target
in targets
) {
339 if (target
is CacheDependency
) {
340 ((CacheDependency
)target
).ItemRemoved();
343 CallCacheItemRemovedCallback((CacheItemRemovedCallback
) target
, reason
);
347 else if (onRemovedTargets
is CacheItemRemovedCallback
) {
348 CallCacheItemRemovedCallback((CacheItemRemovedCallback
) onRemovedTargets
, reason
);
351 ((CacheDependency
) onRemovedTargets
).ItemRemoved();
355 if (_dependency
!= null) {
356 _dependency
.DisposeInternal();
361 internal /*public*/ string DebugDescription(string indent
) {
362 StringBuilder sb
= new StringBuilder();
363 String nlindent
= "\n" + indent
+ " ";
365 sb
.Append(indent
+ "CacheItem");
366 sb
.Append(nlindent
); sb
.Append("_key="); sb
.Append(_key
);
367 sb
.Append(nlindent
); sb
.Append("_value="); sb
.Append(Debug
.GetDescription(_value
, indent
));
368 sb
.Append(nlindent
); sb
.Append("_utcExpires="); sb
.Append(Debug
.FormatUtcDate(_utcExpires
));
369 sb
.Append(nlindent
); sb
.Append("_bits=0x"); sb
.Append(((int)_bits
).ToString("x", CultureInfo
.InvariantCulture
));
372 return sb
.ToString();
376 internal void AddDependent(CacheDependency dependency
) {
378 if (_onRemovedTargets
== null) {
379 _onRemovedTargets
= dependency
;
381 else if (_onRemovedTargets
is Hashtable
) {
382 Hashtable h
= (Hashtable
) _onRemovedTargets
;
383 h
[dependency
] = dependency
;
386 Hashtable h
= new Hashtable(2);
387 h
[_onRemovedTargets
] = _onRemovedTargets
;
388 h
[dependency
] = dependency
;
389 _onRemovedTargets
= h
;
394 internal void RemoveDependent(CacheDependency dependency
) {
396 if (_onRemovedTargets
!= null) {
397 if (_onRemovedTargets
== dependency
) {
398 _onRemovedTargets
= null;
400 else if (_onRemovedTargets
is Hashtable
) {
401 Hashtable h
= (Hashtable
)_onRemovedTargets
;
402 h
.Remove(dependency
);
404 _onRemovedTargets
= null;
412 internal CacheItemRemovedCallback CacheItemRemovedCallback
{
414 CacheItemRemovedCallback callback
= null;
416 if (_onRemovedTargets
!= null) {
417 if (_onRemovedTargets
is Hashtable
) {
418 foreach (DictionaryEntry e
in (Hashtable
)_onRemovedTargets
) {
419 callback
= e
.Value
as CacheItemRemovedCallback
;
424 callback
= _onRemovedTargets
as CacheItemRemovedCallback
;