fix thread safety of Pools, minor FishTable improvements
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -12,6 +12,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using FisheryLib.FunctionPointers;
|
using FisheryLib.FunctionPointers;
|
||||||
|
using FisheryLib.Pools;
|
||||||
using FisheryLib.Utility;
|
using FisheryLib.Utility;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
@@ -227,14 +228,14 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
if (IsTail(bucket, bucketIndex))
|
if (IsTail(bucket, bucketIndex))
|
||||||
{
|
{
|
||||||
var previousEntry = bucket;
|
var previousEntry = bucket;
|
||||||
var tailingEntries = TryGetAndClearTailingEntries(bucketIndex);
|
using var tailingEntries = TryGetAndClearTailingEntries(bucketIndex);
|
||||||
|
|
||||||
SetParentTail(bucketIndex, Tails.SOLO);
|
SetParentTail(bucketIndex, Tails.SOLO);
|
||||||
SetEntryWithoutTail(bucketIndex, entry);
|
SetEntryWithoutTail(bucketIndex, entry);
|
||||||
InsertEntry(previousEntry, ReplaceBehaviour.Throw, true);
|
InsertEntry(previousEntry, ReplaceBehaviour.Throw, true);
|
||||||
|
|
||||||
if (tailingEntries != null)
|
if (tailingEntries != default)
|
||||||
InsertEntries(tailingEntries, ReplaceBehaviour.Throw, true);
|
InsertEntries(tailingEntries.List, ReplaceBehaviour.Throw, true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -245,9 +246,9 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InsertEntries(T[] tailingEntries, ReplaceBehaviour replaceBehaviour, bool shifting = false)
|
private void InsertEntries(List<T> tailingEntries, ReplaceBehaviour replaceBehaviour, bool shifting = false)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < tailingEntries.Length; i++)
|
for (var i = 0; i < tailingEntries.Count; i++)
|
||||||
InsertEntry(tailingEntries[i], replaceBehaviour, shifting);
|
InsertEntry(tailingEntries[i], replaceBehaviour, shifting);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,32 +330,32 @@ public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
|
|||||||
}
|
}
|
||||||
|
|
||||||
var previousEntry = _buckets[bucketIndex];
|
var previousEntry = _buckets[bucketIndex];
|
||||||
var tailingEntries = TryGetAndClearTailingEntries(bucketIndex);
|
using var tailingEntries = TryGetAndClearTailingEntries(bucketIndex);
|
||||||
|
|
||||||
SetParentTail(bucketIndex, Tails.SOLO);
|
SetParentTail(bucketIndex, Tails.SOLO);
|
||||||
SetEntryAsTail(bucketIndex, entry, parentIndex, offset);
|
SetEntryAsTail(bucketIndex, entry, parentIndex, offset);
|
||||||
|
|
||||||
InsertEntry(previousEntry!, ReplaceBehaviour.Throw, true);
|
InsertEntry(previousEntry!, ReplaceBehaviour.Throw, true);
|
||||||
|
|
||||||
if (tailingEntries != null)
|
if (tailingEntries != default)
|
||||||
InsertEntries(tailingEntries, ReplaceBehaviour.Throw, true);
|
InsertEntries(tailingEntries.List, ReplaceBehaviour.Throw, true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private T[]? TryGetAndClearTailingEntries(int bucketIndex)
|
private PooledIList<List<T>> TryGetAndClearTailingEntries(int bucketIndex)
|
||||||
{
|
{
|
||||||
if (!HasTail(bucketIndex))
|
if (!HasTail(bucketIndex))
|
||||||
return null;
|
return default;
|
||||||
|
|
||||||
var tailingEntries = new T[GetTailCount(bucketIndex)];
|
var tailingEntries = new PooledIList<List<T>>();
|
||||||
var i = 0;
|
var tailingEntriesList = tailingEntries.List;
|
||||||
var tailIndex = GetTailIndex(bucketIndex);
|
var tailIndex = GetTailIndex(bucketIndex);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
ref var tailingBucket = ref _buckets[tailIndex];
|
ref var tailingBucket = ref _buckets[tailIndex];
|
||||||
tailingEntries[i++] = tailingBucket!;
|
tailingEntriesList.Add(tailingBucket!);
|
||||||
tailingBucket = default;
|
tailingBucket = default;
|
||||||
|
|
||||||
if (HasTail(tailIndex))
|
if (HasTail(tailIndex))
|
||||||
|
|||||||
@@ -172,9 +172,9 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
|
|||||||
|
|
||||||
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
|
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
|
||||||
|
|
||||||
object? IDictionary.this[object key]
|
object IDictionary.this[object key]
|
||||||
{
|
{
|
||||||
get => this[AssertKeyType(key)];
|
get => this[AssertKeyType(key)]!;
|
||||||
set => this[AssertKeyType(key)] = AssertValueType(value);
|
set => this[AssertKeyType(key)] = AssertValueType(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +198,7 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
|
|||||||
return default!;
|
return default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe TValue? this[TKey key]
|
public unsafe TValue this[TKey key]
|
||||||
{
|
{
|
||||||
[CollectionAccess(CollectionAccessType.Read)]
|
[CollectionAccess(CollectionAccessType.Read)]
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -211,7 +211,7 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
|
|||||||
ref var bucket = ref _buckets[bucketIndex];
|
ref var bucket = ref _buckets[bucketIndex];
|
||||||
|
|
||||||
if (_keyEqualityComparer(key, bucket.Key))
|
if (_keyEqualityComparer(key, bucket.Key))
|
||||||
return bucket.Value;
|
return bucket.Value!;
|
||||||
|
|
||||||
ContinueWithTailOrThrow(ref bucketIndex, key);
|
ContinueWithTailOrThrow(ref bucketIndex, key);
|
||||||
}
|
}
|
||||||
@@ -352,14 +352,14 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
|
|||||||
if (IsTail(ref bucket, bucketIndex))
|
if (IsTail(ref bucket, bucketIndex))
|
||||||
{
|
{
|
||||||
var previousEntry = bucket;
|
var previousEntry = bucket;
|
||||||
var tailingEntries = TryGetAndClearTailingEntries(bucketIndex);
|
using var tailingEntries = TryGetAndClearTailingEntries(bucketIndex);
|
||||||
|
|
||||||
SetParentTail(bucketIndex, Tails.SOLO);
|
SetParentTail(bucketIndex, Tails.SOLO);
|
||||||
SetEntryWithoutTail(bucketIndex, entry);
|
SetEntryWithoutTail(bucketIndex, entry);
|
||||||
InsertEntry(previousEntry, ReplaceBehaviour.Throw, true);
|
InsertEntry(previousEntry, ReplaceBehaviour.Throw, true);
|
||||||
|
|
||||||
if (tailingEntries != null)
|
if (tailingEntries != default)
|
||||||
InsertEntries(tailingEntries, ReplaceBehaviour.Throw, true);
|
InsertEntries(tailingEntries.List, ReplaceBehaviour.Throw, true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -370,9 +370,9 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InsertEntries(Entry[] tailingEntries, ReplaceBehaviour replaceBehaviour, bool shifting = false)
|
private void InsertEntries(List<Entry> tailingEntries, ReplaceBehaviour replaceBehaviour, bool shifting = false)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < tailingEntries.Length; i++)
|
for (var i = 0; i < tailingEntries.Count; i++)
|
||||||
InsertEntry(tailingEntries[i], replaceBehaviour, shifting);
|
InsertEntry(tailingEntries[i], replaceBehaviour, shifting);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,31 +473,31 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
|
|||||||
}
|
}
|
||||||
|
|
||||||
var previousEntry = _buckets[bucketIndex];
|
var previousEntry = _buckets[bucketIndex];
|
||||||
var tailingEntries = TryGetAndClearTailingEntries(bucketIndex);
|
using var tailingEntries = TryGetAndClearTailingEntries(bucketIndex);
|
||||||
|
|
||||||
SetParentTail(bucketIndex, Tails.SOLO);
|
SetParentTail(bucketIndex, Tails.SOLO);
|
||||||
SetEntryAsTail(bucketIndex, entry, parentIndex, offset);
|
SetEntryAsTail(bucketIndex, entry, parentIndex, offset);
|
||||||
InsertEntry(previousEntry, ReplaceBehaviour.Throw, true);
|
InsertEntry(previousEntry, ReplaceBehaviour.Throw, true);
|
||||||
|
|
||||||
if (tailingEntries != null)
|
if (tailingEntries != default)
|
||||||
InsertEntries(tailingEntries, ReplaceBehaviour.Throw, true);
|
InsertEntries(tailingEntries.List, ReplaceBehaviour.Throw, true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Entry[]? TryGetAndClearTailingEntries(int bucketIndex)
|
private PooledIList<List<Entry>> TryGetAndClearTailingEntries(int bucketIndex)
|
||||||
{
|
{
|
||||||
if (!HasTail(bucketIndex))
|
if (!HasTail(bucketIndex))
|
||||||
return null;
|
return default;
|
||||||
|
|
||||||
var tailingEntries = new Entry[GetTailCount(bucketIndex)];
|
var tailingEntries = new PooledIList<List<Entry>>();
|
||||||
var i = 0;
|
var tailingEntriesList = tailingEntries.List;
|
||||||
var tailIndex = GetTailIndex(bucketIndex);
|
var tailIndex = GetTailIndex(bucketIndex);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
ref var tailingBucket = ref _buckets[tailIndex];
|
ref var tailingBucket = ref _buckets[tailIndex];
|
||||||
tailingEntries[i++] = tailingBucket;
|
tailingEntriesList.Add(tailingBucket);
|
||||||
tailingBucket = default;
|
tailingBucket = default;
|
||||||
|
|
||||||
if (HasTail(tailIndex))
|
if (HasTail(tailIndex))
|
||||||
@@ -639,7 +639,7 @@ public class FishTable<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, I
|
|||||||
}
|
}
|
||||||
|
|
||||||
[CollectionAccess(CollectionAccessType.Read | CollectionAccessType.UpdatedContent)]
|
[CollectionAccess(CollectionAccessType.Read | CollectionAccessType.UpdatedContent)]
|
||||||
public unsafe bool TryGetOrAddValue(TKey key, [MaybeNullWhen(false)] out TValue value)
|
public unsafe bool TryGetOrAddValue(TKey key, out TValue value)
|
||||||
{
|
{
|
||||||
var result = true;
|
var result = true;
|
||||||
|
|
||||||
|
|||||||
@@ -28,11 +28,11 @@ namespace FisheryLib;
|
|||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public static class FisheryLib
|
public static class FisheryLib
|
||||||
{
|
{
|
||||||
public const decimal VERSION = 0.39995M;
|
public const decimal VERSION = 0.5M;
|
||||||
|
|
||||||
public static decimal CurrentlyLoadedVersion { get; } = VERSION;
|
public static decimal CurrentlyLoadedVersion { get; } = VERSION;
|
||||||
|
|
||||||
public const string VERSION_STRING = "0.3.999.5";
|
public const string VERSION_STRING = "0.5.0.0";
|
||||||
|
|
||||||
// public static string GetVersion()
|
// public static string GetVersion()
|
||||||
// {
|
// {
|
||||||
|
|||||||
@@ -1,81 +1,81 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<RootNamespace>FisheryLib</RootNamespace>
|
<RootNamespace>FisheryLib</RootNamespace>
|
||||||
<AssemblyName>1Fishery</AssemblyName>
|
<AssemblyName>1Fishery</AssemblyName>
|
||||||
<TargetFramework>net48</TargetFramework>
|
<TargetFramework>net48</TargetFramework>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
<Authors>bradson</Authors>
|
<Authors>bradson</Authors>
|
||||||
<Copyright>Copyright (c) 2022 bradson</Copyright>
|
<Copyright>Copyright (c) 2022 bradson</Copyright>
|
||||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
<EnableNETAnalyzers>True</EnableNETAnalyzers>
|
<EnableNETAnalyzers>True</EnableNETAnalyzers>
|
||||||
<AnalysisLevel>preview-all</AnalysisLevel>
|
<AnalysisLevel>preview-all</AnalysisLevel>
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<DebugType>portable</DebugType>
|
<DebugType>portable</DebugType>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>5</WarningLevel>
|
<WarningLevel>5</WarningLevel>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
<Configurations>v1.4;v1.3</Configurations>
|
<Configurations>v1.4;v1.3</Configurations>
|
||||||
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
|
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
|
||||||
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
|
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
|
||||||
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
|
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
|
||||||
<!-- Version>0.3.800.85</Version -->
|
<!-- Version>0.3.800.85</Version -->
|
||||||
<!-- Deterministic>False</Deterministic -->
|
<!-- Deterministic>False</Deterministic -->
|
||||||
<NoWarn>
|
<NoWarn>
|
||||||
1701;1702;IL2121;CS8500;CS9082;CA1000;CA1002;CA1003;CA1707;CA1010;CA1715;CA1716;CA1724;CA1031;CA1033;
|
1701;1702;IL2121;CS8500;CS9082;CA1000;CA1002;CA1003;CA1707;CA1010;CA1715;CA1716;CA1724;CA1031;CA1033;
|
||||||
CA1034;CA1043;CA1051;CA1305;CA1310;CA1710;CA1711;CA1810;CA1812;CA1819;CA1851;CA2002;CA5394;CA1725;CA1062
|
CA1034;CA1043;CA1051;CA1305;CA1310;CA1710;CA1711;CA1810;CA1812;CA1819;CA1851;CA2002;CA5394;CA1725;CA1062
|
||||||
</NoWarn>
|
</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Choose>
|
<Choose>
|
||||||
<When Condition="'$(Configuration)' == 'v1.4'">
|
<When Condition="'$(Configuration)' == 'v1.4'">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputPath>..\..\1.4\Assemblies\</OutputPath>
|
<OutputPath>..\..\1.4\Assemblies\</OutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.4.*" />
|
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.4.*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</When>
|
</When>
|
||||||
<When Condition="'$(Configuration)' == 'v1.3'">
|
<When Condition="'$(Configuration)' == 'v1.3'">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputPath>..\..\1.3\Assemblies\</OutputPath>
|
<OutputPath>..\..\1.3\Assemblies\</OutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.3.*" />
|
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.3.*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</When>
|
</When>
|
||||||
</Choose>
|
</Choose>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Lib.Harmony" Version="2.*" ExcludeAssets="runtime" />
|
<PackageReference Include="Lib.Harmony" Version="2.2.*" ExcludeAssets="runtime" />
|
||||||
<PackageReference Include="PolySharp" Version="1.*" />
|
<PackageReference Include="PolySharp" Version="1.*" />
|
||||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.*" />
|
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.*" />
|
||||||
<PackageReference Include="Krafs.Publicizer" Version="2.*" />
|
<PackageReference Include="Krafs.Publicizer" Version="2.*" />
|
||||||
|
|
||||||
<Publicize Include="Assembly-CSharp;0Harmony" IncludeVirtualMembers="false" />
|
<Publicize Include="Assembly-CSharp;0Harmony" IncludeVirtualMembers="false" />
|
||||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute" />
|
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute" />
|
||||||
<DoNotPublicize Include="0Harmony:Mono.Collections.Generic.Collection`1" />
|
<DoNotPublicize Include="0Harmony:Mono.Collections.Generic.Collection`1" />
|
||||||
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.buckets" />
|
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.buckets" />
|
||||||
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.entries" />
|
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.entries" />
|
||||||
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.version" />
|
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.version" />
|
||||||
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2+Entry" />
|
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2+Entry" />
|
||||||
<Publicize Include="mscorlib:System.Collections.Generic.List`1._items" />
|
<Publicize Include="mscorlib:System.Collections.Generic.List`1._items" />
|
||||||
<Publicize Include="mscorlib:System.Collections.Generic.List`1._version" />
|
<Publicize Include="mscorlib:System.Collections.Generic.List`1._version" />
|
||||||
<Publicize Include="mscorlib:System.Collections.Generic.List`1._size" />
|
<Publicize Include="mscorlib:System.Collections.Generic.List`1._size" />
|
||||||
<Publicize Include="mscorlib:System.Collections.Generic.List`1.EnsureCapacity" />
|
<Publicize Include="mscorlib:System.Collections.Generic.List`1.EnsureCapacity" />
|
||||||
<Publicize Include="System.Core:System.Collections.Generic.HashSet`1._version" />
|
<Publicize Include="System.Core:System.Collections.Generic.HashSet`1._version" />
|
||||||
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCast" />
|
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCast" />
|
||||||
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCastLong" />
|
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCastLong" />
|
||||||
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeCast" />
|
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeCast" />
|
||||||
<Publicize Include="mscorlib:System.Reflection.Emit.Label.label" />
|
<Publicize Include="mscorlib:System.Reflection.Emit.Label.label" />
|
||||||
<Publicize Include="mscorlib:System.String.m_firstChar" />
|
<Publicize Include="mscorlib:System.String.m_firstChar" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\..\LICENSE">
|
<None Include="..\..\LICENSE">
|
||||||
<Pack>True</Pack>
|
<Pack>True</Pack>
|
||||||
<PackagePath />
|
<PackagePath />
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -9,29 +9,96 @@ namespace FisheryLib.Pools;
|
|||||||
|
|
||||||
public static class FishPool<T> where T : IFishPoolable
|
public static class FishPool<T> where T : IFishPoolable
|
||||||
{
|
{
|
||||||
private static T?[] _freeItems = new T[4];
|
[ThreadStatic]
|
||||||
private static int _freeItemCount;
|
private static T?[]? _itemsThreadStatic;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static int _itemCountThreadStatic;
|
||||||
|
|
||||||
|
private static object _lockObject = new();
|
||||||
|
private static T?[] _itemsGlobal = new T[16];
|
||||||
|
private static int _itemCountGlobal;
|
||||||
|
|
||||||
public static T Get()
|
public static T Get()
|
||||||
{
|
{
|
||||||
if (_freeItemCount == 0)
|
if (_itemCountThreadStatic == 0)
|
||||||
return Reflection.New<T>();
|
FetchThroughLock();
|
||||||
|
|
||||||
|
ref var itemBucket = ref _itemsThreadStatic![--_itemCountThreadStatic];
|
||||||
|
var item = itemBucket;
|
||||||
|
itemBucket = default;
|
||||||
|
return item!;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static void FetchThroughLock()
|
||||||
|
{
|
||||||
|
var itemsThreadStatic = _itemsThreadStatic ??= InitializeItemsThreadStatic();
|
||||||
|
var createNew = false;
|
||||||
|
|
||||||
var freeItem = _freeItems[--_freeItemCount];
|
lock (_lockObject)
|
||||||
_freeItems[_freeItemCount] = default;
|
{
|
||||||
return freeItem!;
|
if (_itemCountGlobal != 0)
|
||||||
|
{
|
||||||
|
for (var i = 8; i-- > 0;)
|
||||||
|
{
|
||||||
|
itemsThreadStatic[i] = _itemsGlobal[--_itemCountGlobal];
|
||||||
|
_itemsGlobal[_itemCountGlobal] = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
createNew = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (createNew)
|
||||||
|
{
|
||||||
|
for (var i = 8; i-- > 0;)
|
||||||
|
itemsThreadStatic[i] = Reflection.New<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
_itemCountThreadStatic = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Return(T item)
|
public static void Return(T item)
|
||||||
{
|
{
|
||||||
item.Reset();
|
item.Reset();
|
||||||
|
|
||||||
if (++_freeItemCount >= _freeItems.Length)
|
|
||||||
ExpandFreeItems();
|
|
||||||
|
|
||||||
_freeItems[_freeItemCount - 1] = item;
|
var itemsThreadStatic = _itemsThreadStatic ??= InitializeItemsThreadStatic();
|
||||||
|
|
||||||
|
if (_itemCountThreadStatic == 16)
|
||||||
|
PushThroughLock();
|
||||||
|
|
||||||
|
itemsThreadStatic[_itemCountThreadStatic++] = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
private static void ExpandFreeItems() => Array.Resize(ref _freeItems, _freeItems.Length << 1);
|
private static void PushThroughLock()
|
||||||
|
{
|
||||||
|
var itemsThreadStatic = _itemsThreadStatic!;
|
||||||
|
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
if (_itemCountGlobal + 8 > _itemsGlobal.Length)
|
||||||
|
ExpandGlobalItems();
|
||||||
|
|
||||||
|
for (var i = 16; i-- > 8;)
|
||||||
|
{
|
||||||
|
ref var itemBucket = ref itemsThreadStatic[i];
|
||||||
|
var item = itemBucket;
|
||||||
|
itemBucket = default;
|
||||||
|
|
||||||
|
_itemsGlobal[_itemCountGlobal++] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_itemCountThreadStatic = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static T?[] InitializeItemsThreadStatic() => new T?[16];
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static void ExpandGlobalItems() => Array.Resize(ref _itemsGlobal, _itemsGlobal.Length << 1);
|
||||||
}
|
}
|
||||||
@@ -12,8 +12,15 @@ public record struct PooledIList<T>() : IDisposable where T : IList, new()
|
|||||||
{
|
{
|
||||||
public T List = Get();
|
public T List = Get();
|
||||||
|
|
||||||
private static T?[] _freeItems = new T[4];
|
[ThreadStatic]
|
||||||
private static int _freeItemCount;
|
private static T?[]? _itemsThreadStatic;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static int _itemCountThreadStatic;
|
||||||
|
|
||||||
|
private static object _lockObject = new();
|
||||||
|
private static T?[] _itemsGlobal = new T[16];
|
||||||
|
private static int _itemCountGlobal;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
@@ -23,27 +30,87 @@ public record struct PooledIList<T>() : IDisposable where T : IList, new()
|
|||||||
Return(List);
|
Return(List);
|
||||||
List = default!;
|
List = default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Get()
|
public static T Get()
|
||||||
{
|
{
|
||||||
if (_freeItemCount == 0)
|
if (_itemCountThreadStatic == 0)
|
||||||
return Reflection.New<T>();
|
FetchThroughLock();
|
||||||
|
|
||||||
|
ref var itemBucket = ref _itemsThreadStatic![--_itemCountThreadStatic];
|
||||||
|
var item = itemBucket;
|
||||||
|
itemBucket = default;
|
||||||
|
return item!;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static void FetchThroughLock()
|
||||||
|
{
|
||||||
|
var itemsThreadStatic = _itemsThreadStatic ??= InitializeItemsThreadStatic();
|
||||||
|
var createNew = false;
|
||||||
|
|
||||||
var freeItem = _freeItems[--_freeItemCount];
|
lock (_lockObject)
|
||||||
_freeItems[_freeItemCount] = default;
|
{
|
||||||
return freeItem!;
|
if (_itemCountGlobal != 0)
|
||||||
|
{
|
||||||
|
for (var i = 8; i-- > 0;)
|
||||||
|
{
|
||||||
|
itemsThreadStatic[i] = _itemsGlobal[--_itemCountGlobal];
|
||||||
|
_itemsGlobal[_itemCountGlobal] = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
createNew = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (createNew)
|
||||||
|
{
|
||||||
|
for (var i = 8; i-- > 0;)
|
||||||
|
itemsThreadStatic[i] = Reflection.New<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
_itemCountThreadStatic = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Return(T item)
|
public static void Return(T item)
|
||||||
{
|
{
|
||||||
item.Clear();
|
item.Clear();
|
||||||
|
|
||||||
if (++_freeItemCount >= _freeItems.Length)
|
|
||||||
ExpandFreeItems();
|
|
||||||
|
|
||||||
_freeItems[_freeItemCount - 1] = item;
|
var itemsThreadStatic = _itemsThreadStatic ??= InitializeItemsThreadStatic();
|
||||||
|
|
||||||
|
if (_itemCountThreadStatic == 16)
|
||||||
|
PushThroughLock();
|
||||||
|
|
||||||
|
itemsThreadStatic[_itemCountThreadStatic++] = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
private static void ExpandFreeItems() => Array.Resize(ref _freeItems, _freeItems.Length << 1);
|
private static void PushThroughLock()
|
||||||
|
{
|
||||||
|
var itemsThreadStatic = _itemsThreadStatic!;
|
||||||
|
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
if (_itemCountGlobal + 8 > _itemsGlobal.Length)
|
||||||
|
ExpandGlobalItems();
|
||||||
|
|
||||||
|
for (var i = 16; i-- > 8;)
|
||||||
|
{
|
||||||
|
ref var itemBucket = ref itemsThreadStatic[i];
|
||||||
|
var item = itemBucket;
|
||||||
|
itemBucket = default;
|
||||||
|
|
||||||
|
_itemsGlobal[_itemCountGlobal++] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_itemCountThreadStatic = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static T?[] InitializeItemsThreadStatic() => new T?[16];
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static void ExpandGlobalItems() => Array.Resize(ref _itemsGlobal, _itemsGlobal.Length << 1);
|
||||||
}
|
}
|
||||||
@@ -40,11 +40,11 @@ public static class SpanExtensions
|
|||||||
=> ref Unsafe.Add(ref span.DangerousGetPinnableReference(), i);
|
=> ref Unsafe.Add(ref span.DangerousGetPinnableReference(), i);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Sort<T>(this Span<T> span, IComparer<T>? comparer)
|
public static void Sort<T>(this Span<T> span, IComparer<T>? comparer = null)
|
||||||
=> ArraySortHelper<T>.Default.Sort(span, comparer);
|
=> ArraySortHelper<T>.Default.Sort(span, comparer);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Sort<TKey, TValue>(this Span<TValue> values, Span<TKey> keys, IComparer<TKey>? comparer)
|
public static void Sort<TKey, TValue>(this Span<TValue> values, Span<TKey> keys, IComparer<TKey>? comparer = null)
|
||||||
=> ArraySortHelper<TKey, TValue>.Default.Sort(keys, values, comparer);
|
=> ArraySortHelper<TKey, TValue>.Default.Sort(keys, values, comparer);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|||||||
Reference in New Issue
Block a user