public ReusableTask <bool> ReadFromCacheAsync(ITorrentData torrent, BlockInfo block, Memory <byte> buffer) { if (torrent == null) { throw new ArgumentNullException(nameof(torrent)); } if (CachedBlocks.TryGetValue(torrent, out List <CachedBlock> blocks)) { for (int i = 0; i < blocks.Count; i++) { var cached = blocks[i]; if (cached.Block != block) { continue; } cached.Buffer.CopyTo(buffer); if (!cached.Flushing) { blocks[i] = cached.SetFlushing(); FlushBlockAsync(torrent, blocks, cached); } Interlocked.Add(ref cacheHits, block.RequestLength); ReadFromCache?.Invoke(this, block); return(ReusableTask.FromResult(true)); } } return(ReusableTask.FromResult(false)); }
public async ReusableTask WriteAsync(ITorrentData torrent, BlockInfo block, Memory <byte> buffer, bool preferSkipCache) { if (preferSkipCache || Capacity < block.RequestLength) { await WriteToFilesAsync(torrent, block, buffer); } else { if (!CachedBlocks.TryGetValue(torrent, out List <CachedBlock> blocks)) { CachedBlocks[torrent] = blocks = new List <CachedBlock> (); } if (CacheUsed > (Capacity - block.RequestLength)) { var firstFlushable = FindFirstFlushable(blocks); if (firstFlushable < 0) { await WriteToFilesAsync(torrent, block, buffer); return; } else { var cached = blocks[firstFlushable]; blocks[firstFlushable] = cached.SetFlushing(); using (cached.BufferReleaser) await WriteToFilesAsync(torrent, cached.Block, cached.Buffer); Interlocked.Add(ref cacheUsed, -cached.Block.RequestLength); blocks.Remove(cached); } } CachedBlock?cache = null; for (int i = 0; i < blocks.Count && !cache.HasValue; i++) { if (blocks[i].Block == block) { cache = blocks[i]; } } if (!cache.HasValue) { var releaser = BufferPool.Rent(block.RequestLength, out Memory <byte> memory); cache = new CachedBlock(block, releaser, memory); blocks.Add(cache.Value); Interlocked.Add(ref cacheUsed, block.RequestLength); } buffer.CopyTo(cache.Value.Buffer); WrittenToCache?.Invoke(this, block); } }
public async ReusableTask WriteAsync(ITorrentData torrent, BlockInfo block, byte[] buffer, bool preferSkipCache) { if (preferSkipCache || Capacity < block.RequestLength) { await WriteToFilesAsync(torrent, block, buffer); } else { if (!CachedBlocks.TryGetValue(torrent, out List <CachedBlock> blocks)) { CachedBlocks[torrent] = blocks = new List <CachedBlock> (); } if (CacheUsed > (Capacity - block.RequestLength)) { var cached = blocks[0]; blocks.RemoveAt(0); Interlocked.Add(ref cacheUsed, -block.RequestLength); using (cached.BufferReleaser) await WriteToFilesAsync(torrent, cached.Block, cached.Buffer); } CachedBlock?cache = null; for (int i = 0; i < blocks.Count && !cache.HasValue; i++) { if (blocks[i].Block == block) { cache = blocks[i]; } } if (!cache.HasValue) { cache = new CachedBlock { Block = block, BufferReleaser = DiskManager.BufferPool.Rent(block.RequestLength, out byte[] _), }; blocks.Add(cache.Value); Interlocked.Add(ref cacheUsed, block.RequestLength); } Buffer.BlockCopy(buffer, 0, cache.Value.Buffer, 0, block.RequestLength); } }
public async ReusableTask <int> ReadAsync(ITorrentData torrent, BlockInfo block, byte[] buffer) { if (torrent == null) { throw new ArgumentNullException(nameof(torrent)); } if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (!CachedBlocks.TryGetValue(torrent, out List <CachedBlock> blocks)) { CachedBlocks[torrent] = blocks = new List <CachedBlock> (); } for (int i = 0; i < blocks.Count; i++) { var cached = blocks[i]; if (cached.Block != block) { continue; } blocks.RemoveAt(i); Interlocked.Add(ref cacheUsed, -block.RequestLength); using (cached.BufferReleaser) { var asyncWrite = WriteToFilesAsync(torrent, block, cached.Buffer); Buffer.BlockCopy(cached.Buffer, 0, buffer, 0, block.RequestLength); Interlocked.Add(ref cacheHits, block.RequestLength); await asyncWrite.ConfigureAwait(false); return(block.RequestLength); } } Interlocked.Add(ref cacheMisses, block.RequestLength); return(await ReadFromFilesAsync(torrent, block, buffer).ConfigureAwait(false)); }