private void RemoveEntry(CacheEntry entry) { _entryLock.EnterWriteLock(); try { // Only remove it if someone hasn't modified it since our lookup CacheEntry currentEntry; if (_entries.TryGetValue(entry.Key, out currentEntry) && object.ReferenceEquals(currentEntry, entry)) { _entries.Remove(entry.Key); } } finally { _entryLock.ExitWriteLock(); } entry.InvokeEvictionCallbacks(); }
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 EntryExpired(CacheEntry entry) { // TODO: For efficiency consider processing these expirations in batches. RemoveEntry(entry); StartScanForExpiredItems(); }
public object Set([NotNull] string key, IEntryLink link, object state, [NotNull] Func<ICacheSetContext, object> create) { CheckDisposed(); CacheEntry priorEntry = null; var now = _clock.UtcNow; var context = new CacheSetContext(key) { State = state, CreationTime = now }; object value = create(context); var entry = new CacheEntry(context, value, _entryExpirationNotification); bool added = false; if (link != null) { // Copy triggers and AbsoluteExpiration to the link. // We do this regardless of it gets cached because the triggers are associated with the value we'll return. if (entry.Context.Triggers != null) { link.AddExpirationTriggers(entry.Context.Triggers); } if (entry.Context.AbsoluteExpiration.HasValue) { link.SetAbsoluteExpiration(entry.Context.AbsoluteExpiration.Value); } } _entryLock.EnterWriteLock(); try { if (_entries.TryGetValue(key, out priorEntry)) { _entries.Remove(key); priorEntry.SetExpired(EvictionReason.Replaced); } if (!entry.CheckExpired(now)) { _entries[key] = entry; entry.AttachTriggers(); added = true; } } finally { _entryLock.ExitWriteLock(); } if (priorEntry != null) { priorEntry.InvokeEvictionCallbacks(); } if (!added) { entry.InvokeEvictionCallbacks(); } StartScanForExpiredItems(); return value; }
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); }
public bool TryGetValue(object key, out object value) { if (key == null) { throw new ArgumentNullException(nameof(key)); } value = null; CacheEntry expiredEntry = null; bool found = false; CheckDisposed(); _entryLock.EnterReadLock(); try { CacheEntry entry; if (_entries.TryGetValue(key, out entry)) { // Check if expired due to expiration tokens, timers, etc. and if so, remove it. if (entry.CheckExpired(_clock.UtcNow)) { expiredEntry = entry; } else { // Refresh sliding expiration, etc. entry.LastAccessed = _clock.UtcNow; value = entry.Value; found = true; var link = EntryLinkHelpers.ContextLink; if (link != null) { // Copy expiration tokens and AbsoluteExpiration to the link if (entry.Options.ExpirationTokens != null) { link.AddExpirationTokens(entry.Options.ExpirationTokens); } if (entry.Options.AbsoluteExpiration.HasValue) { link.SetAbsoluteExpiration(entry.Options.AbsoluteExpiration.Value); } } } } } finally { _entryLock.ExitReadLock(); } if (expiredEntry != null) { // TODO: For efficiency queue this up for batch removal RemoveEntry(expiredEntry); } StartScanForExpiredItems(); return(found); }
private static void InvokeCallbacks(CacheEntry entry) { var callbackRegistrations = entry.PostEvictionCallbacks; entry.PostEvictionCallbacks = null; if (callbackRegistrations == null) { return; } for (int i = 0; i < callbackRegistrations.Count; i++) { var registration = callbackRegistrations[i]; try { registration.EvictionCallback?.Invoke(entry.Key, entry.Value, entry.EvictionReason, registration.State); } catch (Exception) { // This will be invoked on a background thread, don't let it throw. // TODO: LOG } } }