aboutsummaryrefslogtreecommitdiff
path: root/lib/VNLib.Data.Caching.ObjectCache/src/BlobItem.cs
blob: 728875ff6bbe4793be5b613abc5f52c1b23e7c91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/*
* Copyright (c) 2022 Vaughn Nugent
* 
* Library: VNLib
* Package: VNLib.Data.Caching.ObjectCache
* File: BlobItem.cs 
*
* BlobItem.cs is part of VNLib.Data.Caching.ObjectCache which is part of the larger 
* VNLib collection of libraries and utilities.
*
* VNLib.Data.Caching.ObjectCache is free software: you can redistribute it and/or modify 
* it under the terms of the GNU Affero General Public License as 
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* VNLib.Data.Caching.ObjectCache 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.  If not, see https://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;

namespace VNLib.Data.Caching
{
    /// <summary>
    /// A general purpose binary storage item 
    /// </summary>
    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;

        /// <summary>
        /// The time the blob was last modified
        /// </summary>
        public DateTimeOffset LastAccessed { get; private set; }
     

        /// <summary>
        /// Gets the current size of the file (in bytes) as an atomic operation
        /// </summary>
        public int FileSize => (int)_loadedData.Length;
        /// <summary>
        /// The operation synchronization lock
        /// </summary>
        public AsyncReaderWriterLock OpLock { get; }
        /// <summary>
        /// Initializes a new <see cref="BlobItem"/>
        /// </summary>
        /// <param name="heap">The heap to allocate buffers from</param>
        internal BlobItem(IUnmangedHeap heap)
        {
            _loadedData = new(heap);
            OpLock = new AsyncReaderWriterLock(JTX);
            _loaded = true;
            LastAccessed = DateTimeOffset.UtcNow;
        }
        ///<inheritdoc/>
        protected override void Free()
        {
            _loadedData.Dispose();
            OpLock.Dispose();
        }

        /// <summary>
        /// Reads data from the internal buffer and copies it to the specified buffer.
        /// Use the <see cref="FileSize"/> property to obtain the size of the internal buffer
        /// </summary>
        /// <param name="buffer">The buffer to copy data to</param>
        /// <returns>When completed, the number of bytes copied to the buffer</returns>
        public int Read(Span<byte> 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;
        }
        /// <summary>
        /// Overwrites the internal buffer with the contents of the supplied buffer
        /// </summary>
        /// <param name="buffer">The buffer containing data to store within the blob</param>
        /// <returns>A <see cref="ValueTask"/> that completes when write access has been granted and copied</returns>
        /// <exception cref="InvalidOperationException"></exception>
        public void Write(ReadOnlySpan<byte> 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;
        }

        /// <summary>
        /// Writes the contents of the memory buffer to its designated file on the disk
        /// </summary>
        /// <param name="heap">The heap to allocate buffers from</param>
        /// <param name="swapDir">The <see cref="IsolatedStorageDirectory"/> that stores the file</param>
        /// <param name="filename">The name of the file to write data do</param>
        /// <param name="log">A log to write errors to</param>
        /// <returns>A task that completes when the swap to disk is complete</returns>
        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");
            }
        }
        /// <summary>
        /// Reads the contents of the blob into a memory buffer from its designated file on disk
        /// </summary>
        /// <param name="heap">The heap to allocate buffers from</param>
        /// <param name="swapDir">The <see cref="IsolatedStorageDirectory"/> that stores the file</param>
        /// <param name="filename">The name of the file to write the blob data to</param>
        /// <param name="log">A log to write errors to</param>
        /// <returns>A task that completes when the swap from disk is complete</returns>
        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");
            }
        }
        */
    }
}