fedjabosnic / hotfix

Low latency fix engine for .net
GNU Lesser General Public License v3.0
16 stars 5 forks source link

Async IO using completion ports #4

Open fedjabosnic opened 7 years ago

fedjabosnic commented 7 years ago

@GeorgeF0 Our current (basic) tcp/socket transport is probably good enough for now but we might want to consider using non-blocking io based on completion ports. I haven't done a huge amount of research so I don't know all the ins and outs but it's worth flagging up...

See links below for more details:

Note: We should add benchmarks which include the transport before delving into this...

fedjabosnic commented 7 years ago

Given that we now have benchmarks with transports (the demo being especially useful) we can attempt to see what this gives us...

jernejg commented 6 years ago

Hi,

No need to baffle yourself with completion ports (unless you are interested how they work). All of asynchronous i/o bound operations found in .NET BCL are already using completion ports under the hood.

To make the transport layer truly non-blocking (with the optional use of async/await) we have two options:

  1. Your project is targeting .NET framework 4.6.1 where asynchronous operations on System.Net.Sockets.Socket still use the old APM pattern (BeginXXX, EndXXX, NO support for async/await). Dont let the operations with XXXAsync postfix fool you here, you can see that they don't return a Task or Task<T>. If you would like to use async/await on Socket operations you will need to implement it manually or use a library like SocketAwaitable.

  2. If it is possible, upgrade to .NET 4.7.1. This will give you out of the box support to be able to "await" asynchronous operations on System.Net.Sockets.Socket via extension methods defined in SocketTaskExtensions class.

Now we can refactor the code. Use the XXXAsync counterpart of everything you use on the Socket (for example read & write):

        public async Task WriteAsync(byte[] buffer, int offset, int count)
        {
            await _socket.SendAsync(new ArraySegment<byte>(buffer, offset, count), SocketFlags.None);
        }

        public async Task<int> ReadAsync(byte[] buffer, int offset, int count)
        {
            //..same code omitted

            return await _socket.ReceiveAsync(new ArraySegment<byte>(buffer, offset, count), SocketFlags.None);
        }

We need to do the same to Accept, Initiate, Open and all the way up the stack. We will also need to change the logic in the Run function, because OpenAsync will now return straight away.