|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
|
// Licensed under the MIT license.
|
|
|
|
|
|
|
|
|
|
#pragma warning disable 1591
|
|
|
|
|
|
|
|
|
|
//#define RECORD_INFO_WITH_PIN_COUNT
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
|
|
|
|
namespace FASTER.core
|
|
|
|
|
{
|
|
|
|
|
#if RECORD_INFO_WITH_PIN_COUNT
|
|
|
|
|
[StructLayout(LayoutKind.Explicit, Size = 12)]
|
|
|
|
|
#else
|
|
|
|
|
[StructLayout(LayoutKind.Explicit, Size = 8)]
|
|
|
|
|
#endif
|
|
|
|
|
public unsafe struct RecordInfo
|
|
|
|
|
{
|
|
|
|
|
public const int kFinalBitOffset = 48;
|
|
|
|
|
|
|
|
|
|
public const int kTombstoneBitOffset = 49;
|
|
|
|
|
|
|
|
|
|
public const int kInvalidBitOffset = 50;
|
|
|
|
|
|
|
|
|
|
public const int kVersionBits = 13;
|
|
|
|
|
|
|
|
|
|
public const int kVersionShiftInWord = 51;
|
|
|
|
|
|
|
|
|
|
public const long kVersionMaskInWord = ((1L << kVersionBits) - 1) << kVersionShiftInWord;
|
|
|
|
|
|
|
|
|
|
public const long kVersionMaskInInteger = (1L << kVersionBits) - 1;
|
|
|
|
|
|
|
|
|
|
public const long kPreviousAddressMask = (1L << 48) - 1;
|
|
|
|
|
|
|
|
|
|
public const long kFinalBitMask = (1L << kFinalBitOffset);
|
|
|
|
|
|
|
|
|
|
public const long kTombstoneMask = (1L << kTombstoneBitOffset);
|
|
|
|
|
|
|
|
|
|
public const long kInvalidBitMask = (1L << kInvalidBitOffset);
|
|
|
|
|
|
|
|
|
|
#if RECORD_INFO_WITH_PIN_COUNT
|
|
|
|
|
public const int kTotalSizeInBytes = sizeof(long) + sizeof(int);
|
|
|
|
|
|
|
|
|
|
public const int kTotalBits = kTotalSizeInBytes * 8;
|
|
|
|
|
|
|
|
|
|
[FieldOffset(0)]
|
|
|
|
|
private long word;
|
|
|
|
|
|
|
|
|
|
[FieldOffset(sizeof(long))]
|
|
|
|
|
private int access_data;
|
|
|
|
|
|
|
|
|
|
public static void WriteInfo(RecordInfo* info, int checkpointVersion, bool final, bool tombstone, bool invalidBit, long previousAddress)
|
|
|
|
|
{
|
|
|
|
|
info->word = default(long);
|
|
|
|
|
info->Final = final;
|
|
|
|
|
info->Tombstone = tombstone;
|
|
|
|
|
info->Invalid = invalidBit;
|
|
|
|
|
info->PreviousAddress = previousAddress;
|
|
|
|
|
info->Version = checkpointVersion;
|
|
|
|
|
info->access_data = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
public bool TryPin()
|
|
|
|
|
{
|
|
|
|
|
return Interlocked.Increment(ref access_data) > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
public bool TryMarkReadOnly()
|
|
|
|
|
{
|
|
|
|
|
return Interlocked.CompareExchange(ref access_data, int.MinValue, 0) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
public void MarkReadOnly()
|
|
|
|
|
{
|
|
|
|
|
var found_value = Interlocked.CompareExchange(ref access_data, int.MinValue, 0);
|
|
|
|
|
if (found_value != 0)
|
|
|
|
|
{
|
|
|
|
|
int num_iterations = 1000;
|
|
|
|
|
Thread.SpinWait(num_iterations);
|
|
|
|
|
while (Interlocked.CompareExchange(ref access_data, int.MinValue, 0) != 0)
|
|
|
|
|
{
|
|
|
|
|
Thread.SpinWait(num_iterations);
|
|
|
|
|
num_iterations <<= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
public void Unpin()
|
|
|
|
|
{
|
|
|
|
|
Interlocked.Decrement(ref access_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
public const int kTotalSizeInBytes = sizeof(long);
|
|
|
|
|
|
|
|
|
|
public const int kTotalBits = kTotalSizeInBytes * 8;
|
|
|
|
|
|
|
|
|
|
[FieldOffset(0)]
|
|
|
|
|
private long word;
|
|
|
|
|
|
|
|
|
|
public static void WriteInfo(ref RecordInfo info, int checkpointVersion, bool final, bool tombstone, bool invalidBit, long previousAddress)
|
|
|
|
|
{
|
|
|
|
|
info.word = default(long);
|
|
|
|
|
info.Final = final;
|
|
|
|
|
info.Tombstone = tombstone;
|
|
|
|
|
info.Invalid = invalidBit;
|
|
|
|
|
info.PreviousAddress = previousAddress;
|
|
|
|
|
info.Version = checkpointVersion;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string ToString(RecordInfo* info)
|
|
|
|
|
{
|
|
|
|
|
return "RecordHeader Word = " + info->word;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
public bool TryPin()
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
public bool TryMarkReadOnly()
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
public void MarkReadOnly()
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
public void Unpin()
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
public bool IsNull()
|
|
|
|
|
{
|
|
|
|
|
return word == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Tombstone
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return (word & kTombstoneMask) > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value)
|
|
|
|
|
{
|
|
|
|
|
word |= kTombstoneMask;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
word &= ~kTombstoneMask;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Final
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return (word & kFinalBitMask) > 0;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value)
|
|
|
|
|
{
|
|
|
|
|
word |= kFinalBitMask;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
word &= ~kFinalBitMask;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Invalid
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return !((word & kInvalidBitMask) > 0);
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value)
|
|
|
|
|
{
|
|
|
|
|
word &= ~kInvalidBitMask;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
word |= kInvalidBitMask;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int Version
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return (int)(((word & kVersionMaskInWord) >> kVersionShiftInWord) & kVersionMaskInInteger);
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
word &= ~kVersionMaskInWord;
|
|
|
|
|
word |= ((value & kVersionMaskInInteger) << kVersionShiftInWord);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public long PreviousAddress
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return (word & kPreviousAddressMask);
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
word &= ~kPreviousAddressMask;
|
|
|
|
|
word |= (value & kPreviousAddressMask);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
public static int GetLength()
|
|
|
|
|
{
|
|
|
|
|
return kTotalSizeInBytes;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|