// Calling thread need to be first take writelock private bool SetInternal(string key, DiskCacheEntry diskEntry) { if (_inUse.ContainsKey(key)) { return(false); } if (_lruCache.ContainsKey(key)) { return(false); } Volatile.Write(ref diskEntry.InCache, true); diskEntry.Ref(); // for the LRU cache's reference. if (diskEntry.Refs == 1) { _lruCache.Add(key, diskEntry); } else if (diskEntry.Refs > 1) { _inUse.TryAdd(key, diskEntry); } Debug.Assert(diskEntry.Refs > 0, "disk entry should have at least 1 reference"); return(true); }
public async Task <ICacheEntry> GetOrAddAsync(string key, Func <string, Stream, ICacheControl, Task> valueFactory, ICacheControl cacheControl, CancellationToken ct) { using (await _rwLock.ReadLockAsync()) { ICacheEntry found = GetInternal(key); if (!(found is EmptyCacheEntry)) { found.Ref(); // this entry remain valid after releasing the lock return(found); } } using (await _rwLock.WriteLockAsync()) { ICacheEntry found = GetInternal(key); if (!(found is EmptyCacheEntry)) { found.Ref(); // this entry remain valid after releasing the lock return(found); } var randomPath = await _fs.WriteAtomicAsync((sink, cancel) => valueFactory(key, sink, cacheControl), _cacheTmpPath, ct).ConfigureAwait(false); var tmpEntry = new DiskCacheEntry(key, randomPath, this); if (!cacheControl.NoStore) { SetInternal(key, tmpEntry); } var entry = GetInternal(key); entry.Ref(); return(entry); } }
internal void DeleteEntry(DiskCacheEntry diskCacheEntry) { var refs = diskCacheEntry.Refs; Debug.Assert(refs == 0, "disk entry should have 0 reference"); _fs.FileDelete(diskCacheEntry.EntryPath, _cacheTmpPath); diskCacheEntry.EntryPath = ""; }
void FinishErase(string key, DiskCacheEntry entry) { if (entry != null) { Volatile.Write(ref entry.InCache, false); entry.DisposeAsync().Wait(); // not inside LRU cache anymore } }
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; } } } }
internal Stream ReadEntry(DiskCacheEntry diskCacheEntry) { Debug.Assert(diskCacheEntry.Refs > 0, "disk entry should have at least 1 reference"); Debug.Assert(diskCacheEntry.EntryPath != "", "disk entry have been deleted"); return(_fs.FileOpenRead(diskCacheEntry.EntryPath)); }