aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-12-06 14:11:46 -0500
committerLibravatar vnugent <public@vaughnnugent.com>2023-12-06 14:11:46 -0500
commitae18431a78f9e47f816b3a7db80552c9246cc587 (patch)
treee08e8262fd077fedeb4950353da95cdd2e66d772
parenta6a88aae3e6cb39ebd8fe0b63a865168e680ef45 (diff)
fix tcp buffering with optimization, package updates, and C mem lib readmes
-rw-r--r--lib/Net.Transport.SimpleTCP/src/SocketPipeLineWorker.cs55
-rw-r--r--lib/Net.Transport.SimpleTCP/src/VNLib.Net.Transport.SimpleTCP.csproj2
-rw-r--r--lib/Plugins.PluginBase/src/VNLib.Plugins.PluginBase.csproj4
-rw-r--r--lib/Utils.Memory/vnlib_mimalloc/build.readme.txt54
-rw-r--r--lib/Utils.Memory/vnlib_mimalloc/vnlib_mimalloc.c3
-rw-r--r--lib/Utils.Memory/vnlib_rpmalloc/build.readme.txt37
-rw-r--r--lib/Utils.Memory/vnlib_rpmalloc/vnlib_rpmalloc.c3
-rw-r--r--lib/Utils/src/Memory/MemoryUtil.cs59
-rw-r--r--lib/Utils/tests/VNLib.UtilsTests.csproj2
9 files changed, 180 insertions, 39 deletions
diff --git a/lib/Net.Transport.SimpleTCP/src/SocketPipeLineWorker.cs b/lib/Net.Transport.SimpleTCP/src/SocketPipeLineWorker.cs
index db87357..209ab91 100644
--- a/lib/Net.Transport.SimpleTCP/src/SocketPipeLineWorker.cs
+++ b/lib/Net.Transport.SimpleTCP/src/SocketPipeLineWorker.cs
@@ -29,6 +29,7 @@ using System.Threading;
using System.Net.Sockets;
using System.IO.Pipelines;
using System.Threading.Tasks;
+using System.Runtime.InteropServices;
using VNLib.Utils.Memory;
using VNLib.Utils.Memory.Caching;
@@ -234,7 +235,7 @@ namespace VNLib.Net.Transport.Tcp
//Write segment to socket, and upate written data
int written = await sock.SendAsync(reader.Window, SocketFlags.None);
- if(written >= reader.WindowSize)
+ if(written == reader.WindowSize)
{
//All data was written
break;
@@ -274,6 +275,7 @@ namespace VNLib.Net.Transport.Tcp
private FlushResult _recvFlushRes;
+ private int _sysSocketBufferSize;
private async Task RecvDoWorkAsync(Socket sock, bool initialData)
{
@@ -284,7 +286,7 @@ namespace VNLib.Net.Transport.Tcp
try
{
//Avoid syscall?
- int bufferSize = sock.ReceiveBufferSize;
+ _sysSocketBufferSize = sock.ReceiveBufferSize;
//If initial data was buffered, it needs to be published to the reader
if (initialData)
@@ -303,7 +305,7 @@ namespace VNLib.Net.Transport.Tcp
while (true)
{
//Get buffer from pipe writer
- Memory<byte> buffer = RecvPipe.Writer.GetMemory(bufferSize);
+ Memory<byte> buffer = RecvPipe.Writer.GetMemory(_sysSocketBufferSize);
//Wait for data or error from socket
int count = await sock.ReceiveAsync(buffer, SocketFlags.None, _cts.Token);
@@ -397,8 +399,10 @@ namespace VNLib.Net.Transport.Tcp
SendTimer.Restart(SendTimeoutMs);
try
{
+ CopyAndPublishDataOnSendPipe(data);
+
//Send the segment
- ValueTask<FlushResult> result = SendPipe.Writer.WriteAsync(data, cancellation);
+ ValueTask<FlushResult> result = SendPipe.Writer.FlushAsync(cancellation);
//Task completed successfully, so
if (result.IsCompleted)
@@ -430,8 +434,10 @@ namespace VNLib.Net.Transport.Tcp
private ValueTask SendWithoutTimerInternalAsync(ReadOnlyMemory<byte> data, CancellationToken cancellation)
{
+ CopyAndPublishDataOnSendPipe(data);
+
//Send the segment
- ValueTask<FlushResult> result = SendPipe.Writer.WriteAsync(data, cancellation);
+ ValueTask<FlushResult> result = SendPipe.Writer.FlushAsync(cancellation);
//Task completed successfully, so
if (result.IsCompleted)
@@ -454,6 +460,45 @@ namespace VNLib.Net.Transport.Tcp
}
}
+ private void CopyAndPublishDataOnSendPipe(ReadOnlyMemory<byte> src)
+ {
+ /*
+ * Clamp the buffer size to the system socket buffer size. If the
+ * buffer is larger then, we will need to publish multiple segments
+ */
+ if(src.Length > _sysSocketBufferSize)
+ {
+ //Store local src buffer reference to copy to
+ ref byte srcRef = ref MemoryMarshal.GetReference(src.Span);
+
+ uint written = 0;
+ while (written < src.Length)
+ {
+ int dataToCopy = (int)Math.Min(_sysSocketBufferSize, src.Length - written);
+
+ //Get a new buffer span, and ref
+ Span<byte> dest = SendPipe.Writer.GetSpan(dataToCopy);
+ ref byte destRef = ref MemoryMarshal.GetReference(dest);
+
+ //Copy data to the buffer at the new position
+ MemoryUtil.Memmove(ref srcRef, written, ref destRef, 0, (uint)dataToCopy);
+
+ //Advance the writer by the number of bytes written
+ SendPipe.Writer.Advance(dataToCopy);
+
+ //Increment the written count
+ written += (uint)dataToCopy;
+ }
+ }
+ else
+ {
+ //Single segment, just copy to the writer
+ Span<byte> dest = SendPipe.Writer.GetSpan(src.Length);
+ src.Span.CopyTo(dest);
+ SendPipe.Writer.Advance(src.Length);
+ }
+ }
+
ValueTask ITransportInterface.SendAsync(ReadOnlyMemory<byte> data, CancellationToken cancellation)
{
//Use timer if timeout is set, dont otherwise
diff --git a/lib/Net.Transport.SimpleTCP/src/VNLib.Net.Transport.SimpleTCP.csproj b/lib/Net.Transport.SimpleTCP/src/VNLib.Net.Transport.SimpleTCP.csproj
index bfd9fda..ac3e0a1 100644
--- a/lib/Net.Transport.SimpleTCP/src/VNLib.Net.Transport.SimpleTCP.csproj
+++ b/lib/Net.Transport.SimpleTCP/src/VNLib.Net.Transport.SimpleTCP.csproj
@@ -32,7 +32,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="System.IO.Pipelines" Version="7.0.0" />
+ <PackageReference Include="System.IO.Pipelines" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
diff --git a/lib/Plugins.PluginBase/src/VNLib.Plugins.PluginBase.csproj b/lib/Plugins.PluginBase/src/VNLib.Plugins.PluginBase.csproj
index b9d671d..c7a8414 100644
--- a/lib/Plugins.PluginBase/src/VNLib.Plugins.PluginBase.csproj
+++ b/lib/Plugins.PluginBase/src/VNLib.Plugins.PluginBase.csproj
@@ -41,8 +41,8 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="Serilog" Version="3.0.1" />
- <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
+ <PackageReference Include="Serilog" Version="3.1.1" />
+ <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</ItemGroup>
diff --git a/lib/Utils.Memory/vnlib_mimalloc/build.readme.txt b/lib/Utils.Memory/vnlib_mimalloc/build.readme.txt
index e69de29..cd003b6 100644
--- a/lib/Utils.Memory/vnlib_mimalloc/build.readme.txt
+++ b/lib/Utils.Memory/vnlib_mimalloc/build.readme.txt
@@ -0,0 +1,54 @@
+vnlib_mimmalloc Copyright (C) 2023 Vaughn Nugent
+
+vnlib_mimmalloc is a wrapper library for Mimalloc by Microsoft that implements the NativeHeapApi
+functions, and exports them by default for use as a library. The CMake configuration is setup to produce both
+a static and shared library you can link against. The NativeHeapApi.h file is included in the source tree
+of the archive this readme is included in. Simply add the header to your project and link against the library.
+
+The NativHeapApi was designed to consolidate heap based operations into a single interface for the purpose of
+.NET interop. The shared library (DLL) that is produced can be loaded into a .NET application that uses my
+VNLib.Utils library.
+
+LICENSE:
+You also received a copy of the MIT license for mimalloc by Microsoft, and a GNU license for this library from me.
+
+INSTALLATION:
+For the most up-to-date instructions go to my website here: https://www.vaughnnugent.com/resources/software/articles?tags=docs&search=building+native+heap
+
+If you cannot view the website, here are the basic instructions that may become outdated:
+
+PREREQUISITES:
+- Taskfile.dev (https://taskfile.dev/#/installation)
+- CMake (https://cmake.org/download/)
+- MSBuild (Vistual Studio build tools) and the CL.exe compiler-linker (Windows only)
+- GNU Make + GCC (Unix only)
+
+NOTE:
+If you have any mimalloc specific CMake options you want to use, when running task, you can pass them as
+an the MIMALLOC_CMAKE_ARGS env variable.
+
+Example: >task MIMALLOC_CMAKE_ARGS="-DMI_SECURE=ON" (enable secure mode for mimalloc)
+
+See: https://microsoft.github.io/mimalloc/build.html for more information on mimalloc specific CMake options.
+
+BUILDING:
+1. You have already downloaded all the source code to build this library
+2. Navigate to directory containing the Taskfile.yaml file in the root
+3. Run the default task: > task (yes literally just type "task" and hit enter if you installed Task gobally)
+
+WINDOWS:
+The taskfile should print on screen where the output library file was placed. It will be in the build directory
+usually under Debug or Release.
+
+UNIX:
+Navigate to the build directory after the task completes, and both the shared .so and static .a files will be
+in the build directory.
+
+MIMALLOC SPECIFIC NOTES:
+Mimalloc does not support cross-thread allocations on a privately head heap, which is paramount for my intented
+use case. I have not found a way to make this work, so I have implemented a workaround by exporting only the
+shared heap instance. This means that all allocations will be made on the shared heap, and not on a private heap.
+Hopefully in the future I can find a way to make this work, but for now understand that if your use cause relied
+on security from private heaps, you should avoid using this library. That being said, my libraries do not assume
+security features for private heaps only lockless performance. Mimalloc does offer many more security features
+that are worth using.
diff --git a/lib/Utils.Memory/vnlib_mimalloc/vnlib_mimalloc.c b/lib/Utils.Memory/vnlib_mimalloc/vnlib_mimalloc.c
index a3a2e6b..cb8707f 100644
--- a/lib/Utils.Memory/vnlib_mimalloc/vnlib_mimalloc.c
+++ b/lib/Utils.Memory/vnlib_mimalloc/vnlib_mimalloc.c
@@ -37,9 +37,12 @@ HEAP_METHOD_EXPORT ERRNO HEAP_METHOD_CC heapCreate(UnmanagedHeapDescriptor* flag
* things working.
*
* Always clear serialize flag and set shared heap pointer
+ *
+ * Shared heap supports realloc, so set the flag
*/
flags->CreationFlags &= ~(HEAP_CREATION_SERIALZE_ENABLED);
+ flags->CreationFlags |= HEAP_CREATION_SUPPORTS_REALLOC;
flags->HeapPointer = heapGetSharedHeapHandle();
diff --git a/lib/Utils.Memory/vnlib_rpmalloc/build.readme.txt b/lib/Utils.Memory/vnlib_rpmalloc/build.readme.txt
index e69de29..77f2193 100644
--- a/lib/Utils.Memory/vnlib_rpmalloc/build.readme.txt
+++ b/lib/Utils.Memory/vnlib_rpmalloc/build.readme.txt
@@ -0,0 +1,37 @@
+vnlib_rpmalloc Copyright (C) 2023 Vaughn Nugent
+
+vnlib_rpmalloc is a wrapper library for rpmalloc by Mattias Jansson that implements the NativeHeapApi
+functions, and exports them by default for use as a library. The CMake configuration is setup to produce both
+a static and shared library you can link against. The NativeHeapApi.h file is included in the source tree
+of the archive this readme is included in. Simply add the header to your project and link against the library.
+
+The NativHeapApi was designed to consolidate heap based operations into a single interface for the purpose of
+.NET interop. The shared library (DLL) that is produced can be loaded into a .NET application that uses
+my VNLib.Utils library.
+
+LICENSE:
+You also received a copy of the license for rpmalloc by Mattias Jansson, and a GNU license for this library.
+
+INSTALLATION:
+For the most up-to-date instructions go to my website here: https://www.vaughnnugent.com/resources/software/articles?tags=docs&search=building+native+heap
+
+If you cannot view the website, here are the basic instructions that may become outdated:
+
+PREREQUISITES:
+- Taskfile.dev (https://taskfile.dev/#/installation)
+- CMake (https://cmake.org/download/)
+- MSBuild (Vistual Studio build tools) and the CL.exe compiler-linker (Windows only)
+- GNU Make + GCC (Unix only)
+
+BUILDING:
+1. You have already downloaded all the source code to build this library
+2. Navigate to directory containing the Taskfile.yaml file in the root
+3. Run the default task: > task (yes literally just type "task" and hit enter if you installed Task gobally)
+
+WINDOWS:
+The taskfile should print on screen where the output library file was placed. It will be in the build directory
+usually under Debug or Release.
+
+UNIX:
+Navigate to the build directory after the task completes, and both the shared .so and static .a files will be
+in the build directory.
diff --git a/lib/Utils.Memory/vnlib_rpmalloc/vnlib_rpmalloc.c b/lib/Utils.Memory/vnlib_rpmalloc/vnlib_rpmalloc.c
index b1faf85..5173643 100644
--- a/lib/Utils.Memory/vnlib_rpmalloc/vnlib_rpmalloc.c
+++ b/lib/Utils.Memory/vnlib_rpmalloc/vnlib_rpmalloc.c
@@ -152,6 +152,9 @@ HEAP_METHOD_EXPORT HeapHandle HEAP_METHOD_CC heapGetSharedHeapHandle(void)
HEAP_METHOD_EXPORT ERRNO HEAP_METHOD_CC heapCreate(UnmanagedHeapDescriptor* flags)
{
+ //All heaps support resizing
+ flags->CreationFlags |= HEAP_CREATION_SUPPORTS_REALLOC;
+
//Check flags
if (flags->CreationFlags & HEAP_CREATION_IS_SHARED)
{
diff --git a/lib/Utils/src/Memory/MemoryUtil.cs b/lib/Utils/src/Memory/MemoryUtil.cs
index 75e2d4c..5d979c9 100644
--- a/lib/Utils/src/Memory/MemoryUtil.cs
+++ b/lib/Utils/src/Memory/MemoryUtil.cs
@@ -325,10 +325,7 @@ namespace VNLib.Utils.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InitializeBlock<T>(T[] array, uint count) where T: struct
{
- if(array == null)
- {
- throw new ArgumentNullException(nameof(array));
- }
+ ArgumentNullException.ThrowIfNull(array, nameof(array));
//Check bounds
CheckBounds(array, 0, count);
@@ -392,7 +389,18 @@ namespace VNLib.Utils.Memory
/// <typeparam name="T">The structure type</typeparam>
/// <param name="structRef">The reference to the allocated structure</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ZeroStruct<T>(ref T structRef) where T : unmanaged => InitializeBlock(ref structRef, 1);
+ public static void ZeroStruct<T>(ref T structRef) where T : unmanaged
+ {
+ if (Unsafe.IsNullRef(ref structRef))
+ {
+ throw new ArgumentNullException(nameof(structRef));
+ }
+
+ //Get a byte reference to the structure
+ ref byte byteRef = ref Unsafe.As<T, byte>(ref structRef);
+
+ Unsafe.InitBlockUnaligned(ref byteRef, 0, (uint)sizeof(T));
+ }
/// <summary>
/// Zeroes a block of memory pointing to the structure
@@ -730,10 +738,7 @@ namespace VNLib.Utils.Memory
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static void Copy<T>(ReadOnlySpan<T> source, int sourceOffset, IMemoryHandle<T> dest, nuint destOffset, int count) where T: struct
{
- if (dest is null)
- {
- throw new ArgumentNullException(nameof(dest));
- }
+ ArgumentNullException.ThrowIfNull(dest, nameof(dest));
if (count == 0)
{
@@ -783,7 +788,7 @@ namespace VNLib.Utils.Memory
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static void Copy<T>(IMemoryHandle<T> source, nint sourceOffset, Span<T> dest, int destOffset, int count) where T : struct
{
- _ = source ?? throw new ArgumentNullException(nameof(source));
+ ArgumentNullException.ThrowIfNull(source, nameof(source));
//Validate source/dest/count
ValidateCopyArgs(sourceOffset, destOffset, count);
@@ -840,8 +845,8 @@ namespace VNLib.Utils.Memory
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static void Copy<T>(IMemoryHandle<T> source, nuint sourceOffset, IMemoryHandle<T> dest, nuint destOffset, nuint count) where T : unmanaged
{
- _ = source ?? throw new ArgumentNullException(nameof(source));
- _ = dest ?? throw new ArgumentNullException(nameof(dest));
+ ArgumentNullException.ThrowIfNull(source, nameof(source));
+ ArgumentNullException.ThrowIfNull(dest, nameof(dest));
CheckBounds(source, sourceOffset, count);
CheckBounds(dest, destOffset, count);
@@ -897,15 +902,8 @@ namespace VNLib.Utils.Memory
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static void CopyArray<T>(IMemoryHandle<T> source, nuint sourceOffset, T[] dest, nuint destOffset, nuint count) where T : unmanaged
{
- if (source is null)
- {
- throw new ArgumentNullException(nameof(source));
- }
-
- if (dest is null)
- {
- throw new ArgumentNullException(nameof(dest));
- }
+ ArgumentNullException.ThrowIfNull(source, nameof(source));
+ ArgumentNullException.ThrowIfNull(dest, nameof(dest));
if (count == 0)
{
@@ -951,15 +949,8 @@ namespace VNLib.Utils.Memory
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static void CopyArray<T>(T[] source, nuint sourceOffset, IMemoryHandle<T> dest, nuint destOffset, nuint count) where T : unmanaged
{
- if (source is null)
- {
- throw new ArgumentNullException(nameof(source));
- }
-
- if (dest is null)
- {
- throw new ArgumentNullException(nameof(dest));
- }
+ ArgumentNullException.ThrowIfNull(source, nameof(source));
+ ArgumentNullException.ThrowIfNull(dest, nameof(dest));
if (count == 0)
{
@@ -1301,6 +1292,14 @@ namespace VNLib.Utils.Memory
}
/// <summary>
+ /// Gets a managed pointer from the supplied handle
+ /// </summary>
+ /// <param name="handle">A reference to the handle to get the intpr for</param>
+ /// <returns>A managed pointer from the handle</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static IntPtr GetIntptr(ref MemoryHandle handle) => new(handle.Pointer);
+
+ /// <summary>
/// Rounds the requested byte size up to the nearest page
/// number of bytes
/// </summary>
diff --git a/lib/Utils/tests/VNLib.UtilsTests.csproj b/lib/Utils/tests/VNLib.UtilsTests.csproj
index 9053c51..f8cb807 100644
--- a/lib/Utils/tests/VNLib.UtilsTests.csproj
+++ b/lib/Utils/tests/VNLib.UtilsTests.csproj
@@ -16,7 +16,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0">