public async Task ReleaseAsync(DiskCacheEntry entry) { int refCount; // If InCache == false this is called from FinishErase with _lock already held and cause deadlock if (Volatile.Read(ref entry.InCache)) { using (await _rwLock.WriteLockAsync()) { // entry is in "_inUse" and refcount is between 1 .. N // Call Unref inside the lock to avoid other thread incrementing concurrently refCount = entry.Unref(); if (refCount == 1) { // Entry is still in cache but no longer in use. if (_inUse.TryRemove(entry.Key, out _)) { _lruCache.Add(entry.Key, entry); } } if (refCount <= 0) { _inUse.TryRemove(entry.Key, out _); entry.Delete(); return; } } } else { // its safe to decrment unref because Entry is not in cache anymore and refcount can only decrease now if (entry.Refs > 0) { refCount = entry.Unref(); if (refCount == 0) { _inUse.TryRemove(entry.Key, out _); entry.Delete(); return; } } } }
/// <summary>valueFactory /// Get item from cache of call valueFactory if missing /// </summary> /// <param name="key">The key of the value that need to be filled in</param> /// <param name="valueFactory">valueFactory should write result to the stream but not close it</param> /// <param name="ct">Cancellation token</param> /// <returns></returns> public Task <ICacheEntry> GetOrAddAsync(string key, Func <string, Stream, ICacheControl, Task> valueFactory, ICacheControl cacheControl, CancellationToken ct) { return(createEntry.DoAsync(key, async() => { ICacheEntry found = await GetAsync(key, ct).ConfigureAwait(false); if (!(found is EmptyCacheEntry)) { return found; } ArraySegment <byte> buffer; using (var memStream = new MemoryStream()) { await valueFactory(key, memStream, cacheControl).ConfigureAwait(false); memStream.Position = 0; buffer = new ArraySegment <byte>(memStream.ToArray()); } if (!cacheControl.NoStore) { _cache.Add(key, buffer, (ulong)buffer.Count); } return new MemoryCacheEntry(buffer); })); }