feat(core)!: refactor safety/debug defines, remove FreeList in JobSchedular
Refactor to use MHP_ENABLE_SAFETY_CHECKS, MHP_ENABLE_STACKTRACE, and MHP_ENABLE_MIMALLOC for feature toggling. Remove FreeList allocator in JobSchedular and debug-layer code, simplifying memory management. Improve memory leak detection and reporting, update memory allocation API, and guard all safety/debug features with new defines. Update csproj files, README, and code samples to match new API and toggles. Fix and improve collection types for correct behavior with and without safety checks. The codebase is now more modular and easier to configure for different build environments. BREAKING CHANGE: Old defines (ENABLE_SAFETY_CHECKS, ENABLE_DEBUG_LAYER, ENABLE_MIMALLOC) are replaced with MHP_* equivalents. FreeList allocator and related debug features are removed. Some APIs and behaviors have changed for safety/debug configuration.
This commit is contained in:
@@ -33,6 +33,7 @@ This package focuses on practical image decoding with low-level control over mem
|
|||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using Misaki.HighPerformance.Image;
|
using Misaki.HighPerformance.Image;
|
||||||
|
|
||||||
using var stream = File.OpenRead("image.png");
|
using var stream = File.OpenRead("image.png");
|
||||||
using ImageResult image = ImageResult.FromStream(stream);
|
using ImageResult image = ImageResult.FromStream(stream);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
using Misaki.HighPerformance.Collections;
|
using Misaki.HighPerformance.Collections;
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
|
||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.Jobs;
|
namespace Misaki.HighPerformance.Jobs;
|
||||||
|
|
||||||
@@ -195,8 +193,11 @@ public interface IJobScheduler
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Blocks the calling thread until all specified job handles have completed.
|
/// Blocks the calling thread until all specified job handles have completed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The collection handles will be reordered in-place to move completed handles to the front.
|
||||||
|
/// </remarks>
|
||||||
/// <param name="handles">A collection of job handles to wait for.</param>
|
/// <param name="handles">A collection of job handles to wait for.</param>
|
||||||
void WaitAll(params ReadOnlySpan<JobHandle> handles);
|
void WaitAll(params Span<JobHandle> handles);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Waits until any of the specified job handles has completed and returns the first completed handle.
|
/// Waits until any of the specified job handles has completed and returns the first completed handle.
|
||||||
@@ -206,36 +207,6 @@ public interface IJobScheduler
|
|||||||
JobHandle WaitAny(params ReadOnlySpan<JobHandle> handles);
|
JobHandle WaitAny(params ReadOnlySpan<JobHandle> handles);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe partial class JobScheduler
|
|
||||||
{
|
|
||||||
public static int MainThreadIndex => -1;
|
|
||||||
|
|
||||||
public static TempJobAllocator* pTempAllocator;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the allocation handle for the temporary job allocator.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// You must dispose the allocation before the fourth time you call <see cref="TempJobAllocator.AdvanceFrame"/> after obtaining this handle.
|
|
||||||
/// </remarks>
|
|
||||||
public static AllocationHandle TempAllocatorHandle => pTempAllocator->Handle;
|
|
||||||
|
|
||||||
public static void InitTempAllocator(nuint capacityPerFrame)
|
|
||||||
{
|
|
||||||
pTempAllocator = (TempJobAllocator*)MemoryUtility.Malloc((nuint)sizeof(TempJobAllocator));
|
|
||||||
pTempAllocator->Initialize(capacityPerFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ReleaseTempAllocator()
|
|
||||||
{
|
|
||||||
if (pTempAllocator != null)
|
|
||||||
{
|
|
||||||
pTempAllocator->Dispose();
|
|
||||||
MemoryUtility.Free(pTempAllocator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides a mechanism for scheduling and executing jobs across multiple worker threads.
|
/// Provides a mechanism for scheduling and executing jobs across multiple worker threads.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -248,7 +219,6 @@ public sealed unsafe partial class JobScheduler : IJobScheduler, IDisposable
|
|||||||
private const int _STATE_MASK = 0xFFFF;
|
private const int _STATE_MASK = 0xFFFF;
|
||||||
private const int _RC_ONE = 0x10000;
|
private const int _RC_ONE = 0x10000;
|
||||||
|
|
||||||
private FreeList _jobDataAllocator;
|
|
||||||
private readonly ConcurrentSlotMap<JobInfo> _jobInfoPool;
|
private readonly ConcurrentSlotMap<JobInfo> _jobInfoPool;
|
||||||
private readonly ConcurrentQueue<JobHandle> _jobQueue;
|
private readonly ConcurrentQueue<JobHandle> _jobQueue;
|
||||||
private readonly WorkerThread[] _workerThreads;
|
private readonly WorkerThread[] _workerThreads;
|
||||||
@@ -272,7 +242,6 @@ public sealed unsafe partial class JobScheduler : IJobScheduler, IDisposable
|
|||||||
{
|
{
|
||||||
var workerCount = Math.Max(1, threadCount);
|
var workerCount = Math.Max(1, threadCount);
|
||||||
|
|
||||||
_jobDataAllocator = new(8, maxConcurrencyLevel: workerCount + 1);
|
|
||||||
_jobInfoPool = new();
|
_jobInfoPool = new();
|
||||||
_jobQueue = new();
|
_jobQueue = new();
|
||||||
|
|
||||||
@@ -395,7 +364,7 @@ public sealed unsafe partial class JobScheduler : IJobScheduler, IDisposable
|
|||||||
Interlocked.Decrement(ref depJobInfo.dependentCount);
|
Interlocked.Decrement(ref depJobInfo.dependentCount);
|
||||||
|
|
||||||
// Cleanup and fail
|
// Cleanup and fail
|
||||||
_jobDataAllocator.Free(jobInfo.pJobData);
|
NativeMemory.Free(jobInfo.pJobData);
|
||||||
return JobHandle.Invalid;
|
return JobHandle.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,7 +507,7 @@ public sealed unsafe partial class JobScheduler : IJobScheduler, IDisposable
|
|||||||
dependentsToNotify[i] = new JobHandle(info.dependentsID[i], info.dependentsGeneration[i]);
|
dependentsToNotify[i] = new JobHandle(info.dependentsID[i], info.dependentsGeneration[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
_jobDataAllocator.Free(info.pJobData);
|
NativeMemory.Free(info.pJobData);
|
||||||
_jobInfoPool.Remove(handle.ID, handle.Generation);
|
_jobInfoPool.Remove(handle.ID, handle.Generation);
|
||||||
Interlocked.Decrement(ref _totalJobCount);
|
Interlocked.Decrement(ref _totalJobCount);
|
||||||
|
|
||||||
@@ -557,16 +526,13 @@ public sealed unsafe partial class JobScheduler : IJobScheduler, IDisposable
|
|||||||
public JobHandle Schedule<T>(ref readonly T job, int threadIndex, JobHandle dependency)
|
public JobHandle Schedule<T>(ref readonly T job, int threadIndex, JobHandle dependency)
|
||||||
where T : unmanaged, IJob
|
where T : unmanaged, IJob
|
||||||
{
|
{
|
||||||
var pJobData = _jobDataAllocator.Allocate(MemoryUtility.SizeOf<T>(), MemoryUtility.AlignOf<T>());
|
var pJobData = NativeMemory.Alloc((nuint)sizeof(T));
|
||||||
if (pJobData == null)
|
if (pJobData == null)
|
||||||
{
|
{
|
||||||
return JobHandle.Invalid;
|
return JobHandle.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (T* pJob = &job)
|
Unsafe.Copy(pJobData, in job);
|
||||||
{
|
|
||||||
MemoryUtility.MemCpy(pJobData, pJob, MemoryUtility.SizeOf<T>());
|
|
||||||
}
|
|
||||||
|
|
||||||
var jobInfo = new JobInfo
|
var jobInfo = new JobInfo
|
||||||
{
|
{
|
||||||
@@ -597,7 +563,7 @@ public sealed unsafe partial class JobScheduler : IJobScheduler, IDisposable
|
|||||||
public JobHandle ScheduleParallelFor<T>(ref readonly T job, int totalIteration, int batchSize, int threadIndex, JobHandle dependency)
|
public JobHandle ScheduleParallelFor<T>(ref readonly T job, int totalIteration, int batchSize, int threadIndex, JobHandle dependency)
|
||||||
where T : unmanaged, IJobParallelFor
|
where T : unmanaged, IJobParallelFor
|
||||||
{
|
{
|
||||||
var pJobData = _jobDataAllocator.Allocate(MemoryUtility.SizeOf<T>(), MemoryUtility.AlignOf<T>());
|
var pJobData = NativeMemory.Alloc((nuint)sizeof(T));
|
||||||
if (pJobData == null)
|
if (pJobData == null)
|
||||||
{
|
{
|
||||||
return JobHandle.Invalid;
|
return JobHandle.Invalid;
|
||||||
@@ -605,7 +571,7 @@ public sealed unsafe partial class JobScheduler : IJobScheduler, IDisposable
|
|||||||
|
|
||||||
fixed (T* pJob = &job)
|
fixed (T* pJob = &job)
|
||||||
{
|
{
|
||||||
MemoryUtility.MemCpy(pJobData, pJob, MemoryUtility.SizeOf<T>());
|
NativeMemory.Copy(pJobData, pJob, (nuint)sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
var optimalBatchSize = Math.Max(1, batchSize);
|
var optimalBatchSize = Math.Max(1, batchSize);
|
||||||
@@ -645,7 +611,7 @@ public sealed unsafe partial class JobScheduler : IJobScheduler, IDisposable
|
|||||||
public JobHandle ScheduleParallel<T>(ref readonly T job, int totalIteration, int batchSize, int threadIndex, JobHandle dependency)
|
public JobHandle ScheduleParallel<T>(ref readonly T job, int totalIteration, int batchSize, int threadIndex, JobHandle dependency)
|
||||||
where T : unmanaged, IJobParallel
|
where T : unmanaged, IJobParallel
|
||||||
{
|
{
|
||||||
var pJobData = _jobDataAllocator.Allocate(MemoryUtility.SizeOf<T>(), MemoryUtility.AlignOf<T>());
|
var pJobData = NativeMemory.Alloc((nuint)sizeof(T));
|
||||||
if (pJobData == null)
|
if (pJobData == null)
|
||||||
{
|
{
|
||||||
return JobHandle.Invalid;
|
return JobHandle.Invalid;
|
||||||
@@ -653,7 +619,7 @@ public sealed unsafe partial class JobScheduler : IJobScheduler, IDisposable
|
|||||||
|
|
||||||
fixed (T* pJob = &job)
|
fixed (T* pJob = &job)
|
||||||
{
|
{
|
||||||
MemoryUtility.MemCpy(pJobData, pJob, MemoryUtility.SizeOf<T>());
|
NativeMemory.Copy(pJobData, pJob, (nuint)sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
var optimalBatchSize = Math.Max(1, batchSize);
|
var optimalBatchSize = Math.Max(1, batchSize);
|
||||||
@@ -753,30 +719,27 @@ public sealed unsafe partial class JobScheduler : IJobScheduler, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WaitAll(params ReadOnlySpan<JobHandle> handles)
|
public void WaitAll(params Span<JobHandle> handles)
|
||||||
{
|
{
|
||||||
if (handles.Length == 0)
|
if (handles.Length == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using var orderedHandles = new UnsafeArray<JobHandle>(handles.Length, Allocator.Temp);
|
|
||||||
var spin = new SpinWait();
|
var spin = new SpinWait();
|
||||||
var completedCount = 0;
|
var completedCount = 0;
|
||||||
|
|
||||||
orderedHandles.CopyFrom(handles);
|
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
for (var i = completedCount; i < orderedHandles.Length; i++)
|
for (var i = completedCount; i < handles.Length; i++)
|
||||||
{
|
{
|
||||||
var handle = orderedHandles[i];
|
var handle = handles[i];
|
||||||
if (!_jobInfoPool.Contains(handle.ID, handle.Generation))
|
if (!_jobInfoPool.Contains(handle.ID, handle.Generation))
|
||||||
{
|
{
|
||||||
// Move completed handle to the front (completedCount index) to avoid checking it again.
|
// Move completed handle to the front (completedCount index) to avoid checking it again.
|
||||||
var temp = orderedHandles[completedCount];
|
var temp = handles[completedCount];
|
||||||
orderedHandles[completedCount] = handle;
|
handles[completedCount] = handle;
|
||||||
orderedHandles[i] = temp;
|
handles[i] = temp;
|
||||||
|
|
||||||
completedCount++;
|
completedCount++;
|
||||||
}
|
}
|
||||||
@@ -825,7 +788,6 @@ public sealed unsafe partial class JobScheduler : IJobScheduler, IDisposable
|
|||||||
|
|
||||||
_jobInfoPool.Clear();
|
_jobInfoPool.Clear();
|
||||||
_jobQueue.Clear();
|
_jobQueue.Clear();
|
||||||
_jobDataAllocator.Dispose();
|
|
||||||
|
|
||||||
_workSignal.Dispose();
|
_workSignal.Dispose();
|
||||||
_cts.Dispose();
|
_cts.Dispose();
|
||||||
|
|||||||
@@ -5,14 +5,12 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
<AssemblyVersion>1.5.3</AssemblyVersion>
|
<AssemblyVersion>1.5.5</AssemblyVersion>
|
||||||
<Version>$(AssemblyVersion)</Version>
|
<Version>$(AssemblyVersion)</Version>
|
||||||
<Authors>Misaki</Authors>
|
<Authors>Misaki</Authors>
|
||||||
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
|
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
|
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
|
||||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
|
||||||
<ContentTargetFolders>contentFiles</ContentTargetFolders>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
@@ -24,15 +22,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Misaki.HighPerformance.LowLevel\Misaki.HighPerformance.LowLevel.csproj" />
|
|
||||||
<ProjectReference Include="..\Misaki.HighPerformance\Misaki.HighPerformance.csproj" />
|
<ProjectReference Include="..\Misaki.HighPerformance\Misaki.HighPerformance.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="**\*.cs" Exclude="obj\**;bin\**">
|
|
||||||
<Pack>true</Pack>
|
|
||||||
<PackagePath>contentFiles\cs\any\Misaki.HighPerformance.Jobs\</PackagePath>
|
|
||||||
<PackageCopyToOutput>false</PackageCopyToOutput>
|
|
||||||
<BuildAction>Compile</BuildAction>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup></Project>
|
|
||||||
|
|||||||
@@ -37,8 +37,32 @@ This package provides job contracts, scheduling, worker threads, dependency hand
|
|||||||
```csharp
|
```csharp
|
||||||
using Misaki.HighPerformance.Jobs;
|
using Misaki.HighPerformance.Jobs;
|
||||||
|
|
||||||
// Implement IJob, IJobParallelFor, or IJobParallel and schedule the work through JobScheduler.
|
public struct AddJob : IJob
|
||||||
// The scheduler copies job data internally and tracks completion through JobHandle.
|
{
|
||||||
|
public int* pA;
|
||||||
|
public int* pB;
|
||||||
|
public int* pResult;
|
||||||
|
|
||||||
|
public void Execute(ref readonly JobExecutionContext ctx)
|
||||||
|
{
|
||||||
|
*pResult = *pA + *pB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int a = 5;
|
||||||
|
int b = 10;
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
var job = new AddJob
|
||||||
|
{
|
||||||
|
pA = &a,
|
||||||
|
pB = &b,
|
||||||
|
pResult = &result
|
||||||
|
};
|
||||||
|
|
||||||
|
JobHandle handle = jobScheduler.Schedule(job);
|
||||||
|
jobScheduler.Wait(handle);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Package reference
|
## Package reference
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
|
||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.Jobs;
|
namespace Misaki.HighPerformance.Jobs;
|
||||||
|
|
||||||
|
#if false
|
||||||
public unsafe struct TempJobAllocator : IAllocator, IDisposable
|
public unsafe struct TempJobAllocator : IAllocator, IDisposable
|
||||||
{
|
{
|
||||||
private const int _FRAME_LATENCY = 4;
|
private const int _FRAME_LATENCY = 4;
|
||||||
@@ -23,7 +20,7 @@ public unsafe struct TempJobAllocator : IAllocator, IDisposable
|
|||||||
{
|
{
|
||||||
var memoryHandle = default(MemoryHandle);
|
var memoryHandle = default(MemoryHandle);
|
||||||
|
|
||||||
_pArena = (VirtualArena*)AllocationManager.HeapAlloc((nuint)(sizeof(VirtualArena) * _FRAME_LATENCY), MemoryUtility.AlignOf<VirtualArena>(), AllocationOption.Clear, &memoryHandle);
|
_pArena = (VirtualArena*)MemoryUtility.Malloc((nuint)(sizeof(VirtualArena) * _FRAME_LATENCY));
|
||||||
_currentFrameCount = 0;
|
_currentFrameCount = 0;
|
||||||
_currentFrameIndex = 0;
|
_currentFrameIndex = 0;
|
||||||
_memoryHandle = memoryHandle;
|
_memoryHandle = memoryHandle;
|
||||||
@@ -40,31 +37,51 @@ public unsafe struct TempJobAllocator : IAllocator, IDisposable
|
|||||||
Alloc = &Allocate,
|
Alloc = &Allocate,
|
||||||
Realloc = &Reallocate,
|
Realloc = &Reallocate,
|
||||||
Free = &Free,
|
Free = &Free,
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
IsValid = &IsValid,
|
IsValid = &IsValid,
|
||||||
|
#else
|
||||||
|
IsValid = null,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
|
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle* pHandle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var pSelf = (TempJobAllocator*)instance;
|
var pSelf = (TempJobAllocator*)instance;
|
||||||
var pCurrentArena = pSelf->_pArena + pSelf->_currentFrameIndex;
|
var pCurrentArena = pSelf->_pArena + pSelf->_currentFrameIndex;
|
||||||
var ptr = pCurrentArena->Allocate(size, alignment, allocationOption);
|
var ptr = pCurrentArena->Allocate(size, alignment, allocationOption);
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = MemoryHandle.Invalid;
|
*pHandle = MemoryHandle.Invalid;
|
||||||
|
#endif
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Interlocked.Increment(ref pSelf->_allocationsPerFrame[pSelf->_currentFrameIndex]);
|
Interlocked.Increment(ref pSelf->_allocationsPerFrame[pSelf->_currentFrameIndex]);
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = new MemoryHandle(_MAGIC_ID, pSelf->_currentFrameCount);
|
*pHandle = new MemoryHandle(_MAGIC_ID, pSelf->_currentFrameCount);
|
||||||
|
#endif
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
|
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle* pHandle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
return Allocate(instance, newSize, alignment, allocationOption, pHandle);
|
return Allocate(instance, newSize, alignment, allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, pHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pSelf = (TempJobAllocator*)instance;
|
var pSelf = (TempJobAllocator*)instance;
|
||||||
@@ -80,18 +97,23 @@ public unsafe struct TempJobAllocator : IAllocator, IDisposable
|
|||||||
return newPtr;
|
return newPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Free(void* instance, void* ptr, MemoryHandle handle)
|
private static void Free(void* instance, void* ptr
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle handle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// The arena allocator does not free individual blocks, as it manages memory in chunks.
|
|
||||||
var pSelf = (TempJobAllocator*)instance;
|
var pSelf = (TempJobAllocator*)instance;
|
||||||
Interlocked.Decrement(ref pSelf->_allocationsPerFrame[pSelf->_currentFrameIndex]);
|
Interlocked.Decrement(ref pSelf->_allocationsPerFrame[pSelf->_currentFrameIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
private static bool IsValid(void* instance, MemoryHandle handle)
|
private static bool IsValid(void* instance, MemoryHandle handle)
|
||||||
{
|
{
|
||||||
var pSelf = (TempJobAllocator*)instance;
|
var pSelf = (TempJobAllocator*)instance;
|
||||||
return handle.ID == _MAGIC_ID && handle.Generation > pSelf->_currentFrameCount - _FRAME_LATENCY;
|
return handle.ID == _MAGIC_ID && handle.Generation > pSelf->_currentFrameCount - _FRAME_LATENCY;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public int AdvanceFrame()
|
public int AdvanceFrame()
|
||||||
{
|
{
|
||||||
@@ -115,3 +137,4 @@ public unsafe struct TempJobAllocator : IAllocator, IDisposable
|
|||||||
AllocationManager.HeapFree(_pArena, _memoryHandle);
|
AllocationManager.HeapFree(_pArena, _memoryHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
@@ -1,22 +1,22 @@
|
|||||||
global using static Misaki.HighPerformance.LowLevel.Utilities.MemoryUtility;
|
global using static Misaki.HighPerformance.LowLevel.Utilities.MemoryUtility;
|
||||||
|
|
||||||
global using unsafe AllocFunc = delegate*<void*, nuint, nuint, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption
|
global using unsafe AllocFunc = delegate*<void*, nuint, nuint, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle*
|
, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle*
|
||||||
#endif
|
#endif
|
||||||
, void*>;
|
, void*>;
|
||||||
global using unsafe ReallocFunc = delegate*<void*, void*, nuint, nuint, nuint, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption
|
global using unsafe ReallocFunc = delegate*<void*, void*, nuint, nuint, nuint, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle*
|
, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle*
|
||||||
#endif
|
#endif
|
||||||
, void*>;
|
, void*>;
|
||||||
global using unsafe FreeFunc = delegate*<void*, void*
|
global using unsafe FreeFunc = delegate*<void*, void*
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle
|
, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle
|
||||||
#endif
|
#endif
|
||||||
, void>;
|
, void>;
|
||||||
global using unsafe IsValidFunc = delegate*<void*
|
global using unsafe IsValidFunc = delegate*<void*
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle
|
, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle
|
||||||
#endif
|
#endif
|
||||||
, bool>;
|
, bool>;
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
#define ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
|
||||||
using Misaki.HighPerformance.Collections;
|
using Misaki.HighPerformance.Collections;
|
||||||
#endif
|
#endif
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_STACKTRACE
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -32,11 +30,11 @@ public readonly struct AllocationInfo
|
|||||||
get; init;
|
get; init;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_STACKTRACE
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the stack trace at the time of allocation for debugging purposes.
|
/// Gets the stack trace at the time of allocation for debugging purposes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public GCHandle StackTrace
|
public StackTrace? StackTrace
|
||||||
{
|
{
|
||||||
get; init;
|
get; init;
|
||||||
}
|
}
|
||||||
@@ -67,18 +65,6 @@ public readonly struct AllocationManagerInitOpts
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static unsafe class AllocationManager
|
public static unsafe class AllocationManager
|
||||||
{
|
{
|
||||||
#if ENABLE_DEBUG_LAYER
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
private struct AllocationHeader
|
|
||||||
{
|
|
||||||
public AllocationHeader* prev;
|
|
||||||
public AllocationHeader* next;
|
|
||||||
public void* basePtr; // pointer returned by underlying allocator
|
|
||||||
public nuint userSize; // requested size from the user
|
|
||||||
public GCHandle stackHandle; // GCHandle to managed StackTrace
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private struct ArenaAllocator : IAllocator, IDisposable
|
private struct ArenaAllocator : IAllocator, IDisposable
|
||||||
{
|
{
|
||||||
private const int _ARENA_MAGIC_ID = -3941029;
|
private const int _ARENA_MAGIC_ID = -3941029;
|
||||||
@@ -99,7 +85,7 @@ public static unsafe class AllocationManager
|
|||||||
Alloc = &Allocate,
|
Alloc = &Allocate,
|
||||||
Realloc = &Reallocate,
|
Realloc = &Reallocate,
|
||||||
Free = null,
|
Free = null,
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
IsValid = &IsValid
|
IsValid = &IsValid
|
||||||
#else
|
#else
|
||||||
IsValid = null
|
IsValid = null
|
||||||
@@ -110,7 +96,7 @@ public static unsafe class AllocationManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption
|
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, MemoryHandle* pHandle
|
, MemoryHandle* pHandle
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
@@ -119,20 +105,20 @@ public static unsafe class AllocationManager
|
|||||||
var ptr = selfPtr->_arena.Allocate(size, alignment, allocationOption);
|
var ptr = selfPtr->_arena.Allocate(size, alignment, allocationOption);
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = MemoryHandle.Invalid;
|
*pHandle = MemoryHandle.Invalid;
|
||||||
#endif
|
#endif
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = new MemoryHandle(_ARENA_MAGIC_ID, selfPtr->_currentTick);
|
*pHandle = new MemoryHandle(_ARENA_MAGIC_ID, selfPtr->_currentTick);
|
||||||
#endif
|
#endif
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption
|
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, MemoryHandle* pHandle
|
, MemoryHandle* pHandle
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
@@ -140,7 +126,7 @@ public static unsafe class AllocationManager
|
|||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
return Allocate(instance, newSize, alignment, allocationOption
|
return Allocate(instance, newSize, alignment, allocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, pHandle
|
, pHandle
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
@@ -155,13 +141,13 @@ public static unsafe class AllocationManager
|
|||||||
|
|
||||||
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = new MemoryHandle(_ARENA_MAGIC_ID, selfPtr->_currentTick);
|
*pHandle = new MemoryHandle(_ARENA_MAGIC_ID, selfPtr->_currentTick);
|
||||||
#endif
|
#endif
|
||||||
return newPtr;
|
return newPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
private static bool IsValid(void* instance, MemoryHandle handle)
|
private static bool IsValid(void* instance, MemoryHandle handle)
|
||||||
{
|
{
|
||||||
var selfPtr = (ArenaAllocator*)instance;
|
var selfPtr = (ArenaAllocator*)instance;
|
||||||
@@ -195,7 +181,7 @@ public static unsafe class AllocationManager
|
|||||||
Alloc = &Allocate,
|
Alloc = &Allocate,
|
||||||
Realloc = &Reallocate,
|
Realloc = &Reallocate,
|
||||||
Free = &Free,
|
Free = &Free,
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
IsValid = &IsValid
|
IsValid = &IsValid
|
||||||
#else
|
#else
|
||||||
IsValid = null
|
IsValid = null
|
||||||
@@ -204,13 +190,13 @@ public static unsafe class AllocationManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void* Allocate(void* _, nuint size, nuint alignment, AllocationOption allocationOption
|
private static void* Allocate(void* _, nuint size, nuint alignment, AllocationOption allocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, MemoryHandle* pHandle
|
, MemoryHandle* pHandle
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return HeapAlloc(size, alignment, allocationOption
|
return HeapAlloc(size, alignment, allocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, pHandle
|
, pHandle
|
||||||
#else
|
#else
|
||||||
, default
|
, default
|
||||||
@@ -219,7 +205,7 @@ public static unsafe class AllocationManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void* Reallocate(void* _, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption
|
private static void* Reallocate(void* _, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, MemoryHandle* pHandle
|
, MemoryHandle* pHandle
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
@@ -227,7 +213,7 @@ public static unsafe class AllocationManager
|
|||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
return Allocate(null, newSize, alignment, allocationOption
|
return Allocate(null, newSize, alignment, allocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, pHandle
|
, pHandle
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
@@ -242,27 +228,27 @@ public static unsafe class AllocationManager
|
|||||||
|
|
||||||
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
||||||
HeapFree(ptr
|
HeapFree(ptr
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, *pHandle
|
, *pHandle
|
||||||
#else
|
#else
|
||||||
, default
|
, default
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = newHandle;
|
*pHandle = newHandle;
|
||||||
#endif
|
#endif
|
||||||
return newPtr;
|
return newPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Free(void* _, void* ptr
|
private static void Free(void* _, void* ptr
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, MemoryHandle handle
|
, MemoryHandle handle
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
HeapFree(ptr
|
HeapFree(ptr
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, handle
|
, handle
|
||||||
#else
|
#else
|
||||||
, default
|
, default
|
||||||
@@ -270,7 +256,7 @@ public static unsafe class AllocationManager
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
private static bool IsValid(void* _, MemoryHandle handle)
|
private static bool IsValid(void* _, MemoryHandle handle)
|
||||||
{
|
{
|
||||||
return ContainsAllocation(handle);
|
return ContainsAllocation(handle);
|
||||||
@@ -301,7 +287,7 @@ public static unsafe class AllocationManager
|
|||||||
Alloc = &Allocate,
|
Alloc = &Allocate,
|
||||||
Realloc = &Reallocate,
|
Realloc = &Reallocate,
|
||||||
Free = null,
|
Free = null,
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
IsValid = &IsValid
|
IsValid = &IsValid
|
||||||
#else
|
#else
|
||||||
IsValid = null
|
IsValid = null
|
||||||
@@ -349,7 +335,7 @@ public static unsafe class AllocationManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption
|
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, MemoryHandle* pHandle
|
, MemoryHandle* pHandle
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
@@ -359,20 +345,20 @@ public static unsafe class AllocationManager
|
|||||||
var ptr = s_stack.Allocate(size, alignment, allocationOption);
|
var ptr = s_stack.Allocate(size, alignment, allocationOption);
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = MemoryHandle.Invalid;
|
*pHandle = MemoryHandle.Invalid;
|
||||||
#endif
|
#endif
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = new MemoryHandle(_STACK_MAGIC_ID, (int)s_stack.Offset);
|
*pHandle = new MemoryHandle(_STACK_MAGIC_ID, (int)s_stack.Offset);
|
||||||
#endif
|
#endif
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption
|
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, MemoryHandle* pHandle
|
, MemoryHandle* pHandle
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
@@ -380,7 +366,7 @@ public static unsafe class AllocationManager
|
|||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
return Allocate(instance, newSize, alignment, allocationOption
|
return Allocate(instance, newSize, alignment, allocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, pHandle
|
, pHandle
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
@@ -402,7 +388,7 @@ public static unsafe class AllocationManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = new MemoryHandle(_STACK_MAGIC_ID, (int)s_stack.Offset);
|
*pHandle = new MemoryHandle(_STACK_MAGIC_ID, (int)s_stack.Offset);
|
||||||
#endif
|
#endif
|
||||||
return ptr;
|
return ptr;
|
||||||
@@ -416,13 +402,13 @@ public static unsafe class AllocationManager
|
|||||||
|
|
||||||
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = new MemoryHandle(_STACK_MAGIC_ID, (int)s_stack.Offset);
|
*pHandle = new MemoryHandle(_STACK_MAGIC_ID, (int)s_stack.Offset);
|
||||||
#endif
|
#endif
|
||||||
return newPtr;
|
return newPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
private static bool IsValid(void* instance, MemoryHandle handle)
|
private static bool IsValid(void* instance, MemoryHandle handle)
|
||||||
{
|
{
|
||||||
return handle.ID == _STACK_MAGIC_ID && handle.Generation <= (int)s_stack.Offset;
|
return handle.ID == _STACK_MAGIC_ID && handle.Generation <= (int)s_stack.Offset;
|
||||||
@@ -465,7 +451,7 @@ public static unsafe class AllocationManager
|
|||||||
Alloc = &Allocate,
|
Alloc = &Allocate,
|
||||||
Realloc = &Reallocate,
|
Realloc = &Reallocate,
|
||||||
Free = &Free,
|
Free = &Free,
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
IsValid = &IsValid
|
IsValid = &IsValid
|
||||||
#else
|
#else
|
||||||
IsValid = null
|
IsValid = null
|
||||||
@@ -474,54 +460,26 @@ public static unsafe class AllocationManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption
|
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, MemoryHandle* pHandle
|
, MemoryHandle* pHandle
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var selfPtr = (FreeListAllocator*)instance;
|
var selfPtr = (FreeListAllocator*)instance;
|
||||||
#if ENABLE_DEBUG_LAYER
|
|
||||||
var pad = alignment == 0 ? (nuint)IntPtr.Size : alignment;
|
|
||||||
var total = size + (nuint)sizeof(AllocationHeader) + (pad - 1);
|
|
||||||
var basePtr = selfPtr->_freeList.Allocate(total, pad, allocationOption);
|
|
||||||
if (basePtr == null)
|
|
||||||
{
|
|
||||||
*pHandle = MemoryHandle.Invalid;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var user = AlignUp((byte*)basePtr + (nuint)sizeof(AllocationHeader), pad);
|
|
||||||
var header = (AllocationHeader*)(user - (nuint)sizeof(AllocationHeader));
|
|
||||||
|
|
||||||
header->basePtr = basePtr;
|
|
||||||
header->userSize = size;
|
|
||||||
HeaderSetHandle(header, GCHandle.Alloc(new StackTrace(2, true)));
|
|
||||||
|
|
||||||
LinkHeader(header);
|
|
||||||
|
|
||||||
if (allocationOption.HasFlag(AllocationOption.Clear))
|
|
||||||
{
|
|
||||||
MemClear(user, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
*pHandle = AddAllocation(user, size);
|
|
||||||
return user;
|
|
||||||
#else
|
|
||||||
var ptr = selfPtr->_freeList.Allocate(size, alignment, allocationOption);
|
var ptr = selfPtr->_freeList.Allocate(size, alignment, allocationOption);
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = AddAllocation(ptr, size);
|
*pHandle = AddAllocation(ptr, size);
|
||||||
#endif
|
#endif
|
||||||
return ptr;
|
return ptr;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption
|
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, MemoryHandle* pHandle
|
, MemoryHandle* pHandle
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
@@ -529,48 +487,13 @@ public static unsafe class AllocationManager
|
|||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
return Allocate(instance, newSize, alignment, allocationOption
|
return Allocate(instance, newSize, alignment, allocationOption
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, pHandle
|
, pHandle
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var selfPtr = (FreeListAllocator*)instance;
|
var selfPtr = (FreeListAllocator*)instance;
|
||||||
|
|
||||||
#if ENABLE_DEBUG_LAYER
|
|
||||||
var oldHeader = (AllocationHeader*)((byte*)ptr - (nuint)sizeof(AllocationHeader));
|
|
||||||
var handle = HeaderGetHandle(oldHeader);
|
|
||||||
|
|
||||||
var pad = alignment == 0 ? (nuint)IntPtr.Size : alignment;
|
|
||||||
var total = newSize + (nuint)sizeof(AllocationHeader) + (pad - 1);
|
|
||||||
var newBase = selfPtr->_freeList.Allocate(total, pad, allocationOption);
|
|
||||||
if (newBase == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newUser = AlignUp((byte*)newBase + (nuint)sizeof(AllocationHeader), pad);
|
|
||||||
var newHeader = (AllocationHeader*)(newUser - (nuint)sizeof(AllocationHeader));
|
|
||||||
|
|
||||||
newHeader->basePtr = newBase;
|
|
||||||
newHeader->userSize = newSize;
|
|
||||||
HeaderSetHandle(newHeader, handle);
|
|
||||||
|
|
||||||
LinkHeader(newHeader);
|
|
||||||
|
|
||||||
MemCpy(newUser, ptr, Math.Min(oldSize, newSize));
|
|
||||||
if (allocationOption.HasFlag(AllocationOption.Clear) && newSize > oldSize)
|
|
||||||
{
|
|
||||||
MemClear(newUser + oldSize, newSize - oldSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
UnlinkHeader(oldHeader);
|
|
||||||
selfPtr->_freeList.Free(oldHeader->basePtr);
|
|
||||||
RemoveAllocation(*pHandle);
|
|
||||||
|
|
||||||
*pHandle = AddAllocation(newUser, newSize);
|
|
||||||
return newUser;
|
|
||||||
#else
|
|
||||||
var newPtr = selfPtr->_freeList.Allocate(newSize, alignment, allocationOption);
|
var newPtr = selfPtr->_freeList.Allocate(newSize, alignment, allocationOption);
|
||||||
if (newPtr == null)
|
if (newPtr == null)
|
||||||
{
|
{
|
||||||
@@ -581,16 +504,15 @@ public static unsafe class AllocationManager
|
|||||||
|
|
||||||
selfPtr->_freeList.Free(ptr);
|
selfPtr->_freeList.Free(ptr);
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
RemoveAllocation(*pHandle);
|
RemoveAllocation(*pHandle);
|
||||||
*pHandle = AddAllocation(newPtr, newSize);
|
*pHandle = AddAllocation(newPtr, newSize);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return newPtr;
|
return newPtr;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
private static bool IsValid(void* instance, MemoryHandle handle)
|
private static bool IsValid(void* instance, MemoryHandle handle)
|
||||||
{
|
{
|
||||||
return ContainsAllocation(handle);
|
return ContainsAllocation(handle);
|
||||||
@@ -598,23 +520,15 @@ public static unsafe class AllocationManager
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
private static void Free(void* instance, void* ptr
|
private static void Free(void* instance, void* ptr
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, MemoryHandle handle
|
, MemoryHandle handle
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var selfPtr = (FreeListAllocator*)instance;
|
var selfPtr = (FreeListAllocator*)instance;
|
||||||
#if ENABLE_DEBUG_LAYER
|
|
||||||
var header = (AllocationHeader*)((byte*)ptr - (nuint)sizeof(AllocationHeader));
|
|
||||||
UnlinkHeader(header);
|
|
||||||
HeaderFreeHandle(header);
|
|
||||||
selfPtr->_freeList.Free(header->basePtr);
|
|
||||||
RemoveAllocation(handle);
|
|
||||||
#else
|
|
||||||
selfPtr->_freeList.Free(ptr);
|
selfPtr->_freeList.Free(ptr);
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
RemoveAllocation(handle);
|
RemoveAllocation(handle);
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,14 +543,8 @@ public static unsafe class AllocationManager
|
|||||||
private static StackAllocator* s_pStackAllocator;
|
private static StackAllocator* s_pStackAllocator;
|
||||||
private static FreeListAllocator* s_pFreeListAllocator;
|
private static FreeListAllocator* s_pFreeListAllocator;
|
||||||
|
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
private static SpinLock s_liveLock;
|
|
||||||
private static AllocationHeader* s_pLiveHead;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
|
||||||
private static ConcurrentSlotMap<AllocationInfo> s_allocations = null!;
|
private static ConcurrentSlotMap<AllocationInfo> s_allocations = null!;
|
||||||
public static readonly MemoryHandle MagicHandle = new MemoryHandle(int.MinValue, int.MinValue);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the number of live tracked heap allocations.
|
/// Gets the number of live tracked heap allocations.
|
||||||
@@ -644,7 +552,7 @@ public static unsafe class AllocationManager
|
|||||||
public static int LiveAllocationCount => s_allocations.Count;
|
public static int LiveAllocationCount => s_allocations.Count;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private static bool s_initialized;
|
private static volatile bool s_initialized;
|
||||||
private static nuint s_threadLocalStackDefaultSize;
|
private static nuint s_threadLocalStackDefaultSize;
|
||||||
|
|
||||||
public static void Initialize(AllocationManagerInitOpts opts)
|
public static void Initialize(AllocationManagerInitOpts opts)
|
||||||
@@ -654,11 +562,7 @@ public static unsafe class AllocationManager
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
s_liveLock = new SpinLock(false);
|
|
||||||
s_pLiveHead = null;
|
|
||||||
#endif
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
|
||||||
s_allocations = new ConcurrentSlotMap<AllocationInfo>(256);
|
s_allocations = new ConcurrentSlotMap<AllocationInfo>(256);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -679,161 +583,6 @@ public static unsafe class AllocationManager
|
|||||||
s_initialized = true;
|
s_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_DEBUG_LAYER
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static byte* AlignUp(byte* p, nuint alignment)
|
|
||||||
{
|
|
||||||
var a = alignment == 0 ? (nuint)IntPtr.Size : alignment;
|
|
||||||
return (byte*)(((nuint)p + (a - 1)) & ~(a - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static GCHandle HeaderGetHandle(AllocationHeader* header)
|
|
||||||
{
|
|
||||||
return header->stackHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void HeaderSetHandle(AllocationHeader* header, GCHandle handle)
|
|
||||||
{
|
|
||||||
header->stackHandle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void HeaderFreeHandle(AllocationHeader* header)
|
|
||||||
{
|
|
||||||
if (header->stackHandle.IsAllocated)
|
|
||||||
{
|
|
||||||
header->stackHandle.Free();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void LinkHeader(AllocationHeader* header)
|
|
||||||
{
|
|
||||||
var taken = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
s_liveLock.Enter(ref taken);
|
|
||||||
header->prev = null;
|
|
||||||
header->next = s_pLiveHead;
|
|
||||||
if (s_pLiveHead != null)
|
|
||||||
{
|
|
||||||
s_pLiveHead->prev = header;
|
|
||||||
}
|
|
||||||
s_pLiveHead = header;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (taken)
|
|
||||||
{
|
|
||||||
s_liveLock.Exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void UnlinkHeader(AllocationHeader* header)
|
|
||||||
{
|
|
||||||
var taken = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
s_liveLock.Enter(ref taken);
|
|
||||||
var prev = header->prev;
|
|
||||||
var next = header->next;
|
|
||||||
|
|
||||||
if (prev != null)
|
|
||||||
{
|
|
||||||
prev->next = next;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
s_pLiveHead = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next != null)
|
|
||||||
{
|
|
||||||
next->prev = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
header->prev = header->next = null;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (taken)
|
|
||||||
{
|
|
||||||
s_liveLock.Exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void* DebugAllocate(nuint size, nuint alignment)
|
|
||||||
{
|
|
||||||
// Over-allocate to fit header + alignment padding; we align the user pointer, header is placed just before it.
|
|
||||||
var pad = alignment == 0 ? (nuint)IntPtr.Size : alignment;
|
|
||||||
var total = size + (nuint)sizeof(AllocationHeader) + (pad - 1);
|
|
||||||
|
|
||||||
var basePtr = AlignedAlloc(total, pad);
|
|
||||||
var user = AlignUp((byte*)basePtr + (nuint)sizeof(AllocationHeader), pad);
|
|
||||||
var header = (AllocationHeader*)(user - (nuint)sizeof(AllocationHeader));
|
|
||||||
|
|
||||||
header->basePtr = basePtr;
|
|
||||||
header->userSize = size;
|
|
||||||
HeaderSetHandle(header, GCHandle.Alloc(new StackTrace(2, true)));
|
|
||||||
|
|
||||||
LinkHeader(header);
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void DebugFree(void* userPtr)
|
|
||||||
{
|
|
||||||
var header = (AllocationHeader*)((byte*)userPtr - (nuint)sizeof(AllocationHeader));
|
|
||||||
UnlinkHeader(header);
|
|
||||||
HeaderFreeHandle(header);
|
|
||||||
AlignedFree(header->basePtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void* DebugReallocate(void* userPtr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption)
|
|
||||||
{
|
|
||||||
if (userPtr == null)
|
|
||||||
{
|
|
||||||
return DebugAllocate(newSize, alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
var oldHeader = (AllocationHeader*)((byte*)userPtr - (nuint)sizeof(AllocationHeader));
|
|
||||||
var handle = HeaderGetHandle(oldHeader); // preserve original allocation StackTrace
|
|
||||||
|
|
||||||
var pad = alignment == 0 ? (nuint)IntPtr.Size : alignment;
|
|
||||||
var total = newSize + (nuint)sizeof(AllocationHeader) + (pad - 1);
|
|
||||||
var newBase = AlignedAlloc(total, pad);
|
|
||||||
var newUser = AlignUp((byte*)newBase + (nuint)sizeof(AllocationHeader), pad);
|
|
||||||
var newHeader = (AllocationHeader*)(newUser - (nuint)sizeof(AllocationHeader));
|
|
||||||
|
|
||||||
newHeader->basePtr = newBase;
|
|
||||||
newHeader->userSize = newSize;
|
|
||||||
HeaderSetHandle(newHeader, handle); // transfer ownership to the new header
|
|
||||||
|
|
||||||
LinkHeader(newHeader);
|
|
||||||
|
|
||||||
// Mirror original behavior: copy newSize bytes
|
|
||||||
MemCpy(newUser, userPtr, newSize);
|
|
||||||
if (allocationOption.HasFlag(AllocationOption.Clear) && newSize > oldSize)
|
|
||||||
{
|
|
||||||
MemClear(newUser + oldSize, newSize - oldSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlink and free the old block (without freeing the StackTrace pHandle again)
|
|
||||||
//oldHeader->stackHandle = GCHandle.FromIntPtr(0);
|
|
||||||
UnlinkHeader(oldHeader);
|
|
||||||
AlignedFree(oldHeader->basePtr);
|
|
||||||
|
|
||||||
return newUser;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a reference to the allocation pHandle for the specified allocator type.
|
/// Gets a reference to the allocation pHandle for the specified allocator type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -867,12 +616,7 @@ public static unsafe class AllocationManager
|
|||||||
{
|
{
|
||||||
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
||||||
|
|
||||||
#if ENABLE_DEBUG_LAYER
|
|
||||||
var ptr = DebugAllocate(size, alignment);
|
|
||||||
#else
|
|
||||||
var ptr = AlignedAlloc(size, alignment);
|
var ptr = AlignedAlloc(size, alignment);
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
*pHandle = MemoryHandle.Invalid;
|
*pHandle = MemoryHandle.Invalid;
|
||||||
@@ -884,7 +628,9 @@ public static unsafe class AllocationManager
|
|||||||
MemClear(ptr, size);
|
MemClear(ptr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = AddAllocation(ptr, size);
|
*pHandle = AddAllocation(ptr, size);
|
||||||
|
#endif
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -898,18 +644,9 @@ public static unsafe class AllocationManager
|
|||||||
{
|
{
|
||||||
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
||||||
|
|
||||||
#if ENABLE_DEBUG_LAYER
|
|
||||||
if (handle != MagicHandle)
|
|
||||||
{
|
|
||||||
DebugFree(ptr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
AlignedFree(ptr);
|
AlignedFree(ptr);
|
||||||
}
|
|
||||||
|
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
RemoveAllocation(handle);
|
RemoveAllocation(handle);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -922,9 +659,9 @@ public static unsafe class AllocationManager
|
|||||||
{
|
{
|
||||||
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
||||||
|
|
||||||
if (TryGetAllocation(handle, out var ptr))
|
if (TryGetAllocation(handle, out var info))
|
||||||
{
|
{
|
||||||
HeapFree((void*)ptr, handle);
|
HeapFree((void*)info.Address, handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -953,22 +690,23 @@ public static unsafe class AllocationManager
|
|||||||
/// Registers a memory allocation and returns a handle that can be used to manage or reference the allocated memory.
|
/// Registers a memory allocation and returns a handle that can be used to manage or reference the allocated memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Always returns an invalid handle if ENABLE_SAFETY_CHECKS is disabled.
|
/// Always returns an invalid handle if MHP_ENABLE_SAFETY_CHECKS is disabled.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="ptr">A pointer to the memory block to be registered. The pointer must reference a valid, allocated memory region.</param>
|
/// <param name="ptr">A pointer to the memory block to be registered. The pointer must reference a valid, allocated memory region.</param>
|
||||||
|
/// <param name="size">The size of the memory block to be registered.</param>
|
||||||
/// <returns>A MemoryHandle representing the registered allocation.</returns>
|
/// <returns>A MemoryHandle representing the registered allocation.</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static MemoryHandle AddAllocation(void* ptr, nuint size)
|
public static MemoryHandle AddAllocation(void* ptr, nuint size)
|
||||||
{
|
{
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
||||||
|
|
||||||
var info = new AllocationInfo
|
var info = new AllocationInfo
|
||||||
{
|
{
|
||||||
Address = (IntPtr)ptr,
|
Address = (IntPtr)ptr,
|
||||||
Size = size,
|
Size = size,
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_STACKTRACE
|
||||||
StackTrace = GCHandle.Alloc(new StackTrace(1, true))
|
StackTrace = new StackTrace(1, true)
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -990,9 +728,9 @@ public static unsafe class AllocationManager
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool RemoveAllocation(MemoryHandle handle)
|
public static bool RemoveAllocation(MemoryHandle handle)
|
||||||
{
|
{
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
||||||
return s_allocations.Remove(handle.ID, handle.Generation);
|
return s_allocations.Remove(handle.ID, handle.Generation, out var info);
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
@@ -1005,16 +743,16 @@ public static unsafe class AllocationManager
|
|||||||
/// Always returns false if debug layer is disabled, and the output pointer will be set to <see cref="IntPtr.Zero"/>.
|
/// Always returns false if debug layer is disabled, and the output pointer will be set to <see cref="IntPtr.Zero"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="handle">The memory handle identifying the allocation to retrieve allocation.</param>
|
/// <param name="handle">The memory handle identifying the allocation to retrieve allocation.</param>
|
||||||
/// <param name="ptr">When this method returns, contains the pointer to the memory allocation if found; otherwise, <see cref="IntPtr.Zero"/>.</param>
|
/// <param name="info">When this method returns, contains the allocation information associated with the specified handle, if the allocation was found; otherwise, an uninitialized value.</param>
|
||||||
/// <returns>true if the allocation was found and <paramref name="ptr"/> contains a valid pointer; otherwise, false.</returns>
|
/// <returns>true if the allocation was found and <paramref name="info"/> contains valid allocation information; otherwise, false.</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool TryGetAllocation(MemoryHandle handle, out IntPtr ptr)
|
public static bool TryGetAllocation(MemoryHandle handle, out AllocationInfo info)
|
||||||
{
|
{
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
||||||
return s_allocations.TryGetElement(handle.ID, handle.Generation, out ptr);
|
return s_allocations.TryGetElement(handle.ID, handle.Generation, out info);
|
||||||
#else
|
#else
|
||||||
ptr = IntPtr.Zero;
|
info = default;
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -1032,14 +770,8 @@ public static unsafe class AllocationManager
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool ContainsAllocation(MemoryHandle handle)
|
public static bool ContainsAllocation(MemoryHandle handle)
|
||||||
{
|
{
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
||||||
|
|
||||||
if (handle == MagicHandle)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return s_allocations.Contains(handle.ID, handle.Generation);
|
return s_allocations.Contains(handle.ID, handle.Generation);
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
@@ -1056,51 +788,12 @@ public static unsafe class AllocationManager
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_DEBUG_LAYER
|
s_initialized = false;
|
||||||
// In debug mode, walk the intrusive list to surface any leaks.
|
|
||||||
var snapshot = new List<AllocationInfo>();
|
|
||||||
var taken = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
s_liveLock.Enter(ref taken);
|
|
||||||
if (s_pLiveHead != null)
|
|
||||||
{
|
|
||||||
snapshot.Capacity = 128;
|
|
||||||
for (var p = s_pLiveHead; p != null; p = p->next)
|
|
||||||
{
|
|
||||||
var trace = (StackTrace)HeaderGetHandle(p).Target!;
|
|
||||||
snapshot.Add(new AllocationInfo
|
|
||||||
{
|
|
||||||
Size = p->userSize,
|
|
||||||
StackTrace = trace
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (taken)
|
|
||||||
{
|
|
||||||
s_liveLock.Exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nuint unfreeBytes = 0u;
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
foreach (var info in snapshot)
|
if (s_allocations.Count > 0)
|
||||||
{
|
{
|
||||||
unfreeBytes += info.Size;
|
throw new MemoryLeakException(s_allocations);
|
||||||
}
|
|
||||||
|
|
||||||
if (unfreeBytes > 0u)
|
|
||||||
{
|
|
||||||
throw new MemoryLeakException(CollectionsMarshal.AsSpan(snapshot));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
|
||||||
if (s_allocations.Count != 0)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"There are still {s_allocations.Count} live tracked allocations.");
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1109,7 +802,5 @@ public static unsafe class AllocationManager
|
|||||||
s_pFreeListAllocator->Dispose();
|
s_pFreeListAllocator->Dispose();
|
||||||
|
|
||||||
Free(s_pArenaAllocator);
|
Free(s_pArenaAllocator);
|
||||||
|
|
||||||
s_initialized = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,31 +26,55 @@ public unsafe struct MemoryPool<T, TOpts> : IDisposable
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Allocate(void* pAllocator, nuint size, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
|
private static void* Allocate(void* pAllocator, nuint size, nuint alignment, AllocationOption allocationOption
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle* pHandle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return ((T*)pAllocator)->Allocate(size, alignment, allocationOption);
|
return ((T*)pAllocator)->Allocate(size, alignment, allocationOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Reallocate(void* pAllocator, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
|
private static void* Reallocate(void* pAllocator, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle* pHandle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
return Allocate(pAllocator, newSize, alignment, allocationOption, pHandle);
|
return Allocate(pAllocator, newSize, alignment, allocationOption
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
|
, pHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var newPtr = Allocate(pAllocator, newSize, alignment, allocationOption, pHandle);
|
var newPtr = Allocate(pAllocator, newSize, alignment, allocationOption
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
|
, pHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
if (newPtr == null)
|
if (newPtr == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
||||||
Free(pAllocator, ptr, *pHandle);
|
Free(pAllocator, ptr
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
|
, *pHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
return newPtr;
|
return newPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Free(void* pAllocator, void* ptr, MemoryHandle handle)
|
private static void Free(void* pAllocator, void* ptr
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle handle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
((T*)pAllocator)->Free(ptr);
|
((T*)pAllocator)->Free(ptr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public unsafe partial struct Stack : IMemoryAllocator<Stack, Stack.CreationOpts>
|
|||||||
_allocator = allocator;
|
_allocator = allocator;
|
||||||
_handle = handle;
|
_handle = handle;
|
||||||
_originalOffset = allocator->_offset;
|
_originalOffset = allocator->_offset;
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
_allocator->_activeScopeCount++;
|
_allocator->_activeScopeCount++;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ public unsafe partial struct Stack : IMemoryAllocator<Stack, Stack.CreationOpts>
|
|||||||
if (_allocator != null)
|
if (_allocator != null)
|
||||||
{
|
{
|
||||||
_allocator->_offset = _allocator->_offset > _originalOffset ? _originalOffset : _allocator->_offset;
|
_allocator->_offset = _allocator->_offset > _originalOffset ? _originalOffset : _allocator->_offset;
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
_allocator->_activeScopeCount--;
|
_allocator->_activeScopeCount--;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ public unsafe partial struct Stack : IMemoryAllocator<Stack, Stack.CreationOpts>
|
|||||||
private byte* _buffer;
|
private byte* _buffer;
|
||||||
private nuint _size;
|
private nuint _size;
|
||||||
private nuint _offset;
|
private nuint _offset;
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
private uint _activeScopeCount;
|
private uint _activeScopeCount;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ public unsafe partial struct Stack : IMemoryAllocator<Stack, Stack.CreationOpts>
|
|||||||
_buffer = (byte*)Malloc(size);
|
_buffer = (byte*)Malloc(size);
|
||||||
_size = size;
|
_size = size;
|
||||||
_offset = 0;
|
_offset = 0;
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
_activeScopeCount = 0;
|
_activeScopeCount = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ public unsafe partial struct Stack : IMemoryAllocator<Stack, Stack.CreationOpts>
|
|||||||
/// there is insufficient space in the buffer.</returns>
|
/// there is insufficient space in the buffer.</returns>
|
||||||
public void* Allocate(nuint size, nuint alignment, AllocationOption allocationOption = AllocationOption.None)
|
public void* Allocate(nuint size, nuint alignment, AllocationOption allocationOption = AllocationOption.None)
|
||||||
{
|
{
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
if (_activeScopeCount == 0)
|
if (_activeScopeCount == 0)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Allocations can only be made within an active memory scope.");
|
throw new InvalidOperationException("Allocations can only be made within an active memory scope.");
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public unsafe struct VirtualStack : IMemoryAllocator<VirtualStack, VirtualStack.
|
|||||||
_allocator = allocator;
|
_allocator = allocator;
|
||||||
_handle = handle;
|
_handle = handle;
|
||||||
_originalOffset = allocator->_allocatedOffset;
|
_originalOffset = allocator->_allocatedOffset;
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
_allocator->_activeScopeCount++;
|
_allocator->_activeScopeCount++;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ public unsafe struct VirtualStack : IMemoryAllocator<VirtualStack, VirtualStack.
|
|||||||
if (_allocator != null)
|
if (_allocator != null)
|
||||||
{
|
{
|
||||||
_allocator->_allocatedOffset = _allocator->_allocatedOffset > _originalOffset ? _originalOffset : _allocator->_allocatedOffset;
|
_allocator->_allocatedOffset = _allocator->_allocatedOffset > _originalOffset ? _originalOffset : _allocator->_allocatedOffset;
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
_allocator->_activeScopeCount--;
|
_allocator->_activeScopeCount--;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ public unsafe struct VirtualStack : IMemoryAllocator<VirtualStack, VirtualStack.
|
|||||||
private nuint _reserveCapacity;
|
private nuint _reserveCapacity;
|
||||||
private nuint _committedSize;
|
private nuint _committedSize;
|
||||||
private nuint _allocatedOffset;
|
private nuint _allocatedOffset;
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
private uint _activeScopeCount;
|
private uint _activeScopeCount;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ public unsafe struct VirtualStack : IMemoryAllocator<VirtualStack, VirtualStack.
|
|||||||
|
|
||||||
_baseAddress = (byte*)Mmap(null, _reserveCapacity, VirtualAllocationFlags.Reserve);
|
_baseAddress = (byte*)Mmap(null, _reserveCapacity, VirtualAllocationFlags.Reserve);
|
||||||
|
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
_activeScopeCount = 0;
|
_activeScopeCount = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ public unsafe struct VirtualStack : IMemoryAllocator<VirtualStack, VirtualStack.
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public void* Allocate(nuint size, nuint alignment, AllocationOption option = AllocationOption.None)
|
public void* Allocate(nuint size, nuint alignment, AllocationOption option = AllocationOption.None)
|
||||||
{
|
{
|
||||||
#if ENABLE_SAFETY_CHECKS
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
if (_activeScopeCount == 0)
|
if (_activeScopeCount == 0)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Allocations can only be made within an active memory scope.");
|
throw new InvalidOperationException("Allocations can only be made within an active memory scope.");
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ public unsafe interface IUnsafeCollection : IDisposable
|
|||||||
/// Indicates whether the object has been created. Returns true if the object is created, otherwise false.
|
/// Indicates whether the object has been created. Returns true if the object is created, otherwise false.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// If ENABLE_DEBUG_LAYER is not defined, this property will only check if the underlying pointer is not null, which may not be sufficient to determine if the collection is fully initialized and ready for use.
|
/// If MHP_ENABLE_STACKTRACE is not defined, this property will only check if the underlying pointer is not null, which may not be sufficient to determine if the collection is fully initialized and ready for use.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
bool IsCreated
|
bool IsCreated
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
private readonly int _sizeOfTValue;
|
private readonly int _sizeOfTValue;
|
||||||
private readonly int _log2MinGrowth;
|
private readonly int _log2MinGrowth;
|
||||||
|
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
private MemoryHandle _memoryHandle;
|
private MemoryHandle _memoryHandle;
|
||||||
#endif
|
#endif
|
||||||
private AllocationHandle _allocationHandle;
|
private AllocationHandle _allocationHandle;
|
||||||
@@ -96,7 +96,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
if (_buffer != null)
|
if (_buffer != null)
|
||||||
{
|
{
|
||||||
if (_allocationHandle.IsValid != null)
|
if (_allocationHandle.IsValid != null)
|
||||||
@@ -161,7 +161,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
[Conditional("ENABLE_SAFETY_CHECKS")]
|
[Conditional("MHP_ENABLE_SAFETY_CHECKS")]
|
||||||
private readonly void ThrowIfNotCreated()
|
private readonly void ThrowIfNotCreated()
|
||||||
{
|
{
|
||||||
if (!IsCreated)
|
if (!IsCreated)
|
||||||
@@ -256,11 +256,11 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
throw new InvalidOperationException("Target allocation handle does not support allocation.");
|
throw new InvalidOperationException("Target allocation handle does not support allocation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
MemoryHandle memHandle;
|
MemoryHandle memHandle;
|
||||||
#endif
|
#endif
|
||||||
var buf = (byte*)_allocationHandle.Alloc(_allocationHandle.State, (uint)totalSize, (nuint)_alignment, allocationOption
|
var buf = (byte*)_allocationHandle.Alloc(_allocationHandle.State, (uint)totalSize, (nuint)_alignment, allocationOption
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, &memHandle
|
, &memHandle
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
@@ -269,7 +269,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
_keys = (TKey*)(_buffer + keyOffset);
|
_keys = (TKey*)(_buffer + keyOffset);
|
||||||
_next = (int*)(_buffer + nextOffset);
|
_next = (int*)(_buffer + nextOffset);
|
||||||
_buckets = (int*)(_buffer + bucketOffset);
|
_buckets = (int*)(_buffer + bucketOffset);
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
_memoryHandle = memHandle;
|
_memoryHandle = memHandle;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -284,7 +284,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
var oldNext = _next;
|
var oldNext = _next;
|
||||||
var oldBuckets = _buckets;
|
var oldBuckets = _buckets;
|
||||||
var oldBucketCapacity = _bucketCapacity;
|
var oldBucketCapacity = _bucketCapacity;
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
var oldMemoryHandle = _memoryHandle;
|
var oldMemoryHandle = _memoryHandle;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -306,7 +306,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
if (_allocationHandle.Free != null)
|
if (_allocationHandle.Free != null)
|
||||||
{
|
{
|
||||||
_allocationHandle.Free(_allocationHandle.State, oldBuffer
|
_allocationHandle.Free(_allocationHandle.State, oldBuffer
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, oldMemoryHandle
|
, oldMemoryHandle
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
@@ -725,7 +725,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
if (_allocationHandle.Free != null)
|
if (_allocationHandle.Free != null)
|
||||||
{
|
{
|
||||||
_allocationHandle.Free(_allocationHandle.State, _buffer
|
_allocationHandle.Free(_allocationHandle.State, _buffer
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, _memoryHandle
|
, _memoryHandle
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public readonly unsafe struct ReadOnlyUnsafeCollection<T> : IEnumerable<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
[Conditional("ENABLE_SAFETY_CHECKS")]
|
[Conditional("MHP_ENABLE_SAFETY_CHECKS")]
|
||||||
private readonly void CheckIndexBounds(int index)
|
private readonly void CheckIndexBounds(int index)
|
||||||
{
|
{
|
||||||
if (index >= _count)
|
if (index >= _count)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ public unsafe struct UnTypedArray : IUnTypedCollection
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
if (_buffer != null)
|
if (_buffer != null)
|
||||||
{
|
{
|
||||||
if (_allocationHandle.IsValid != null)
|
if (_allocationHandle.IsValid != null)
|
||||||
@@ -34,6 +35,9 @@ public unsafe struct UnTypedArray : IUnTypedCollection
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
#else
|
||||||
|
return _buffer != null;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,12 +61,20 @@ public unsafe struct UnTypedArray : IUnTypedCollection
|
|||||||
throw new InvalidOperationException("Target allocation handle does not support allocation.");
|
throw new InvalidOperationException("Target allocation handle does not support allocation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
MemoryHandle memHandle;
|
MemoryHandle memHandle;
|
||||||
_buffer = handle.Alloc(_allocationHandle.State, size, alignment, allocationOption, &memHandle);
|
#endif
|
||||||
|
var buff = handle.Alloc(handle.State, size, alignment, allocationOption
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
|
, &memHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
_size = size;
|
_size = size;
|
||||||
_alignment = alignment;
|
_alignment = alignment;
|
||||||
|
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
_memoryHandle = memHandle;
|
_memoryHandle = memHandle;
|
||||||
|
#endif
|
||||||
_allocationHandle = handle;
|
_allocationHandle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,10 +121,18 @@ public unsafe struct UnTypedArray : IUnTypedCollection
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
var memHandle = _memoryHandle;
|
var memHandle = _memoryHandle;
|
||||||
_buffer = _allocationHandle.Realloc(_allocationHandle.State, _buffer, _size, newSize, _alignment, option, &memHandle);
|
#endif
|
||||||
|
_buffer = _allocationHandle.Realloc(_allocationHandle.State, _buffer, _size, newSize, _alignment, option
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
|
, &memHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
_size = newSize;
|
_size = newSize;
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
_memoryHandle = memHandle;
|
_memoryHandle = memHandle;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -257,7 +277,11 @@ public unsafe struct UnTypedArray : IUnTypedCollection
|
|||||||
|
|
||||||
if (_allocationHandle.Free != null)
|
if (_allocationHandle.Free != null)
|
||||||
{
|
{
|
||||||
_allocationHandle.Free(_allocationHandle.State, _buffer, _memoryHandle);
|
_allocationHandle.Free(_allocationHandle.State, _buffer
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
|
, _memoryHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffer = null;
|
_buffer = null;
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
|
|
||||||
private T* _buffer;
|
private T* _buffer;
|
||||||
private int _count;
|
private int _count;
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
private MemoryHandle _memoryHandle;
|
private MemoryHandle _memoryHandle;
|
||||||
#endif
|
#endif
|
||||||
private AllocationHandle _allocationHandle;
|
private AllocationHandle _allocationHandle;
|
||||||
@@ -108,7 +108,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
if (_buffer != null)
|
if (_buffer != null)
|
||||||
{
|
{
|
||||||
if (_allocationHandle.IsValid != null)
|
if (_allocationHandle.IsValid != null)
|
||||||
@@ -167,17 +167,17 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
throw new InvalidOperationException("Target allocation handle does not support allocation.");
|
throw new InvalidOperationException("Target allocation handle does not support allocation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
MemoryHandle memHandle;
|
MemoryHandle memHandle;
|
||||||
#endif
|
#endif
|
||||||
var buff = handle.Alloc(handle.State, (nuint)(count * sizeof(T)), AlignOf<T>(), allocationOption
|
var buff = handle.Alloc(handle.State, (nuint)(count * sizeof(T)), AlignOf<T>(), allocationOption
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, &memHandle
|
, &memHandle
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
_buffer = (T*)buff;
|
_buffer = (T*)buff;
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
_memoryHandle = memHandle;
|
_memoryHandle = memHandle;
|
||||||
#endif
|
#endif
|
||||||
_allocationHandle = handle;
|
_allocationHandle = handle;
|
||||||
@@ -213,7 +213,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
[Conditional("ENABLE_SAFETY_CHECKS")]
|
[Conditional("MHP_ENABLE_SAFETY_CHECKS")]
|
||||||
private readonly void ThrowIfNotCreated()
|
private readonly void ThrowIfNotCreated()
|
||||||
{
|
{
|
||||||
if (!IsCreated)
|
if (!IsCreated)
|
||||||
@@ -223,7 +223,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
[Conditional("ENABLE_SAFETY_CHECKS")]
|
[Conditional("MHP_ENABLE_SAFETY_CHECKS")]
|
||||||
private readonly void CheckIndexBounds(int index)
|
private readonly void CheckIndexBounds(int index)
|
||||||
{
|
{
|
||||||
ThrowIfNotCreated();
|
ThrowIfNotCreated();
|
||||||
@@ -258,16 +258,16 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
MemoryHandle memHandle = _memoryHandle;
|
MemoryHandle memHandle = _memoryHandle;
|
||||||
#endif
|
#endif
|
||||||
var elemSize = SizeOf<T>();
|
var elemSize = SizeOf<T>();
|
||||||
_buffer = (T*)_allocationHandle.Realloc(_allocationHandle.State, _buffer, (nuint)Count * elemSize, (nuint)newSize * elemSize, AlignOf<T>(), option
|
_buffer = (T*)_allocationHandle.Realloc(_allocationHandle.State, _buffer, (nuint)Count * elemSize, (nuint)newSize * elemSize, AlignOf<T>(), option
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, &memHandle
|
, &memHandle
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
_memoryHandle = memHandle;
|
_memoryHandle = memHandle;
|
||||||
#endif
|
#endif
|
||||||
_count = newSize;
|
_count = newSize;
|
||||||
@@ -424,7 +424,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
if (_allocationHandle.Free != null)
|
if (_allocationHandle.Free != null)
|
||||||
{
|
{
|
||||||
_allocationHandle.Free(_allocationHandle.State, _buffer
|
_allocationHandle.Free(_allocationHandle.State, _buffer
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
, _memoryHandle
|
, _memoryHandle
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -207,14 +207,14 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
[Conditional("ENABLE_SAFETY_CHECKS")]
|
[Conditional("MHP_ENABLE_SAFETY_CHECKS")]
|
||||||
private readonly void CheckNoResizeCapacity(int count)
|
private readonly void CheckNoResizeCapacity(int count)
|
||||||
{
|
{
|
||||||
CheckNoResizeCapacity(count, Count);
|
CheckNoResizeCapacity(count, Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
[Conditional("ENABLE_SAFETY_CHECKS")]
|
[Conditional("MHP_ENABLE_SAFETY_CHECKS")]
|
||||||
private readonly void CheckNoResizeCapacity(int index, int count)
|
private readonly void CheckNoResizeCapacity(int index, int count)
|
||||||
{
|
{
|
||||||
if (index + count > Capacity)
|
if (index + count > Capacity)
|
||||||
|
|||||||
@@ -14,15 +14,21 @@ public class MemoryLeakException : Exception
|
|||||||
|
|
||||||
public override string Message => _message;
|
public override string Message => _message;
|
||||||
|
|
||||||
public MemoryLeakException(ReadOnlySpan<AllocationInfo> infos)
|
public MemoryLeakException(IEnumerable<AllocationInfo> infos)
|
||||||
{
|
{
|
||||||
var stringBuilder = new StringBuilder();
|
var stringBuilder = new StringBuilder();
|
||||||
stringBuilder.AppendLine($"Found {infos.Length} memory lakes!");
|
stringBuilder.AppendLine($"Found {infos.Count()} memory lakes!");
|
||||||
|
|
||||||
|
#if MHP_ENABLE_STACKTRACE
|
||||||
|
stringBuilder.AppendLine();
|
||||||
|
|
||||||
foreach (var info in infos)
|
foreach (var info in infos)
|
||||||
{
|
{
|
||||||
stringBuilder.AppendLine(GetMessage(info.StackTrace));
|
GetMessage(stringBuilder, info.StackTrace);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
stringBuilder.AppendLine("No stack trace information available. Please enable MHP_ENABLE_STACKTRACE for detailed leak information.");
|
||||||
|
#endif
|
||||||
|
|
||||||
_message = stringBuilder.ToString();
|
_message = stringBuilder.ToString();
|
||||||
}
|
}
|
||||||
@@ -32,14 +38,14 @@ public class MemoryLeakException : Exception
|
|||||||
_message = message;
|
_message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetMessage(StackTrace? stackTrace)
|
private static void GetMessage(StringBuilder stringBuilder, StackTrace? stackTrace)
|
||||||
{
|
{
|
||||||
if (stackTrace == null)
|
if (stackTrace == null)
|
||||||
{
|
{
|
||||||
return "No stack trace available.";
|
stringBuilder.AppendLine("No stack trace available.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var stringBuilder = new StringBuilder();
|
|
||||||
stringBuilder.AppendLine("Memory leak detected at: ");
|
stringBuilder.AppendLine("Memory leak detected at: ");
|
||||||
|
|
||||||
for (var i = 0; i < stackTrace.FrameCount; i++)
|
for (var i = 0; i < stackTrace.FrameCount; i++)
|
||||||
@@ -49,10 +55,9 @@ public class MemoryLeakException : Exception
|
|||||||
|
|
||||||
if (frame != null)
|
if (frame != null)
|
||||||
{
|
{
|
||||||
stringBuilder.AppendLine($"File: {fileName}, Method: {DiagnosticMethodInfo.Create(frame)?.Name}, Line: {frame.GetFileLineNumber()}");
|
var methodInfo = DiagnosticMethodInfo.Create(frame);
|
||||||
|
stringBuilder.AppendLine($"File: {fileName}, Type: {methodInfo?.DeclaringTypeName}, Method: {methodInfo?.Name}, Line: {frame.GetFileLineNumber()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return stringBuilder.ToString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<Authors>Misaki</Authors>
|
<Authors>Misaki</Authors>
|
||||||
<AssemblyVersion>1.5.4</AssemblyVersion>
|
<AssemblyVersion>1.6.0</AssemblyVersion>
|
||||||
<Version>$(AssemblyVersion)</Version>
|
<Version>$(AssemblyVersion)</Version>
|
||||||
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
|
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
|
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<IsAotCompatible>True</IsAotCompatible>
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
<DefineConstants>$(DefineConstants);ENABLE_SAFETY_CHECKS</DefineConstants>
|
<!--<DefineConstants>$(DefineConstants);MHP_ENABLE_SAFETY_CHECKS;MHP_ENABLE_STACKTRACE</DefineConstants>-->
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
|||||||
@@ -59,12 +59,12 @@ arr.Dispose();
|
|||||||
AllocationManager.Dispose();
|
AllocationManager.Dispose();
|
||||||
```
|
```
|
||||||
|
|
||||||
You can enable debug features for leak detection and use-after-free checks by defining `ENABLE_SAFETY_CHECKS` in your project. And define `ENABLE_DEBUG_LAYER` to enable additional debug features such as tracking allocations and providing detailed error messages.
|
You can enable debug features for leak detection and use-after-free checks by defining `MHP_ENABLE_SAFETY_CHECKS` in your project. And define `MHP_ENABLE_STACKTRACE` to enable additional debug features such as tracking allocations and providing detailed error messages.
|
||||||
> Which means if you disable the safety checks, the library will not perform any safety checks and provide the maximum performance, and it will be your responsibility to ensure correct usage to avoid memory leaks and undefined behavior.
|
> Which means if you disable the safety checks, the library will not perform any safety checks and provide the maximum performance, and it will be your responsibility to ensure correct usage to avoid memory leaks and undefined behavior.
|
||||||
> Even `IUnsafeCollection.IsCreated` will only check if the internal pointer is non-null, without verifying the actual validity of the memory.
|
> Even `IUnsafeCollection.IsCreated` will only check if the internal pointer is non-null, without verifying the actual validity of the memory.
|
||||||
|
|
||||||
You can also define `ENABLE_MIMALLOC` to use mimalloc as the underlying allocator instead of the default C allocator.
|
You can also define `MHP_ENABLE_MIMALLOC` to use mimalloc as the underlying allocator instead of the default C allocator.
|
||||||
> Using mimalloc requires to install the TerraFX.Interop.Mimalloc package and ensure the native mimalloc library is available at runtime.
|
> Using mimalloc requires to install the `TerraFX.Interop.Mimalloc` package.
|
||||||
|
|
||||||
## Package reference
|
## Package reference
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
#if ENABLE_MIMALLOC
|
#if MHP_ENABLE_MIMALLOC
|
||||||
using TerraFX.Interop.Mimalloc;
|
using TerraFX.Interop.Mimalloc;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ public static unsafe partial class MemoryUtility
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void* Malloc(nuint size)
|
public static void* Malloc(nuint size)
|
||||||
{
|
{
|
||||||
#if ENABLE_MIMALLOC
|
#if MHP_ENABLE_MIMALLOC
|
||||||
return Mimalloc.mi_malloc(size);
|
return Mimalloc.mi_malloc(size);
|
||||||
#elif NET6_0_OR_GREATER
|
#elif NET6_0_OR_GREATER
|
||||||
return NativeMemory.Alloc(size);
|
return NativeMemory.Alloc(size);
|
||||||
@@ -86,7 +86,7 @@ public static unsafe partial class MemoryUtility
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void* Calloc(nuint size)
|
public static void* Calloc(nuint size)
|
||||||
{
|
{
|
||||||
#if ENABLE_MIMALLOC
|
#if MHP_ENABLE_MIMALLOC
|
||||||
return Mimalloc.mi_zalloc(size);
|
return Mimalloc.mi_zalloc(size);
|
||||||
#elif NET6_0_OR_GREATER
|
#elif NET6_0_OR_GREATER
|
||||||
return NativeMemory.AllocZeroed(size);
|
return NativeMemory.AllocZeroed(size);
|
||||||
@@ -106,7 +106,7 @@ public static unsafe partial class MemoryUtility
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void* AlignedAlloc(nuint size, nuint alignment)
|
public static void* AlignedAlloc(nuint size, nuint alignment)
|
||||||
{
|
{
|
||||||
#if ENABLE_MIMALLOC
|
#if MHP_ENABLE_MIMALLOC
|
||||||
return Mimalloc.mi_aligned_alloc(alignment, size);
|
return Mimalloc.mi_aligned_alloc(alignment, size);
|
||||||
#elif NET6_0_OR_GREATER
|
#elif NET6_0_OR_GREATER
|
||||||
return NativeMemory.AlignedAlloc(size, alignment);
|
return NativeMemory.AlignedAlloc(size, alignment);
|
||||||
@@ -124,7 +124,7 @@ public static unsafe partial class MemoryUtility
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void* Realloc(void* ptr, nuint size)
|
public static void* Realloc(void* ptr, nuint size)
|
||||||
{
|
{
|
||||||
#if ENABLE_MIMALLOC
|
#if MHP_ENABLE_MIMALLOC
|
||||||
return Mimalloc.mi_realloc(ptr, size);
|
return Mimalloc.mi_realloc(ptr, size);
|
||||||
#elif NET6_0_OR_GREATER
|
#elif NET6_0_OR_GREATER
|
||||||
return NativeMemory.Realloc(ptr, size);
|
return NativeMemory.Realloc(ptr, size);
|
||||||
@@ -144,7 +144,7 @@ public static unsafe partial class MemoryUtility
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void* AlignedRealloc(void* ptr, nuint size, nuint alignment)
|
public static void* AlignedRealloc(void* ptr, nuint size, nuint alignment)
|
||||||
{
|
{
|
||||||
#if ENABLE_MIMALLOC
|
#if MHP_ENABLE_MIMALLOC
|
||||||
return Mimalloc.mi_realloc_aligned(ptr, size, alignment);
|
return Mimalloc.mi_realloc_aligned(ptr, size, alignment);
|
||||||
#elif NET6_0_OR_GREATER
|
#elif NET6_0_OR_GREATER
|
||||||
return NativeMemory.AlignedRealloc(ptr, size, alignment);
|
return NativeMemory.AlignedRealloc(ptr, size, alignment);
|
||||||
@@ -172,7 +172,7 @@ public static unsafe partial class MemoryUtility
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Free(void* ptr)
|
public static void Free(void* ptr)
|
||||||
{
|
{
|
||||||
#if ENABLE_MIMALLOC
|
#if MHP_ENABLE_MIMALLOC
|
||||||
Mimalloc.mi_free(ptr);
|
Mimalloc.mi_free(ptr);
|
||||||
#elif NET6_0_OR_GREATER
|
#elif NET6_0_OR_GREATER
|
||||||
NativeMemory.Free(ptr);
|
NativeMemory.Free(ptr);
|
||||||
@@ -189,7 +189,7 @@ public static unsafe partial class MemoryUtility
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void AlignedFree(void* ptr)
|
public static void AlignedFree(void* ptr)
|
||||||
{
|
{
|
||||||
#if ENABLE_MIMALLOC
|
#if MHP_ENABLE_MIMALLOC
|
||||||
Mimalloc.mi_free(ptr);
|
Mimalloc.mi_free(ptr);
|
||||||
#elif NET6_0_OR_GREATER
|
#elif NET6_0_OR_GREATER
|
||||||
NativeMemory.AlignedFree(ptr);
|
NativeMemory.AlignedFree(ptr);
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ using Misaki.HighPerformance.Mathematics;
|
|||||||
float radians = math.radians(90f);
|
float radians = math.radians(90f);
|
||||||
float degrees = math.degrees(radians);
|
float degrees = math.degrees(radians);
|
||||||
float tau = math.TAU;
|
float tau = math.TAU;
|
||||||
|
|
||||||
|
float3 a = new float3(1f, 2f, 3f);
|
||||||
|
float3 b = new float3(4f, 5f, 6f);
|
||||||
|
float3 c = math.cross(a, b);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Package reference
|
## Package reference
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.Test.Benchmark;
|
namespace Misaki.HighPerformance.Test.Benchmark;
|
||||||
@@ -31,8 +31,6 @@ public class HashCodeBenchmark
|
|||||||
private Dictionary<Type, int> _hashCache = new();
|
private Dictionary<Type, int> _hashCache = new();
|
||||||
//private UnsafeHashMap<Guid, int> _hashMap = new(16);
|
//private UnsafeHashMap<Guid, int> _hashMap = new(16);
|
||||||
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
//~HashCodeBenchmark()
|
//~HashCodeBenchmark()
|
||||||
//{
|
//{
|
||||||
// Dispose();
|
// Dispose();
|
||||||
|
|||||||
@@ -2,35 +2,6 @@ using Misaki.HighPerformance.LowLevel.Buffer;
|
|||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|
||||||
//BenchmarkRunner.Run<SPMDBenchmark>();
|
//BenchmarkRunner.Run<SPMDBenchmark>();
|
||||||
//var hashMap = new UnsafeHashMap<int, int>(10, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
|
||||||
//hashMap[0] = 5;
|
|
||||||
//hashMap[1] = 6;
|
|
||||||
|
|
||||||
//Console.WriteLine(hashMap[1]);
|
|
||||||
|
|
||||||
//ref var v = ref hashMap.GetValueRefOrAddDefault(1, out var exists);
|
|
||||||
|
|
||||||
//Console.WriteLine(exists);
|
|
||||||
|
|
||||||
//v = 10;
|
|
||||||
//Console.WriteLine(hashMap[1]);
|
|
||||||
|
|
||||||
//hashMap.Dispose();
|
|
||||||
|
|
||||||
//class TestClass
|
|
||||||
//{
|
|
||||||
// private UnsafeHashMap<int, int> _map;
|
|
||||||
// ref int Test()
|
|
||||||
// {
|
|
||||||
// ref var x = ref _map.GetValueRef(9, out var exists);
|
|
||||||
// if (exists)
|
|
||||||
// {
|
|
||||||
// return ref x;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return ref Unsafe.NullRef<int>();
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
var opts = new AllocationManagerInitOpts
|
var opts = new AllocationManagerInitOpts
|
||||||
{
|
{
|
||||||
@@ -41,8 +12,11 @@ var opts = new AllocationManagerInitOpts
|
|||||||
|
|
||||||
AllocationManager.Initialize(opts);
|
AllocationManager.Initialize(opts);
|
||||||
|
|
||||||
using var arr = new UnsafeArray<int>(10, Allocator.FreeList);
|
var arr = new UnsafeArray<int>(10, Allocator.Persistent);
|
||||||
var marr = new int[10];
|
var arrcpy = arr;
|
||||||
arr.CopyTo(marr);
|
arr.Dispose();
|
||||||
|
|
||||||
|
Console.WriteLine(arr.IsCreated);
|
||||||
|
Console.WriteLine(arrcpy.IsCreated);
|
||||||
|
|
||||||
AllocationManager.Dispose();
|
AllocationManager.Dispose();
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ public class TestAllocationManager
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
var leaks = AllocationManager.LiveAllocationCount;
|
var leaks = AllocationManager.LiveAllocationCount;
|
||||||
Assert.AreEqual(0, leaks);
|
Assert.AreEqual(0, leaks);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,8 +41,10 @@ public class TestAllocationManager
|
|||||||
}
|
}
|
||||||
catch (MemoryLeakException)
|
catch (MemoryLeakException)
|
||||||
{
|
{
|
||||||
|
#if MHP_ENABLE_SAFETY_CHECKS
|
||||||
var leaks = AllocationManager.LiveAllocationCount;
|
var leaks = AllocationManager.LiveAllocationCount;
|
||||||
Assert.AreEqual(2, leaks);
|
Assert.AreEqual(2, leaks);
|
||||||
|
#endif
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -50,6 +54,8 @@ public class TestAllocationManager
|
|||||||
array2.Dispose();
|
array2.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
Assert.Fail("Expected MemoryLeakException was not thrown.");
|
Assert.Fail("Expected MemoryLeakException was not thrown.");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ public class ConcurrentSlotMap<T> : IEnumerable<T>
|
|||||||
{
|
{
|
||||||
private struct SlotEntry
|
private struct SlotEntry
|
||||||
{
|
{
|
||||||
public T? value;
|
public T value;
|
||||||
public int generation;
|
public int generation;
|
||||||
public int isValid;
|
public int isValid;
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ public class ConcurrentSlotMap<T> : IEnumerable<T>
|
|||||||
public Enumerator(ConcurrentSlotMap<T> slotMap)
|
public Enumerator(ConcurrentSlotMap<T> slotMap)
|
||||||
{
|
{
|
||||||
_slotMap = slotMap;
|
_slotMap = slotMap;
|
||||||
_currentIndex = 0;
|
_currentIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly T Current => _slotMap._data[_currentIndex].value!;
|
public readonly T Current => _slotMap._data[_currentIndex].value!;
|
||||||
@@ -42,7 +42,10 @@ public class ConcurrentSlotMap<T> : IEnumerable<T>
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset() => _currentIndex = 0;
|
public void Reset()
|
||||||
|
{
|
||||||
|
_currentIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
@@ -177,11 +180,17 @@ public class ConcurrentSlotMap<T> : IEnumerable<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool Remove(int slotIndex, int generation)
|
public bool Remove(int slotIndex, int generation)
|
||||||
|
{
|
||||||
|
return Remove(slotIndex, generation, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(int slotIndex, int generation, [MaybeNullWhen(false)] out T value)
|
||||||
{
|
{
|
||||||
var capacity = Volatile.Read(ref _capacity);
|
var capacity = Volatile.Read(ref _capacity);
|
||||||
|
|
||||||
if (slotIndex < 0 || slotIndex >= capacity)
|
if (slotIndex < 0 || slotIndex >= capacity)
|
||||||
{
|
{
|
||||||
|
value = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,6 +199,7 @@ public class ConcurrentSlotMap<T> : IEnumerable<T>
|
|||||||
// Check if slot is valid and generation matches
|
// Check if slot is valid and generation matches
|
||||||
if (Volatile.Read(ref slot.isValid) == 0 || Volatile.Read(ref slot.generation) != generation)
|
if (Volatile.Read(ref slot.isValid) == 0 || Volatile.Read(ref slot.generation) != generation)
|
||||||
{
|
{
|
||||||
|
value = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,13 +207,15 @@ public class ConcurrentSlotMap<T> : IEnumerable<T>
|
|||||||
if (Interlocked.CompareExchange(ref slot.isValid, 0, 1) == 1)
|
if (Interlocked.CompareExchange(ref slot.isValid, 0, 1) == 1)
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref slot.generation);
|
Interlocked.Increment(ref slot.generation);
|
||||||
slot.value = default;
|
value = slot.value;
|
||||||
|
slot.value = default!;
|
||||||
|
|
||||||
_freeSlots.Enqueue(slotIndex);
|
_freeSlots.Enqueue(slotIndex);
|
||||||
Interlocked.Decrement(ref _count);
|
Interlocked.Decrement(ref _count);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value = default;
|
||||||
return false; // Another thread already removed it
|
return false; // Another thread already removed it
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,11 +300,9 @@ public class ConcurrentSlotMap<T> : IEnumerable<T>
|
|||||||
ref var slot = ref _data[i];
|
ref var slot = ref _data[i];
|
||||||
Volatile.Write(ref slot.isValid, 0);
|
Volatile.Write(ref slot.isValid, 0);
|
||||||
slot.generation = 0;
|
slot.generation = 0;
|
||||||
slot.value = default;
|
slot.value = default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
_freeSlots.Clear();
|
_freeSlots.Clear();
|
||||||
|
|
||||||
Add(default!, out _);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
<Authors>Misaki</Authors>
|
<Authors>Misaki</Authors>
|
||||||
<AssemblyVersion>1.0.5</AssemblyVersion>
|
<AssemblyVersion>1.0.6</AssemblyVersion>
|
||||||
<Version>$(AssemblyVersion)</Version>
|
<Version>$(AssemblyVersion)</Version>
|
||||||
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
|
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
|
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ values.Add(10);
|
|||||||
values.Add(20);
|
values.Add(20);
|
||||||
values.Add(30);
|
values.Add(30);
|
||||||
|
|
||||||
|
ref int firstValue = ref values[0];
|
||||||
|
|
||||||
Span<int> span = values.AsSpan();
|
Span<int> span = values.AsSpan();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user