From b78036ed9a1030d619b8f9de4dceabbfaa07861f Mon Sep 17 00:00:00 2001 From: vnugent Date: Sat, 4 Mar 2023 23:41:23 -0500 Subject: Prefer Monitor performance to SemaphoreSlim for private heaps --- lib/Utils/src/Memory/RpMallocPrivateHeap.cs | 4 +- lib/Utils/src/Memory/UnmanagedHeapBase.cs | 69 +++++++++++++++-------------- lib/Utils/src/Memory/Win32PrivateHeap.cs | 32 +++++++------ lib/Utils/tests/ERRNOTest.cs | 9 +--- 4 files changed, 58 insertions(+), 56 deletions(-) (limited to 'lib/Utils') diff --git a/lib/Utils/src/Memory/RpMallocPrivateHeap.cs b/lib/Utils/src/Memory/RpMallocPrivateHeap.cs index 0af91a0..323f228 100644 --- a/lib/Utils/src/Memory/RpMallocPrivateHeap.cs +++ b/lib/Utils/src/Memory/RpMallocPrivateHeap.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -251,7 +251,7 @@ namespace VNLib.Utils.Memory //Destroy the heap rpmalloc_heap_release(handle); //Release base - return base.ReleaseHandle(); + return true; } /// diff --git a/lib/Utils/src/Memory/UnmanagedHeapBase.cs b/lib/Utils/src/Memory/UnmanagedHeapBase.cs index 1f7dc7f..4f5084a 100644 --- a/lib/Utils/src/Memory/UnmanagedHeapBase.cs +++ b/lib/Utils/src/Memory/UnmanagedHeapBase.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -23,7 +23,6 @@ */ using System; -using System.Threading; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; @@ -41,7 +40,7 @@ namespace VNLib.Utils.Memory /// /// The heap synchronization handle /// - protected readonly SemaphoreSlim HeapLock; + protected readonly object HeapLock; /// /// The global heap zero flag @@ -55,7 +54,7 @@ namespace VNLib.Utils.Memory /// A flag that indicates if the handle is owned by the instance protected UnmanagedHeapBase(bool globalZero, bool ownsHandle) : base(ownsHandle) { - HeapLock = new(1, 1); + HeapLock = new(); GlobalZero = globalZero; } @@ -68,21 +67,25 @@ namespace VNLib.Utils.Memory //Force zero if global flag is set zero |= GlobalZero; bool handleCountIncremented = false; + //Increment handle count to prevent premature release DangerousAddRef(ref handleCountIncremented); + //Failed to increment ref count, class has been disposed if (!handleCountIncremented) { throw new ObjectDisposedException("The handle has been released"); } + try { - //wait for lock - HeapLock.Wait(); - //Alloc block - LPVOID block = AllocBlock(elements, size, zero); - //release lock - HeapLock.Release(); + LPVOID block; + //Enter lock + lock(HeapLock) + { + //Alloc block + block = AllocBlock(elements, size, zero); + } //Check if block was allocated return block != IntPtr.Zero ? block : throw new NativeMemoryOutOfMemoryException("Failed to allocate the requested block"); } @@ -99,18 +102,22 @@ namespace VNLib.Utils.Memory public bool Free(ref LPVOID block) { bool result; + //If disposed, set the block handle to zero and exit to avoid raising exceptions during finalization if (IsClosed || IsInvalid) { block = IntPtr.Zero; return true; } + //wait for lock - HeapLock.Wait(); - //Free block - result = FreeBlock(block); - //Release lock before releasing handle - HeapLock.Release(); + lock (HeapLock) + { + //Free block + result = FreeBlock(block); + //Release lock before releasing handle + } + //Decrement handle count DangerousRelease(); //set block to invalid @@ -123,33 +130,29 @@ namespace VNLib.Utils.Memory /// public void Resize(ref LPVOID block, nuint elements, nuint size, bool zero) { - //wait for lock - HeapLock.Wait(); - /* - * Realloc may return a null pointer if allocation fails - * so check the results and only assign the block pointer - * if the result is valid. Otherwise pointer block should - * be left untouched - */ - LPVOID newBlock = ReAllocBlock(block, elements, size, zero); - //release lock - HeapLock.Release(); + LPVOID newBlock; + + lock (HeapLock) + { + /* + * Realloc may return a null pointer if allocation fails + * so check the results and only assign the block pointer + * if the result is valid. Otherwise pointer block should + * be left untouched + */ + newBlock = ReAllocBlock(block, elements, size, zero); + } + //Check block if (newBlock == IntPtr.Zero) { throw new NativeMemoryOutOfMemoryException("The memory block could not be resized"); } + //Set the new block block = newBlock; } - /// - protected override bool ReleaseHandle() - { - HeapLock.Dispose(); - return true; - } - /// /// Allocates a block of memory from the heap /// diff --git a/lib/Utils/src/Memory/Win32PrivateHeap.cs b/lib/Utils/src/Memory/Win32PrivateHeap.cs index bdf22f9..9911195 100644 --- a/lib/Utils/src/Memory/Win32PrivateHeap.cs +++ b/lib/Utils/src/Memory/Win32PrivateHeap.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -26,10 +26,12 @@ using System; using System.Diagnostics; using System.Runtime.Versioning; using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; using DWORD = System.Int64; using LPVOID = System.IntPtr; + namespace VNLib.Utils.Memory { /// @@ -136,11 +138,11 @@ namespace VNLib.Utils.Memory { bool result; //Lock the heap before validating - HeapLock.Wait(); - //validate the block on the current heap - result = HeapValidate(handle, HEAP_NO_FLAGS, block); - //Unlock the heap - HeapLock.Release(); + lock (HeapLock) + { + //validate the block on the current heap + result = HeapValidate(handle, HEAP_NO_FLAGS, block); + } return result; } @@ -153,12 +155,13 @@ namespace VNLib.Utils.Memory public bool Validate() { bool result; + //Lock the heap before validating - HeapLock.Wait(); - //validate the entire heap - result = HeapValidate(handle, HEAP_NO_FLAGS, IntPtr.Zero); - //Unlock the heap - HeapLock.Release(); + lock (HeapLock) + { + //validate the entire heap + result = HeapValidate(handle, HEAP_NO_FLAGS, IntPtr.Zero); + } return result; } @@ -168,9 +171,10 @@ namespace VNLib.Utils.Memory #if TRACE Trace.WriteLine($"Win32 private heap {handle:x} destroyed"); #endif - return HeapDestroy(handle) && base.ReleaseHandle(); + return HeapDestroy(handle); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override sealed LPVOID AllocBlock(nuint elements, nuint size, bool zero) { nuint bytes = checked(elements * size); @@ -178,9 +182,11 @@ namespace VNLib.Utils.Memory return HeapAlloc(handle, zero ? HEAP_ZERO_MEMORY : HEAP_NO_FLAGS, bytes); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override sealed bool FreeBlock(LPVOID block) => HeapFree(handle, HEAP_NO_FLAGS, block); - + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override sealed LPVOID ReAllocBlock(LPVOID block, nuint elements, nuint size, bool zero) { nuint bytes = checked(elements * size); diff --git a/lib/Utils/tests/ERRNOTest.cs b/lib/Utils/tests/ERRNOTest.cs index bd14b50..c8a9e61 100644 --- a/lib/Utils/tests/ERRNOTest.cs +++ b/lib/Utils/tests/ERRNOTest.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.UtilsTests @@ -22,16 +22,9 @@ * along with VNLib.UtilsTests. If not, see http://www.gnu.org/licenses/. */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; -using VNLib.Utils; - namespace VNLib.Utils.Tests { [TestClass] -- cgit