You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@ -0,0 +1,283 @@
|
||||
// <copyright file="MemoryCacheEntry.cs" company="Microsoft">
|
||||
// Copyright (c) 2009 Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace System.Runtime.Caching {
|
||||
internal class MemoryCacheEntry: MemoryCacheKey {
|
||||
const byte EntryStateMask = 0x1f;
|
||||
|
||||
private Object _value;
|
||||
private DateTime _utcCreated;
|
||||
// expiration
|
||||
private DateTime _utcAbsExp;
|
||||
private TimeSpan _slidingExp;
|
||||
private ExpiresEntryRef _expiresEntryRef;
|
||||
private byte _expiresBucket; // index of the expiration list (bucket)
|
||||
// usage
|
||||
private byte _usageBucket; // index of the usage list (== priority-1)
|
||||
private UsageEntryRef _usageEntryRef; // ref into the usage list
|
||||
private DateTime _utcLastUpdateUsage; // time we last updated usage
|
||||
|
||||
private CacheEntryRemovedCallback _callback;
|
||||
private SeldomUsedFields _fields; // optimization to reduce workingset when the entry hasn't any dependencies
|
||||
|
||||
class SeldomUsedFields {
|
||||
internal Collection<ChangeMonitor> _dependencies; // the entry's dependency needs to be disposed when the entry is released
|
||||
internal Dictionary<MemoryCacheEntryChangeMonitor, MemoryCacheEntryChangeMonitor> _dependents; // dependents must be notified when this entry is removed
|
||||
internal MemoryCache _cache;
|
||||
internal Tuple<MemoryCacheStore, MemoryCacheEntry> _updateSentinel; // the MemoryCacheEntry (and its associated store) of the OnUpdateSentinel for this entry, if there is one
|
||||
}
|
||||
|
||||
internal Object Value {
|
||||
get { return _value; }
|
||||
}
|
||||
|
||||
internal bool HasExpiration() {
|
||||
return _utcAbsExp < DateTime.MaxValue;
|
||||
}
|
||||
|
||||
internal DateTime UtcAbsExp {
|
||||
get { return _utcAbsExp; }
|
||||
set { _utcAbsExp = value; }
|
||||
}
|
||||
|
||||
internal DateTime UtcCreated {
|
||||
get { return _utcCreated; }
|
||||
}
|
||||
|
||||
internal ExpiresEntryRef ExpiresEntryRef {
|
||||
get { return _expiresEntryRef; }
|
||||
set { _expiresEntryRef = value; }
|
||||
}
|
||||
|
||||
internal byte ExpiresBucket {
|
||||
get { return _expiresBucket; }
|
||||
set { _expiresBucket = value; }
|
||||
}
|
||||
|
||||
internal bool InExpires() {
|
||||
return !_expiresEntryRef.IsInvalid;
|
||||
}
|
||||
|
||||
internal TimeSpan SlidingExp {
|
||||
get { return _slidingExp; }
|
||||
}
|
||||
|
||||
internal EntryState State {
|
||||
get { return (EntryState)(_bits & EntryStateMask); }
|
||||
set { _bits = (byte)(((uint)_bits & ~(uint)EntryStateMask) | (uint)value); }
|
||||
}
|
||||
|
||||
internal byte UsageBucket {
|
||||
get { return _usageBucket; }
|
||||
}
|
||||
|
||||
internal UsageEntryRef UsageEntryRef {
|
||||
get { return _usageEntryRef; }
|
||||
set { _usageEntryRef = value; }
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Grandfathered suppression from original caching code checkin")]
|
||||
internal DateTime UtcLastUpdateUsage {
|
||||
get { return _utcLastUpdateUsage; }
|
||||
set { _utcLastUpdateUsage = value; }
|
||||
}
|
||||
|
||||
internal MemoryCacheEntry(String key,
|
||||
Object value,
|
||||
DateTimeOffset absExp,
|
||||
TimeSpan slidingExp,
|
||||
CacheItemPriority priority,
|
||||
Collection<ChangeMonitor> dependencies,
|
||||
CacheEntryRemovedCallback removedCallback,
|
||||
MemoryCache cache) : base(key) {
|
||||
if (value == null) {
|
||||
throw new ArgumentNullException("value");
|
||||
}
|
||||
_utcCreated = DateTime.UtcNow;
|
||||
_value = value;
|
||||
|
||||
_slidingExp = slidingExp;
|
||||
if (_slidingExp > TimeSpan.Zero) {
|
||||
_utcAbsExp = _utcCreated + _slidingExp;
|
||||
}
|
||||
else {
|
||||
_utcAbsExp = absExp.UtcDateTime;
|
||||
}
|
||||
|
||||
_expiresEntryRef = ExpiresEntryRef.INVALID;
|
||||
_expiresBucket = 0xff;
|
||||
|
||||
_usageEntryRef = UsageEntryRef.INVALID;
|
||||
if (priority == CacheItemPriority.NotRemovable) {
|
||||
_usageBucket = 0xff;
|
||||
}
|
||||
else {
|
||||
_usageBucket = 0;
|
||||
}
|
||||
|
||||
_callback = removedCallback;
|
||||
|
||||
if (dependencies != null) {
|
||||
_fields = new SeldomUsedFields();
|
||||
_fields._dependencies = dependencies;
|
||||
_fields._cache = cache;
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddDependent(MemoryCache cache, MemoryCacheEntryChangeMonitor dependent) {
|
||||
lock (this) {
|
||||
if (State > EntryState.AddedToCache) {
|
||||
return;
|
||||
}
|
||||
if (_fields == null) {
|
||||
_fields = new SeldomUsedFields();
|
||||
}
|
||||
if (_fields._cache == null) {
|
||||
_fields._cache = cache;
|
||||
}
|
||||
if (_fields._dependents == null) {
|
||||
_fields._dependents = new Dictionary<MemoryCacheEntryChangeMonitor, MemoryCacheEntryChangeMonitor>();
|
||||
}
|
||||
_fields._dependents[dependent] = dependent;
|
||||
}
|
||||
}
|
||||
|
||||
private void CallCacheEntryRemovedCallback(MemoryCache cache, CacheEntryRemovedReason reason) {
|
||||
if (_callback == null) {
|
||||
return;
|
||||
}
|
||||
CacheEntryRemovedArguments args = new CacheEntryRemovedArguments(cache, reason, new CacheItem(Key, _value));
|
||||
try {
|
||||
_callback(args);
|
||||
}
|
||||
catch {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
internal void CallNotifyOnChanged() {
|
||||
if (_fields != null && _fields._dependencies != null) {
|
||||
foreach (ChangeMonitor monitor in _fields._dependencies) {
|
||||
monitor.NotifyOnChanged(new OnChangedCallback(this.OnDependencyChanged));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Associates this entry with an update sentinel. If this entry has a sliding expiration, we need to
|
||||
// touch the sentinel so that it doesn't expire.
|
||||
internal void ConfigureUpdateSentinel(MemoryCacheStore sentinelStore, MemoryCacheEntry sentinelEntry) {
|
||||
lock (this) {
|
||||
if (_fields == null) {
|
||||
_fields = new SeldomUsedFields();
|
||||
}
|
||||
_fields._updateSentinel = Tuple.Create(sentinelStore, sentinelEntry);
|
||||
}
|
||||
}
|
||||
|
||||
internal bool HasUsage() {
|
||||
return _usageBucket != 0xff;
|
||||
}
|
||||
|
||||
internal bool InUsage() {
|
||||
return !_usageEntryRef.IsInvalid;
|
||||
}
|
||||
|
||||
private void OnDependencyChanged(Object state) {
|
||||
if (State == EntryState.AddedToCache) {
|
||||
_fields._cache.RemoveEntry(this.Key, this, CacheEntryRemovedReason.ChangeMonitorChanged);
|
||||
}
|
||||
}
|
||||
|
||||
internal void Release(MemoryCache cache, CacheEntryRemovedReason reason) {
|
||||
State = EntryState.Closed;
|
||||
|
||||
// Are there any cache entries that depend on this entry?
|
||||
// If so, we need to fire their dependencies.
|
||||
Dictionary<MemoryCacheEntryChangeMonitor, MemoryCacheEntryChangeMonitor>.KeyCollection deps = null;
|
||||
// clone the dependents
|
||||
lock (this) {
|
||||
if (_fields != null && _fields._dependents != null && _fields._dependents.Count > 0) {
|
||||
deps = _fields._dependents.Keys;
|
||||
// set to null so RemoveDependent does not attempt to access it, since we're not
|
||||
// using a copy of the KeyCollection.
|
||||
_fields._dependents = null;
|
||||
Dbg.Assert(_fields._dependents == null, "_fields._dependents == null");
|
||||
}
|
||||
}
|
||||
if (deps != null) {
|
||||
foreach (MemoryCacheEntryChangeMonitor dependent in deps) {
|
||||
if (dependent != null) {
|
||||
dependent.OnCacheEntryReleased();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CallCacheEntryRemovedCallback(cache, reason);
|
||||
|
||||
// Dispose any dependencies
|
||||
if (_fields != null && _fields._dependencies != null) {
|
||||
foreach (ChangeMonitor monitor in _fields._dependencies) {
|
||||
monitor.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void RemoveDependent(MemoryCacheEntryChangeMonitor dependent) {
|
||||
lock (this) {
|
||||
if (_fields != null && _fields._dependents != null) {
|
||||
_fields._dependents.Remove(dependent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateSlidingExp(DateTime utcNow, CacheExpires expires) {
|
||||
if (_slidingExp > TimeSpan.Zero) {
|
||||
DateTime utcNewExpires = utcNow + _slidingExp;
|
||||
if (utcNewExpires - _utcAbsExp >= CacheExpires.MIN_UPDATE_DELTA || utcNewExpires < _utcAbsExp) {
|
||||
expires.UtcUpdate(this, utcNewExpires);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateSlidingExpForUpdateSentinel() {
|
||||
// We don't need a lock to get information about the update sentinel
|
||||
SeldomUsedFields fields = _fields;
|
||||
if (fields != null) {
|
||||
Tuple<MemoryCacheStore, MemoryCacheEntry> sentinelInfo = fields._updateSentinel;
|
||||
|
||||
// touch the update sentinel to keep it from expiring
|
||||
if (sentinelInfo != null) {
|
||||
MemoryCacheStore sentinelStore = sentinelInfo.Item1;
|
||||
MemoryCacheEntry sentinelEntry = sentinelInfo.Item2;
|
||||
sentinelStore.UpdateExpAndUsage(sentinelEntry, updatePerfCounters: false); // perf counters shouldn't be polluted by touching update sentinel entry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateUsage(DateTime utcNow, CacheUsage usage) {
|
||||
// update, but not more frequently than once per second.
|
||||
if (InUsage() && _utcLastUpdateUsage < utcNow - CacheUsage.CORRELATED_REQUEST_TIMEOUT) {
|
||||
_utcLastUpdateUsage = utcNow;
|
||||
usage.Update(this);
|
||||
if (_fields != null && _fields._dependencies != null) {
|
||||
foreach (ChangeMonitor monitor in _fields._dependencies) {
|
||||
MemoryCacheEntryChangeMonitor m = monitor as MemoryCacheEntryChangeMonitor;
|
||||
if (m == null) {
|
||||
continue;
|
||||
}
|
||||
foreach (MemoryCacheEntry e in m.Dependencies) {
|
||||
MemoryCacheStore store = e._fields._cache.GetStore(e);
|
||||
e.UpdateUsage(utcNow, store.Usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user