HLSF single thread pass, dead lock on multi thread test

Signed-off-by: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com>
This commit is contained in:
Youmu Liu
2025-08-22 22:10:05 +08:00
parent fb9398e8be
commit 539300b3fa
172 changed files with 2660 additions and 1862 deletions
@@ -63,7 +63,6 @@ namespace StgSharp.HighPerformance.Memory
return new hlsfHandle(handle, size);
}
fallback:
i = level;
s = segmentIndex;
while (!TryPopLevelForAllocation(i, s, out handle))
@@ -79,8 +78,7 @@ namespace StgSharp.HighPerformance.Memory
throw new OverflowException("Out of memory: Unable to allocate new 16MB block or find suitable bucket for allocation.");
}
Thread.Sleep(0);
goto fallback;
break;
}
}
@@ -89,15 +87,17 @@ namespace StgSharp.HighPerformance.Memory
if (handle != null)
{
AcquireEntryNextLock(handle);
uint allocSize = (uint)((segmentIndex + 1) * levelSizeArray[level]);
SliceMemory(handle, (segmentIndex + 1) * levelSizeArray[level]);
handle->Level = level;
handle->Size = allocSize;
SetEntryState(handle, EntryState.Allocated);
}
return new hlsfHandle(handle, size);
}
finally
{
ReleaseEntryNextLock(handle);
ReleaseEntrySpinLock(handle);
}
}
@@ -105,26 +105,32 @@ namespace StgSharp.HighPerformance.Memory
{
Entry* current = e;
ulong n = e->NextNear;
Entry* next = e;
Entry* next = (Entry*)n;
ulong originOffset = e->Position - (ulong)m_Buffer;
ulong finalOffset = originOffset + (ulong)offset;
ulong end = e->Position + e->Size - (ulong)m_Buffer;
int remainSize = (int)(e->Size - offset);
AcquireEntryNextLock(e);
AcquireEntryPrevLock(next);
AcquireEntryBothLocks(e);
if (next != null) {
AcquireEntryPrevLock(next);
}
try
{
if ((originOffset & 63) != 0) {
throw new InvalidOperationException("Entry position offset is not aligned to 64 bytes.");
}
int minLevel = Math.Max((BitOperations.TrailingZeroCount(originOffset) - 6) / 2, 0);
int i = minLevel >= levelSizeArray.Length ? levelSizeArray.Length - 1 : minLevel;
for (; i < levelSizeArray.Length; i++)
{
int levelSize = levelSizeArray[i];
int blockCount = remainSize / levelSize % 4;
ulong board = (finalOffset + (ulong)levelSize - 1) & ~((ulong)levelSize - 1);
int blockCount = (int)(ulong.Min(board, end) / (ulong)levelSize);
if (blockCount == 0)
{
break;
@@ -145,7 +151,9 @@ namespace StgSharp.HighPerformance.Memory
newEntry->PreviousNear = (ulong)current;
newEntry->NextNear = n;
current->NextNear = (ulong)newEntry;
next->PreviousNear = (ulong)newEntry;
if (next != null) {
next->PreviousNear = (ulong)newEntry;
}
newEntry->Level = i;
PushLevel(i, blockCount - 1, bucket);
@@ -160,16 +168,16 @@ namespace StgSharp.HighPerformance.Memory
ReleaseEntryNextLock(old);
AcquireEntryNextLock(current);
}
if (remainSize <= 0)
{
ReleaseEntryNextLock(current);
ReleaseEntryPrevLock(next);
if (remainSize <= 0) {
return;
}
for (; i >= 0; i--)
{
int levelSize = levelSizeArray[i];
int blockCount = remainSize / levelSize % 4;
ulong board = (finalOffset + (ulong)levelSize - 1) & ~((ulong)levelSize - 1);
int blockCount = (int)(ulong.Min(board, end) / (ulong)levelSize);
if (blockCount == 0)
{
continue;
@@ -188,9 +196,11 @@ namespace StgSharp.HighPerformance.Memory
// link new entry
newEntry->PreviousNear = (ulong)current;
current->NextNear = (ulong)newEntry;
newEntry->NextNear = n;
next->PreviousNear = (ulong)newEntry;
current->NextNear = (ulong)newEntry;
if (next != null) {
next->PreviousNear = (ulong)newEntry;
}
newEntry->Level = i;
PushLevel(i, blockCount - 1, bucket);
@@ -216,9 +226,10 @@ namespace StgSharp.HighPerformance.Memory
}
finally
{
// Release locks in reverse order
ReleaseEntryNextLock(current);
ReleaseEntryPrevLock(next);
ReleaseEntryBothLocks(e);
if (next != null) {
ReleaseEntryPrevLock(next);
}
}
}
@@ -324,6 +324,7 @@ namespace StgSharp.HighPerformance.Memory
return false;
}
if (previous != null) {
previous->NextLevel = n;
}
@@ -381,7 +382,9 @@ namespace StgSharp.HighPerformance.Memory
}
int thread = Environment.CurrentManagedThreadId;
return Interlocked.CompareExchange(ref node->PrevLock, thread, 0) == 0;
return
Interlocked.CompareExchange(ref node->PrevLock, thread, 0) == 0 ||
Volatile.Read(ref node->PrevLock) == thread;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -422,7 +425,9 @@ namespace StgSharp.HighPerformance.Memory
}
int thread = Environment.CurrentManagedThreadId;
return Interlocked.CompareExchange(ref node->NextLock, thread, 0) == 0;
return
Interlocked.CompareExchange(ref node->NextLock, thread, 0) == 0 ||
Volatile.Read(ref node->NextLock) == thread;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -461,20 +466,59 @@ namespace StgSharp.HighPerformance.Memory
return true;
}
if (TryAcquireBucketNodePrevLock(node))
int thread = Environment.CurrentManagedThreadId;
int retryCount = 0;
while (retryCount < 1000)
{
if (TryAcquireBucketNodeNextLock(node))
if (Interlocked.CompareExchange(ref node->PrevLock, thread, 0) == 0 ||
Volatile.Read(ref node->PrevLock) == thread)
{
return true;
} else
{
ReleaseBucketNodePrevLock(node); // 回滚
return false;
if (Interlocked.CompareExchange(ref node->NextLock, thread, 0) == 0 ||
Volatile.Read(ref node->NextLock) == thread)
{
return true;
} else
{
Volatile.Write(ref node->PrevLock, 0);
}
}
retryCount++;
Thread.Sleep(0); // 让出 CPU
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AcquireBucketNodeBothLocks(BucketNode* node)
{
if (node == null) {
return;
}
int thread = Environment.CurrentManagedThreadId;
while (true)
{
if (Interlocked.CompareExchange(ref node->PrevLock, thread, 0) == 0 ||
Volatile.Read(ref node->PrevLock) == thread)
{
if (Interlocked.CompareExchange(ref node->NextLock, thread, 0) == 0 ||
Volatile.Read(ref node->NextLock) == thread)
{
return;
} else
{
Volatile.Write(ref node->PrevLock, 0);
}
}
Thread.Sleep(0); // 让出 CPU
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReleaseBucketNodeBothLocks(BucketNode* node)
{
@@ -486,17 +530,6 @@ namespace StgSharp.HighPerformance.Memory
ReleaseBucketNodeNextLock(node);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AcquireBucketNodeBothLocks(BucketNode* node)
{
if (node == null) {
return;
}
AcquireBucketNodePrevLock(node);
AcquireBucketNodeNextLock(node);
}
#endregion
}
}
@@ -41,6 +41,44 @@ namespace StgSharp.HighPerformance.Memory
private object _lock = new object();
public void DisplayAllocatedEntryLayout(bool singleStep)
{
lock (_lock)
{
Entry* cur = _spareMemory;
while (cur != null)
{
if (cur->State == EntryState.Allocated)
{
Console.WriteLine(EntryToString(cur));
if (singleStep) {
Console.ReadKey();
}
}
cur = (Entry*)cur->PreviousNear;
}
}
}
public void DisplayEmptyEntryLayout(bool singleStep)
{
lock (_lock)
{
Entry* cur = _spareMemory;
while (cur != null)
{
if (cur->State == EntryState.Empty)
{
Console.WriteLine(EntryToString(cur));
if (singleStep) {
Console.ReadKey();
}
}
cur = (Entry*)cur->PreviousNear;
}
}
}
public void DisplayEntryLayout(bool singleStep)
{
lock (_lock)
@@ -28,13 +28,6 @@
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using hlsfHandle = StgSharp.HighPerformance.Memory.HybridLayerSegregatedFitAllocationHandle;
namespace StgSharp.HighPerformance.Memory
@@ -75,9 +68,6 @@ namespace StgSharp.HighPerformance.Memory
// Step 7: Push the merged entry back to appropriate bucket
PushLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
// Note: If mergedEntry is different from original e, the original e
// and any merged entries have already been freed to _entries by MergeAndRemoveFromBuckets
}
finally
{
@@ -49,16 +49,16 @@ namespace StgSharp.HighPerformance.Memory
private bool AssertBackwardBoundary(ulong current, Entry* target, int level)
{
int size = (level * 2) + 6;
ulong align = current >> size;
return align == target->Position >> size && align == (current + target->Size) >> size;
ulong align = (ulong)levelSizeArray[level + 1];
ulong targetOffset = target->Position - (ulong)m_Buffer;
return current / align == targetOffset / align;
}
private bool AssertForwardBoundary(ulong current, Entry* target, int level)
{
int size = (level * 2) + 6;
ulong align = current >> size;
return align == target->Position >> size && align == (current - target->Size) >> size;
ulong align = (ulong)levelSizeArray[level + 1];
ulong targetOffset = target->Position + target->Size - (ulong)m_Buffer;
return current / align == targetOffset / align;
}
/// <summary>
@@ -209,6 +209,10 @@ namespace StgSharp.HighPerformance.Memory
return;
}
AcquireEntrySpinLock(entry);
entry->LockBuffer = 0;
entry->State = EntryState.Empty;
entry->PreviousNear = EmptyHandle;
entry->NextNear = EmptyHandle;
_entries.Free((nuint)entry);
}
@@ -308,15 +312,13 @@ namespace StgSharp.HighPerformance.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetEntryState(Entry* entry)
{
return entry != null ? Volatile.Read(ref entry->State) : 0;
return Volatile.Read(ref entry->State);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void SetEntryState(Entry* entry, int newState)
{
if (entry != null) {
Volatile.Write(ref entry->State, newState);
}
Volatile.Write(ref entry->State, newState);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -343,7 +345,6 @@ namespace StgSharp.HighPerformance.Memory
return GetEntryState(entry) == EntryState.Merging;
}
// Entry 前驱锁操作(控制PreviousNear字段)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryAcquireEntryPrevLock(Entry* entry)
{
@@ -352,7 +353,9 @@ namespace StgSharp.HighPerformance.Memory
}
int thread = Environment.CurrentManagedThreadId;
return Interlocked.CompareExchange(ref entry->PrevLock, thread, 0) == 0;
return
Interlocked.CompareExchange(ref entry->PrevLock, thread, 0) == 0 ||
Volatile.Read(ref entry->PrevLock) == thread;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -392,7 +395,9 @@ namespace StgSharp.HighPerformance.Memory
}
int thread = Environment.CurrentManagedThreadId;
return Interlocked.CompareExchange(ref entry->NextLock, thread, 0) == 0;
return
Interlocked.CompareExchange(ref entry->NextLock, thread, 0) == 0 ||
Volatile.Read(ref entry->NextLock) == thread;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -432,17 +437,23 @@ namespace StgSharp.HighPerformance.Memory
return true;
}
if (TryAcquireEntryPrevLock(entry))
int thread = Environment.CurrentManagedThreadId;
// 尝试同时获取前后锁
if (Interlocked.CompareExchange(ref entry->PrevLock, thread, 0) == 0 ||
Volatile.Read(ref entry->PrevLock) == thread)
{
if (TryAcquireEntryNextLock(entry))
if (Interlocked.CompareExchange(ref entry->NextLock, thread, 0) == 0 ||
Volatile.Read(ref entry->NextLock) == thread)
{
return true;
} else
{
ReleaseEntryPrevLock(entry); // 回滚
return false;
// 回滚前锁
Volatile.Write(ref entry->PrevLock, 0);
}
}
return false;
}
@@ -464,8 +475,22 @@ namespace StgSharp.HighPerformance.Memory
return;
}
AcquireEntryPrevLock(entry);
AcquireEntryNextLock(entry);
int thread = Environment.CurrentManagedThreadId;
// 循环直到成功获取两个锁
while (true)
{
if ((Interlocked.CompareExchange(ref entry->PrevLock, thread, 0) == 0 ||
Volatile.Read(ref entry->PrevLock) == thread) &&
(Interlocked.CompareExchange(ref entry->NextLock, thread, 0) == 0 ||
Volatile.Read(ref entry->NextLock) == thread)) {
return;
}
// 回滚前锁
Volatile.Write(ref entry->PrevLock, 0);
Thread.Sleep(0);
}
}
#endregion