From a845f8a1c0fd48d3837539fd5f348c101d45a83f Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 3 Oct 2021 18:04:53 -0700 Subject: [PATCH] feat: Networking v3 using epoll/kqueue (#813) * Replaces networking internals with epoll/kqueue --- Projects/Server/Main.cs | 4 +- Projects/Server/Network/ISocket.cs | 4 + .../Server/Network/NetState/DumpNetStates.cs | 2 +- Projects/Server/Network/NetState/NetState.cs | 303 +++++++----------- Projects/Server/Network/NetworkSocket.cs | 10 + .../Server/Network/PollGroup/EPollGroup.cs | 206 ++++++++++++ .../Network/PollGroup/KQueuePollGroup.cs | 248 ++++++++++++++ .../Server/Network/PollGroup/PollGroup.cs | 39 +++ Projects/Server/Network/TcpServer.cs | 22 +- Projects/Server/Server.csproj | 9 +- Projects/Server/wepoll.dll | Bin 0 -> 21504 bytes Projects/UOContent/Compression/ZstdArchive.cs | 4 +- THIRD-PARTY-NOTICES | 31 ++ version.json | 2 +- 14 files changed, 686 insertions(+), 198 deletions(-) mode change 100644 => 100755 Projects/Server/Network/NetState/NetState.cs create mode 100755 Projects/Server/Network/PollGroup/EPollGroup.cs create mode 100644 Projects/Server/Network/PollGroup/KQueuePollGroup.cs create mode 100644 Projects/Server/Network/PollGroup/PollGroup.cs create mode 100644 Projects/Server/wepoll.dll diff --git a/Projects/Server/Main.cs b/Projects/Server/Main.cs index 1d56edbb6..49f48e669 100644 --- a/Projects/Server/Main.cs +++ b/Projects/Server/Main.cs @@ -55,7 +55,8 @@ namespace Server public static readonly bool IsDarwin = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); public static readonly bool IsFreeBSD = RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD); public static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || IsFreeBSD; - public static readonly bool Unix = IsDarwin || IsFreeBSD || IsLinux; + public static readonly bool IsBSD = IsDarwin || IsFreeBSD; + public static readonly bool Unix = IsBSD || IsLinux; private const string AssembliesConfiguration = "Data/assemblies.json"; @@ -516,7 +517,6 @@ namespace Server // Handle networking TcpServer.Slice(); - NetState.HandleAllReceives(); NetState.Slice(); // Execute captured post-await methods (like Timer.Pause) diff --git a/Projects/Server/Network/ISocket.cs b/Projects/Server/Network/ISocket.cs index b1182c77a..75d3d8475 100644 --- a/Projects/Server/Network/ISocket.cs +++ b/Projects/Server/Network/ISocket.cs @@ -23,6 +23,8 @@ namespace Server.Network { public interface ISocket { + public IntPtr Handle { get; } + public EndPoint LocalEndPoint { get; } public EndPoint RemoteEndPoint { get; } @@ -33,6 +35,8 @@ namespace Server.Network public Task ReceiveAsync(IList> buffer, SocketFlags flags); + public int Receive(IList> buffers, SocketFlags flags); + public void Shutdown(SocketShutdown how); public void Close(); diff --git a/Projects/Server/Network/NetState/DumpNetStates.cs b/Projects/Server/Network/NetState/DumpNetStates.cs index ad0e3a821..6992254dc 100644 --- a/Projects/Server/Network/NetState/DumpNetStates.cs +++ b/Projects/Server/Network/NetState/DumpNetStates.cs @@ -32,7 +32,7 @@ namespace Server.Network foreach (var ns in TcpServer.Instances) { - file.WriteLine($"{ns}, {ns._recvState}, {ns._sendState}, {ns._protocolState}, {ns._parserState}"); + file.WriteLine($"{ns}, {ns._protocolState}, {ns._parserState}"); } } } diff --git a/Projects/Server/Network/NetState/NetState.cs b/Projects/Server/Network/NetState/NetState.cs old mode 100644 new mode 100755 index c60147f81..136b84320 --- a/Projects/Server/Network/NetState/NetState.cs +++ b/Projects/Server/Network/NetState/NetState.cs @@ -21,7 +21,7 @@ using System.IO; using System.Net; using System.Net.Sockets; using System.Runtime.CompilerServices; -using System.Threading; +using System.Runtime.InteropServices; using Server.Accounting; using Server.Diagnostics; using Server.Gumps; @@ -43,12 +43,15 @@ namespace Server.Network private const int RecvPipeSize = 1024 * 64; private const int SendPipeSize = 1024 * 256; - private static int GumpCap = 512; - private static int HuePickerCap = 512; - private static int MenuCap = 512; - private static int PacketPerSecondThreshold = 3000; + private const int GumpCap = 512; + private const int HuePickerCap = 512; + private const int MenuCap = 512; + private const int PacketPerSecondThreshold = 3000; + private static NetState[] _polledStates = new NetState[2048]; + private static readonly IPollGroup _pollGroup = PollGroup.Create(); private static readonly Queue FlushPending = new(2048); + private static readonly Queue FlushedPartials = new(2048); private static readonly ConcurrentQueue Disposed = new(); public static NetStateCreatedCallback CreatedCallback { get; set; } @@ -56,7 +59,7 @@ namespace Server.Network private readonly string _toString; private ClientVersion _version; private long _nextActivityCheck; - private int _running; + private bool _running = true; private volatile DecodePacket _packetDecoder; private volatile EncodePacket _packetEncoder; private bool _flushQueued; @@ -66,14 +69,12 @@ namespace Server.Network internal int _authId; internal int _seed; - internal ParserState _parserState = ParserState.Uninitialized; - internal ProtocolState _protocolState = ProtocolState.Uninitialized; - internal RecvState _recvState = RecvState.Uninitialized; - internal SendState _sendState = SendState.Uninitialized; + internal ParserState _parserState = ParserState.AwaitingNextPacket; + internal ProtocolState _protocolState = ProtocolState.AwaitingSeed; + internal GCHandle _handle; internal enum ParserState { - Uninitialized, AwaitingNextPacket, AwaitingPartialPacket, ProcessingPacket, @@ -83,7 +84,6 @@ namespace Server.Network internal enum ProtocolState { - Uninitialized, AwaitingSeed, // Based on the way the seed arrives, we know if this is a login server or a game server connection LoginServer_AwaitingLogin, @@ -96,32 +96,6 @@ namespace Server.Network Error } - internal enum RecvState - { - Uninitialized, - AwaitingMemory, - AwaitingRecv, - DataReceived, - Exited, - } - - internal enum SendState - { - Uninitialized, - AwaitingData, - Sending, - SendCompleted, - Exited, - } - - public static void Configure() - { - GumpCap = ServerConfiguration.GetOrUpdateSetting("netstate.gumpCap", GumpCap); - HuePickerCap = ServerConfiguration.GetOrUpdateSetting("netstate.huePickerCap", HuePickerCap); - MenuCap = ServerConfiguration.GetOrUpdateSetting("netstate.menuCap", MenuCap); - PacketPerSecondThreshold = ServerConfiguration.GetOrUpdateSetting("netstate.packetsPerSecondThreshold", PacketPerSecondThreshold); - } - public static void Initialize() { Timer.DelayCall(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1.5), CheckAllAlive); @@ -138,6 +112,7 @@ namespace Server.Network RecvPipe = new Pipe(GC.AllocateUninitializedArray(RecvPipeSize)); SendPipe = new Pipe(GC.AllocateUninitializedArray(SendPipeSize)); _nextActivityCheck = Core.TickCount + 30000; + ConnectedOn = Core.Now; try { @@ -151,7 +126,17 @@ namespace Server.Network _toString = "(error)"; } - ConnectedOn = Core.Now; + _handle = GCHandle.Alloc(this); + + try + { + _pollGroup.Add(this); + } + catch (Exception ex) + { + TraceException(ex); + Disconnect("Unable to add socket to poll group"); + } CreatedCallback?.Invoke(this); } @@ -180,12 +165,6 @@ namespace Server.Network public List Trades { get; } - public bool Running - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _running == 1; - } - public bool Seeded { get; set; } public Pipe RecvPipe { get; } @@ -524,33 +503,21 @@ namespace Server.Network } } - internal void Start() - { - if (Interlocked.CompareExchange(ref _running, 1, 0) == 1 || Connection == null) - { - return; - } - - _parserState = ParserState.AwaitingNextPacket; - _protocolState = ProtocolState.AwaitingSeed; - - ThreadPool.UnsafeQueueUserWorkItem(RecvTask, null); - ThreadPool.UnsafeQueueUserWorkItem(SendTask, null); - } - public void HandleReceive() { - if (!Running) + if (!_running) { return; } + ReceiveData(); + var reader = RecvPipe.Reader; try { // Process as many packets as we can synchronously - while (Running && _parserState != ParserState.Error && _protocolState != ProtocolState.Error) + while (_running && _parserState != ParserState.Error && _protocolState != ProtocolState.Error) { var result = reader.TryRead(); var length = result.Length; @@ -574,12 +541,6 @@ namespace Server.Network { switch (_protocolState) { - case ProtocolState.Uninitialized: - { - HandleError(packetId, packetLength); - return; - } - case ProtocolState.AwaitingSeed: { if (packetId == 0xEF) @@ -803,83 +764,56 @@ namespace Server.Network return ParserState.AwaitingNextPacket; } - private async void SendTask(object state) + private bool Flush() { + _flushQueued = false; + + if (Connection == null) + { + return true; + } + + SendPipe.Writer.Flush(); + var reader = SendPipe.Reader; + var result = reader.TryRead(); + + if (result.IsClosed || result.Length == 0) + { + return true; + } + + var bytesWritten = 0; try { - while (Running) - { - _sendState = SendState.AwaitingData; - - var result = await reader.Read(); - - if (result.IsClosed || !Running) - { - break; - } - - if (result.Length <= 0) - { - continue; - } - - _sendState = SendState.Sending; - - var bytesWritten = await Connection.SendAsync(result.Buffer, SocketFlags.None); - - _sendState = SendState.SendCompleted; - - if (bytesWritten > 0) - { - _nextActivityCheck = Core.TickCount + 90000; - reader.Advance((uint)bytesWritten); - } - } - - // Grab any remaining data and flush it - var data = reader.TryRead(); - - if (data.Length > 0) - { - _sendState = SendState.Sending; - Connection.Send(data.Buffer, SocketFlags.None); - - reader.Advance((uint)data.Length); - } + bytesWritten = Connection.Send(result.Buffer, SocketFlags.None); } catch (SocketException ex) { - // If the user closes the connection (or the recv side does) - // between the check for m_Running above and the call to SendAsync, - // we can still get a socket exception here. That's ok. + // Socket exceptions are generally ok, just spammy #if DEBUG Console.WriteLine(ex); #endif + + Disconnect(string.Empty); } catch (Exception ex) { #if DEBUG Console.WriteLine(ex); #endif + Disconnect($"Disconnected with error: {ex}"); TraceException(ex); } - finally - { - try - { - Connection.Shutdown(SocketShutdown.Both); - Connection.Close(); - } - catch (Exception ex) - { - TraceException(ex); - } - Disconnect("Exiting SendTask."); - _sendState = SendState.Exited; + if (bytesWritten > 0) + { + _nextActivityCheck = Core.TickCount + 90000; + reader.Advance((uint)bytesWritten); } + + return bytesWritten == result.Length; } private void DecodePacket(ArraySegment[] buffer, ref int length) @@ -888,47 +822,21 @@ namespace Server.Network _packetDecoder?.Invoke(cBuffer, ref length); } - private async void RecvTask(object state) + private void ReceiveData() { - var socket = Connection; var writer = RecvPipe.Writer; + var result = writer.TryGetMemory(); + + if (result.IsClosed || result.Length == 0) + { + return; + } + + var bytesWritten = 0; try { - while (Running) - { - _recvState = RecvState.AwaitingMemory; - var result = await writer.GetMemory(); - - if (result.IsClosed || !Running) - { - break; - } - - if (result.Length <= 0) - { - continue; - } - - _recvState = RecvState.AwaitingRecv; - - var bytesWritten = await socket.ReceiveAsync(result.Buffer, SocketFlags.None); - if (bytesWritten <= 0) - { - break; - } - - _recvState = RecvState.DataReceived; - - DecodePacket(result.Buffer, ref bytesWritten); - - writer.Advance((uint)bytesWritten); - _nextActivityCheck = Core.TickCount + 90000; - - // No need to flush - } - - Disconnect(string.Empty); + bytesWritten = Connection.Receive(result.Buffer, SocketFlags.None); } catch (SocketException ex) { @@ -945,31 +853,20 @@ namespace Server.Network #if DEBUG Console.WriteLine(ex); #endif - Disconnect("RecvTask exited unexpectedly."); + Disconnect($"Disconnected with error: {ex}"); TraceException(ex); } - finally - { - _recvState = RecvState.Exited; - } - } - public static void HandleAllReceives() - { - foreach (var ns in TcpServer.Instances) + if (bytesWritten <= 0) { - ns.HandleReceive(); - } - } - - public void Flush() - { - if (Connection != null) - { - SendPipe.Writer.Flush(); + Disconnect(string.Empty); + return; } - _flushQueued = false; + DecodePacket(result.Buffer, ref bytesWritten); + + writer.Advance((uint)bytesWritten); + _nextActivityCheck = Core.TickCount + 90000; } public static void FlushAll() @@ -982,16 +879,36 @@ namespace Server.Network public static void Slice() { - while (FlushPending.Count != 0) + int count = _pollGroup.Poll(ref _polledStates); + + if (count > 0) { - FlushPending.Dequeue()?.Flush(); + for (int i = 0; i < count; i++) + { + _polledStates[i].HandleReceive(); + _polledStates[i] = null; + } } + while (FlushPending.TryDequeue(out var ns)) + { + if (!ns.Flush()) + { + // Incomplete data, so we need to requeue + FlushedPartials.Enqueue(ns); + } + } + + var hasDisposes = !Disposed.IsEmpty; while (Disposed.TryDequeue(out var ns)) { - TcpServer.Instances.Remove(ns); ns.Dispose(); } + + if (hasDisposes) + { + _pollGroup.Poll(ref _polledStates); + } } public void CheckAlive(long curTicks) @@ -1045,11 +962,13 @@ namespace Server.Network public void Disconnect(string reason) { - if (Interlocked.CompareExchange(ref _running, 0, 1) == 0) + if (!_running) { return; } + _running = false; + try { if (_disconnectReason != string.Empty) @@ -1063,7 +982,6 @@ namespace Server.Network } _disconnectReason = reason; - Disposed.Enqueue(this); } @@ -1095,6 +1013,11 @@ namespace Server.Network { TraceDisconnect(_disconnectReason, _toString); + if (_running) + { + throw new Exception("Disconnected a NetState that is still running."); + } + #if THREADGUARD if (Thread.CurrentThread != Core.Thread) { @@ -1106,8 +1029,18 @@ namespace Server.Network } #endif - RecvPipe.Writer.Close(); - SendPipe.Writer.Close(); + TcpServer.Instances.Remove(this); + try + { + _pollGroup.Remove(this); + } + catch (Exception ex) + { + TraceException(ex); + } + + Connection.Close(); + _handle.Free(); var m = Mobile; Mobile = null; diff --git a/Projects/Server/Network/NetworkSocket.cs b/Projects/Server/Network/NetworkSocket.cs index e4e4c3a8a..f762ba1cf 100644 --- a/Projects/Server/Network/NetworkSocket.cs +++ b/Projects/Server/Network/NetworkSocket.cs @@ -32,6 +32,12 @@ namespace Server.Network get => _connection; } + public IntPtr Handle + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _connection.Handle; + } + public EndPoint LocalEndPoint { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -58,6 +64,10 @@ namespace Server.Network public Task ReceiveAsync(IList> buffers, SocketFlags flags) => _connection.ReceiveAsync(buffers, flags); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Receive(IList> buffers, SocketFlags flags) => + _connection.Receive(buffers, flags); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Shutdown(SocketShutdown how) => _connection.Shutdown(how); diff --git a/Projects/Server/Network/PollGroup/EPollGroup.cs b/Projects/Server/Network/PollGroup/EPollGroup.cs new file mode 100755 index 000000000..bf615e4cc --- /dev/null +++ b/Projects/Server/Network/PollGroup/EPollGroup.cs @@ -0,0 +1,206 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: EPollGroup.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Runtime.InteropServices; + +namespace Server.Network +{ + public sealed class EPollGroup : IPollGroup + { + [Flags] + private enum epoll_flags + { + NONE = 0, + CLOEXEC = 0x02000000, + NONBLOCK = 0x04000, + } + + [Flags] + private enum epoll_events : uint + { + EPOLLIN = 0x001, + EPOLLPRI = 0x002, + EPOLLOUT = 0x004, + EPOLLRDNORM = 0x040, + EPOLLRDBAND = 0x080, + EPOLLWRNORM = 0x100, + EPOLLWRBAND = 0x200, + EPOLLMSG = 0x400, + EPOLLERR = 0x008, + EPOLLHUP = 0x010, + EPOLLRDHUP = 0x2000, + EPOLLONESHOT = 1 << 30, + EPOLLET = unchecked((uint)(1 << 31)) + } + + private enum epoll_op + { + EPOLL_CTL_ADD = 1, + EPOLL_CTL_DEL = 2, + EPOLL_CTL_MOD = 3, + } + + [StructLayout(LayoutKind.Explicit)] + private struct epoll_data + { + [FieldOffset(0)] + public int fd; + + [FieldOffset(0)] + public IntPtr ptr; + + [FieldOffset(0)] + public uint u32; + + [FieldOffset(0)] + public ulong u64; + } + + [StructLayout(LayoutKind.Sequential)] + private struct epoll_event + { + public epoll_events events; + public epoll_data data; + } + + private static class Windows + { + [DllImport("wepoll.dll", SetLastError = true)] + public static extern int epoll_create1(epoll_flags flags); + + [DllImport("wepoll.dll", SetLastError = true)] + public static extern int epoll_close(int epfd); + + [DllImport("wepoll.dll", SetLastError = true)] + public static extern int epoll_ctl(int epfd, epoll_op op, int fd, ref epoll_event ee); + + [DllImport("wepoll.dll", SetLastError = true)] + public static extern int epoll_wait(int epfd, [In, Out] epoll_event[] ee, int maxevents, int timeout); + } + + private static class Linux + { + [DllImport("libc", SetLastError = true)] + public static extern int epoll_create1(epoll_flags flags); + + [DllImport("libc", SetLastError = true)] + public static extern int epoll_close(int epfd); + + [DllImport("libc", SetLastError = true)] + public static extern int epoll_ctl(int epfd, epoll_op op, int fd, ref epoll_event ee); + + [DllImport("libc", SetLastError = true)] + public static extern int epoll_wait(int epfd, [In, Out] epoll_event[] ee, int maxevents, int timeout); + } + + private readonly int _epHndle; + + public EPollGroup() + { + _epHndle = Core.IsWindows ? Windows.epoll_create1(epoll_flags.NONE) : Linux.epoll_create1(epoll_flags.NONE); + + if (_epHndle == 0) + { + throw new Exception("Unable to initialize poll group"); + } + } + + public void Dispose() + { + if (Core.IsWindows) + { + Windows.epoll_close(_epHndle); + } + else + { + Linux.epoll_close(_epHndle); + } + } + + public void Add(NetState state) + { + var ev = new epoll_event + { + events = epoll_events.EPOLLIN | epoll_events.EPOLLERR + }; + + ev.data.ptr = (IntPtr)state._handle; + + var rc = Core.IsWindows ? + Windows.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_ADD, (int)state.Connection.Handle, ref ev) : + Linux.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_ADD, (int)state.Connection.Handle, ref ev); + + + if (rc != 0) + { + throw new Exception($"epoll_ctl failed with error code {Marshal.GetLastWin32Error()}"); + } + } + + public void Remove(NetState state) + { + var ev = new epoll_event + { + events = epoll_events.EPOLLIN | epoll_events.EPOLLERR, + }; + ev.data.ptr = (IntPtr)state._handle; + + var rc = Core.IsWindows ? + Windows.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_DEL, (int)state.Connection.Handle, ref ev) : + Linux.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_DEL, (int)state.Connection.Handle, ref ev); + + if (rc != 0) + { + throw new Exception($"epoll_ctl failed with error code {Marshal.GetLastWin32Error()}"); + } + } + + private epoll_event[] _events = new epoll_event[2048]; + + public int Poll(ref NetState[] states) + { + if (states.Length > _events.Length) + { + var newLength = Math.Max(states.Length, _events.Length + (_events.Length >> 2)); + _events = new epoll_event[newLength]; + } + + var rc = Core.IsWindows ? + Windows.epoll_wait(_epHndle, _events, states.Length, 0) : + Linux.epoll_wait(_epHndle, _events, states.Length, 0); + + if (rc <= 0) + { + return rc; + } + + int count = 0; + + for (int i = 0; i < rc; i++) + { + if (((GCHandle)_events[i].data.ptr).Target is not NetState state) + { + continue; + } + + states[count++] = state; + } + + return count; + } + + } +} diff --git a/Projects/Server/Network/PollGroup/KQueuePollGroup.cs b/Projects/Server/Network/PollGroup/KQueuePollGroup.cs new file mode 100644 index 000000000..41466b968 --- /dev/null +++ b/Projects/Server/Network/PollGroup/KQueuePollGroup.cs @@ -0,0 +1,248 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: KQueuePollGroup.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Server.Network +{ + public class KQueuePollGroup : IPollGroup + { + [StructLayout(LayoutKind.Sequential)] + private struct kevent + { + public IntPtr ident; + public kqueue_filter filter; + public kqueue_flags flags; + public kqueue_fflags fflags; + public IntPtr data; + public IntPtr udata; + } + + [Flags] + private enum kqueue_filter : short + { + READ = -1, + WRITE = -2, + AIO = -3, + VNODE = -4, + PROC = -5, + SIGNAL = -6, + TIMER = -7, + MACHPORT = -8, + FS = -9, + USER = -10, + UNUSED = -11, + VM = -12, + EXCEPT = -15 + } + + [Flags] + private enum kqueue_flags : ushort + { + ADD = 0x0001, + DELETE = 0x0002, + ENABLE = 0x0004, + DISABLE = 0x0008, + ONESHOT = 0x0010, + CLEAR = 0x0020, + RECEIPT = 0x0040, + DISPATCH = 0x0080, + UDATA_SPECIFIC = 0x0100, + DISPATCH2 = (DISPATCH | UDATA_SPECIFIC), + VANISHED = 0x0200, + SYSFLAGS = 0xF000, + FLAG0 = 0x1000, + FLAG1 = 0x2000, + EOF = 0x8000, + ERROR = 0x4000 + } + + [Flags] + private enum kqueue_fflags : uint + { + TRIGGER = 0x01000000, + FFNOP = 0x00000000, + FFAND = 0x40000000, + FFOR = 0x80000000, + FFCOPY = 0xc0000000, + FFCTRLMASK = 0xc0000000, + FFFLAGSMASK = 0x00ffffff, + LOWAT = 0x00000001, + DELETE = 0x00000001, + WRITE = 0x00000002, + EXTEND = 0x00000004, + ATTRIB = 0x00000008, + LINK = 0x00000010, + RENAME = 0x00000020, + REVOKE = 0x00000040, + NONE = 0x00000080, + } + + [StructLayout(LayoutKind.Sequential)] + struct timespec + { + public long tv_sec; + public long tv_nsec; + + public timespec(long sec, long nsec) + { + tv_sec = sec; + tv_nsec = nsec; + } + } + + private static readonly kevent[] _singleEvent = new kevent[1]; + private static readonly IntPtr _zeroTimeoutPtr; + // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable + private static readonly timespec _zeroTimeout; + + static KQueuePollGroup() + { + _zeroTimeout = new timespec(0, 0); + _zeroTimeoutPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); + Marshal.StructureToPtr(_zeroTimeout, _zeroTimeoutPtr, false); + } + + private static class BSD + { + [DllImport ("libc", SetLastError = true)] + public static extern int close (int fd); + + [DllImport("libc", SetLastError = true)] + public static extern int kqueue(); + + [DllImport("libc", SetLastError = true)] + public static extern int kevent(int kq, kevent[] changelist, int nchanges, [In, Out] kevent[] eventlist, int nevents, IntPtr timeout); + + public static int kevent( + int kq, + IntPtr ident, + kqueue_filter filter, + kqueue_flags flags, + kqueue_fflags fflags = 0, + IntPtr data = default, + IntPtr udata = default + ) + { + _singleEvent[0] = new kevent + { + ident = ident, + filter = filter, + flags = flags, + fflags = fflags, + data = data, + udata = udata + }; + + var rc = kevent(kq, _singleEvent, 1, null, 0, _zeroTimeoutPtr); + if (rc != 0) + { + throw new Exception($"kqueue failed to {flags} with error code {Marshal.GetLastWin32Error()}"); + } + + if (_singleEvent[0].flags.HasFlag(kqueue_flags.ERROR)) + { + throw new IOException($"kqueue failed to {flags} with error {_singleEvent[0].data}"); + } + + return rc; + } + } + + private readonly int _kqueueHndle; + + public KQueuePollGroup() + { + _kqueueHndle = BSD.kqueue(); + + if (_kqueueHndle == 0) + { + throw new Exception("Unable to initialize poll group"); + } + } + + public void Dispose() + { + BSD.close(_kqueueHndle); + Marshal.FreeHGlobal(_zeroTimeoutPtr); + } + + public void Add(NetState state) + { + var rc = BSD.kevent( + _kqueueHndle, + state.Connection.Handle, + kqueue_filter.READ | kqueue_filter.WRITE, + kqueue_flags.ADD | kqueue_flags.CLEAR, + udata: (IntPtr)state._handle + ); + + if (rc != 0) + { + throw new Exception($"kevent failed with error code {Marshal.GetLastWin32Error()}"); + } + } + + public void Remove(NetState state) + { + var rc = BSD.kevent( + _kqueueHndle, + state.Connection.Handle, + kqueue_filter.READ | kqueue_filter.WRITE, + kqueue_flags.DELETE, + udata: (IntPtr)state._handle + ); + + if (rc != 0) + { + throw new Exception($"kevent failed with error code {Marshal.GetLastWin32Error()}"); + } + } + + private kevent[] _events = new kevent[2048]; + + public int Poll(ref NetState[] states) + { + if (states.Length > _events.Length) + { + var newLength = Math.Max(states.Length, _events.Length + (_events.Length >> 2)); + _events = new kevent[newLength]; + } + + var rc = BSD.kevent(_kqueueHndle, null, 0, _events, _events.Length, _zeroTimeoutPtr); + + if (rc <= 0) + { + return rc; + } + + int count = 0; + + for (int i = 0; i < rc; i++) + { + if (((GCHandle)_events[i].udata).Target is not NetState state) + { + continue; + } + + states[count++] = state; + } + + return count; + } + } +} diff --git a/Projects/Server/Network/PollGroup/PollGroup.cs b/Projects/Server/Network/PollGroup/PollGroup.cs new file mode 100644 index 000000000..9089f871b --- /dev/null +++ b/Projects/Server/Network/PollGroup/PollGroup.cs @@ -0,0 +1,39 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: PollGroup.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; + +namespace Server.Network +{ + public interface IPollGroup : IDisposable + { + void Add(NetState state); + void Remove(NetState state); + int Poll(ref NetState[] states); + } + + public static class PollGroup + { + public static IPollGroup Create() + { + if (Core.IsBSD) + { + return new KQueuePollGroup(); + } + + return new EPollGroup(); + } + } +} diff --git a/Projects/Server/Network/TcpServer.cs b/Projects/Server/Network/TcpServer.cs index 06d7bd744..428c40cf9 100644 --- a/Projects/Server/Network/TcpServer.cs +++ b/Projects/Server/Network/TcpServer.cs @@ -84,6 +84,14 @@ namespace Server.Network Listeners = listeners.ToArray(); } + public static void Shutdown() + { + foreach (var listener in Listeners) + { + listener.Server.Close(); + } + } + public static IEnumerable GetListeningAddresses(IPEndPoint ipep) => NetworkInterface.GetAllNetworkInterfaces().SelectMany(adapter => adapter.GetIPProperties().UnicastAddresses @@ -93,12 +101,19 @@ namespace Server.Network public static TcpListener CreateListener(IPEndPoint ipep) { - var listener = new TcpListener(ipep); - listener.Server.ExclusiveAddressUse = false; + var listener = new TcpListener(ipep) + { + Server = + { + LingerState = new LingerOption(false, 0), + ExclusiveAddressUse = true, + NoDelay = true + } + }; try { - listener.Start(8); + listener.Start(32); return listener; } catch (SocketException se) @@ -130,7 +145,6 @@ namespace Server.Network { Instances.Add(ns); ns.LogInfo("Connected. [{0} Online]", Instances.Count); - ns.Start(); } } diff --git a/Projects/Server/Server.csproj b/Projects/Server/Server.csproj index aa97275b0..a0d4042b7 100755 --- a/Projects/Server/Server.csproj +++ b/Projects/Server/Server.csproj @@ -31,6 +31,12 @@ + + + Always + Always + + @@ -46,7 +52,4 @@ - - - diff --git a/Projects/Server/wepoll.dll b/Projects/Server/wepoll.dll new file mode 100644 index 0000000000000000000000000000000000000000..d3f207d746070b8b0b540ca110c5a6822f10accb GIT binary patch literal 21504 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 literal 0 HcmV?d00001 diff --git a/Projects/UOContent/Compression/ZstdArchive.cs b/Projects/UOContent/Compression/ZstdArchive.cs index 08246902c..72cc56e94 100755 --- a/Projects/UOContent/Compression/ZstdArchive.cs +++ b/Projects/UOContent/Compression/ZstdArchive.cs @@ -23,7 +23,7 @@ namespace Server.Compression StartInfo = new ProcessStartInfo { FileName = _pathToZstd, - Arguments = $"--no-progress -d \"{fileNamePath}\" -o \"${tempTarArchive}\"" + Arguments = $"-q -d \"{fileNamePath}\" -o \"${tempTarArchive}\"" } }; @@ -70,7 +70,7 @@ namespace Server.Compression StartInfo = new ProcessStartInfo { FileName = Path.Combine(_pathToZstd, "zstd.exe"), - Arguments = $"--no-progress -10 \"{tempTarArchive}\" -o \"{destinationArchiveFileName}\"" + Arguments = $"-q -10 \"{tempTarArchive}\" -o \"{destinationArchiveFileName}\"" } }; diff --git a/THIRD-PARTY-NOTICES b/THIRD-PARTY-NOTICES index 76fdea608..68a241499 100644 --- a/THIRD-PARTY-NOTICES +++ b/THIRD-PARTY-NOTICES @@ -84,3 +84,34 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +License notice for wepoll +--------------------------- +wepoll - epoll for Windows +https://github.com/piscisaureus/wepoll + +Copyright 2012-2020, Bert Belder +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/version.json b/version.json index 629bc3bab..dd101024d 100644 --- a/version.json +++ b/version.json @@ -1,4 +1,4 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "0.8.2" + "version": "0.8.3" }