System.Web.Configuration.(AdapterDictionary,AuthenticationMode,MachineKeyValidation...
[mono.git] / mcs / class / System.Web / System.Web.Caching / Cache.cs
1 //
2 // System.Web.Caching.Cache
3 //
4 // Author(s):
5 //  Lluis Sanchez (lluis@ximian.com)
6 //  Marek Habersack <mhabersack@novell.com>
7 //
8 // (C) 2005-2009 Novell, Inc (http://novell.com)
9 //
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System.Threading;
32 using System.Collections;
33 using System.Collections.Generic;
34 using System.Linq;
35 using System.Security.Permissions;
36 using System.Web.Configuration;
37
38 namespace System.Web.Caching
39 {
40         // CAS - no InheritanceDemand here as the class is sealed
41         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
42         public sealed class Cache: IEnumerable
43         {
44                 const int LOW_WATER_MARK = 10000; // Target number of items if high water mark is reached
45                 const int HIGH_WATER_MARK = 15000; // We start collection after exceeding this count
46                 
47                 public static readonly DateTime NoAbsoluteExpiration = DateTime.MaxValue;
48                 public static readonly TimeSpan NoSlidingExpiration = TimeSpan.Zero;
49
50                 // cacheLock will be released in the code below without checking whether it was
51                 // actually acquired. The API doesn't offer a reliable way to check whether the lock
52                 // is being held by the current thread and since Mono does't implement CER
53                 // (Constrained Execution Regions -
54                 // http://msdn.microsoft.com/en-us/library/ms228973.aspx) currently, we have no
55                 // reliable way of recording the information that the lock has been successfully
56                 // acquired.
57                 // It can happen that a Thread.Abort occurs while acquiring the lock and the lock
58                 // isn't  actually held. In this case the attempt to release a lock will throw an
59                 // exception. It's better than a race of setting a boolean flag  after acquiring the
60                 // lock and then relying upon it here to release it - that may cause a deadlock
61                 // should we fail to release the lock  which was successfully acquired but
62                 // Thread.Abort happened right after that during the stloc instruction to set the
63                 // boolean flag. Once CERs are supported we can use the boolean flag reliably.
64                 ReaderWriterLockSlim cacheLock;
65                 CacheItemLRU cache;
66                 CacheItemPriorityQueue timedItems;
67                 Timer expirationTimer;
68                 long expirationTimerPeriod = 0;
69                 Cache dependencyCache;
70                 bool? disableExpiration;
71                 long privateBytesLimit = -1;
72                 long percentagePhysicalMemoryLimit = -1;
73                 
74                 bool DisableExpiration {
75                         get {
76                                 if (disableExpiration == null) {
77                                         var cs = WebConfigurationManager.GetWebApplicationSection ("system.web/caching/cache") as CacheSection;
78                                         if (cs == null)
79                                                 disableExpiration = false;
80                                         else
81                                                 disableExpiration = (bool)cs.DisableExpiration;
82                                 }
83
84                                 return (bool)disableExpiration;
85                         }
86                 }
87
88                 public long EffectivePrivateBytesLimit {
89                         get {
90                                 if (privateBytesLimit == -1) {
91                                         var cs = WebConfigurationManager.GetWebApplicationSection ("system.web/caching/cache") as CacheSection;
92                                         if (cs == null)
93                                                 privateBytesLimit = 0;
94                                         else
95                                                 privateBytesLimit = cs.PrivateBytesLimit;
96
97                                         if (privateBytesLimit == 0) {
98                                                 // http://blogs.msdn.com/tmarq/archive/2007/06/25/some-history-on-the-asp-net-cache-memory-limits.aspx
99                                                 // TODO: calculate
100                                                 privateBytesLimit = 734003200;
101                                         }
102                                 }
103
104                                 return privateBytesLimit;
105                         }
106                 }
107
108                 public long EffectivePercentagePhysicalMemoryLimit {
109                         get {
110                                 if (percentagePhysicalMemoryLimit == -1) {
111                                         var cs = WebConfigurationManager.GetWebApplicationSection ("system.web/caching/cache") as CacheSection;
112                                         if (cs == null)
113                                                 percentagePhysicalMemoryLimit = 0;
114                                         else
115                                                 percentagePhysicalMemoryLimit = cs.PercentagePhysicalMemoryUsedLimit;
116
117                                         if (percentagePhysicalMemoryLimit == 0) {
118                                                 // http://blogs.msdn.com/tmarq/archive/2007/06/25/some-history-on-the-asp-net-cache-memory-limits.aspx
119                                                 // TODO: calculate
120                                                 percentagePhysicalMemoryLimit = 97;
121                                         }
122                                 }
123
124                                 return percentagePhysicalMemoryLimit;
125                         }
126                 }
127                 
128                 public Cache ()
129                 {
130                         cacheLock = new ReaderWriterLockSlim ();
131                         cache = new CacheItemLRU (this, HIGH_WATER_MARK, LOW_WATER_MARK);
132                 }
133
134                 public int Count {
135                         get { return cache.Count; }
136                 }
137                 
138                 public object this [string key] {
139                         get { return Get (key); }
140                         set { Insert (key, value); }
141                 }
142
143                 // Must ALWAYS be called with the cache write lock held
144                 CacheItem RemoveCacheItem (string key)
145                 {
146                         if (key == null)
147                                 return null;
148
149                         CacheItem ret = cache [key];
150                         if (ret == null)
151                                 return null;
152                         
153                         if (timedItems != null)
154                                 timedItems.OnItemDisable (ret);
155                         
156                         ret.Disabled = true;
157                         cache.Remove (key);
158                         
159                         return ret;
160                 }
161                 
162                 public object Add (string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback)
163                 {
164                         if (key == null)
165                                 throw new ArgumentNullException ("key");
166                         
167                         try {
168                                 cacheLock.EnterWriteLock ();
169                                 CacheItem it = cache [key];
170
171                                 if (it != null)
172                                         return it.Value;
173                                 Insert (key, value, dependencies, absoluteExpiration, slidingExpiration, priority, onRemoveCallback, null, false);
174                         } finally {
175                                 // See comment at the top of the file, above cacheLock declaration
176                                 cacheLock.ExitWriteLock ();
177                         }
178                                 
179                         return null;
180                 }
181                 
182                 public object Get (string key)
183                 {
184                         try {
185                                 cacheLock.EnterUpgradeableReadLock ();
186                                 CacheItem it = cache [key];
187                                 if (it == null)
188                                         return null;
189                                 
190                                 if (it.Dependency != null && it.Dependency.HasChanged) {
191                                         try {
192                                                 cacheLock.EnterWriteLock ();
193                                                 if (!NeedsUpdate (it, CacheItemUpdateReason.DependencyChanged, false))
194                                                         Remove (it.Key, CacheItemRemovedReason.DependencyChanged, false, true);
195                                         } finally {
196                                                 // See comment at the top of the file, above cacheLock declaration
197                                                 cacheLock.ExitWriteLock ();
198                                         }
199                                         
200                                         return null;
201                                 }
202
203                                 if (!DisableExpiration) {
204                                         if (it.SlidingExpiration != NoSlidingExpiration) {
205                                                 it.AbsoluteExpiration = DateTime.Now + it.SlidingExpiration;
206                                                 // Cast to long is ok since we know that sliding expiration
207                                                 // is less than 365 days (31536000000ms)
208                                                 long remaining = (long)it.SlidingExpiration.TotalMilliseconds;
209                                                 it.ExpiresAt = it.AbsoluteExpiration.Ticks;
210                                                 
211                                                 if (expirationTimer != null && (expirationTimerPeriod == 0 || expirationTimerPeriod > remaining)) {
212                                                         expirationTimerPeriod = remaining;
213                                                         expirationTimer.Change (expirationTimerPeriod, expirationTimerPeriod);
214                                                 }
215                                         
216                                         } else if (DateTime.Now >= it.AbsoluteExpiration) {
217                                                 try {
218                                                         cacheLock.EnterWriteLock ();
219                                                         if (!NeedsUpdate (it, CacheItemUpdateReason.Expired, false))
220                                                                 Remove (key, CacheItemRemovedReason.Expired, false, true);
221                                                 } finally {
222                                                         // See comment at the top of the file, above cacheLock declaration
223                                                         cacheLock.ExitWriteLock ();
224                                                 }
225
226                                                 return null;
227                                         }
228                                 }
229                                 
230                                 return it.Value;
231                         } finally {
232                                 // See comment at the top of the file, above cacheLock declaration
233                                 cacheLock.ExitUpgradeableReadLock ();
234                         }
235                 }
236                 
237                 public void Insert (string key, object value)
238                 {
239                         Insert (key, value, null, NoAbsoluteExpiration, NoSlidingExpiration, CacheItemPriority.Normal, null, null, true);
240                 }
241                 
242                 public void Insert (string key, object value, CacheDependency dependencies)
243                 {
244                         Insert (key, value, dependencies, NoAbsoluteExpiration, NoSlidingExpiration, CacheItemPriority.Normal, null, null, true);
245                 }
246                 
247                 public void Insert (string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration)
248                 {
249                         Insert (key, value, dependencies, absoluteExpiration, slidingExpiration, CacheItemPriority.Normal, null, null, true);
250                 }
251
252                 public void Insert (string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration,
253                                     CacheItemUpdateCallback onUpdateCallback)
254                 {
255                         Insert (key, value, dependencies, absoluteExpiration, slidingExpiration, CacheItemPriority.Normal, null, onUpdateCallback, true);
256                 }
257                 
258                 public void Insert (string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration,
259                                     CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback)
260                 {
261                         Insert (key, value, dependencies, absoluteExpiration, slidingExpiration, priority, onRemoveCallback, null, true);
262                 }
263
264                 void Insert (string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration,
265                              CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback, CacheItemUpdateCallback onUpdateCallback, bool doLock)
266                 {
267                         if (key == null)
268                                 throw new ArgumentNullException ("key");
269                         if (value == null)
270                                 throw new ArgumentNullException ("value");
271                         if (slidingExpiration < TimeSpan.Zero || slidingExpiration > TimeSpan.FromDays (365))
272                                 throw new ArgumentNullException ("slidingExpiration");
273                         if (absoluteExpiration != NoAbsoluteExpiration && slidingExpiration != NoSlidingExpiration)
274                                 throw new ArgumentException ("Both absoluteExpiration and slidingExpiration are specified");
275                                 
276                         CacheItem ci = new CacheItem ();
277                         ci.Value = value;
278                         ci.Key = key;
279                         
280                         if (dependencies != null) {
281                                 ci.Dependency = dependencies;
282                                 dependencies.DependencyChanged += new EventHandler (OnDependencyChanged);
283                                 dependencies.SetCache (DependencyCache);
284                         }
285
286                         ci.Priority = priority;
287                         SetItemTimeout (ci, absoluteExpiration, slidingExpiration, onRemoveCallback, onUpdateCallback, key, doLock);
288                 }
289                 
290                 internal void SetItemTimeout (string key, DateTime absoluteExpiration, TimeSpan slidingExpiration, bool doLock)
291                 {
292                         CacheItem ci = null;                    
293                         try {
294                                 if (doLock)
295                                         cacheLock.EnterWriteLock ();
296                                 
297                                 ci = cache [key];
298                                 if (ci != null)
299                                         SetItemTimeout (ci, absoluteExpiration, slidingExpiration, ci.OnRemoveCallback, null, key, false);
300                         } finally {
301                                 if (doLock) {
302                                         // See comment at the top of the file, above cacheLock declaration
303                                         cacheLock.ExitWriteLock ();
304                                 }
305                         }
306                 }
307
308                 void SetItemTimeout (CacheItem ci, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemRemovedCallback onRemoveCallback,
309                                      CacheItemUpdateCallback onUpdateCallback, string key, bool doLock)
310                 {
311                         bool disableExpiration = DisableExpiration;
312
313                         if (!disableExpiration) {
314                                 ci.SlidingExpiration = slidingExpiration;
315                                 if (slidingExpiration != NoSlidingExpiration)
316                                         ci.AbsoluteExpiration = DateTime.Now + slidingExpiration;
317                                 else
318                                         ci.AbsoluteExpiration = absoluteExpiration;                     
319                         }
320                         
321                         ci.OnRemoveCallback = onRemoveCallback;
322                         ci.OnUpdateCallback = onUpdateCallback;
323                         
324                         try {
325                                 if (doLock)
326                                         cacheLock.EnterWriteLock ();
327
328                                 if (key != null) {
329                                         cache [key] = ci;
330                                         cache.EvictIfNecessary ();
331                                 }
332                                 
333                                 ci.LastChange = DateTime.Now;
334                                 if (!disableExpiration && ci.AbsoluteExpiration != NoAbsoluteExpiration) {
335                                         bool enqueue;
336                                         if (ci.IsTimedItem) {
337                                                 enqueue = UpdateTimedItem (ci);
338                                                 if (!enqueue)
339                                                         UpdateTimerPeriod (ci);
340                                         } else
341                                                 enqueue = true;
342
343                                         if (enqueue) {
344                                                 ci.IsTimedItem = true;
345                                                 EnqueueTimedItem (ci);
346                                         }
347                                         
348                                 }
349                         } finally {
350                                 if (doLock) {
351                                         // See comment at the top of the file, above cacheLock declaration
352                                         cacheLock.ExitWriteLock ();
353                                 }
354                         }
355                 }
356
357                 // MUST be called with cache lock held
358                 bool UpdateTimedItem (CacheItem item)
359                 {
360                         if (timedItems == null)
361                                 return true;
362
363                         item.ExpiresAt = item.AbsoluteExpiration.Ticks;
364                         return !timedItems.Update (item);
365                 }
366
367                 // MUST be called with cache lock held
368                 void UpdateTimerPeriod (CacheItem item)
369                 {
370                         if (timedItems == null)
371                                 timedItems = new CacheItemPriorityQueue ();
372
373                         long remaining = Math.Max (0, (long)(item.AbsoluteExpiration - DateTime.Now).TotalMilliseconds);
374                         item.ExpiresAt = item.AbsoluteExpiration.Ticks;
375                         
376                         if (remaining > 4294967294)
377                                 // Maximum due time for timer
378                                 // Item will expire properly anyway, as the timer will be
379                                 // rescheduled for the item's expiration time once that item is
380                                 // bubbled to the top of the priority queue.
381                                 remaining = 4294967294;
382
383                         if (expirationTimer != null && expirationTimerPeriod <= remaining)
384                                 return;
385                         expirationTimerPeriod = remaining;
386
387                         if (expirationTimer == null)
388                                 expirationTimer = new Timer (new TimerCallback (ExpireItems), null, expirationTimerPeriod, expirationTimerPeriod);
389                         else
390                                 expirationTimer.Change (expirationTimerPeriod, expirationTimerPeriod);
391                 }
392                 
393                 // MUST be called with cache lock held
394                 void EnqueueTimedItem (CacheItem item)
395                 {
396                         UpdateTimerPeriod (item);
397                         timedItems.Enqueue (item);
398                 }
399
400                 public object Remove (string key)
401                 {
402                         return Remove (key, CacheItemRemovedReason.Removed, true, true);
403                 }
404                 
405                 internal object Remove (string key, CacheItemRemovedReason reason, bool doLock, bool invokeCallback)
406                 {
407                         CacheItem it = null;
408                         try {
409                                 if (doLock)
410                                         cacheLock.EnterWriteLock ();
411                                 
412                                 it = RemoveCacheItem (key);
413                         } finally {
414                                 if (doLock) {
415                                         // See comment at the top of the file, above cacheLock declaration
416                                         cacheLock.ExitWriteLock ();
417                                 }
418                         }
419
420                         object ret = null;
421                         if (it != null) {
422                                 if (it.Dependency != null) {
423                                         it.Dependency.SetCache (null);
424                                         it.Dependency.DependencyChanged -= new EventHandler (OnDependencyChanged);
425                                         it.Dependency.Dispose ();
426                                 }
427                                 if (invokeCallback && it.OnRemoveCallback != null) {
428                                         try {
429                                                 it.OnRemoveCallback (key, it.Value, reason);
430                                         } catch {
431                                                 //TODO: anything to be done here?
432                                         }
433                                 }
434                                 ret = it.Value;
435                                 it.Value = null;
436                                 it.Key = null;
437                                 it.Dependency = null;
438                                 it.OnRemoveCallback = null;
439                                 it.OnUpdateCallback = null;
440                                 it = null;
441                         }
442
443                         return ret;
444                 }
445
446                 // Used when shutting down the application so that
447                 // session_end events are sent for all sessions.
448                 internal void InvokePrivateCallbacks ()
449                 {
450                         try {
451                                 cacheLock.EnterReadLock ();
452                                 cache.InvokePrivateCallbacks ();
453                         }  finally {
454                                 // See comment at the top of the file, above cacheLock declaration
455                                 cacheLock.ExitReadLock ();
456                         }
457                 }
458
459                 public IDictionaryEnumerator GetEnumerator ()
460                 {
461                         List <CacheItem> list = null;
462                         try {
463                                 cacheLock.EnterReadLock ();
464                                 list = cache.ToList ();
465                         } finally {
466                                 // See comment at the top of the file, above cacheLock declaration
467                                 cacheLock.ExitReadLock ();
468                         }
469                         
470                         return new CacheItemEnumerator (list);
471                 }
472                 
473                 IEnumerator IEnumerable.GetEnumerator ()
474                 {
475                         return GetEnumerator ();
476                 }
477                 
478                 void OnDependencyChanged (object o, EventArgs a)
479                 {
480                         CheckDependencies ();
481                 }
482
483                 bool NeedsUpdate (CacheItem item, CacheItemUpdateReason reason, bool needLock)
484                 {
485                         try {
486                                 if (needLock)
487                                         cacheLock.EnterWriteLock ();
488                                 
489                                 if (item == null || item.OnUpdateCallback == null)
490                                         return false;
491
492                                 object expensiveObject;
493                                 CacheDependency dependency;
494                                 DateTime absoluteExpiration;
495                                 TimeSpan slidingExpiration;
496                                 string key = item.Key;
497                                 CacheItemUpdateCallback updateCB = item.OnUpdateCallback;
498                                 
499                                 updateCB (key, reason, out expensiveObject, out dependency, out absoluteExpiration, out slidingExpiration);
500                                 if (expensiveObject == null)
501                                         return false;
502
503                                 CacheItemPriority priority = item.Priority;
504                                 CacheItemRemovedCallback removeCB = item.OnRemoveCallback;
505                                 CacheItemRemovedReason whyRemoved;
506
507                                 switch (reason) {
508                                         case CacheItemUpdateReason.Expired:
509                                                 whyRemoved = CacheItemRemovedReason.Expired;
510                                                 break;
511
512                                         case CacheItemUpdateReason.DependencyChanged:
513                                                 whyRemoved = CacheItemRemovedReason.DependencyChanged;
514                                                 break;
515
516                                         default:
517                                                 whyRemoved = CacheItemRemovedReason.Removed;
518                                                 break;
519                                 }
520                                 
521                                 Remove (key, whyRemoved, false, false);
522                                 Insert (key, expensiveObject, dependency, absoluteExpiration, slidingExpiration, priority, removeCB, updateCB, false);
523                                 
524                                 return true;
525                         } catch (Exception) {
526                                 return false;
527                         } finally {
528                                 if (needLock) {
529                                         // See comment at the top of the file, above cacheLock declaration
530                                         cacheLock.ExitWriteLock ();
531                                 }
532                         }
533                 }
534                 
535                 void ExpireItems (object data)
536                 {
537                         DateTime now = DateTime.Now;
538                         CacheItem item = null;
539
540                         expirationTimer.Change (Timeout.Infinite, Timeout.Infinite);
541                         try {
542                                 cacheLock.EnterWriteLock ();
543                                 while (true) {
544                                         item = timedItems.Peek ();
545                                         
546                                         if (item == null) {
547                                                 if (timedItems.Count == 0)
548                                                         break;
549                                                 
550                                                 timedItems.Dequeue ();
551                                                 continue;
552                                         }
553                                                 
554                                         if (!item.Disabled && item.ExpiresAt > now.Ticks)
555                                                 break;
556                                         
557                                         if (item.Disabled) {
558                                                 item = timedItems.Dequeue ();
559                                                 continue;
560                                         }
561
562                                         item = timedItems.Dequeue ();
563                                         if (item != null)
564                                                 if (!NeedsUpdate (item, CacheItemUpdateReason.Expired, false))
565                                                         Remove (item.Key, CacheItemRemovedReason.Expired, false, true);
566                                 }
567                         } finally {
568                                 // See comment at the top of the file, above cacheLock declaration
569                                 cacheLock.ExitWriteLock ();
570                         }
571
572                         if (item != null) {
573                                 long remaining = Math.Max (0, (long)(item.AbsoluteExpiration - now).TotalMilliseconds);
574                                 if (remaining > 0 && (expirationTimerPeriod == 0 || expirationTimerPeriod > remaining)) {
575                                         expirationTimerPeriod = remaining;
576                                         expirationTimer.Change (expirationTimerPeriod, expirationTimerPeriod);
577                                         return;
578                                 }
579                                 if (expirationTimerPeriod > 0)
580                                         return;
581                         }
582
583                         expirationTimer.Change (Timeout.Infinite, Timeout.Infinite);
584                         expirationTimerPeriod = 0;
585                 }
586                 
587                 internal void CheckDependencies ()
588                 {
589                         try {
590                                 cacheLock.EnterWriteLock ();
591                                 List <CacheItem> list = cache.SelectItems (it => {
592                                         if (it == null)
593                                                 return false;
594                                         if (it.Dependency != null && it.Dependency.HasChanged && !NeedsUpdate (it, CacheItemUpdateReason.DependencyChanged, false))
595                                                 return true;
596                                         return false;
597                                 });
598                                 
599                                 foreach (CacheItem it in list)
600                                         Remove (it.Key, CacheItemRemovedReason.DependencyChanged, false, true);
601                                 list.Clear ();
602                                 list.TrimExcess ();
603                         } finally {
604                                 // See comment at the top of the file, above cacheLock declaration
605                                 cacheLock.ExitWriteLock ();
606                         }
607                 }
608                 
609                 internal DateTime GetKeyLastChange (string key)
610                 {
611                         try {
612                                 cacheLock.EnterReadLock ();
613                                 CacheItem it = cache [key];
614
615                                 if (it == null)
616                                         return DateTime.MaxValue;
617                                 
618                                 return it.LastChange;
619                         } finally {
620                                 // See comment at the top of the file, above cacheLock declaration
621                                 cacheLock.ExitReadLock ();
622                         }
623                 }
624
625                 internal Cache DependencyCache {
626                         get {
627                                 if (dependencyCache == null)
628                                         return this;
629
630                                 return dependencyCache;
631                         }
632                         set { dependencyCache = value; }
633                 }
634         }
635 }