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..41e288d43
--- /dev/null
+++ b/experimental/ManagedRIOHttpServer/App.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
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..b79a2ab4e
--- /dev/null
+++ b/experimental/ManagedRIOHttpServer/ManagedRIOHttpServer.csproj
@@ -0,0 +1,88 @@
+
+
+
+
+ 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
+
+
+
+
+
+
+ ..\..\packages\System.Numerics.Vectors.4.1.0-beta-23019\lib\net46\System.Numerics.Vectors.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/experimental/ManagedRIOHttpServer/Program.cs b/experimental/ManagedRIOHttpServer/Program.cs
new file mode 100644
index 000000000..d72dcda88
--- /dev/null
+++ b/experimental/ManagedRIOHttpServer/Program.cs
@@ -0,0 +1,75 @@
+// 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.Numerics;
+using System.Threading;
+
+namespace ManagedRIOHttpServer
+{
+ public sealed class Program
+ {
+ static void Main(string[] args)
+ {
+ if (IntPtr.Size != 8)
+ {
+ Console.WriteLine("ManagedRIOHttpServer needs to be run in x64 mode");
+ return;
+ }
+
+ ThreadPool.SetMinThreads(100, 100);
+
+ Console.WriteLine("Starting Managed Registered IO Server");
+ Console.WriteLine("* Hardware Accelerated SIMD: {0}", Vector.IsHardwareAccelerated);
+ Console.WriteLine("* Vector.Count: {0}", Vector.Count);
+
+ try
+ {
+ var server = new RIOServer(5000);
+ server.Start();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Start up issue {0}", ex.Message);
+ }
+ }
+
+ //static byte[] careBytes = new byte[] {
+ // 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ // 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ // 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ // 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ // 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ // 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ // 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ // 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ //};
+
+ //public static void LowerCaseSIMD(ArraySegment data)
+ //{
+ // if (data.Offset + data.Count + Vector.Count < data.Array.Length)
+ // {
+ // throw new ArgumentOutOfRangeException("Nope");
+ // }
+ // var A = new Vector(65); // A
+ // var Z = new Vector(90); // Z
+
+ // for (var o = data.Offset; o < data.Count - Vector.Count; o += Vector.Count)
+ // {
+ // var v = new Vector(data.Array, o);
+
+ // v = Vector.ConditionalSelect(
+ // Vector.BitwiseAnd(
+ // Vector.GreaterThanOrEqual(v, A),
+ // Vector.LessThanOrEqual(v, Z)
+ // ),
+ // Vector.BitwiseOr(new Vector(0x20), v), // 0010 0000
+ // v
+ // );
+ // v.CopyTo(data.Array, o);
+ // }
+ //}
+ }
+
+}
+
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/RIOServer.cs b/experimental/ManagedRIOHttpServer/RIOServer.cs
new file mode 100644
index 000000000..40c76dd88
--- /dev/null
+++ b/experimental/ManagedRIOHttpServer/RIOServer.cs
@@ -0,0 +1,331 @@
+// 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.Numerics;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using ManagedRIOHttpServer.RegisteredIO;
+
+namespace ManagedRIOHttpServer
+{
+ sealed class RIOServer
+ {
+ #region "Body"
+ private const string bodyStr = "Hello, World!";
+ private static byte[] _bodyBytesSource = Encoding.UTF8.GetBytes(bodyStr);
+
+ private ThreadLocal _threadBody = new ThreadLocal(()=> {
+ var bytes = new byte[_bodyBytesSource.Length];
+ Buffer.BlockCopy(_bodyBytesSource, 0, bytes, 0, bytes.Length);
+ return bytes;
+ }, true);
+ #endregion
+
+ #region "Headers"
+ private const string headersKeepAliveStr = "HTTP/1.1 200 OK\r\n" +
+ "Server:-RIO-\r\n" +
+ "Content-Type:text/plain\r\n" +
+ "Content-Length:13\r\n" +
+ "Date:DDD, dd mmm yyyy hh:mm:ss GMT" +
+ "\r\n\r\n";
+ private static readonly byte[] _headerBytesSource = Encoding.ASCII.GetBytes(headersKeepAliveStr);
+
+ private Timer _updateDateTimer;
+
+ private static byte[] InitaliseHeader()
+ {
+ var bytes = new byte[_headerBytesSource.Length];
+ Buffer.BlockCopy(_headerBytesSource, 0, bytes, 0, bytes.Length);
+ return bytes;
+ }
+
+ private int headerIndex = 0;
+ private ThreadLocal _threadHeader0 = new ThreadLocal(InitaliseHeader, true);
+ private ThreadLocal _threadHeader1 = new ThreadLocal(InitaliseHeader, true);
+ private void SetupHeaderUpdate()
+ {
+ var start = _headerBytesSource.Length - 33;
+ _updateDateTimer = new Timer((obj) =>
+ {
+ var date = DateTime.UtcNow.ToString("r");
+ Encoding.ASCII.GetBytes(date, 0, date.Length, _headerBytesSource, start);
+
+ var newIndex = (headerIndex + 1) & 1;
+ var headers = newIndex == 0 ? _threadHeader0 : _threadHeader1;
+
+ foreach (var header in headers.Values)
+ {
+ Buffer.BlockCopy(_headerBytesSource, start, header, start, date.Length);
+ }
+
+ headerIndex = newIndex;
+ }, null, 0, 1000);
+ }
+ #endregion
+
+ public RIOServer(ushort port)
+ {
+ SetupHeaderUpdate();
+ Port = port;
+ }
+
+ public ushort Port { get; }
+
+ public void Start()
+ {
+ var ss = new RIOTcpServer(Port, 0, 0, 0, 0);
+ Console.WriteLine("* Listening on port: {0}", Port);
+
+ while (true)
+ {
+ var socket = ss.Accept();
+ ThreadPool.UnsafeQueueUserWorkItem(Serve, socket);
+ }
+ }
+
+ public Task StartAsync(CancellationToken token)
+ {
+ return Task.Run(() =>
+ {
+ var ss = new RIOTcpServer(Port, 0, 0, 0, 0);
+ Console.WriteLine("* Listening on port: {0}", Port);
+
+ while (!token.IsCancellationRequested)
+ {
+ var socket = ss.Accept();
+ ThreadPool.UnsafeQueueUserWorkItem(Serve, socket);
+ }
+ });
+ }
+
+ private 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
+ }
+
+ // 65 delimiter bytes to allow offset of 1 start
+ static readonly byte[] delimiterBytes = new byte[] {
+ 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, // SSE 128
+ 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, // AVX 256
+ 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa,
+ 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, 0xd, 0xa, // AVX2 512
+ 0xd
+ };
+ private async Task ServeSocket(RIOTcpConnection socket)
+ {
+ try
+ {
+ var buffer0 = new byte[8192 + 64 + 64]; // max header size + AVX2 + cache line buffer
+ var buffer1 = new byte[8192 + 64 + 64]; // max header size + AVX2 + cache line buffer
+ var receiveBuffer0 = new ArraySegment(buffer0, 0, buffer0.Length);
+ var receiveBuffer1 = new ArraySegment(buffer1, 0, buffer1.Length);
+
+ var receiveTask = socket.ReceiveAsync(receiveBuffer0, CancellationToken.None);
+
+
+ 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
+ if (overflow > 0)
+ {
+ unsafe
+ {
+ fixed (byte* b = buffer)
+ {
+ 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;
+
+ var delimStart = new Vector(0xd); // '\r'
+ var delimNext = new Vector(0xa); // '\n'
+ //var vTrue = new Vector(careBytes, 64);
+ var delimVector = new Vector(delimiterBytes, 0);
+
+ var alignedDelim = Vector.AsVectorInt32(delimVector);
+
+ var ul = r - 3;
+ var hasStart = false;
+
+
+ for (var i = start; i < buffer.Length - Vector.Count; i += Vector.Count)
+ {
+ if (i > r)
+ {
+ break;
+ }
+ // buffer is more than 15 bytes larger than read for safety
+ var v0 = new Vector(buffer, i);
+ var v1 = new Vector(buffer, i + 1);
+
+ hasStart = Vector.EqualsAny(v0, delimStart);
+ var hasSecond = Vector.EqualsAny(v1, delimNext);
+ if (hasStart)
+ {
+ if (hasSecond)
+ {
+ // contains header line terminator
+
+ var v2 = new Vector(buffer, i + 2);
+ var v3 = new Vector(buffer, i + 3);
+ if (Vector.EqualsAny(alignedDelim, Vector.AsVectorInt32(v0)))
+ {
+ // contains headers terminator offset 0 bytes from Int32 start
+ count++;
+ last = i + Vector.Count + 1; // cheat, can't be another header terminator within 16 bytes
+ continue;
+ }
+ if (Vector.EqualsAny(alignedDelim, Vector.AsVectorInt32(v1)))
+ {
+ // contains headers terminator offset 1 bytes from Int32 start
+ count++;
+ last = i + Vector.Count + 2; // cheat, can't be another header terminator within 16 bytes
+ continue;
+ }
+ if (Vector.EqualsAny(alignedDelim, Vector.AsVectorInt32(v2)))
+ {
+ // contains headers terminator offset 2 bytes from Int32 start
+ count++;
+ last = i + Vector.Count + 3; // cheat, can't be another header terminator within 16 bytes
+ continue;
+ }
+ if (Vector.EqualsAny(alignedDelim, Vector.AsVectorInt32(v3)))
+ {
+ // contains headers terminator offset 3 bytes from Int32 start
+ count++;
+ last = i + Vector.Count + 2; // cheat, can't be another header terminator within 16 bytes
+ continue;
+ }
+ }
+ }
+ }
+
+ if (hasStart && last < r)
+ {
+ unsafe
+ {
+ fixed (byte* b = buffer)
+ {
+ // 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 headerBytes = headerIndex == 0 ? _threadHeader0.Value : _threadHeader1.Value;
+ var bodyBytes = _threadBody.Value;
+
+ var headerBuffer = new ArraySegment(headerBytes, 0, headerBytes.Length);
+ var bodyBuffer = new ArraySegment(bodyBytes, 0, bodyBytes.Length);
+
+ for (var i = 1; i < count; i++)
+ {
+ socket.QueueSend(headerBuffer, false);
+ socket.QueueSend(bodyBuffer, false);
+ }
+ socket.QueueSend(headerBuffer, 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/RegisteredIO/RIOBufferPool.cs b/experimental/ManagedRIOHttpServer/RegisteredIO/RIOBufferPool.cs
new file mode 100644
index 000000000..67137cb35
--- /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)) * 4; // MTU - (IPv4 Header + TCP Header)
+ private const int PooledPacketSize = PacketSize + 12 + 64; // PacketSize + 12 + 64 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 = 0u;
+ 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..f6d7d0610
--- /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 * 2;
+ 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..7c2a52d24
--- /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 = 256;
+ 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:close\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:close\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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/experimental/ManagedRIOHttpServer/packages.config b/experimental/ManagedRIOHttpServer/packages.config
new file mode 100644
index 000000000..40fc6dcb7
--- /dev/null
+++ b/experimental/ManagedRIOHttpServer/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file