Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Web / Cache / CacheEntry.cs
blobe7c2c42c434a186e63ac0adc00d9e76b58e03efb
1 //------------------------------------------------------------------------------
2 // <copyright file="CacheEntry.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
7 /*
8 * CacheEntry
9 *
10 * Copyright (c) 1998-1999, Microsoft Corporation
14 namespace System.Web.Caching {
15 using System.Text;
16 using System.Threading;
17 using System.Web;
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 */
30 int _hashCode;
32 internal CacheKey(String key, bool isPublic) {
33 if (key == null) {
34 throw new ArgumentNullException("key");
37 _key = key;
38 if (isPublic) {
39 _bits = BitPublic;
41 else if (key[0] == CacheInternal.PrefixOutputCache[0]) {
42 _bits |= BitOutputCache;
45 #if DBG
46 if (!isPublic) {
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);
50 #endif
53 internal String Key {
54 get { return _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() {
66 if (_hashCode == 0) {
67 _hashCode = _key.GetHashCode();
70 return _hashCode;
73 #if DBG
74 public override string ToString() {
75 return (IsPublic ? "P:" : "I:") + _key;
77 #endif
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
96 Closed = 0x10,
99 const byte EntryStateMask = 0x1f;
100 // protected const byte BitPublic = 0x20;
102 // item
103 object _value; /* value */
104 DateTime _utcCreated; /* creation date */
107 // expiration
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 */
113 // usage
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;
119 // dependencies
120 CacheDependency _dependency; /* dependencies this item has */
121 object _onRemovedTargets; /* targets of OnRemove notification */
124 * ctor.
127 internal CacheEntry(
128 String key,
129 Object value,
130 CacheDependency dependency,
131 CacheItemRemovedCallback onRemovedHandler,
132 DateTime utcAbsoluteExpiration,
133 TimeSpan slidingExpiration,
134 CacheItemPriority priority,
135 bool isPublic,
136 CacheInternal cache) :
138 base(key, isPublic) {
140 if (value == null) {
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");
156 _value = value;
157 _dependency = dependency;
158 _onRemovedTargets = onRemovedHandler;
160 _utcCreated = DateTime.UtcNow;
161 _slidingExpiration = slidingExpiration;
162 if (_slidingExpiration > TimeSpan.Zero) {
163 _utcExpires = _utcCreated + _slidingExpiration;
165 else {
166 _utcExpires = utcAbsoluteExpiration;
169 _expiresEntryRef = ExpiresEntryRef.INVALID;
170 _expiresBucket = 0xff;
172 _usageEntryRef = UsageEntryRef.INVALID;
173 if (priority == CacheItemPriority.NotRemovable) {
174 _usageBucket = 0xff;
176 else {
177 _usageBucket = (byte) (priority - 1);
180 _cache = cache;
183 internal Object Value {
184 get {return _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) {
278 if (IsPublic) {
279 try {
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);
286 else {
287 callback(_key, _value, reason);
290 catch (Exception e) {
291 // for public need to report application error
292 HttpApplicationFactory.RaiseError(e);
294 try {
295 WebBaseEvent.RaiseRuntimeError(e, this);
297 catch {
301 else {
302 // for private items just make the call and eat any exceptions
303 try {
304 using (new ApplicationImpersonationContext()) {
305 callback(_key, _value, reason);
308 catch {
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;
325 lock (this) {
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();
342 else {
343 CallCacheItemRemovedCallback((CacheItemRemovedCallback) target, reason);
347 else if (onRemovedTargets is CacheItemRemovedCallback) {
348 CallCacheItemRemovedCallback((CacheItemRemovedCallback) onRemovedTargets, reason);
350 else {
351 ((CacheDependency) onRemovedTargets).ItemRemoved();
355 if (_dependency != null) {
356 _dependency.DisposeInternal();
360 #if DBG
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));
370 sb.Append("\n");
372 return sb.ToString();
374 #endif
376 internal void AddDependent(CacheDependency dependency) {
377 lock (this) {
378 if (_onRemovedTargets == null) {
379 _onRemovedTargets = dependency;
381 else if (_onRemovedTargets is Hashtable) {
382 Hashtable h = (Hashtable) _onRemovedTargets;
383 h[dependency] = dependency;
385 else {
386 Hashtable h = new Hashtable(2);
387 h[_onRemovedTargets] = _onRemovedTargets;
388 h[dependency] = dependency;
389 _onRemovedTargets = h;
394 internal void RemoveDependent(CacheDependency dependency) {
395 lock (this) {
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);
403 if (h.Count == 0) {
404 _onRemovedTargets = null;
411 #if USE_MEMORY_CACHE
412 internal CacheItemRemovedCallback CacheItemRemovedCallback {
413 get {
414 CacheItemRemovedCallback callback = null;
415 lock (this) {
416 if (_onRemovedTargets != null) {
417 if (_onRemovedTargets is Hashtable) {
418 foreach (DictionaryEntry e in (Hashtable)_onRemovedTargets) {
419 callback = e.Value as CacheItemRemovedCallback;
420 break;
423 else {
424 callback = _onRemovedTargets as CacheItemRemovedCallback;
428 return callback;
431 #endif