private void RemoveEntry(CacheEntry entry) { if (EntriesCollection.Remove(new KeyValuePair <object, CacheEntry>(entry.Key, entry))) { if (_options.SizeLimit.HasValue) { Interlocked.Add(ref _cacheSize, -entry.Size !.Value); } entry.InvokeEvictionCallbacks(); } }
private void SetEntry(CacheEntry entry) { if (_disposed) { // No-op instead of throwing since this is called during CacheEntry.Dispose return; } if (_options.SizeLimit.HasValue && !entry.Size.HasValue) { throw new InvalidOperationException($"Cache entry must specify a value for {nameof(entry.Size)} when {nameof(_options.SizeLimit)} is set."); } var utcNow = _options.Clock !.UtcNow; DateTimeOffset?absoluteExpiration = null; if (entry._absoluteExpirationRelativeToNow.HasValue) { absoluteExpiration = utcNow + entry._absoluteExpirationRelativeToNow; } else if (entry._absoluteExpiration.HasValue) { absoluteExpiration = entry._absoluteExpiration; } // Applying the option's absolute expiration only if it's not already smaller. // This can be the case if a dependent cache entry has a smaller value, and // it was set by cascading it to its parent. if (absoluteExpiration.HasValue) { if (!entry._absoluteExpiration.HasValue || absoluteExpiration.Value < entry._absoluteExpiration.Value) { entry._absoluteExpiration = absoluteExpiration; } } // Initialize the last access timestamp at the time the entry is added entry.LastAccessed = utcNow; if (_entries.TryGetValue(entry.Key, out CacheEntry? priorEntry)) { priorEntry.SetExpired(EvictionReason.Replaced); } var exceedsCapacity = UpdateCacheSizeExceedsCapacity(entry); if (!entry.CheckExpired(utcNow) && !exceedsCapacity) { var entryAdded = false; if (priorEntry == null) { // Try to add the new entry if no previous entries exist. entryAdded = _entries.TryAdd(entry.Key, entry); } else { // Try to update with the new entry if a previous entries exist. entryAdded = _entries.TryUpdate(entry.Key, entry, priorEntry); if (entryAdded) { if (_options.SizeLimit.HasValue) { // The prior entry was removed, decrease the by the prior entry's size Interlocked.Add(ref _cacheSize, -priorEntry.Size !.Value); } } else { // The update will fail if the previous entry was removed after retrival. // Adding the new entry will succeed only if no entry has been added since. // This guarantees removing an old entry does not prevent adding a new entry. entryAdded = _entries.TryAdd(entry.Key, entry); } } if (entryAdded) { entry.AttachTokens(); } else { if (_options.SizeLimit.HasValue) { // Entry could not be added, reset cache size Interlocked.Add(ref _cacheSize, -entry.Size !.Value); } entry.SetExpired(EvictionReason.Replaced); entry.InvokeEvictionCallbacks(); } if (priorEntry != null) { priorEntry.InvokeEvictionCallbacks(); } } else { if (exceedsCapacity) { // The entry was not added due to overcapacity entry.SetExpired(EvictionReason.Capacity); TriggerOvercapacityCompaction(); } else { if (_options.SizeLimit.HasValue) { // Entry could not be added due to being expired, reset cache size Interlocked.Add(ref _cacheSize, -entry.Size !.Value); } } entry.InvokeEvictionCallbacks(); if (priorEntry != null) { RemoveEntry(priorEntry); } } StartScanForExpiredItems(utcNow); }