/* * Copyright (c) 2022 Vaughn Nugent * * Library: VNLib * Package: VNLib.Data.Caching * File: BlobItem.cs * * BlobItem.cs is part of VNLib.Data.Caching which is part of the larger * VNLib collection of libraries and utilities. * * VNLib.Data.Caching is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 2 of the License, * or (at your option) any later version. * * VNLib.Data.Caching is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VNLib.Data.Caching. If not, see http://www.gnu.org/licenses/. */ using System; using System.IO; using System.Threading; using System.Threading.Tasks; using VNLib.Utils; using VNLib.Utils.IO; using VNLib.Utils.Memory; using VNLib.Utils.Logging; using VNLib.Utils.Extensions; #nullable enable namespace VNLib.Data.Caching { /// /// A general purpose binary storage item /// public class BlobItem //: VnDisposeable { /* private static readonly JoinableTaskContext JTX = new(); private static readonly Semaphore CentralSwapLock = new(Environment.ProcessorCount, Environment.ProcessorCount); private readonly VnMemoryStream _loadedData; private bool _loaded; /// /// The time the blob was last modified /// public DateTimeOffset LastAccessed { get; private set; } /// /// Gets the current size of the file (in bytes) as an atomic operation /// public int FileSize => (int)_loadedData.Length; /// /// The operation synchronization lock /// public AsyncReaderWriterLock OpLock { get; } /// /// Initializes a new /// /// The heap to allocate buffers from internal BlobItem(IUnmangedHeap heap) { _loadedData = new(heap); OpLock = new AsyncReaderWriterLock(JTX); _loaded = true; LastAccessed = DateTimeOffset.UtcNow; } /// protected override void Free() { _loadedData.Dispose(); OpLock.Dispose(); } /// /// Reads data from the internal buffer and copies it to the specified buffer. /// Use the property to obtain the size of the internal buffer /// /// The buffer to copy data to /// When completed, the number of bytes copied to the buffer public int Read(Span buffer) { //Make sure the blob has been swapped back into memory if (!_loaded) { throw new InvalidOperationException("The blob was not loaded from the disk"); } //Read all data from the buffer and write it to the output buffer _loadedData.AsSpan().CopyTo(buffer); //Update last-accessed LastAccessed = DateTimeOffset.UtcNow; return (int)_loadedData.Length; } /// /// Overwrites the internal buffer with the contents of the supplied buffer /// /// The buffer containing data to store within the blob /// A that completes when write access has been granted and copied /// public void Write(ReadOnlySpan buffer) { //Make sure the blob has been swapped back into memory if (!_loaded) { throw new InvalidOperationException("The blob was not loaded from the disk"); } //Reset the buffer _loadedData.SetLength(buffer.Length); _loadedData.Seek(0, SeekOrigin.Begin); _loadedData.Write(buffer); LastAccessed = DateTimeOffset.UtcNow; } /// /// Writes the contents of the memory buffer to its designated file on the disk /// /// The heap to allocate buffers from /// The that stores the file /// The name of the file to write data do /// A log to write errors to /// A task that completes when the swap to disk is complete internal async Task SwapToDiskAsync(IUnmangedHeap heap, DirectoryInfo swapDir, string filename, ILogProvider log) { try { //Wait for write lock await using (AsyncReaderWriterLock.Releaser releaser = await OpLock.WriteLockAsync()) { //Enter swap lock await CentralSwapLock; try { //Open swap file data stream await using FileStream swapFile = swapDir.OpenFile(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, bufferSize: 8128); //reset swap file swapFile.SetLength(0); //Seek loaded-data back to 0 before writing _loadedData.Seek(0, SeekOrigin.Begin); //Write loaded data to disk await _loadedData.CopyToAsync(swapFile, 8128, heap); } finally { CentralSwapLock.Release(); } //Release memory held by stream _loadedData.SetLength(0); //Clear loaded flag _loaded = false; LastAccessed = DateTimeOffset.UtcNow; } log.Debug("Blob {name} swapped to disk", filename); } catch(Exception ex) { log.Error(ex, "Blob swap to disk error"); } } /// /// Reads the contents of the blob into a memory buffer from its designated file on disk /// /// The heap to allocate buffers from /// The that stores the file /// The name of the file to write the blob data to /// A log to write errors to /// A task that completes when the swap from disk is complete internal async Task SwapFromDiskAsync(IUnmangedHeap heap, DirectoryInfo swapDir, string filename, ILogProvider log) { try { //Wait for write lock await using (AsyncReaderWriterLock.Releaser releaser = await OpLock.WriteLockAsync()) { //Enter swap lock await CentralSwapLock; try { //Open swap file data stream await using FileStream swapFile = swapDir.OpenFile(filename, FileMode.OpenOrCreate, FileAccess.Read, bufferSize:8128); //Copy from disk to memory await swapFile.CopyToAsync(_loadedData, 8128, heap); } finally { CentralSwapLock.Release(); } //Set loaded flag _loaded = true; LastAccessed = DateTimeOffset.UtcNow; } log.Debug("Blob {name} swapped from disk", filename); } catch(Exception ex) { log.Error(ex, "Blob swap from disk error"); } } */ } }