From 5d743ce44aa97907a1e2ac9e76c8a5aca840ce9b Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 22 Jul 2015 04:09:06 +0100 Subject: [PATCH 1/9] Managed RIO Server --- benchmarks.sln | 27 ++ experimental/ManagedRIOHttpServer/App.config | 6 + experimental/ManagedRIOHttpServer/LICENSE.txt | 12 + .../ManagedRIOHttpServer.csproj | 81 +++++ experimental/ManagedRIOHttpServer/Program.cs | 228 +++++++++++++ .../Properties/AssemblyInfo.cs | 36 ++ experimental/ManagedRIOHttpServer/README.md | 45 +++ .../RegisteredIO/RIOBufferPool.cs | 118 +++++++ .../RegisteredIO/RIOImports.cs | 322 ++++++++++++++++++ .../RegisteredIO/RIOPooledSegment.cs | 37 ++ .../RegisteredIO/RIOReceiveTask.cs | 104 ++++++ .../RegisteredIO/RIOTcpConnection.cs | 262 ++++++++++++++ .../RegisteredIO/RIOTcpServer.cs | 135 ++++++++ .../RegisteredIO/RIOThreadPool.cs | 213 ++++++++++++ .../RegisteredIO/RIOWorkBundle.cs | 23 ++ .../RegisteredIO/RIO_BUFSEGMENT.cs | 23 ++ .../RIO_NOTIFICATION_COMPLETION.cs | 14 + .../RIO_NOTIFICATION_COMPLETION_EVENT.cs | 15 + .../RIO_NOTIFICATION_COMPLETION_IOCP.cs | 17 + .../RIO_NOTIFICATION_COMPLETION_TYPE.cs | 12 + .../RegisteredIO/RIO_RESULT.cs | 16 + .../ManagedRIOHttpServer/app.manifest | 76 +++++ 22 files changed, 1822 insertions(+) create mode 100644 experimental/ManagedRIOHttpServer/App.config create mode 100644 experimental/ManagedRIOHttpServer/LICENSE.txt create mode 100644 experimental/ManagedRIOHttpServer/ManagedRIOHttpServer.csproj create mode 100644 experimental/ManagedRIOHttpServer/Program.cs create mode 100644 experimental/ManagedRIOHttpServer/Properties/AssemblyInfo.cs create mode 100644 experimental/ManagedRIOHttpServer/README.md create mode 100644 experimental/ManagedRIOHttpServer/RegisteredIO/RIOBufferPool.cs create mode 100644 experimental/ManagedRIOHttpServer/RegisteredIO/RIOImports.cs create mode 100644 experimental/ManagedRIOHttpServer/RegisteredIO/RIOPooledSegment.cs create mode 100644 experimental/ManagedRIOHttpServer/RegisteredIO/RIOReceiveTask.cs create mode 100644 experimental/ManagedRIOHttpServer/RegisteredIO/RIOTcpConnection.cs create mode 100644 experimental/ManagedRIOHttpServer/RegisteredIO/RIOTcpServer.cs create mode 100644 experimental/ManagedRIOHttpServer/RegisteredIO/RIOThreadPool.cs create mode 100644 experimental/ManagedRIOHttpServer/RegisteredIO/RIOWorkBundle.cs create mode 100644 experimental/ManagedRIOHttpServer/RegisteredIO/RIO_BUFSEGMENT.cs create mode 100644 experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION.cs create mode 100644 experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION_EVENT.cs create mode 100644 experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION_IOCP.cs create mode 100644 experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION_TYPE.cs create mode 100644 experimental/ManagedRIOHttpServer/RegisteredIO/RIO_RESULT.cs create mode 100644 experimental/ManagedRIOHttpServer/app.manifest diff --git a/benchmarks.sln b/benchmarks.sln index a0d2c30a4..a95224d28 100644 --- a/benchmarks.sln +++ b/benchmarks.sln @@ -23,52 +23,78 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PureNativeHttpServer", "exp EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Benchmarks", "src\Benchmarks\Benchmarks.xproj", "{EC62ACF4-8B19-41C2-B699-D75CAB7763DF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedRIOHttpServer", "experimental\ManagedRIOHttpServer\ManagedRIOHttpServer.csproj", "{E635C37F-65EA-422C-A3E5-6B48422BF23F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {07773C38-B3F3-4D6C-B318-29C88F016AA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {07773C38-B3F3-4D6C-B318-29C88F016AA9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07773C38-B3F3-4D6C-B318-29C88F016AA9}.Debug|x64.ActiveCfg = Debug|Any CPU {07773C38-B3F3-4D6C-B318-29C88F016AA9}.Debug|x86.ActiveCfg = Debug|Any CPU {07773C38-B3F3-4D6C-B318-29C88F016AA9}.Debug|x86.Build.0 = Debug|Any CPU {07773C38-B3F3-4D6C-B318-29C88F016AA9}.Release|Any CPU.ActiveCfg = Release|Any CPU {07773C38-B3F3-4D6C-B318-29C88F016AA9}.Release|Any CPU.Build.0 = Release|Any CPU + {07773C38-B3F3-4D6C-B318-29C88F016AA9}.Release|x64.ActiveCfg = Release|Any CPU {07773C38-B3F3-4D6C-B318-29C88F016AA9}.Release|x86.ActiveCfg = Release|Any CPU {07773C38-B3F3-4D6C-B318-29C88F016AA9}.Release|x86.Build.0 = Release|Any CPU {1AE4BE9E-0E15-4B91-A32C-46DB4E1710BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1AE4BE9E-0E15-4B91-A32C-46DB4E1710BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1AE4BE9E-0E15-4B91-A32C-46DB4E1710BF}.Debug|x64.ActiveCfg = Debug|Any CPU {1AE4BE9E-0E15-4B91-A32C-46DB4E1710BF}.Debug|x86.ActiveCfg = Debug|Any CPU {1AE4BE9E-0E15-4B91-A32C-46DB4E1710BF}.Debug|x86.Build.0 = Debug|Any CPU {1AE4BE9E-0E15-4B91-A32C-46DB4E1710BF}.Release|Any CPU.ActiveCfg = Release|Any CPU {1AE4BE9E-0E15-4B91-A32C-46DB4E1710BF}.Release|Any CPU.Build.0 = Release|Any CPU + {1AE4BE9E-0E15-4B91-A32C-46DB4E1710BF}.Release|x64.ActiveCfg = Release|Any CPU {1AE4BE9E-0E15-4B91-A32C-46DB4E1710BF}.Release|x86.ActiveCfg = Release|Any CPU {1AE4BE9E-0E15-4B91-A32C-46DB4E1710BF}.Release|x86.Build.0 = Release|Any CPU {4A829ECA-062D-4C1F-BE88-E72E7BC972C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4A829ECA-062D-4C1F-BE88-E72E7BC972C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A829ECA-062D-4C1F-BE88-E72E7BC972C3}.Debug|x64.ActiveCfg = Debug|Any CPU {4A829ECA-062D-4C1F-BE88-E72E7BC972C3}.Debug|x86.ActiveCfg = Debug|Any CPU {4A829ECA-062D-4C1F-BE88-E72E7BC972C3}.Debug|x86.Build.0 = Debug|Any CPU {4A829ECA-062D-4C1F-BE88-E72E7BC972C3}.Release|Any CPU.ActiveCfg = Release|Any CPU {4A829ECA-062D-4C1F-BE88-E72E7BC972C3}.Release|Any CPU.Build.0 = Release|Any CPU + {4A829ECA-062D-4C1F-BE88-E72E7BC972C3}.Release|x64.ActiveCfg = Release|Any CPU {4A829ECA-062D-4C1F-BE88-E72E7BC972C3}.Release|x86.ActiveCfg = Release|Any CPU {4A829ECA-062D-4C1F-BE88-E72E7BC972C3}.Release|x86.Build.0 = Release|Any CPU {A27BFB9B-3CA6-4A26-A7BD-66854399BED3}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {A27BFB9B-3CA6-4A26-A7BD-66854399BED3}.Debug|x64.ActiveCfg = Debug|Win32 {A27BFB9B-3CA6-4A26-A7BD-66854399BED3}.Debug|x86.ActiveCfg = Debug|Win32 {A27BFB9B-3CA6-4A26-A7BD-66854399BED3}.Debug|x86.Build.0 = Debug|Win32 {A27BFB9B-3CA6-4A26-A7BD-66854399BED3}.Release|Any CPU.ActiveCfg = Release|Win32 + {A27BFB9B-3CA6-4A26-A7BD-66854399BED3}.Release|x64.ActiveCfg = Release|Win32 {A27BFB9B-3CA6-4A26-A7BD-66854399BED3}.Release|x86.ActiveCfg = Release|Win32 {A27BFB9B-3CA6-4A26-A7BD-66854399BED3}.Release|x86.Build.0 = Release|Win32 {EC62ACF4-8B19-41C2-B699-D75CAB7763DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EC62ACF4-8B19-41C2-B699-D75CAB7763DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC62ACF4-8B19-41C2-B699-D75CAB7763DF}.Debug|x64.ActiveCfg = Debug|Any CPU {EC62ACF4-8B19-41C2-B699-D75CAB7763DF}.Debug|x86.ActiveCfg = Debug|Any CPU {EC62ACF4-8B19-41C2-B699-D75CAB7763DF}.Debug|x86.Build.0 = Debug|Any CPU {EC62ACF4-8B19-41C2-B699-D75CAB7763DF}.Release|Any CPU.ActiveCfg = Release|Any CPU {EC62ACF4-8B19-41C2-B699-D75CAB7763DF}.Release|Any CPU.Build.0 = Release|Any CPU + {EC62ACF4-8B19-41C2-B699-D75CAB7763DF}.Release|x64.ActiveCfg = Release|Any CPU {EC62ACF4-8B19-41C2-B699-D75CAB7763DF}.Release|x86.ActiveCfg = Release|Any CPU {EC62ACF4-8B19-41C2-B699-D75CAB7763DF}.Release|x86.Build.0 = Release|Any CPU + {E635C37F-65EA-422C-A3E5-6B48422BF23F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E635C37F-65EA-422C-A3E5-6B48422BF23F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E635C37F-65EA-422C-A3E5-6B48422BF23F}.Debug|x64.ActiveCfg = Debug|Any CPU + {E635C37F-65EA-422C-A3E5-6B48422BF23F}.Debug|x64.Build.0 = Debug|Any CPU + {E635C37F-65EA-422C-A3E5-6B48422BF23F}.Debug|x86.ActiveCfg = Debug|Any CPU + {E635C37F-65EA-422C-A3E5-6B48422BF23F}.Debug|x86.Build.0 = Debug|Any CPU + {E635C37F-65EA-422C-A3E5-6B48422BF23F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E635C37F-65EA-422C-A3E5-6B48422BF23F}.Release|Any CPU.Build.0 = Release|Any CPU + {E635C37F-65EA-422C-A3E5-6B48422BF23F}.Release|x64.ActiveCfg = Release|Any CPU + {E635C37F-65EA-422C-A3E5-6B48422BF23F}.Release|x64.Build.0 = Release|Any CPU + {E635C37F-65EA-422C-A3E5-6B48422BF23F}.Release|x86.ActiveCfg = Release|Any CPU + {E635C37F-65EA-422C-A3E5-6B48422BF23F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -79,5 +105,6 @@ Global {4A829ECA-062D-4C1F-BE88-E72E7BC972C3} = {C2EB60CC-CA5A-4E49-A3F8-4432FB117869} {A27BFB9B-3CA6-4A26-A7BD-66854399BED3} = {C2EB60CC-CA5A-4E49-A3F8-4432FB117869} {EC62ACF4-8B19-41C2-B699-D75CAB7763DF} = {995FCFF9-E5F6-4BDD-8E5B-FBDEA21145F9} + {E635C37F-65EA-422C-A3E5-6B48422BF23F} = {C2EB60CC-CA5A-4E49-A3F8-4432FB117869} EndGlobalSection EndGlobal diff --git a/experimental/ManagedRIOHttpServer/App.config b/experimental/ManagedRIOHttpServer/App.config new file mode 100644 index 000000000..2d2a12d81 --- /dev/null +++ b/experimental/ManagedRIOHttpServer/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/experimental/ManagedRIOHttpServer/LICENSE.txt b/experimental/ManagedRIOHttpServer/LICENSE.txt new file mode 100644 index 000000000..e52ddbc6e --- /dev/null +++ b/experimental/ManagedRIOHttpServer/LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) Illyriad Games. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +these files except in compliance with the License. You may obtain a copy of the +License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. \ No newline at end of file diff --git a/experimental/ManagedRIOHttpServer/ManagedRIOHttpServer.csproj b/experimental/ManagedRIOHttpServer/ManagedRIOHttpServer.csproj new file mode 100644 index 000000000..fc3ce4a00 --- /dev/null +++ b/experimental/ManagedRIOHttpServer/ManagedRIOHttpServer.csproj @@ -0,0 +1,81 @@ + + + + + Debug + AnyCPU + {E635C37F-65EA-422C-A3E5-6B48422BF23F} + Exe + Properties + ManagedRIOHttpServer + ManagedRIOHttpServer + v4.6 + 512 + true + + + + x64 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + x64 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + app.manifest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/experimental/ManagedRIOHttpServer/Program.cs b/experimental/ManagedRIOHttpServer/Program.cs new file mode 100644 index 000000000..0df4a1811 --- /dev/null +++ b/experimental/ManagedRIOHttpServer/Program.cs @@ -0,0 +1,228 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using ManagedRIOHttpServer.RegisteredIO; + +namespace ManagedRIOHttpServer +{ + public sealed class Program + { + static readonly string headersKeepAliveStr = "HTTP/1.1 200 OK\r\n" + + "Content-Type: text/plain\r\n" + + "Content-Length:13\r\n" + + "Connection:keep-alive\r\n" + + "Server:-RIO-\r\n" + + "Date:"; + + private static byte[] _headersBytes = Encoding.UTF8.GetBytes(headersKeepAliveStr); + + static readonly string bodyStr = "\r\n\r\n" + + "Hello, World!"; + + private static byte[] _bodyBytes = Encoding.UTF8.GetBytes(bodyStr); + + + static void Main(string[] args) + { + Console.WriteLine("Starting Managed Registered IO Server"); + unsafe + { + if (sizeof(IntPtr) != 8) + { + Console.WriteLine("ManagedRIOHttpServer needs to be run in x64 mode"); + return; + } + } + + try { + // TODO: Use safehandles everywhere! + var ss = new RIOTcpServer(5000, 0, 0, 0, 0); + + ThreadPool.SetMinThreads(100, 100); + + while (true) + { + var socket = ss.Accept(); + ThreadPool.UnsafeQueueUserWorkItem(Serve, socket); + } + } catch (Exception ex) + { + Console.WriteLine("Start up issue {0}", ex.Message); + } + } + + + static void Serve(object state) + { + var socket = (RIOTcpConnection)state; +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + ServeSocket(socket); +#pragma warning restore CS4014 + } + + static async Task ServeSocket(RIOTcpConnection socket) + { + try + { + var headerBuffer = new ArraySegment(_headersBytes, 0, _headersBytes.Length); + var bodyBuffer = new ArraySegment(_bodyBytes, 0, _bodyBytes.Length); + var buffer0 = new byte[2048]; + var buffer1 = new byte[2048]; + var receiveBuffer0 = new ArraySegment(buffer0, 0, buffer0.Length); + var receiveBuffer1 = new ArraySegment(buffer1, 0, buffer1.Length); + + var receiveTask = socket.ReceiveAsync(receiveBuffer0, CancellationToken.None); + + var dateBytes = Encoding.UTF8.GetBytes("DDD, dd mmm yyyy hh:mm:ss GMT"); + + var loop = 0; + var overflow = 0; + // need to check for keep alive + + while (true) + { + int r = (int)await receiveTask; + receiveTask = socket.ReceiveAsync((loop & 1) == 1 ? receiveBuffer0 : receiveBuffer1, CancellationToken.None); + + if (r == 0) + { + if (loop > 0) + { + socket.FlushSends(); + } + break; + } + + var buffer = (loop & 1) == 0 ? buffer0 : buffer1; + + // need to handle packet splits + + var count = 0; + var start = 0; + + // pipelining check + unsafe + { + + fixed (byte* b = buffer) + { + if (overflow > 0) + { + // TODO: some more corner cases + better handling + switch (overflow) + { + case 1: + if (b[0] == 0xa && b[1] == 0xd && b[2] == 0xa) + { + count++; + start = 3; + } + break; + case 2: + if (b[0] == 0xd && b[1] == 0xa) + { + count++; + start = 2; + } + break; + case 3: + if (b[0] == 0xa) + { + count++; + start = 1; + } + break; + } + overflow = 0; + } + + var last = start; + + // need to read 4 bytes to match so end loop 3 bytes earlier than count + var ul = r - 3; + for (var i = start; i < ul; i++) + { + if (b[i] == 0xd && b[i + 1] == 0xa && b[i + 2] == 0xd && b[i + 3] == 0xa) + { + count++; + i += 3; + last = i + 1; + } + } + // TODO: some more corner cases + better handling + if (last < r) + { + // doesn't end with terminator + switch (r - last) + { + case 1: + if (b[r - 1] == 0xd) + { + overflow++; + } + break; + case 2: + if (b[r - 2] == 0xd && b[r - 1] == 0xa) + { + overflow += 2; + break; + } + goto case 1; + case 3: + default: + if (b[r - 3] == 0xd && b[r - 2] == 0xa && b[r - 1] == 0xd) + { + overflow += 3; + break; + } + goto case 2; + } + } + else + { + overflow = 0; + } + } + } + + if (count == 0) + { + socket.SendCachedBad(); + break; + } + + var date = DateTime.UtcNow.ToString("r"); + Encoding.UTF8.GetBytes(date, 0, dateBytes.Length, dateBytes, 0); + + for (var i = 1; i < count; i++) + { + socket.QueueSend(headerBuffer, false); + socket.QueueSend(new ArraySegment(dateBytes), false); + socket.QueueSend(bodyBuffer, false); + } + socket.QueueSend(headerBuffer, false); + socket.QueueSend(new ArraySegment(dateBytes), false); + // force send if not more ready to recieve/pack + var nextReady = receiveTask.IsCompleted; + socket.QueueSend(bodyBuffer, (!nextReady)); + + loop++; + } + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + finally + { + socket.Close(); + } + } + } + +} + diff --git a/experimental/ManagedRIOHttpServer/Properties/AssemblyInfo.cs b/experimental/ManagedRIOHttpServer/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..da8459962 --- /dev/null +++ b/experimental/ManagedRIOHttpServer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ManagedRIOServer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Illyriad Games")] +[assembly: AssemblyProduct("ManagedRIOServer")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e635c37f-65ea-422c-a3e5-6b48422bf23f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/experimental/ManagedRIOHttpServer/README.md b/experimental/ManagedRIOHttpServer/README.md new file mode 100644 index 000000000..ac4a69d5a --- /dev/null +++ b/experimental/ManagedRIOHttpServer/README.md @@ -0,0 +1,45 @@ +# Managed C# Registered IO Http Server +Mostly an exploration into calling the [Winsock high-speed networking Registered I/O extensions](https://msdn.microsoft.com/en-us/library/windows/desktop/ms740642(v=vs.85).aspx) +from managed code. + +A variation of the ```NativeHttpServer``` behaviour + +# Caution +It runs. + +Horribly, **horribly** hacky. + +There is precious little documentation about even using RIO from native code; so at the moment its more like a trial and error, +organically grown testbed of functions to call and their signatures than even a sensibly organised bit of code :( + +But looks like it can be done! :) + +# How to run + +Needs the x64 v4.6 CLR; compile release and run the ```ManagedRIOHttpServer.exe``` in the ```bin\Release``` folder from a command prompt. + +Or double click on ```ManagedRIOHttpServer.exe``` from windows explorer. + +Listens on port 5000 + +# Todo +* Potentially higher read thoughput by posting multiple receives at a time to and to allow read buffering +(application managed rather than Winsock managed for RIO); +as sends are read dependant in this test - may increase overall throughput. +* Deallocate resources correctly. +* Clean up code. + +# About Registered IO + +Registered RIO //build/ announce from 2011 +http://channel9.msdn.com/events/Build/BUILD2011/SAC-593T + +> Microsoft Windows 8 and Windows Server 2012 introduce new Windows Sockets programming elements. + +>A set of high-speed networking extensions are available for increased networking performance with lower latency and jitter. These extensions targeted primarily for server applications use pre-registered data buffers and completion queues to increase performance. + +>The following are new Windows Sockets functions added to support Winsock high-speed networking Registered I/O extensions: + +https://msdn.microsoft.com/en-us/library/windows/desktop/ms740642(v=vs.85).aspx + + diff --git a/experimental/ManagedRIOHttpServer/RegisteredIO/RIOBufferPool.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOBufferPool.cs new file mode 100644 index 000000000..7e2cd2dda --- /dev/null +++ b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOBufferPool.cs @@ -0,0 +1,118 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; +using System.Runtime.InteropServices; + +namespace ManagedRIOHttpServer.RegisteredIO +{ + public sealed class RIOBufferPool : IDisposable + { + RIO_BUFSEGMENT[] _segments; + private byte[] _underlyingBuffer; + public const int PacketSize = 1500 - (20 + 20); // MTU - (IPv4 Header + TCP Header) + private const int PooledPacketSize = PacketSize + 64; // 32 + PacketSize + 32 w false sharing cache guard bytes + private const int PerAllocationCount = RIOThreadPool.PreAllocSocketsPerThread * (RIOTcpConnection.MaxPendingReceives + RIOTcpConnection.MaxPendingSends); + private const int BufferLength = (PooledPacketSize) * PerAllocationCount; // Amount to pin per alloc 9.4 MB ish; into LOH + + private ConcurrentQueue _availableSegments; + private ConcurrentQueue _allocatedBuffers; + private RIO _rio; + + private struct AllocatedBuffer + { + public byte[] Buffer; + public GCHandle PinnedBuffer; + public IntPtr BufferId; + } + + public RIOBufferPool(RIO rio) + { + _rio = rio; + _allocatedBuffers = new ConcurrentQueue(); + _availableSegments = new ConcurrentQueue(); + + _underlyingBuffer = new byte[BufferLength]; + + } + + public void Initalize() + { + + var pinnedBuffer = GCHandle.Alloc(_underlyingBuffer, GCHandleType.Pinned); + var address = Marshal.UnsafeAddrOfPinnedArrayElement(_underlyingBuffer, 0); + var bufferId = _rio.RegisterBuffer(address, BufferLength); + + _allocatedBuffers.Enqueue(new AllocatedBuffer() { Buffer = _underlyingBuffer, PinnedBuffer = pinnedBuffer, BufferId = bufferId }); + + _segments = new RIO_BUFSEGMENT[PerAllocationCount]; + _availableSegments = new ConcurrentQueue(); + var offset = 32u; + for (var i = 0; i < _segments.Length; i++) + { + _segments[i] = new RIO_BUFSEGMENT(bufferId, offset, PacketSize); + _availableSegments.Enqueue(i); + offset += PooledPacketSize; + } + + } + + public RIOPooledSegment GetBuffer() + { + int bufferNo; + if (_availableSegments.TryDequeue(out bufferNo)) + { + return new RIOPooledSegment(bufferNo, this, _segments[bufferNo], _underlyingBuffer); + } + else + { + throw new NotImplementedException("Out of pooled buffers; not implemented dynamic expansion"); + } + } + internal void ReleaseBuffer(int bufferIndex) + { + _availableSegments.Enqueue(bufferIndex); + } + + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + private void Dispose(bool disposing) + { + if (!disposedValue) + { + AllocatedBuffer buffer; + while (_allocatedBuffers.TryDequeue(out buffer)) + { + _rio.DeregisterBuffer(buffer.BufferId); + buffer.PinnedBuffer.Free(); + } + + if (disposing) + { + _segments = null; + _underlyingBuffer = null; + _rio = null; + _availableSegments = null; + _allocatedBuffers = null; + } + + disposedValue = true; + } + } + + ~RIOBufferPool() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + } + +} diff --git a/experimental/ManagedRIOHttpServer/RegisteredIO/RIOImports.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOImports.cs new file mode 100644 index 000000000..9879fc409 --- /dev/null +++ b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOImports.cs @@ -0,0 +1,322 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Security; + +namespace ManagedRIOHttpServer.RegisteredIO +{ + public sealed class RIO + { + public RIOImports.RIORegisterBuffer RegisterBuffer; + + public RIOImports.RIOCreateCompletionQueue CreateCompletionQueue; + public RIOImports.RIOCreateRequestQueue CreateRequestQueue; + + + public RIOImports.RIOReceive Receive; + public RIOImports.RIOSend Send; + + public RIOImports.RIONotify Notify; + + public RIOImports.RIOCloseCompletionQueue CloseCompletionQueue; + public RIOImports.RIODequeueCompletion DequeueCompletion; + public RIOImports.RIODeregisterBuffer DeregisterBuffer; + public RIOImports.RIOResizeCompletionQueue ResizeCompletionQueue; + public RIOImports.RIOResizeRequestQueue ResizeRequestQueue; + + + public const long CachedValue = long.MinValue; + + public RIO() + { + } + } + + public static class RIOImports + { + const string WS2_32 = "WS2_32.dll"; + + [StructLayout(LayoutKind.Sequential)] + private unsafe struct RIO_EXTENSION_FUNCTION_TABLE + { + public UInt32 cbSize; + + public IntPtr RIOReceive; + public IntPtr RIOReceiveEx; + public IntPtr RIOSend; + public IntPtr RIOSendEx; + public IntPtr RIOCloseCompletionQueue; + public IntPtr RIOCreateCompletionQueue; + public IntPtr RIOCreateRequestQueue; + public IntPtr RIODequeueCompletion; + public IntPtr RIODeregisterBuffer; + public IntPtr RIONotify; + public IntPtr RIORegisterBuffer; + public IntPtr RIOResizeCompletionQueue; + public IntPtr RIOResizeRequestQueue; + } + + readonly static IntPtr RIO_INVALID_BUFFERID = (IntPtr)0xFFFFFFFF; + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public delegate IntPtr RIORegisterBuffer([In] IntPtr DataBuffer, [In] UInt32 DataLength); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public delegate void RIODeregisterBuffer([In] IntPtr BufferId); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public unsafe delegate bool RIOSend([In] IntPtr SocketQueue, [In] RIO_BUFSEGMENT* RioBuffer, [In] UInt32 DataBufferCount, [In] RIO_SEND_FLAGS Flags, [In] long RequestCorrelation); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public delegate bool RIOReceive([In] IntPtr SocketQueue, [In] ref RIO_BUFSEGMENT RioBuffer, [In] UInt32 DataBufferCount, [In] RIO_RECEIVE_FLAGS Flags, [In] long RequestCorrelation); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public delegate IntPtr RIOCreateCompletionQueue([In] uint QueueSize, [In] RIO_NOTIFICATION_COMPLETION NotificationCompletion); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public delegate void RIOCloseCompletionQueue([In] IntPtr CQ); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public delegate IntPtr RIOCreateRequestQueue( + [In] IntPtr Socket, + [In] UInt32 MaxOutstandingReceive, + [In] UInt32 MaxReceiveDataBuffers, + [In] UInt32 MaxOutstandingSend, + [In] UInt32 MaxSendDataBuffers, + [In] IntPtr ReceiveCQ, + [In] IntPtr SendCQ, + [In] long ConnectionCorrelation + ); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public delegate uint RIODequeueCompletion([In] IntPtr CQ, [In] IntPtr ResultArray, [In] uint ResultArrayLength); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public delegate Int32 RIONotify([In] IntPtr CQ); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public delegate bool RIOResizeCompletionQueue([In] IntPtr CQ, [In] UInt32 QueueSize); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public delegate bool RIOResizeRequestQueue([In] IntPtr RQ, [In] UInt32 MaxOutstandingReceive, [In] UInt32 MaxOutstandingSend); + + const uint IOC_OUT = 0x40000000; + const uint IOC_IN = 0x80000000; + const uint IOC_INOUT = IOC_IN | IOC_OUT; + const uint IOC_WS2 = 0x08000000; + const uint IOC_VENDOR = 0x18000000; + const uint SIO_GET_MULTIPLE_EXTENSION_FUNCTION_POINTER = IOC_INOUT | IOC_WS2 | 36; + + const int SIO_LOOPBACK_FAST_PATH = -1744830448;// IOC_IN | IOC_WS2 | 16; + + const int TCP_NODELAY = 0x0001; + const int IPPROTO_TCP = 6; + + public unsafe static RIO Initalize(IntPtr socket) + { + + UInt32 dwBytes = 0; + RIO_EXTENSION_FUNCTION_TABLE rio = new RIO_EXTENSION_FUNCTION_TABLE(); + Guid RioFunctionsTableId = new Guid("8509e081-96dd-4005-b165-9e2ee8c79e3f"); + + + int True = -1; + + int result = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*)&True, 4); + if (result != 0) + { + var error = RIOImports.WSAGetLastError(); + RIOImports.WSACleanup(); + throw new Exception(String.Format("ERROR: setsockopt TCP_NODELAY returned {0}", error)); + } + + result = WSAIoctlGeneral(socket, SIO_LOOPBACK_FAST_PATH, + &True, 4, null, 0, + out dwBytes, IntPtr.Zero, IntPtr.Zero); + + if (result != 0) + { + var error = RIOImports.WSAGetLastError(); + RIOImports.WSACleanup(); + throw new Exception(String.Format("ERROR: WSAIoctl SIO_LOOPBACK_FAST_PATH returned {0}", error)); + } + + result = WSAIoctl(socket, SIO_GET_MULTIPLE_EXTENSION_FUNCTION_POINTER, + ref RioFunctionsTableId, 16, ref rio, + sizeof(RIO_EXTENSION_FUNCTION_TABLE), + out dwBytes, IntPtr.Zero, IntPtr.Zero); + + if (result != 0) + { + var error = RIOImports.WSAGetLastError(); + RIOImports.WSACleanup(); + throw new Exception(String.Format("ERROR: RIOInitalize returned {0}", error)); + } + else + { + RIO rioFunctions = new RIO(); + + rioFunctions.RegisterBuffer = Marshal.GetDelegateForFunctionPointer(rio.RIORegisterBuffer); + + rioFunctions.CreateCompletionQueue = Marshal.GetDelegateForFunctionPointer(rio.RIOCreateCompletionQueue); + + rioFunctions.CreateRequestQueue = Marshal.GetDelegateForFunctionPointer(rio.RIOCreateRequestQueue); + + rioFunctions.Notify = Marshal.GetDelegateForFunctionPointer(rio.RIONotify); + rioFunctions.DequeueCompletion = Marshal.GetDelegateForFunctionPointer(rio.RIODequeueCompletion); + + rioFunctions.Receive = Marshal.GetDelegateForFunctionPointer(rio.RIOReceive); + rioFunctions.Send = Marshal.GetDelegateForFunctionPointer(rio.RIOSend); + + rioFunctions.CloseCompletionQueue = Marshal.GetDelegateForFunctionPointer(rio.RIOCloseCompletionQueue); + rioFunctions.DeregisterBuffer = Marshal.GetDelegateForFunctionPointer(rio.RIODeregisterBuffer); + rioFunctions.ResizeCompletionQueue = Marshal.GetDelegateForFunctionPointer(rio.RIOResizeCompletionQueue); + rioFunctions.ResizeRequestQueue = Marshal.GetDelegateForFunctionPointer(rio.RIOResizeRequestQueue); + + return rioFunctions; + } + } + + [DllImport(WS2_32, SetLastError = true), SuppressUnmanagedCodeSecurity] + private static extern int WSAIoctl( + [In] IntPtr socket, + [In] uint dwIoControlCode, + [In] ref Guid lpvInBuffer, + [In] uint cbInBuffer, + [In, Out] ref RIO_EXTENSION_FUNCTION_TABLE lpvOutBuffer, + [In] int cbOutBuffer, + [Out] out uint lpcbBytesReturned, + [In] IntPtr lpOverlapped, + [In] IntPtr lpCompletionRoutine + ); + + [DllImport(WS2_32, SetLastError = true, EntryPoint = "WSAIoctl"), SuppressUnmanagedCodeSecurity] + private unsafe static extern int WSAIoctlGeneral( + [In] IntPtr socket, + [In] int dwIoControlCode, + [In] int* lpvInBuffer, + [In] uint cbInBuffer, + [In] int* lpvOutBuffer, + [In] int cbOutBuffer, + [Out] out uint lpcbBytesReturned, + [In] IntPtr lpOverlapped, + [In] IntPtr lpCompletionRoutine + ); + + [DllImport(WS2_32, SetLastError = true, CharSet = CharSet.Ansi, BestFitMapping = true, ThrowOnUnmappableChar = true), SuppressUnmanagedCodeSecurity] + internal static extern SocketError WSAStartup([In] short wVersionRequested, [Out] out WSAData lpWSAData ); + + [DllImport(WS2_32, SetLastError = true, CharSet = CharSet.Ansi), SuppressUnmanagedCodeSecurity] + public static extern IntPtr WSASocket([In] ADDRESS_FAMILIES af, [In] SOCKET_TYPE type, [In] PROTOCOL protocol, [In] IntPtr lpProtocolInfo, [In] Int32 group, [In] SOCKET_FLAGS dwFlags ); + + [DllImport(WS2_32, SetLastError = true), SuppressUnmanagedCodeSecurity] + public static extern ushort htons([In] ushort hostshort); + + [DllImport(WS2_32, SetLastError = true, CharSet = CharSet.Ansi)] + public static extern int bind(IntPtr s, ref sockaddr_in name, int namelen); + + [DllImport(WS2_32, SetLastError = true), SuppressUnmanagedCodeSecurity] + public static extern int listen(IntPtr s, int backlog); + + [DllImport(WS2_32, SetLastError = true), SuppressUnmanagedCodeSecurity] + public unsafe static extern int setsockopt(IntPtr s, int level, int optname, char* optval, int optlen); + + [DllImport(WS2_32, SetLastError = true), SuppressUnmanagedCodeSecurity] + public static extern IntPtr accept(IntPtr s, IntPtr addr, int addrlen); + + [DllImport(WS2_32), SuppressUnmanagedCodeSecurity] + public static extern Int32 WSAGetLastError(); + + [DllImport(WS2_32, SetLastError = true), SuppressUnmanagedCodeSecurity] + public static extern Int32 WSACleanup(); + + [DllImport(WS2_32, SetLastError = true), SuppressUnmanagedCodeSecurity] + public static extern int closesocket(IntPtr s); + + public const int SOCKET_ERROR = -1; + public const int INVALID_SOCKET = -1; + } + + public enum ADDRESS_FAMILIES : short + { + AF_INET = 2, + } + + public enum SOCKET_TYPE : short + { + SOCK_STREAM = 1, + } + + public enum PROTOCOL : short + { + IPPROTO_TCP = 6, + } + + public enum SOCKET_FLAGS : UInt32 + { + OVERLAPPED = 0x01, + MULTIPOINT_C_ROOT = 0x02, + MULTIPOINT_C_LEAF = 0x04, + MULTIPOINT_D_ROOT = 0x08, + MULTIPOINT_D_LEAF = 0x10, + ACCESS_SYSTEM_SECURITY = 0x40, + NO_HANDLE_INHERIT = 0x80, + REGISTERED_IO = 0x100 + } + + public enum RIO_SEND_FLAGS : UInt32 + { + NONE = 0x00000000, + DONT_NOTIFY = 0x00000001, + DEFER = 0x00000002, + COMMIT_ONLY = 0x00000008 + } + public enum RIO_RECEIVE_FLAGS : UInt32 + { + NONE = 0x00000000, + DONT_NOTIFY = 0x00000001, + DEFER = 0x00000002, + WAITALL = 0x00000004, + COMMIT_ONLY = 0x00000008 + } + + [StructLayout(LayoutKind.Sequential)] + internal struct WSAData + { + internal short wVersion; + internal short wHighVersion; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)] + internal string szDescription; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)] + internal string szSystemStatus; + internal short iMaxSockets; + internal short iMaxUdpDg; + internal IntPtr lpVendorInfo; + } + + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct sockaddr_in + { + public ADDRESS_FAMILIES sin_family; + public ushort sin_port; + public in_addr sin_addr; + public fixed byte sin_zero[8]; + } + + [StructLayout(LayoutKind.Explicit, Size = 4)] + public struct in_addr + { + [FieldOffset(0)] + public byte s_b1; + [FieldOffset(1)] + public byte s_b2; + [FieldOffset(2)] + public byte s_b3; + [FieldOffset(3)] + public byte s_b4; + } +} diff --git a/experimental/ManagedRIOHttpServer/RegisteredIO/RIOPooledSegment.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOPooledSegment.cs new file mode 100644 index 000000000..c1f90d947 --- /dev/null +++ b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOPooledSegment.cs @@ -0,0 +1,37 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace ManagedRIOHttpServer.RegisteredIO +{ + public struct RIOPooledSegment : IDisposable + { + public readonly byte[] Buffer; + internal RIO_BUFSEGMENT RioBuffer; + public readonly int PoolIndex; + private RIOBufferPool _owningPool; + internal RIOPooledSegment(int index, RIOBufferPool owningPool, RIO_BUFSEGMENT segment, byte[] buffer) + { + PoolIndex = index; + _owningPool = owningPool; + RioBuffer = segment; + Buffer = buffer; + } + + public int Offset + { + get + { + return (int)RioBuffer.Offset; + } + } + + #region IDisposable Support + public void Dispose() + { + _owningPool.ReleaseBuffer(PoolIndex); + } + #endregion + } +} diff --git a/experimental/ManagedRIOHttpServer/RegisteredIO/RIOReceiveTask.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOReceiveTask.cs new file mode 100644 index 000000000..7b555a7a5 --- /dev/null +++ b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOReceiveTask.cs @@ -0,0 +1,104 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace ManagedRIOHttpServer.RegisteredIO +{ + public sealed class RIOReceiveTask : INotifyCompletion, ICriticalNotifyCompletion + { + private readonly static Action CALLBACK_RAN = () => { }; + private bool _isCompleted; + private Action _continuation; + + private uint _bytesTransferred; + private uint _requestCorrelation; + private ArraySegment _buffer; + internal RIOPooledSegment _segment; + private RIOTcpConnection _connection; + + public RIOReceiveTask(RIOTcpConnection connection, RIOPooledSegment segment) + { + _segment = segment; + _connection = connection; + } + + internal void Reset() + { + _bytesTransferred = 0; + _isCompleted = false; + _continuation = null; + } + internal void SetBuffer(ArraySegment buffer) + { + _buffer = buffer; + } + internal void Complete(uint bytesTransferred, uint requestCorrelation) + { + _bytesTransferred = bytesTransferred; + _requestCorrelation = requestCorrelation; + _isCompleted = true; + + Action continuation = _continuation ?? Interlocked.CompareExchange(ref _continuation, CALLBACK_RAN, null); + if (continuation != null) + { + CompleteCallback(continuation); + } + } + + internal void CompleteCallback(Action continuation) + { + ThreadPool.UnsafeQueueUserWorkItem(UnsafeCallback, continuation); + } + + public RIOReceiveTask GetAwaiter() { return this; } + + public bool IsCompleted { get { return _isCompleted; } } + + private void UnsafeCallback(object state) + { + ((Action)state)(); + } + + public void OnCompleted(Action continuation) + { + throw new NotImplementedException(); + } + + [System.Security.SecurityCritical] + public void UnsafeOnCompleted(Action continuation) + { + if (_continuation == CALLBACK_RAN || + Interlocked.CompareExchange( + ref _continuation, continuation, null) == CALLBACK_RAN) + { + CompleteCallback(continuation); + } + } + public uint GetResult() + { + var bytesTransferred = _bytesTransferred; + Buffer.BlockCopy(_segment.Buffer, _segment.Offset, _buffer.Array, _buffer.Offset, (int)bytesTransferred); + Reset(); + _connection.PostReceive(_requestCorrelation); + return bytesTransferred; + } + + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + internal void Dispose() + { + if (!disposedValue) + { + disposedValue = true; + _segment.Dispose(); + } + } + + #endregion + + } +} diff --git a/experimental/ManagedRIOHttpServer/RegisteredIO/RIOTcpConnection.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOTcpConnection.cs new file mode 100644 index 000000000..8167026a0 --- /dev/null +++ b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOTcpConnection.cs @@ -0,0 +1,262 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; + +namespace ManagedRIOHttpServer.RegisteredIO +{ + public unsafe sealed class RIOTcpConnection : IDisposable + { + long _connectionId; + IntPtr _socket; + IntPtr _requestQueue; + RIO _rio; + RIOWorkBundle _wb; + + long _sendCount = 0; + long _receiveRequestCount = 0; + + RIOReceiveTask[] _receiveTasks; + RIOPooledSegment[] _sendSegments; + ArraySegment[] _receiveRequestBuffers; + public const int MaxPendingReceives = 16; + public const int MaxPendingSends = MaxPendingReceives * 4; + public const int IOCPOverflowEvents = 8; + const int ReceiveMask = MaxPendingReceives - 1; + const int SendMask = MaxPendingSends - 1; + + internal RIOTcpConnection(IntPtr socket, long connectionId, RIOWorkBundle wb, RIO rio) + { + _socket = socket; + _connectionId = connectionId; + _rio = rio; + _wb = wb; + + _requestQueue = _rio.CreateRequestQueue(_socket, MaxPendingReceives + IOCPOverflowEvents, 1, MaxPendingSends + IOCPOverflowEvents, 1, wb.completionQueue, wb.completionQueue, connectionId); + if (_requestQueue == IntPtr.Zero) + { + var error = RIOImports.WSAGetLastError(); + RIOImports.WSACleanup(); + throw new Exception(String.Format("ERROR: CreateRequestQueue returned {0}", error)); + } + + _receiveTasks = new RIOReceiveTask[MaxPendingReceives]; + _receiveRequestBuffers = new ArraySegment[MaxPendingReceives]; + + for (var i = 0; i < _receiveTasks.Length; i++) + { + _receiveTasks[i] = new RIOReceiveTask(this, wb.bufferPool.GetBuffer()); + } + + _sendSegments = new RIOPooledSegment[MaxPendingSends]; + for (var i = 0; i < _sendSegments.Length; i++) + { + _sendSegments[i] = wb.bufferPool.GetBuffer(); + } + + wb.connections.TryAdd(connectionId, this); + + for (var i = 0; i < _receiveTasks.Length; i++) + { + PostReceive(i); + } + } + + const RIO_SEND_FLAGS MessagePart = RIO_SEND_FLAGS.DEFER | RIO_SEND_FLAGS.DONT_NOTIFY; + const RIO_SEND_FLAGS MessageEnd = RIO_SEND_FLAGS.NONE; + + int _currentOffset = 0; + public void FlushSends() + { + var segment = _sendSegments[_sendCount & SendMask]; + if (_currentOffset > 0) + { + segment.RioBuffer.Length = (uint)_currentOffset; + if (!_rio.Send(_requestQueue, &segment.RioBuffer, 1, MessageEnd, -_sendCount - 1)) + { + ReportError("Flush"); + } + _currentOffset = 0; + _sendCount++; + } + } + public void QueueSend(ArraySegment buffer, bool isEnd) + { + var segment = _sendSegments[_sendCount & SendMask]; + var count = buffer.Count; + var offset = buffer.Offset; + + do + { + var length = count >= RIOBufferPool.PacketSize - _currentOffset ? RIOBufferPool.PacketSize - _currentOffset : count; + Buffer.BlockCopy(buffer.Array, offset, segment.Buffer, segment.Offset + _currentOffset, length); + _currentOffset += length; + + if (_currentOffset == RIOBufferPool.PacketSize) + { + segment.RioBuffer.Length = RIOBufferPool.PacketSize; + _sendCount++; + if (!_rio.Send(_requestQueue, &segment.RioBuffer, 1, (((_sendCount & SendMask) == 0) ? MessageEnd : MessagePart), -_sendCount - 1)) + { + ReportError("Send"); + } + _currentOffset = 0; + segment = _sendSegments[_sendCount & SendMask]; + } + else if (_currentOffset > RIOBufferPool.PacketSize) + { + throw new Exception("Overflowed buffer"); + } + + offset += length; + count -= length; + } while (count > 0); + + if (isEnd) + { + if (_currentOffset > 0) + { + segment.RioBuffer.Length = (uint)_currentOffset; + if (!_rio.Send(_requestQueue, &segment.RioBuffer, 1, MessageEnd, -_sendCount - 1)) + { + ReportError("Send"); + return; + } + _currentOffset = 0; + _sendCount++; + } + else + { + if (!_rio.Send(_requestQueue, null, 0, RIO_SEND_FLAGS.COMMIT_ONLY, 0)) + { + ReportError("Commit"); + return; + } + _currentOffset = 0; + _sendCount++; + } + } + } + + private static void ReportError(string type) + { + var errorNo = RIOImports.WSAGetLastError(); + + string errorMessage; + switch (errorNo) + { + case 10014: // WSAEFAULT + errorMessage = type + " failed: WSAEFAULT - The system detected an invalid pointer address in attempting to use a pointer argument in a call."; + break; + case 10022: // WSAEINVAL + errorMessage = type + " failed: WSAEINVAL - the SocketQueue parameter is not valid, the Flags parameter contains an value not valid for a send operation, or the integrity of the completion queue has been compromised."; + break; + case 10055: // WSAENOBUFS + errorMessage = type + " failed: WSAENOBUFS - Sufficient memory could not be allocated, the I/O completion queue associated with the SocketQueue parameter is full."; + break; + case 997: // WSA_IO_PENDING + errorMessage = type + " failed? WSA_IO_PENDING - The operation has been successfully initiated and the completion will be queued at a later time."; + break; + case 995: // WSA_OPERATION_ABORTED + errorMessage = type + " failed. WSA_OPERATION_ABORTED - The operation has been canceled while the receive operation was pending. ."; + break; + default: + errorMessage = string.Format(type + " failed: WSA error code {0}", errorNo); + break; + } + throw new ApplicationException(errorMessage); + + } + + public void SendCachedBad() + { + fixed (RIO_BUFSEGMENT* pSeg = &_wb.cachedBad) + { + _rio.Send(_requestQueue, pSeg, 1, MessageEnd, RIO.CachedValue); + } + } + public void SendCachedBusy() + { + fixed (RIO_BUFSEGMENT* pSeg = &_wb.cachedBusy) + { + _rio.Send(_requestQueue, pSeg, 1, MessageEnd, RIO.CachedValue); + } + } + + public void CompleteReceive(long RequestCorrelation, uint BytesTransferred) + { + var receiveIndex = RequestCorrelation & ReceiveMask; + var receiveTask = _receiveTasks[receiveIndex]; + receiveTask.Complete(BytesTransferred, (uint)receiveIndex); + } + + internal void PostReceive(long receiveIndex) + { + var receiveTask = _receiveTasks[receiveIndex]; + if (!_rio.Receive(_requestQueue, ref receiveTask._segment.RioBuffer, 1, RIO_RECEIVE_FLAGS.NONE, receiveIndex)) + { + ReportError("Receive"); + return; + } + } + + public RIOReceiveTask ReceiveAsync(ArraySegment buffer, CancellationToken cancellationToken) + { + var receiveIndex = (Interlocked.Increment(ref _receiveRequestCount) - 1) & ReceiveMask; + var receiveTask = _receiveTasks[receiveIndex]; + receiveTask.SetBuffer(buffer); + return receiveTask; + } + + public void Close() + { + Dispose(true); + } + + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + private void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + //_receiveTask.Dispose(); + } + + RIOTcpConnection connection; + _wb.connections.TryRemove(_connectionId, out connection); + RIOImports.closesocket(_socket); + for (var i = 0; i < _receiveTasks.Length; i++) + { + _receiveTasks[i].Dispose(); + } + + for (var i = 0; i < _sendSegments.Length; i++) + { + _sendSegments[i].Dispose(); + } + + disposedValue = true; + } + } + + ~RIOTcpConnection() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(false); + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + } + +} diff --git a/experimental/ManagedRIOHttpServer/RegisteredIO/RIOTcpServer.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOTcpServer.cs new file mode 100644 index 000000000..120c9b40f --- /dev/null +++ b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOTcpServer.cs @@ -0,0 +1,135 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Net.Sockets; +using System.Threading; + +namespace ManagedRIOHttpServer.RegisteredIO +{ + public sealed class RIOTcpServer + { + IntPtr _socket; + RIO _rio; + RIOThreadPool _pool; + + long _connectionId; + + public RIOTcpServer(ushort port, byte address1, byte address2, byte address3, byte address4) + { + var version = new Version(2, 2); + WSAData data; + SocketError result = RIOImports.WSAStartup((short)version.Raw, out data); + if (result != SocketError.Success) + { + var error = RIOImports.WSAGetLastError(); + throw new Exception(string.Format("ERROR: WSAStartup returned {0}", error)); + } + + _socket = RIOImports.WSASocket(ADDRESS_FAMILIES.AF_INET, SOCKET_TYPE.SOCK_STREAM, PROTOCOL.IPPROTO_TCP, IntPtr.Zero, 0, SOCKET_FLAGS.REGISTERED_IO); + if (_socket == IntPtr.Zero) + { + var error = RIOImports.WSAGetLastError(); + RIOImports.WSACleanup(); + throw new Exception(string.Format("ERROR: WSASocket returned {0}", error)); + } + + _rio = RIOImports.Initalize(_socket); + + + _pool = new RIOThreadPool(_rio, _socket, CancellationToken.None); + _connectionId = 0; + Start(port, address1, address2, address3, address4); + } + + private void Start(ushort port, byte address1, byte address2, byte address3, byte address4) + { + // BIND + in_addr inAddress = new in_addr(); + inAddress.s_b1 = address1; + inAddress.s_b2 = address2; + inAddress.s_b3 = address3; + inAddress.s_b4 = address4; + + sockaddr_in sa = new sockaddr_in(); + sa.sin_family = ADDRESS_FAMILIES.AF_INET; + sa.sin_port = RIOImports.htons(port); + sa.sin_addr = inAddress; + + int result; + unsafe + { + var size = sizeof(sockaddr_in); + result = RIOImports.bind(_socket, ref sa, size); + } + if (result == RIOImports.SOCKET_ERROR) + { + RIOImports.WSACleanup(); + throw new Exception("bind failed"); + } + + // LISTEN + result = RIOImports.listen(_socket, 2048); + if (result == RIOImports.SOCKET_ERROR) + { + RIOImports.WSACleanup(); + throw new Exception("listen failed"); + } + } + public RIOTcpConnection Accept() + { + IntPtr accepted = RIOImports.accept(_socket, IntPtr.Zero, 0); + if (accepted == new IntPtr(-1)) + { + var error = RIOImports.WSAGetLastError(); + RIOImports.WSACleanup(); + throw new Exception(string.Format("listen failed with {0}", error)); + } + var connection = Interlocked.Increment(ref _connectionId); + return new RIOTcpConnection(accepted, connection, _pool.GetWorker(connection), _rio); + } + + public void Stop() + { + RIOImports.WSACleanup(); + } + + public struct Version + { + public ushort Raw; + + public Version(byte major, byte minor) + { + Raw = major; + Raw <<= 8; + Raw += minor; + } + + public byte Major + { + get + { + ushort result = Raw; + result >>= 8; + return (byte)result; + } + } + + public byte Minor + { + get + { + ushort result = Raw; + result &= 0x00FF; + return (byte)result; + } + } + + public override string ToString() + { + return string.Format("{0}.{1}", Major, Minor); + } + } + } + +} diff --git a/experimental/ManagedRIOHttpServer/RegisteredIO/RIOThreadPool.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOThreadPool.cs new file mode 100644 index 000000000..7a636befa --- /dev/null +++ b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOThreadPool.cs @@ -0,0 +1,213 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; +using System.Threading; + +namespace ManagedRIOHttpServer.RegisteredIO +{ + internal class RIOThreadPool + { + private RIO _rio; + private CancellationToken _token; + private int _maxThreads; + + public const int PreAllocSocketsPerThread = 512; + private const int MaxOutsandingCompletions = (RIOTcpConnection.MaxPendingReceives + RIOTcpConnection.IOCPOverflowEvents + + RIOTcpConnection.MaxPendingSends + RIOTcpConnection.IOCPOverflowEvents) + * PreAllocSocketsPerThread; + + private IntPtr _socket; + + internal RIOWorkBundle GetWorker(long connetionId) + { + return _workers[(connetionId % _maxThreads)]; + } + + private RIOWorkBundle[] _workers; + + public unsafe RIOThreadPool(RIO rio, IntPtr socket, CancellationToken token) + { + _socket = socket; + _rio = rio; + _token = token; + + _maxThreads = Environment.ProcessorCount; + + _workers = new RIOWorkBundle[_maxThreads]; + for (var i = 0; i < _workers.Length; i++) + { + var worker = new RIOWorkBundle() + { + id = i, + bufferPool = new RIOBufferPool(_rio) + }; + worker.completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, IntPtr.Zero, 0, 0); + + if (worker.completionPort == IntPtr.Zero) + { + var error = GetLastError(); + RIOImports.WSACleanup(); + throw new Exception(string.Format("ERROR: CreateIoCompletionPort returned {0}", error)); + } + + var completionMethod = new RIO_NOTIFICATION_COMPLETION() + { + Type = RIO_NOTIFICATION_COMPLETION_TYPE.IOCP_COMPLETION, + Iocp = new RIO_NOTIFICATION_COMPLETION_IOCP() + { + IocpHandle = worker.completionPort, + QueueCorrelation = (ulong)i, + Overlapped = (NativeOverlapped*)(-1)// nativeOverlapped + } + }; + worker.completionQueue = _rio.CreateCompletionQueue(MaxOutsandingCompletions, completionMethod); + + if (worker.completionQueue == IntPtr.Zero) + { + var error = RIOImports.WSAGetLastError(); + RIOImports.WSACleanup(); + throw new Exception(String.Format("ERROR: CreateCompletionQueue returned {0}", error)); + } + + worker.connections = new ConcurrentDictionary(); + worker.thread = new Thread(GetThreadStart(i)); + worker.thread.Name = "RIOThread " + i.ToString(); + worker.thread.IsBackground = true; + _workers[i] = worker; + } + + // gc + GC.Collect(2, GCCollectionMode.Forced, true, true); + GC.WaitForPendingFinalizers(); + GC.Collect(2, GCCollectionMode.Forced, true, true); + + //GC.Collect(2, GCCollectionMode.Forced, true); + //GC.WaitForPendingFinalizers(); + //GC.Collect(2, GCCollectionMode.Forced, true); + + for (var i = 0; i < _workers.Length; i++) + { + // pin buffers + _workers[i].bufferPool.Initalize(); + } + + + for (var i = 0; i < _workers.Length; i++) + { + _workers[i].thread.Start(); + } + } + private ThreadStart GetThreadStart(int i) + { + return new ThreadStart(() => + { + Process(i); + }); + + } + + static readonly string badResponseStr = "HTTP/1.1 400 Bad Request\r\n" + + "Content-Type: text/plain;charset=UTF-8\r\n" + + "Content-Length: 0\r\n" + + "Connection: keep-alive\r\n" + + "Server: -RIO-\r\n" + + "\r\n"; + + private static byte[] _badResponseBytes = Encoding.UTF8.GetBytes(badResponseStr); + + static readonly string busyResponseStr = "HTTP/1.1 503 Service Unavailable\r\n" + + "Content-Type: text/plain;charset=UTF-8\r\n" + + "Content-Length: 4\r\n" + + "Connection: keep-alive\r\n" + + "Server: -RIO-\r\n" + + "\r\n" + + "Busy"; + + private static byte[] _busyResponseBytes = Encoding.UTF8.GetBytes(busyResponseStr); + + const int maxResults = 1024; + private unsafe void Process(int id) + { + RIO_RESULT* results = stackalloc RIO_RESULT[maxResults]; + uint bytes, key; + NativeOverlapped* overlapped; + + var worker = _workers[id]; + var completionPort = worker.completionPort; + var cq = worker.completionQueue; + + RIOPooledSegment cachedBadBuffer = worker.bufferPool.GetBuffer(); + Buffer.BlockCopy(_badResponseBytes, 0, cachedBadBuffer.Buffer, cachedBadBuffer.Offset, _badResponseBytes.Length); + cachedBadBuffer.RioBuffer.Length = (uint)_badResponseBytes.Length; + worker.cachedBad = cachedBadBuffer.RioBuffer; + + RIOPooledSegment cachedBusyBuffer = worker.bufferPool.GetBuffer(); + Buffer.BlockCopy(_busyResponseBytes, 0, cachedBusyBuffer.Buffer, cachedBusyBuffer.Offset, _busyResponseBytes.Length); + cachedBusyBuffer.RioBuffer.Length = (uint)_busyResponseBytes.Length; + worker.cachedBusy = cachedBusyBuffer.RioBuffer; + + uint count; + RIO_RESULT result; + while (!_token.IsCancellationRequested) + { + _rio.Notify(cq); + var sucess = GetQueuedCompletionStatus(completionPort, out bytes, out key, out overlapped, -1); + if (sucess) + { + var activatedCompletionPort = false; + while ((count = _rio.DequeueCompletion(cq, (IntPtr)results, maxResults)) > 0) + { + for (var i = 0; i < count; i++) + { + result = results[i]; + if (result.RequestCorrelation >= 0) + { + // receive + RIOTcpConnection connection; + if (worker.connections.TryGetValue(result.ConnectionCorrelation, out connection)) + { + + connection.CompleteReceive(result.RequestCorrelation, result.BytesTransferred); + } + } + } + + if (!activatedCompletionPort) + { + _rio.Notify(cq); + activatedCompletionPort = true; + } + } + } + else + { + var error = GetLastError(); + if (error != 258) + { + throw new Exception(string.Format("ERROR: GetQueuedCompletionStatusEx returned {0}", error)); + } + } + } + cachedBadBuffer.Dispose(); + cachedBusyBuffer.Dispose(); + } + + const string Kernel_32 = "Kernel32"; + const long INVALID_HANDLE_VALUE = -1; + + [DllImport(Kernel_32, SetLastError = true), SuppressUnmanagedCodeSecurity] + private unsafe static extern IntPtr CreateIoCompletionPort(long handle, IntPtr hExistingCompletionPort, int puiCompletionKey, uint uiNumberOfConcurrentThreads); + + [DllImport(Kernel_32, SetLastError = true), SuppressUnmanagedCodeSecurity] + private static extern unsafe bool GetQueuedCompletionStatus(IntPtr CompletionPort, out uint lpNumberOfBytes, out uint lpCompletionKey, out NativeOverlapped* lpOverlapped, int dwMilliseconds); + + [DllImport(Kernel_32, SetLastError = true), SuppressUnmanagedCodeSecurity] + private static extern long GetLastError(); + + } +} diff --git a/experimental/ManagedRIOHttpServer/RegisteredIO/RIOWorkBundle.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOWorkBundle.cs new file mode 100644 index 000000000..72a7ccc38 --- /dev/null +++ b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOWorkBundle.cs @@ -0,0 +1,23 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace ManagedRIOHttpServer.RegisteredIO +{ + internal unsafe class RIOWorkBundle + { + public int id; + public IntPtr completionPort; + public IntPtr completionQueue; + + public ConcurrentDictionary connections; + public Thread thread; + + public RIOBufferPool bufferPool; + public RIO_BUFSEGMENT cachedBad; + public RIO_BUFSEGMENT cachedBusy; + } +} diff --git a/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_BUFSEGMENT.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_BUFSEGMENT.cs new file mode 100644 index 000000000..a5ead8266 --- /dev/null +++ b/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_BUFSEGMENT.cs @@ -0,0 +1,23 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; + +namespace ManagedRIOHttpServer.RegisteredIO +{ + [StructLayout(LayoutKind.Sequential)] + public struct RIO_BUFSEGMENT + { + public RIO_BUFSEGMENT(IntPtr bufferId, uint offset, uint length) + { + BufferId = bufferId; + Offset = offset; + Length = length; + } + + IntPtr BufferId; + public readonly uint Offset; + public uint Length; + } +} diff --git a/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION.cs new file mode 100644 index 000000000..f288a447d --- /dev/null +++ b/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION.cs @@ -0,0 +1,14 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Runtime.InteropServices; + +namespace ManagedRIOHttpServer.RegisteredIO +{ + [StructLayout(LayoutKind.Sequential)] + public struct RIO_NOTIFICATION_COMPLETION + { + public RIO_NOTIFICATION_COMPLETION_TYPE Type; + public RIO_NOTIFICATION_COMPLETION_IOCP Iocp; + } +} diff --git a/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION_EVENT.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION_EVENT.cs new file mode 100644 index 000000000..a38206f7d --- /dev/null +++ b/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION_EVENT.cs @@ -0,0 +1,15 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; + +namespace ManagedRIOHttpServer.RegisteredIO +{ + [StructLayout(LayoutKind.Sequential)] + public struct RIO_NOTIFICATION_COMPLETION_EVENT + { + public IntPtr EventHandle; + public bool NotifyReset; + } +} diff --git a/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION_IOCP.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION_IOCP.cs new file mode 100644 index 000000000..e5435e6ff --- /dev/null +++ b/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION_IOCP.cs @@ -0,0 +1,17 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace ManagedRIOHttpServer.RegisteredIO +{ + [StructLayout(LayoutKind.Sequential)] + public unsafe struct RIO_NOTIFICATION_COMPLETION_IOCP + { + public IntPtr IocpHandle; + public ulong QueueCorrelation; + public NativeOverlapped* Overlapped; + } +} diff --git a/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION_TYPE.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION_TYPE.cs new file mode 100644 index 000000000..b614789ed --- /dev/null +++ b/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_NOTIFICATION_COMPLETION_TYPE.cs @@ -0,0 +1,12 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace ManagedRIOHttpServer.RegisteredIO +{ + public enum RIO_NOTIFICATION_COMPLETION_TYPE : int + { + POLLING = 0, + EVENT_COMPLETION = 1, + IOCP_COMPLETION = 2 + } +} diff --git a/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_RESULT.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_RESULT.cs new file mode 100644 index 000000000..735124b98 --- /dev/null +++ b/experimental/ManagedRIOHttpServer/RegisteredIO/RIO_RESULT.cs @@ -0,0 +1,16 @@ +// Copyright (c) Illyriad Games. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Runtime.InteropServices; + +namespace ManagedRIOHttpServer.RegisteredIO +{ + [StructLayout(LayoutKind.Sequential)] + public struct RIO_RESULT + { + public int Status; + public uint BytesTransferred; + public long ConnectionCorrelation; + public long RequestCorrelation; + } +} diff --git a/experimental/ManagedRIOHttpServer/app.manifest b/experimental/ManagedRIOHttpServer/app.manifest new file mode 100644 index 000000000..3a84e59a7 --- /dev/null +++ b/experimental/ManagedRIOHttpServer/app.manifest @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 784eb517926824a081f6aab017e616c1696294b4 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 24 Jul 2015 02:18:40 +0100 Subject: [PATCH 2/9] SIMD Vectorisation and NUMA fixes --- experimental/ManagedRIOHttpServer/App.config | 5 + .../ManagedRIOHttpServer.csproj | 6 + experimental/ManagedRIOHttpServer/Program.cs | 133 +++++++++++++++--- .../RegisteredIO/RIOBufferPool.cs | 6 +- .../RegisteredIO/RIOTcpConnection.cs | 2 +- .../RegisteredIO/RIOThreadPool.cs | 2 +- .../ManagedRIOHttpServer/packages.config | 4 + 7 files changed, 130 insertions(+), 28 deletions(-) create mode 100644 experimental/ManagedRIOHttpServer/packages.config diff --git a/experimental/ManagedRIOHttpServer/App.config b/experimental/ManagedRIOHttpServer/App.config index 2d2a12d81..41e288d43 100644 --- a/experimental/ManagedRIOHttpServer/App.config +++ b/experimental/ManagedRIOHttpServer/App.config @@ -3,4 +3,9 @@ + + + + + diff --git a/experimental/ManagedRIOHttpServer/ManagedRIOHttpServer.csproj b/experimental/ManagedRIOHttpServer/ManagedRIOHttpServer.csproj index fc3ce4a00..9bfac980d 100644 --- a/experimental/ManagedRIOHttpServer/ManagedRIOHttpServer.csproj +++ b/experimental/ManagedRIOHttpServer/ManagedRIOHttpServer.csproj @@ -41,6 +41,11 @@ + + + ..\..\packages\System.Numerics.Vectors.4.1.0-beta-23019\lib\net46\System.Numerics.Vectors.dll + True + @@ -69,6 +74,7 @@ +