using System.Runtime.InteropServices;
using System.Threading;
using static Ryujinx.Common.Memory.PartialUnmaps.PartialUnmapHelpers;
namespace Ryujinx.Common.Memory.PartialUnmaps
{
///
/// A simple fixed size thread safe map that can be used from native code.
/// Integer thread IDs map to corresponding structs.
///
/// The value type for the map
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ThreadLocalMap where T : unmanaged
{
public const int MapSize = 20;
public Array20 ThreadIds;
public Array20 Structs;
public static readonly int ThreadIdsOffset;
public static readonly int StructsOffset;
///
/// Populates the field offsets for use when emitting native code.
///
static ThreadLocalMap()
{
ThreadLocalMap instance = new();
ThreadIdsOffset = OffsetOf(ref instance, ref instance.ThreadIds);
StructsOffset = OffsetOf(ref instance, ref instance.Structs);
}
///
/// Gets the index of a given thread ID in the map, or reserves one.
/// When reserving a struct, its value is set to the given initial value.
/// Returns -1 when there is no space to reserve a new entry.
///
/// Thread ID to use as a key
/// Initial value of the associated struct.
/// The index of the entry, or -1 if none
public int GetOrReserve(int threadId, T initial)
{
// Try get a match first.
for (int i = 0; i < MapSize; i++)
{
int compare = Interlocked.CompareExchange(ref ThreadIds[i], threadId, threadId);
if (compare == threadId)
{
return i;
}
}
// Try get a free entry. Since the id is assumed to be unique to this thread, we know it doesn't exist yet.
for (int i = 0; i < MapSize; i++)
{
int compare = Interlocked.CompareExchange(ref ThreadIds[i], threadId, 0);
if (compare == 0)
{
Structs[i] = initial;
return i;
}
}
return -1;
}
///
/// Gets the struct value for a given map entry.
///
/// Index of the entry
/// A reference to the struct value
public ref T GetValue(int index)
{
return ref Structs[index];
}
///
/// Releases an entry from the map.
///
/// Index of the entry to release
public void Release(int index)
{
Interlocked.Exchange(ref ThreadIds[index], 0);
}
}
}