From 98e1c1905cd4304028c1fd2d90ed47f0996bdb3e Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Mon, 16 Oct 2023 10:08:51 -0400 Subject: [PATCH] Possibly fix address reuse issue forever Child processes may have been ~~inheriting~~ stealing the bind test handle. --- .../Session/SessionControllerFactory.cs | 5 +-- .../Extensions/SocketExtensions.cs | 43 ++++++++++--------- .../System/ProcessExecutor.cs | 32 +++++++++++++- 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs index 86e24f7f63..d1ba7ff2c6 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs @@ -217,7 +217,6 @@ namespace Tgstation.Server.Host.Components.Session if (!launchParameters.Port.HasValue) throw new InvalidOperationException("Given port is null!"); - PortBindTest(launchParameters.Port.Value); switch (dmbProvider.CompileJob.MinimumSecurityLevel) { case DreamDaemonSecurity.Ultrasafe: @@ -255,8 +254,8 @@ namespace Tgstation.Server.Host.Components.Session var engineType = dmbProvider.EngineVersion.Engine.Value; if (engineType == EngineType.Byond) await CheckPagerIsNotRunning(); - else if (engineType == EngineType.OpenDream && platformIdentifier.IsWindows) - await asyncDelayer.Delay(TimeSpan.FromSeconds(2), cancellationToken); // prevent socket reuse after bind test + + PortBindTest(launchParameters.Port.Value); string outputFilePath = null; var preserveLogFile = true; diff --git a/src/Tgstation.Server.Host/Extensions/SocketExtensions.cs b/src/Tgstation.Server.Host/Extensions/SocketExtensions.cs index 948aedbc72..75adcf9a25 100644 --- a/src/Tgstation.Server.Host/Extensions/SocketExtensions.cs +++ b/src/Tgstation.Server.Host/Extensions/SocketExtensions.cs @@ -1,6 +1,8 @@ using System.Net; using System.Net.Sockets; +using Tgstation.Server.Host.System; + namespace Tgstation.Server.Host.Extensions { /// @@ -14,26 +16,27 @@ namespace Tgstation.Server.Host.Extensions /// The port number to bind to. /// If IPV6 should be tested as well. public static void BindTest(ushort port, bool includeIPv6) - { - using var socket = new Socket( - includeIPv6 - ? AddressFamily.InterNetworkV6 - : AddressFamily.InterNetwork, - SocketType.Stream, - ProtocolType.Tcp); - - socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, true); - socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false); - socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true); - if (includeIPv6) - socket.DualMode = true; - - socket.Bind( - new IPEndPoint( + => ProcessExecutor.WithProcessLaunchExclusivity(() => + { + using var socket = new Socket( includeIPv6 - ? IPAddress.IPv6Any - : IPAddress.Any, - port)); - } + ? AddressFamily.InterNetworkV6 + : AddressFamily.InterNetwork, + SocketType.Stream, + ProtocolType.Tcp); + + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, true); + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false); + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true); + if (includeIPv6) + socket.DualMode = true; + + socket.Bind( + new IPEndPoint( + includeIPv6 + ? IPAddress.IPv6Any + : IPAddress.Any, + port)); + }); } } diff --git a/src/Tgstation.Server.Host/System/ProcessExecutor.cs b/src/Tgstation.Server.Host/System/ProcessExecutor.cs index 581977bb5b..e680b09256 100644 --- a/src/Tgstation.Server.Host/System/ProcessExecutor.cs +++ b/src/Tgstation.Server.Host/System/ProcessExecutor.cs @@ -13,6 +13,11 @@ namespace Tgstation.Server.Host.System /// sealed class ProcessExecutor : IProcessExecutor { + /// + /// for . + /// + static readonly ReaderWriterLockSlim ExclusiveProcessLaunchLock = new (); + /// /// The for the . /// @@ -33,6 +38,23 @@ namespace Tgstation.Server.Host.System /// readonly ILoggerFactory loggerFactory; + /// + /// Runs a given making sure to not launch any processes while its running. + /// + /// The to execute. + public static void WithProcessLaunchExclusivity(Action action) + { + ExclusiveProcessLaunchLock.EnterWriteLock(); + try + { + action(); + } + finally + { + ExclusiveProcessLaunchLock.ExitWriteLock(); + } + } + /// /// Initializes a new instance of the class. /// @@ -127,7 +149,15 @@ namespace Tgstation.Server.Host.System try { - handle.Start(); + ExclusiveProcessLaunchLock.EnterReadLock(); + try + { + handle.Start(); + } + finally + { + ExclusiveProcessLaunchLock.ExitReadLock(); + } processStartTcs?.SetResult(); }