// 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; } } }