From 644287faa6eebd0df4f31131bd15904dc25d7a9c Mon Sep 17 00:00:00 2001 From: LotP1 Date: Thu, 9 Jan 2025 01:53:01 +0100 Subject: [PATCH 1/6] Initial implementation --- src/Ryujinx.HLE/HLEConfiguration.cs | 7 +++++++ src/Ryujinx.HLE/HOS/Horizon.cs | 3 ++- src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs | 4 +++- src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs | 2 +- .../HOS/Kernel/Process/KProcessCapabilities.cs | 17 +++++++++++------ .../HOS/Kernel/Threading/KScheduler.cs | 18 +++++++++++++----- src/Ryujinx/AppHost.cs | 2 ++ src/Ryujinx/Headless/HeadlessRyujinx.Init.cs | 5 ++++- 8 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/Ryujinx.HLE/HLEConfiguration.cs b/src/Ryujinx.HLE/HLEConfiguration.cs index 8ac76508f..8e6ad570c 100644 --- a/src/Ryujinx.HLE/HLEConfiguration.cs +++ b/src/Ryujinx.HLE/HLEConfiguration.cs @@ -103,6 +103,11 @@ namespace Ryujinx.HLE /// internal readonly bool EnablePtc; + /// + /// The amount of cpu threads that should be used for emulation. + /// + internal readonly int CpuCoresCount; + /// /// Control if the guest application should be told that there is a Internet connection available. /// @@ -208,6 +213,7 @@ namespace Ryujinx.HLE VSyncMode vSyncMode, bool enableDockedMode, bool enablePtc, + int cpuCoresCount, bool enableInternetAccess, IntegrityCheckLevel fsIntegrityCheckLevel, int fsGlobalAccessLogMode, @@ -241,6 +247,7 @@ namespace Ryujinx.HLE CustomVSyncInterval = customVSyncInterval; EnableDockedMode = enableDockedMode; EnablePtc = enablePtc; + CpuCoresCount = cpuCoresCount; EnableInternetAccess = enableInternetAccess; FsIntegrityCheckLevel = fsIntegrityCheckLevel; FsGlobalAccessLogMode = fsGlobalAccessLogMode; diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index f9c5ddecf..b56e5cc1d 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -122,7 +122,8 @@ namespace Ryujinx.HLE.HOS device, device.Memory, device.Configuration.MemoryConfiguration.ToKernelMemorySize(), - device.Configuration.MemoryConfiguration.ToKernelMemoryArrange()); + device.Configuration.MemoryConfiguration.ToKernelMemoryArrange(), + device.Configuration.CpuCoresCount); Device = device; diff --git a/src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs b/src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs index 89d788c54..9b1dd0c3f 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs @@ -58,11 +58,13 @@ namespace Ryujinx.HLE.HOS.Kernel Switch device, MemoryBlock memory, MemorySize memorySize, - MemoryArrange memoryArrange) + MemoryArrange memoryArrange, + int cpuCoresCount) { TickSource = tickSource; Device = device; Memory = memory; + KScheduler.CpuCoresCount = cpuCoresCount; Running = true; diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index b4aa5ca5c..82c3d2e70 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -277,7 +277,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return result; } - result = Capabilities.InitializeForUser(capabilities, MemoryManager); + result = Capabilities.InitializeForUser(capabilities, MemoryManager, IsApplication); if (result != Result.Success) { diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs index ebab67bb8..63a21e930 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs @@ -35,15 +35,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Process DebuggingFlags &= ~3u; KernelReleaseVersion = KProcess.KernelVersionPacked; - return Parse(capabilities, memoryManager); + return Parse(capabilities, memoryManager, false); } - public Result InitializeForUser(ReadOnlySpan capabilities, KPageTableBase memoryManager) + public Result InitializeForUser(ReadOnlySpan capabilities, KPageTableBase memoryManager, bool isApplication) { - return Parse(capabilities, memoryManager); + return Parse(capabilities, memoryManager, isApplication); } - private Result Parse(ReadOnlySpan capabilities, KPageTableBase memoryManager) + private Result Parse(ReadOnlySpan capabilities, KPageTableBase memoryManager, bool isApplication) { int mask0 = 0; int mask1 = 0; @@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process if (cap.GetCapabilityType() != CapabilityType.MapRange) { - Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager); + Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager, isApplication); if (result != Result.Success) { @@ -120,7 +120,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return Result.Success; } - private Result ParseCapability(uint cap, ref int mask0, ref int mask1, KPageTableBase memoryManager) + private Result ParseCapability(uint cap, ref int mask0, ref int mask1, KPageTableBase memoryManager, bool isApplication) { CapabilityType code = cap.GetCapabilityType(); @@ -176,6 +176,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process AllowedCpuCoresMask = GetMaskFromMinMax(lowestCpuCore, highestCpuCore); AllowedThreadPriosMask = GetMaskFromMinMax(lowestThreadPrio, highestThreadPrio); + if (isApplication && lowestCpuCore == 0 && highestCpuCore != 2) + Ryujinx.Common.Logging.Logger.Error?.Print(Ryujinx.Common.Logging.LogClass.Application, $"Application requested cores with index range {lowestCpuCore} to {highestCpuCore}! Report this to @LotP on the Ryujinx/Ryubing discord server!"); + else if (isApplication) + Ryujinx.Common.Logging.Logger.Info?.Print(Ryujinx.Common.Logging.LogClass.Application, $"Application requested cores with index range {lowestCpuCore} to {highestCpuCore}"); + break; } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs index 8ef77902c..19f1b8be0 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs @@ -9,13 +9,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading partial class KScheduler : IDisposable { public const int PrioritiesCount = 64; - public const int CpuCoresCount = 4; + public static int CpuCoresCount; private const int RoundRobinTimeQuantumMs = 10; - private static readonly int[] _preemptionPriorities = { 59, 59, 59, 63 }; - - private static readonly int[] _srcCoresHighestPrioThreads = new int[CpuCoresCount]; + private static int[] _srcCoresHighestPrioThreads; private readonly KernelContext _context; private readonly int _coreId; @@ -47,6 +45,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _coreId = coreId; _currentThread = null; + + if (_srcCoresHighestPrioThreads == null) + { + _srcCoresHighestPrioThreads = new int[CpuCoresCount]; + } + } + + private static int PreemptionPriorities(int index) + { + return index == CpuCoresCount - 1 ? 63 : 59; } public static ulong SelectThreads(KernelContext context) @@ -437,7 +445,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading for (int core = 0; core < CpuCoresCount; core++) { - RotateScheduledQueue(context, core, _preemptionPriorities[core]); + RotateScheduledQueue(context, core, PreemptionPriorities(core)); } context.CriticalSection.Leave(); diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index a35a79e86..e3bb3b338 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -918,6 +918,7 @@ namespace Ryujinx.Ava // Initialize Configuration. var memoryConfiguration = ConfigurationState.Instance.System.DramSize.Value; + int cpuCoresCount = 4; //Switch 1 has 4 cores Device = new Switch(new HLEConfiguration( VirtualFileSystem, @@ -934,6 +935,7 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Graphics.VSyncMode, ConfigurationState.Instance.System.EnableDockedMode, ConfigurationState.Instance.System.EnablePtc, + cpuCoresCount, ConfigurationState.Instance.System.EnableInternetAccess, ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, ConfigurationState.Instance.System.FsGlobalAccessLogMode, diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs index 19d2fb94e..5c6a518a8 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -1,4 +1,4 @@ -using DiscordRPC; +using DiscordRPC; using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.SDL2; using Ryujinx.Ava; @@ -329,6 +329,8 @@ namespace Ryujinx.Headless renderer = new ThreadedRenderer(renderer); } + int cpuCoresCount = 4; //Switch 1 has 4 cores + HLEConfiguration configuration = new(_virtualFileSystem, _libHacHorizonManager, _contentManager, @@ -343,6 +345,7 @@ namespace Ryujinx.Headless options.VSyncMode, !options.DisableDockedMode, !options.DisablePTC, + cpuCoresCount, options.EnableInternetAccess, !options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, options.FsGlobalAccessLogMode, -- 2.47.1 From 65d5eaa127815a72a3e5000428300da89e4c42fa Mon Sep 17 00:00:00 2001 From: LotP1 Date: Thu, 9 Jan 2025 17:12:59 +0100 Subject: [PATCH 2/6] change const values to relative values --- src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs | 2 +- src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs b/src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs index f5ecba752..e05fc8397 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Kernel return result; } - process.DefaultCpuCore = 3; + process.DefaultCpuCore = KScheduler.CpuCoresCount - 1; context.Processes.TryAdd(process.Pid, process); diff --git a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index 2f487243d..1b6433af6 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -2683,7 +2683,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidCombination; } - if ((uint)preferredCore > 3) + if ((uint)preferredCore > KScheduler.CpuCoresCount - 1) { if ((preferredCore | 2) != -1) { -- 2.47.1 From ae9f90261785ca53434663f4d14f6579e4c1a3b9 Mon Sep 17 00:00:00 2001 From: LotP1 Date: Thu, 9 Jan 2025 17:19:47 +0100 Subject: [PATCH 3/6] refactor --- src/Ryujinx.HLE/HLEConfiguration.cs | 7 ------- src/Ryujinx.HLE/HOS/Horizon.cs | 2 +- src/Ryujinx.HLE/Switch.cs | 2 ++ src/Ryujinx/AppHost.cs | 2 -- src/Ryujinx/Headless/HeadlessRyujinx.Init.cs | 3 --- 5 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/Ryujinx.HLE/HLEConfiguration.cs b/src/Ryujinx.HLE/HLEConfiguration.cs index 8e6ad570c..8ac76508f 100644 --- a/src/Ryujinx.HLE/HLEConfiguration.cs +++ b/src/Ryujinx.HLE/HLEConfiguration.cs @@ -103,11 +103,6 @@ namespace Ryujinx.HLE /// internal readonly bool EnablePtc; - /// - /// The amount of cpu threads that should be used for emulation. - /// - internal readonly int CpuCoresCount; - /// /// Control if the guest application should be told that there is a Internet connection available. /// @@ -213,7 +208,6 @@ namespace Ryujinx.HLE VSyncMode vSyncMode, bool enableDockedMode, bool enablePtc, - int cpuCoresCount, bool enableInternetAccess, IntegrityCheckLevel fsIntegrityCheckLevel, int fsGlobalAccessLogMode, @@ -247,7 +241,6 @@ namespace Ryujinx.HLE CustomVSyncInterval = customVSyncInterval; EnableDockedMode = enableDockedMode; EnablePtc = enablePtc; - CpuCoresCount = cpuCoresCount; EnableInternetAccess = enableInternetAccess; FsIntegrityCheckLevel = fsIntegrityCheckLevel; FsGlobalAccessLogMode = fsGlobalAccessLogMode; diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index b56e5cc1d..aed65dea9 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -123,7 +123,7 @@ namespace Ryujinx.HLE.HOS device.Memory, device.Configuration.MemoryConfiguration.ToKernelMemorySize(), device.Configuration.MemoryConfiguration.ToKernelMemoryArrange(), - device.Configuration.CpuCoresCount); + device.CpuCoresCount); Device = device; diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index 25e65354f..e15fab03a 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -32,6 +32,8 @@ namespace Ryujinx.HLE public TamperMachine TamperMachine { get; } public IHostUIHandler UIHandler { get; } + public int CpuCoresCount = 4; //Switch 1 has 4 cores + public VSyncMode VSyncMode { get; set; } = VSyncMode.Switch; public bool CustomVSyncIntervalEnabled { get; set; } = false; public int CustomVSyncInterval { get; set; } diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index e3bb3b338..a35a79e86 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -918,7 +918,6 @@ namespace Ryujinx.Ava // Initialize Configuration. var memoryConfiguration = ConfigurationState.Instance.System.DramSize.Value; - int cpuCoresCount = 4; //Switch 1 has 4 cores Device = new Switch(new HLEConfiguration( VirtualFileSystem, @@ -935,7 +934,6 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Graphics.VSyncMode, ConfigurationState.Instance.System.EnableDockedMode, ConfigurationState.Instance.System.EnablePtc, - cpuCoresCount, ConfigurationState.Instance.System.EnableInternetAccess, ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, ConfigurationState.Instance.System.FsGlobalAccessLogMode, diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs index 5c6a518a8..7d75ac7c1 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -329,8 +329,6 @@ namespace Ryujinx.Headless renderer = new ThreadedRenderer(renderer); } - int cpuCoresCount = 4; //Switch 1 has 4 cores - HLEConfiguration configuration = new(_virtualFileSystem, _libHacHorizonManager, _contentManager, @@ -345,7 +343,6 @@ namespace Ryujinx.Headless options.VSyncMode, !options.DisableDockedMode, !options.DisablePTC, - cpuCoresCount, options.EnableInternetAccess, !options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, options.FsGlobalAccessLogMode, -- 2.47.1 From 24d3de26546008e71bf350fe3e94c8c85f41d3ca Mon Sep 17 00:00:00 2001 From: LotP1 Date: Thu, 9 Jan 2025 17:33:12 +0100 Subject: [PATCH 4/6] refactor part 2 --- src/Ryujinx.HLE/HOS/Horizon.cs | 3 +-- src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index aed65dea9..f9c5ddecf 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -122,8 +122,7 @@ namespace Ryujinx.HLE.HOS device, device.Memory, device.Configuration.MemoryConfiguration.ToKernelMemorySize(), - device.Configuration.MemoryConfiguration.ToKernelMemoryArrange(), - device.CpuCoresCount); + device.Configuration.MemoryConfiguration.ToKernelMemoryArrange()); Device = device; diff --git a/src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs b/src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs index 9b1dd0c3f..7e2e9cacc 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs @@ -58,13 +58,12 @@ namespace Ryujinx.HLE.HOS.Kernel Switch device, MemoryBlock memory, MemorySize memorySize, - MemoryArrange memoryArrange, - int cpuCoresCount) + MemoryArrange memoryArrange) { TickSource = tickSource; Device = device; Memory = memory; - KScheduler.CpuCoresCount = cpuCoresCount; + KScheduler.CpuCoresCount = device.CpuCoresCount; Running = true; -- 2.47.1 From 37ac497c2ea8953aaf1ac1aa4d778e513d458658 Mon Sep 17 00:00:00 2001 From: LotP1 Date: Thu, 9 Jan 2025 17:36:32 +0100 Subject: [PATCH 5/6] include discord invite link --- src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs index 63a21e930..5c9f4f100 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs @@ -177,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process AllowedThreadPriosMask = GetMaskFromMinMax(lowestThreadPrio, highestThreadPrio); if (isApplication && lowestCpuCore == 0 && highestCpuCore != 2) - Ryujinx.Common.Logging.Logger.Error?.Print(Ryujinx.Common.Logging.LogClass.Application, $"Application requested cores with index range {lowestCpuCore} to {highestCpuCore}! Report this to @LotP on the Ryujinx/Ryubing discord server!"); + Ryujinx.Common.Logging.Logger.Error?.Print(Ryujinx.Common.Logging.LogClass.Application, $"Application requested cores with index range {lowestCpuCore} to {highestCpuCore}! Report this to @LotP on the Ryujinx/Ryubing discord server (discord.gg/ryujinx)!"); else if (isApplication) Ryujinx.Common.Logging.Logger.Info?.Print(Ryujinx.Common.Logging.LogClass.Application, $"Application requested cores with index range {lowestCpuCore} to {highestCpuCore}"); -- 2.47.1 From 198cb09467dba65335cab2347f0cd531f11e0037 Mon Sep 17 00:00:00 2001 From: LotP1 Date: Thu, 9 Jan 2025 18:33:13 +0100 Subject: [PATCH 6/6] fix service kernel capabilities --- src/Ryujinx.HLE/HOS/Horizon.cs | 2 +- src/Ryujinx.HLE/HOS/Services/ServerBase.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index f9c5ddecf..627b649df 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -284,7 +284,7 @@ namespace Ryujinx.HLE.HOS ProcessCreationInfo creationInfo = new("Service", 1, 0, 0x8000000, 1, Flags, 0, 0); uint[] defaultCapabilities = { - 0x030363F7, + (((uint)KScheduler.CpuCoresCount - 1) << 24) + (((uint)KScheduler.CpuCoresCount - 1) << 16) + 0x63F7u, 0x1FFFFFCF, 0x207FFFEF, 0x47E0060F, diff --git a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs index f67699b90..40329aa36 100644 --- a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -24,14 +24,14 @@ namespace Ryujinx.HLE.HOS.Services // not large enough. private const int PointerBufferSize = 0x8000; - private readonly static uint[] _defaultCapabilities = { - 0x030363F7, + private static uint[] _defaultCapabilities => [ + (((uint)KScheduler.CpuCoresCount - 1) << 24) + (((uint)KScheduler.CpuCoresCount - 1) << 16) + 0x63F7u, 0x1FFFFFCF, 0x207FFFEF, 0x47E0060F, 0x0048BFFF, 0x01007FFF, - }; + ]; // The amount of time Dispose() will wait to Join() the thread executing the ServerLoop() private static readonly TimeSpan _threadJoinTimeout = TimeSpan.FromSeconds(3); -- 2.47.1