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/Allocator/VarLenBlittableScanIterator.cs

229 lines
8.0 KiB

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
using System.Threading;
using System.Diagnostics;
namespace FASTER.core
{
/// <summary>
/// Scan iterator for hybrid log
/// </summary>
public class VariableLengthBlittableScanIterator<Key, Value> : IFasterScanIterator<Key, Value>
where Key : new()
where Value : new()
{
private readonly int frameSize;
private readonly VariableLengthBlittableAllocator<Key, Value> hlog;
private readonly long beginAddress, endAddress;
private readonly BlittableFrame frame;
private readonly CountdownEvent[] loaded;
private bool first = true;
private long currentAddress, nextAddress;
private long currentPhysicalAddress;
/// <summary>
/// Current address
/// </summary>
public long CurrentAddress => currentAddress;
/// <summary>
/// Constructor
/// </summary>
/// <param name="hlog"></param>
/// <param name="beginAddress"></param>
/// <param name="endAddress"></param>
/// <param name="scanBufferingMode"></param>
public unsafe VariableLengthBlittableScanIterator(VariableLengthBlittableAllocator<Key, Value> hlog, long beginAddress, long endAddress, ScanBufferingMode scanBufferingMode)
{
this.hlog = hlog;
if (beginAddress == 0)
beginAddress = hlog.GetFirstValidLogicalAddress(0);
this.beginAddress = beginAddress;
this.endAddress = endAddress;
currentAddress = -1;
nextAddress = beginAddress;
if (scanBufferingMode == ScanBufferingMode.SinglePageBuffering)
frameSize = 1;
else if (scanBufferingMode == ScanBufferingMode.DoublePageBuffering)
frameSize = 2;
else if (scanBufferingMode == ScanBufferingMode.NoBuffering)
{
frameSize = 0;
return;
}
frame = new BlittableFrame(frameSize, hlog.PageSize, hlog.GetDeviceSectorSize());
loaded = new CountdownEvent[frameSize];
// Only load addresses flushed to disk
if (nextAddress < hlog.HeadAddress)
{
var frameNumber = (nextAddress >> hlog.LogPageSizeBits) % frameSize;
hlog.AsyncReadPagesFromDeviceToFrame
(nextAddress >> hlog.LogPageSizeBits,
1, endAddress, AsyncReadPagesCallback, Empty.Default,
frame, out loaded[frameNumber]);
}
}
/// <summary>
/// Gets reference to current key
/// </summary>
/// <returns></returns>
public ref Key GetKey()
{
return ref hlog.GetKey(currentPhysicalAddress);
}
/// <summary>
/// Gets reference to current value
/// </summary>
/// <returns></returns>
public ref Value GetValue()
{
return ref hlog.GetValue(currentPhysicalAddress);
}
/// <summary>
/// Get next record in iterator
/// </summary>
/// <param name="recordInfo"></param>
/// <returns></returns>
public bool GetNext(out RecordInfo recordInfo)
{
recordInfo = default(RecordInfo);
currentAddress = nextAddress;
while (true)
{
// Check for boundary conditions
if (currentAddress >= endAddress)
{
return false;
}
if (currentAddress < hlog.BeginAddress)
{
throw new Exception("Iterator address is less than log BeginAddress " + hlog.BeginAddress);
}
if (frameSize == 0 && currentAddress < hlog.HeadAddress)
{
throw new Exception("Iterator address is less than log HeadAddress in memory-scan mode");
}
var currentPage = currentAddress >> hlog.LogPageSizeBits;
var offset = currentAddress & hlog.PageSizeMask;
if (currentAddress < hlog.HeadAddress)
BufferAndLoad(currentAddress, currentPage, currentPage % frameSize);
var physicalAddress = default(long);
if (currentAddress >= hlog.HeadAddress)
physicalAddress = hlog.GetPhysicalAddress(currentAddress);
else
physicalAddress = frame.GetPhysicalAddress(currentPage % frameSize, offset);
// Check if record fits on page, if not skip to next page
var recordSize = hlog.GetRecordSize(physicalAddress);
if ((currentAddress & hlog.PageSizeMask) + recordSize > hlog.PageSize)
{
currentAddress = (1 + (currentAddress >> hlog.LogPageSizeBits)) << hlog.LogPageSizeBits;
continue;
}
ref var info = ref hlog.GetInfo(physicalAddress);
if (info.Invalid || info.IsNull())
{
currentAddress += recordSize;
continue;
}
currentPhysicalAddress = physicalAddress;
recordInfo = info;
nextAddress = currentAddress + recordSize;
return true;
}
}
/// <summary>
/// Get next record in iterator
/// </summary>
/// <param name="recordInfo"></param>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public bool GetNext(out RecordInfo recordInfo, out Key key, out Value value)
{
throw new NotSupportedException("Use GetNext(out RecordInfo) to retrieve references to key/value");
}
/// <summary>
/// Dispose the iterator
/// </summary>
public void Dispose()
{
frame?.Dispose();
}
private unsafe void BufferAndLoad(long currentAddress, long currentPage, long currentFrame)
{
if (first || (currentAddress & hlog.PageSizeMask) == 0)
{
// Prefetch pages based on buffering mode
if (frameSize == 1)
{
if (!first)
{
hlog.AsyncReadPagesFromDeviceToFrame(currentAddress >> hlog.LogPageSizeBits, 1, endAddress, AsyncReadPagesCallback, Empty.Default, frame, out loaded[currentFrame]);
}
}
else
{
var endPage = endAddress >> hlog.LogPageSizeBits;
if ((endPage > currentPage) &&
((endPage > currentPage + 1) || ((endAddress & hlog.PageSizeMask) != 0)))
{
hlog.AsyncReadPagesFromDeviceToFrame(1 + (currentAddress >> hlog.LogPageSizeBits), 1, endAddress, AsyncReadPagesCallback, Empty.Default, frame, out loaded[(currentPage + 1) % frameSize]);
}
}
first = false;
}
loaded[currentFrame].Wait();
}
private unsafe void AsyncReadPagesCallback(uint errorCode, uint numBytes, NativeOverlapped* overlap)
{
if (errorCode != 0)
{
Trace.TraceError("OverlappedStream GetQueuedCompletionStatus error: {0}", errorCode);
}
var result = (PageAsyncReadResult<Empty>)Overlapped.Unpack(overlap).AsyncResult;
if (result.freeBuffer1 != null)
{
hlog.PopulatePage(result.freeBuffer1.GetValidPointer(), result.freeBuffer1.required_bytes, result.page);
result.freeBuffer1.Return();
result.freeBuffer1 = null;
}
if (result.handle != null)
{
result.handle.Signal();
}
Interlocked.MemoryBarrier();
Overlapped.Free(overlap);
}
}
}

Powered by TurnKey Linux.