diff --git a/build/Version.props b/build/Version.props index c11adcf2720..79032aba9b4 100644 --- a/build/Version.props +++ b/build/Version.props @@ -9,7 +9,7 @@ 7.0.0 13.2.0 15.2.0 - 7.1.0 + 7.1.1 5.9.0 1.4.1 1.2.1 diff --git a/src/DMAPI/tgs.dm b/src/DMAPI/tgs.dm index dc49d2c6f02..a4fb6d40be7 100644 --- a/src/DMAPI/tgs.dm +++ b/src/DMAPI/tgs.dm @@ -1,6 +1,6 @@ // tgstation-server DMAPI -#define TGS_DMAPI_VERSION "7.1.0" +#define TGS_DMAPI_VERSION "7.1.1" // All functions and datums outside this document are subject to change with any version and should not be relied on. @@ -496,7 +496,7 @@ /// Returns a list of connected [/datum/tgs_chat_channel]s if TGS is present, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping! /world/proc/TgsChatChannelInfo() return - + /** * Trigger an event in TGS. Requires TGS version >= 6.3.0. Returns [TRUE] if the event was triggered successfully, [FALSE] otherwise. This function may sleep! * diff --git a/src/DMAPI/tgs/v5/api.dm b/src/DMAPI/tgs/v5/api.dm index 9b64931f8f9..95b8edd3ee5 100644 --- a/src/DMAPI/tgs/v5/api.dm +++ b/src/DMAPI/tgs/v5/api.dm @@ -48,6 +48,10 @@ var/datum/tgs_version/api_version = ApiVersion() version = null // we want this to be the TGS version, not the interop version + + // sleep once to prevent an issue where world.Export on the first tick can hang indefinitely + sleep(world.tick_lag) + var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands(), DMAPI5_PARAMETER_TOPIC_PORT = GetTopicPort())) if(!istype(bridge_response)) TGS_ERROR_LOG("Failed initial bridge request!") diff --git a/src/DMAPI/tgs/v5/topic.dm b/src/DMAPI/tgs/v5/topic.dm index e66edc27206..e1f2cb63857 100644 --- a/src/DMAPI/tgs/v5/topic.dm +++ b/src/DMAPI/tgs/v5/topic.dm @@ -177,7 +177,8 @@ reattach_response[DMAPI5_PARAMETER_CUSTOM_COMMANDS] = ListCustomCommands() reattach_response[DMAPI5_PARAMETER_TOPIC_PORT] = GetTopicPort() - pending_events.Cut() + for(var/eventId in pending_events) + pending_events[eventId] = TRUE return reattach_response diff --git a/src/Tgstation.Server.Host/System/WindowsProcessFeatures.cs b/src/Tgstation.Server.Host/System/WindowsProcessFeatures.cs index e842bd9db25..f7ad2096a1a 100644 --- a/src/Tgstation.Server.Host/System/WindowsProcessFeatures.cs +++ b/src/Tgstation.Server.Host/System/WindowsProcessFeatures.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -68,28 +69,40 @@ public void SuspendProcess(global::System.Diagnostics.Process process) { ArgumentNullException.ThrowIfNull(process); - process.Refresh(); - foreach (ProcessThread thread in process.Threads) + var suspendedThreadIds = new HashSet(); + bool suspendedNewThreads; + do { - var threadId = (uint)thread.Id; - logger.LogTrace("Suspending thread {threadId}...", threadId); - var pOpenThread = NativeMethods.OpenThread(NativeMethods.ThreadAccess.SuspendResume, false, threadId); - if (pOpenThread == IntPtr.Zero) - { - logger.LogDebug(new Win32Exception(), "Failed to open thread {threadId}!", threadId); - continue; - } - - try - { - if (NativeMethods.SuspendThread(pOpenThread) == UInt32.MaxValue) - throw new Win32Exception(); - } - finally + suspendedNewThreads = false; + process.Refresh(); + foreach (ProcessThread thread in process.Threads) { - NativeMethods.CloseHandle(pOpenThread); + var threadId = (uint)thread.Id; + + if (!suspendedThreadIds.Add(threadId)) + continue; + + suspendedNewThreads = true; + logger.LogTrace("Suspending thread {threadId}...", threadId); + var pOpenThread = NativeMethods.OpenThread(NativeMethods.ThreadAccess.SuspendResume, false, threadId); + if (pOpenThread == IntPtr.Zero) + { + logger.LogDebug(new Win32Exception(), "Failed to open thread {threadId}!", threadId); + continue; + } + + try + { + if (NativeMethods.SuspendThread(pOpenThread) == UInt32.MaxValue) + throw new Win32Exception(); + } + finally + { + NativeMethods.CloseHandle(pOpenThread); + } } } + while (suspendedNewThreads); } /// diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index 4a453d4f510..6a59da10bbf 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -526,7 +526,7 @@ async Task DumpTests(bool mini, CancellationToken cancellationToken) }, cancellationToken); Assert.AreEqual(mini, updated.Minidumps); var dumpJob = await instanceClient.DreamDaemon.CreateDump(cancellationToken); - await WaitForJob(dumpJob, 30, false, null, cancellationToken); + await WaitForJob(dumpJob, 60, false, null, cancellationToken); var dumpFiles = Directory.GetFiles(Path.Combine( instanceClient.Metadata.Path, "Diagnostics", "ProcessDumps"), testVersion.Engine == EngineType.OpenDream ? "*.net.dmp" : "*.dmp"); @@ -729,28 +729,41 @@ void TestLinuxIsntBeingFuckingCheekyAboutFilePaths(DreamDaemonResponse currentSt var foundLivePath = false; var allPaths = new List(); - Assert.IsFalse(proc.HasExited); - foreach (var fd in Directory.GetFiles($"/proc/{pid}/fd")) + var features = new PosixProcessFeatures( + new Lazy(Mock.Of()), + Mock.Of(), + Mock.Of>()); + + features.SuspendProcess(proc); + try { - var sb = new StringBuilder(UInt16.MaxValue); - if (Syscall.readlink(fd, sb) == -1) - throw new UnixIOException(Stdlib.GetLastError()); + Assert.IsFalse(proc.HasExited); + foreach (var fd in Directory.GetFiles($"/proc/{pid}/fd")) + { + var sb = new StringBuilder(UInt16.MaxValue); + if (Syscall.readlink(fd, sb) == -1) + throw new UnixIOException(Stdlib.GetLastError()); - var path = sb.ToString(); + var path = sb.ToString(); - allPaths.Add($"Path: {path}"); - if (path.Contains($"Game/{previousStatus.DirectoryName}")) - failingLinks.Add($"Found fd {fd} resolving to previous absolute path game dir path: {path}"); + allPaths.Add($"Path: {path}"); + if (path.Contains($"Game/{previousStatus.DirectoryName}")) + failingLinks.Add($"Found fd {fd} resolving to previous absolute path game dir path: {path}"); - if (path.Contains($"Game/{currentStatus.ActiveCompileJob.DirectoryName}")) - failingLinks.Add($"Found fd {fd} resolving to current absolute path game dir path: {path}"); + if (path.Contains($"Game/{currentStatus.ActiveCompileJob.DirectoryName}")) + failingLinks.Add($"Found fd {fd} resolving to current absolute path game dir path: {path}"); - if (path.Contains($"Game/Live")) - foundLivePath = true; - } + if (path.Contains($"Game/Live")) + foundLivePath = true; + } - if (!foundLivePath) - failingLinks.Add($"Failed to find a path containing the 'Live' directory!"); + if (!foundLivePath) + failingLinks.Add($"Failed to find a path containing the 'Live' directory!"); + } + finally + { + features.ResumeProcess(proc); + } Assert.IsTrue(failingLinks.Count == 0, String.Join(Environment.NewLine, failingLinks.Concat(allPaths))); }