You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Zero/ZeroLevel/Services/FASTER/Device/StorageDeviceBase.cs

280 lines
11 KiB

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace FASTER.core
{
/// <summary>
///
/// </summary>
public abstract class StorageDeviceBase : IDevice
{
/// <summary>
///
/// </summary>
public uint SectorSize { get; }
/// <summary>
///
/// </summary>
public string FileName { get; }
/// <summary>
/// <see cref="IDevice.Capacity"/>
/// </summary>
public long Capacity { get; }
/// <summary>
/// <see cref="IDevice.StartSegment"/>
/// </summary>
public int StartSegment { get { return startSegment; } }
/// <summary>
/// <see cref="IDevice.EndSegment"/>
/// </summary>
public int EndSegment { get { return endSegment; } }
/// <summary>
/// <see cref="IDevice.SegmentSize"/>
/// </summary>
public long SegmentSize { get { return segmentSize; } }
/// <summary>
/// Segment size
/// </summary>
protected long segmentSize;
private int segmentSizeBits;
private ulong segmentSizeMask;
/// <summary>
/// Instance of the epoch protection framework in the current system.
/// A device may have internal in-memory data structure that requires epoch protection under concurrent access.
/// </summary>
protected LightEpoch epoch;
/// <summary>
/// start and end segment corresponding to <see cref="StartSegment"/> and <see cref="EndSegment"/>. Subclasses are
/// allowed to modify these as needed.
/// </summary>
protected int startSegment = 0, endSegment = -1;
/// <summary>
/// Initializes a new StorageDeviceBase
/// </summary>
/// <param name="filename">Name of the file to use</param>
/// <param name="sectorSize">The smallest unit of write of the underlying storage device (e.g. 512 bytes for a disk) </param>
/// <param name="capacity">The maximal number of bytes this storage device can accommondate, or CAPAPCITY_UNSPECIFIED if there is no such limit </param>
public StorageDeviceBase(string filename, uint sectorSize, long capacity)
{
FileName = filename;
SectorSize = sectorSize;
segmentSize = -1;
segmentSizeBits = 64;
segmentSizeMask = ~0UL;
Capacity = capacity;
}
/// <summary>
/// Initialize device
/// </summary>
/// <param name="segmentSize"></param>
/// <param name="epoch"></param>
public virtual void Initialize(long segmentSize, LightEpoch epoch = null)
{
Debug.Assert(Capacity == -1 || Capacity % segmentSize == 0, "capacity must be a multiple of segment sizes");
this.segmentSize = segmentSize;
this.epoch = epoch;
if (!Utility.IsPowerOfTwo(segmentSize))
{
if (segmentSize != -1)
throw new Exception("Invalid segment size: " + segmentSize);
segmentSizeBits = 64;
segmentSizeMask = ~0UL;
}
else
{
segmentSizeBits = Utility.GetLogBase2((ulong)segmentSize);
segmentSizeMask = (ulong)segmentSize - 1;
}
}
/// <summary>
///
/// </summary>
/// <param name="alignedSourceAddress"></param>
/// <param name="alignedDestinationAddress"></param>
/// <param name="numBytesToWrite"></param>
/// <param name="callback"></param>
/// <param name="asyncResult"></param>
public void WriteAsync(IntPtr alignedSourceAddress, ulong alignedDestinationAddress, uint numBytesToWrite, IOCompletionCallback callback, IAsyncResult asyncResult)
{
int segment = (int)(segmentSizeBits < 64 ? alignedDestinationAddress >> segmentSizeBits : 0);
// If the device has bounded space, and we are writing a new segment, need to check whether an existing segment needs to be evicted.
if (Capacity != Devices.CAPACITY_UNSPECIFIED && Utility.MonotonicUpdate(ref endSegment, segment, out int oldEnd))
{
// Attempt to update the stored range until there are enough space on the tier to accomodate the current logTail
int newStartSegment = endSegment - (int)(Capacity >> segmentSizeBits);
// Assuming that we still have enough physical capacity to write another segment, even if delete does not immediately free up space.
TruncateUntilSegmentAsync(newStartSegment, r => { }, null);
}
WriteAsync(
alignedSourceAddress,
segment,
alignedDestinationAddress & segmentSizeMask,
numBytesToWrite, callback, asyncResult);
}
/// <summary>
///
/// </summary>
/// <param name="alignedSourceAddress"></param>
/// <param name="alignedDestinationAddress"></param>
/// <param name="aligned_read_length"></param>
/// <param name="callback"></param>
/// <param name="asyncResult"></param>
public void ReadAsync(ulong alignedSourceAddress, IntPtr alignedDestinationAddress, uint aligned_read_length, IOCompletionCallback callback, IAsyncResult asyncResult)
{
var segment = segmentSizeBits < 64 ? alignedSourceAddress >> segmentSizeBits : 0;
ReadAsync(
(int)segment,
alignedSourceAddress & segmentSizeMask,
alignedDestinationAddress,
aligned_read_length, callback, asyncResult);
}
/// <summary>
/// <see cref="IDevice.RemoveSegmentAsync(int, AsyncCallback, IAsyncResult)"/>
/// </summary>
/// <param name="segment"></param>
/// <param name="callback"></param>
/// <param name="result"></param>
public abstract void RemoveSegmentAsync(int segment, AsyncCallback callback, IAsyncResult result);
/// <summary>
/// <see cref="IDevice.RemoveSegment(int)"/>
/// By default the implementation calls into <see cref="RemoveSegmentAsync(int, AsyncCallback, IAsyncResult)"/>
/// </summary>
/// <param name="segment"></param>
public virtual void RemoveSegment(int segment)
{
ManualResetEventSlim completionEvent = new ManualResetEventSlim(false);
RemoveSegmentAsync(segment, r => completionEvent.Set(), null);
completionEvent.Wait();
}
/// <summary>
/// <see cref="IDevice.TruncateUntilSegmentAsync(int, AsyncCallback, IAsyncResult)"/>
/// </summary>
/// <param name="toSegment"></param>
/// <param name="callback"></param>
/// <param name="result"></param>
public void TruncateUntilSegmentAsync(int toSegment, AsyncCallback callback, IAsyncResult result)
{
// Reset begin range to at least toAddress
if (!Utility.MonotonicUpdate(ref startSegment, toSegment, out int oldStart))
{
// If no-op, invoke callback and return immediately
callback(result);
return;
}
CountdownEvent countdown = new CountdownEvent(toSegment - oldStart);
// This action needs to be epoch-protected because readers may be issuing reads to the deleted segment, unaware of the delete.
// Because of earlier compare-and-swap, the caller has exclusive access to the range [oldStartSegment, newStartSegment), and there will
// be no double deletes.
epoch.BumpCurrentEpoch(() =>
{
for (int i = oldStart; i < toSegment; i++)
{
RemoveSegmentAsync(i, r => {
if (countdown.Signal())
{
callback(r);
countdown.Dispose();
}
}, result);
}
});
}
/// <summary>
/// <see cref="IDevice.TruncateUntilSegment(int)"/>
/// </summary>
/// <param name="toSegment"></param>
public void TruncateUntilSegment(int toSegment)
{
using (ManualResetEventSlim completionEvent = new ManualResetEventSlim(false))
{
TruncateUntilSegmentAsync(toSegment, r => completionEvent.Set(), null);
completionEvent.Wait();
}
}
/// <summary>
/// <see cref="IDevice.TruncateUntilAddressAsync(long, AsyncCallback, IAsyncResult)"/>
/// </summary>
/// <param name="toAddress"></param>
/// <param name="callback"></param>
/// <param name="result"></param>
public virtual void TruncateUntilAddressAsync(long toAddress, AsyncCallback callback, IAsyncResult result)
{
// Truncate only up to segment boundary if address is not aligned
TruncateUntilSegmentAsync((int)(toAddress >> segmentSizeBits), callback, result);
}
/// <summary>
/// <see cref="IDevice.TruncateUntilAddress(long)"/>
/// </summary>
/// <param name="toAddress"></param>
public virtual void TruncateUntilAddress(long toAddress)
{
using (ManualResetEventSlim completionEvent = new ManualResetEventSlim(false))
{
TruncateUntilAddressAsync(toAddress, r => completionEvent.Set(), null);
completionEvent.Wait();
}
}
/// <summary>
///
/// </summary>
/// <param name="sourceAddress"></param>
/// <param name="segmentId"></param>
/// <param name="destinationAddress"></param>
/// <param name="numBytesToWrite"></param>
/// <param name="callback"></param>
/// <param name="asyncResult"></param>
public abstract void WriteAsync(IntPtr sourceAddress, int segmentId, ulong destinationAddress, uint numBytesToWrite, IOCompletionCallback callback, IAsyncResult asyncResult);
/// <summary>
///
/// </summary>
/// <param name="segmentId"></param>
/// <param name="sourceAddress"></param>
/// <param name="destinationAddress"></param>
/// <param name="readLength"></param>
/// <param name="callback"></param>
/// <param name="asyncResult"></param>
public abstract void ReadAsync(int segmentId, ulong sourceAddress, IntPtr destinationAddress, uint readLength, IOCompletionCallback callback, IAsyncResult asyncResult);
/// <summary>
///
/// </summary>
public abstract void Close();
}
}

Powered by TurnKey Linux.