static void SetExpiredManyTimes(CacheEntry cacheEntry) { var now = DateTimeOffset.UtcNow; for (int i = 0; i < 1_000; i++) { cacheEntry.SetExpired(EvictionReason.Expired); // modifies CacheEntry._state Assert.True(cacheEntry.CheckExpired(now)); cacheEntry.Value = cacheEntry; // modifies CacheEntry._state Assert.True(cacheEntry.CheckExpired(now)); cacheEntry.SetExpired(EvictionReason.Expired); // modifies CacheEntry._state Assert.True(cacheEntry.CheckExpired(now)); cacheEntry.Dispose(); // might modify CacheEntry._state Assert.True(cacheEntry.CheckExpired(now)); } }
internal bool CheckForExpiredTokens(CacheEntry cacheEntry) { if (_expirationTokens != null) { for (int i = 0; i < _expirationTokens.Count; i++) { IChangeToken expiredToken = _expirationTokens[i]; if (expiredToken.HasChanged) { cacheEntry.SetExpired(EvictionReason.TokenExpired); return(true); } } } return(false); }
internal 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(SR.Format(SR.CacheEntryHasEmptySize, nameof(entry.Size), nameof(_options.SizeLimit))); } DateTimeOffset utcNow = _options.Clock.UtcNow; // 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 (entry.AbsoluteExpirationRelativeToNow.HasValue) { var absoluteExpiration = utcNow + entry.AbsoluteExpirationRelativeToNow.Value; if (!entry.AbsoluteExpiration.HasValue || absoluteExpiration < entry.AbsoluteExpiration.Value) { entry.AbsoluteExpiration = absoluteExpiration; } } // Initialize the last access timestamp at the time the entry is added entry.LastAccessed = utcNow; CoherentState coherentState = _coherentState; // Clear() can update the reference in the meantime if (coherentState._entries.TryGetValue(entry.Key, out CacheEntry priorEntry)) { priorEntry.SetExpired(EvictionReason.Replaced); } if (entry.CheckExpired(utcNow)) { entry.InvokeEvictionCallbacks(); if (priorEntry != null) { coherentState.RemoveEntry(priorEntry, _options); } StartScanForExpiredItemsIfNeeded(utcNow); return; } bool exceedsCapacity = UpdateCacheSizeExceedsCapacity(entry, coherentState); if (!exceedsCapacity) { bool entryAdded = false; if (priorEntry == null) { // Try to add the new entry if no previous entries exist. entryAdded = coherentState._entries.TryAdd(entry.Key, entry); } else { // Try to update with the new entry if a previous entries exist. entryAdded = coherentState._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 coherentState._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 = coherentState._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 coherentState._cacheSize, -entry.Size.Value); } entry.SetExpired(EvictionReason.Replaced); entry.InvokeEvictionCallbacks(); } if (priorEntry != null) { priorEntry.InvokeEvictionCallbacks(); } } else { entry.SetExpired(EvictionReason.Capacity); TriggerOvercapacityCompaction(); entry.InvokeEvictionCallbacks(); if (priorEntry != null) { coherentState.RemoveEntry(priorEntry, _options); } } StartScanForExpiredItemsIfNeeded(utcNow); }
public object Set(object key, object value, MemoryCacheEntryOptions cacheEntryOptions) { if (key == null) { throw new ArgumentNullException(nameof(key)); } CheckDisposed(); CacheEntry priorEntry = null; var utcNow = _clock.UtcNow; DateTimeOffset?absoluteExpiration = null; if (cacheEntryOptions.AbsoluteExpirationRelativeToNow.HasValue) { absoluteExpiration = utcNow + cacheEntryOptions.AbsoluteExpirationRelativeToNow; } else if (cacheEntryOptions.AbsoluteExpiration.HasValue) { if (cacheEntryOptions.AbsoluteExpiration <= utcNow) { throw new ArgumentOutOfRangeException( nameof(MemoryCacheEntryOptions.AbsoluteExpiration), cacheEntryOptions.AbsoluteExpiration.Value, "The absolute expiration value must be in the future."); } absoluteExpiration = cacheEntryOptions.AbsoluteExpiration; } var entry = new CacheEntry( key, value, utcNow, absoluteExpiration, cacheEntryOptions, _entryExpirationNotification); var link = EntryLinkHelpers.ContextLink; if (link != null) { // Copy expiration tokens and AbsoluteExpiration to the link. // We do this regardless of it gets cached because the tokens are associated with the value we'll return. if (entry.Options.ExpirationTokens != null) { link.AddExpirationTokens(entry.Options.ExpirationTokens); } if (absoluteExpiration.HasValue) { link.SetAbsoluteExpiration(absoluteExpiration.Value); } } bool added = false; _entryLock.EnterWriteLock(); try { if (_entries.TryGetValue(key, out priorEntry)) { _entries.Remove(key); priorEntry.SetExpired(EvictionReason.Replaced); } if (!entry.CheckExpired(utcNow)) { _entries[key] = entry; entry.AttachTokens(); added = true; } } finally { _entryLock.ExitWriteLock(); } if (priorEntry != null) { priorEntry.InvokeEvictionCallbacks(); } if (!added) { entry.InvokeEvictionCallbacks(); } StartScanForExpiredItems(); return(value); }
private void SetEntry(CacheEntry entry) { if (_disposed) { // No-op instead of throwing since this is called during CacheEntry.Dispose return; } var utcNow = _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; CacheEntry priorEntry; if (_entries.TryGetValue(entry.Key, out priorEntry)) { priorEntry.SetExpired(EvictionReason.Replaced); } if (!entry.CheckExpired(utcNow)) { 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) { // 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 { entry.SetExpired(EvictionReason.Replaced); entry.InvokeEvictionCallbacks(); } if (priorEntry != null) { priorEntry.InvokeEvictionCallbacks(); } } else { entry.InvokeEvictionCallbacks(); if (priorEntry != null) { RemoveEntry(priorEntry); } } StartScanForExpiredItems(); }