From 507eba34a8894729d0b23d3d52a0b9b22c84666f Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sat, 9 Oct 2021 13:52:53 -0700 Subject: [PATCH] fix: Replaces PollGroup with nuget (#823) --- .../Server/Network/PollGroup/EPollGroup.cs | 197 -------------- .../Network/PollGroup/KQueuePollGroup.cs | 241 ------------------ .../Server/Network/PollGroup/PollGroup.cs | 41 --- Projects/Server/Server.csproj | 29 +-- Projects/Server/wepoll.dll | Bin 21504 -> 0 bytes 5 files changed, 13 insertions(+), 495 deletions(-) delete mode 100755 Projects/Server/Network/PollGroup/EPollGroup.cs delete mode 100644 Projects/Server/Network/PollGroup/KQueuePollGroup.cs delete mode 100644 Projects/Server/Network/PollGroup/PollGroup.cs delete mode 100644 Projects/Server/wepoll.dll diff --git a/Projects/Server/Network/PollGroup/EPollGroup.cs b/Projects/Server/Network/PollGroup/EPollGroup.cs deleted file mode 100755 index bc51526e8..000000000 --- a/Projects/Server/Network/PollGroup/EPollGroup.cs +++ /dev/null @@ -1,197 +0,0 @@ -/************************************************************************* - * 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.Net.Sockets; -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, Size = 8)] - 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.Explicit, Pack = 4)] - private struct epoll_event - { - [FieldOffset(0)] - public epoll_events events; - [FieldOffset(4)] - 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; - private readonly bool _isWindows; - - public EPollGroup() - { - _isWindows = Core.IsWindows; - _epHndle = _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 (_isWindows) - { - Windows.epoll_close(_epHndle); - } - else - { - Linux.epoll_close(_epHndle); - } - } - - public void Add(Socket socket, GCHandle handle) - { - var ev = new epoll_event - { - events = epoll_events.EPOLLIN | epoll_events.EPOLLERR - }; - - ev.data.ptr = (IntPtr)handle; - - var rc = _isWindows ? - Windows.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_ADD, (int)socket.Handle, ref ev) : - Linux.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_ADD, (int)socket.Handle, ref ev); - - - if (rc != 0) - { - throw new Exception($"epoll_ctl failed with error code {Marshal.GetLastWin32Error()}"); - } - } - - public void Remove(Socket socket) - { - var ev = new epoll_event { events = epoll_events.EPOLLIN | epoll_events.EPOLLERR }; - - var rc = _isWindows ? - Windows.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_DEL, (int)socket.Handle, ref ev) : - Linux.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_DEL, (int)socket.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 GCHandle[] handles) - { - if (handles.Length > _events.Length) - { - var newLength = Math.Max(handles.Length, _events.Length + (_events.Length >> 2)); - _events = new epoll_event[newLength]; - } - - var rc = _isWindows ? - Windows.epoll_wait(_epHndle, _events, handles.Length, 0) : - Linux.epoll_wait(_epHndle, _events, handles.Length, 0); - - if (rc <= 0) - { - return rc; - } - - for (int i = 0; i < rc; i++) - { - handles[i] = (GCHandle)_events[i].data.ptr; - } - - return rc; - } - - } -} diff --git a/Projects/Server/Network/PollGroup/KQueuePollGroup.cs b/Projects/Server/Network/PollGroup/KQueuePollGroup.cs deleted file mode 100644 index 278e88406..000000000 --- a/Projects/Server/Network/PollGroup/KQueuePollGroup.cs +++ /dev/null @@ -1,241 +0,0 @@ -/************************************************************************* - * 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.Net.Sockets; -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(Socket socket, GCHandle handle) - { - var rc = BSD.kevent( - _kqueueHndle, - socket.Handle, - kqueue_filter.READ | kqueue_filter.WRITE, - kqueue_flags.ADD | kqueue_flags.CLEAR, - udata: (IntPtr)handle - ); - - if (rc != 0) - { - throw new Exception($"kevent failed with error code {Marshal.GetLastWin32Error()}"); - } - } - - public void Remove(Socket socket) - { - var rc = BSD.kevent( - _kqueueHndle, - socket.Handle, - kqueue_filter.READ | kqueue_filter.WRITE, - kqueue_flags.DELETE - ); - - if (rc != 0) - { - throw new Exception($"kevent failed with error code {Marshal.GetLastWin32Error()}"); - } - } - - private kevent[] _events = new kevent[2048]; - - public int Poll(ref GCHandle[] handles) - { - if (handles.Length > _events.Length) - { - var newLength = Math.Max(handles.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; - } - - for (int i = 0; i < rc; i++) - { - handles[i] = (GCHandle)_events[i].udata; - } - - return rc; - } - } -} diff --git a/Projects/Server/Network/PollGroup/PollGroup.cs b/Projects/Server/Network/PollGroup/PollGroup.cs deleted file mode 100644 index 0fa4fa875..000000000 --- a/Projects/Server/Network/PollGroup/PollGroup.cs +++ /dev/null @@ -1,41 +0,0 @@ -/************************************************************************* - * 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; -using System.Net.Sockets; -using System.Runtime.InteropServices; - -namespace Server.Network -{ - public interface IPollGroup : IDisposable - { - void Add(Socket sock, GCHandle handle); - void Remove(Socket sock); - int Poll(ref GCHandle[] handles); - } - - public static class PollGroup - { - public static IPollGroup Create() - { - if (Core.IsBSD) - { - return new KQueuePollGroup(); - } - - return new EPollGroup(); - } - } -} diff --git a/Projects/Server/Server.csproj b/Projects/Server/Server.csproj index a0d4042b7..031478398 100755 --- a/Projects/Server/Server.csproj +++ b/Projects/Server/Server.csproj @@ -13,32 +13,29 @@ - - - - - - - - + - - + + + + + + + + + + + - - - Always - Always - - + diff --git a/Projects/Server/wepoll.dll b/Projects/Server/wepoll.dll deleted file mode 100644 index f9383691d3ce8f8c96fb244c0d4908fe4bbbd484..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21504 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