diff --git a/src/Tgstation.Server.Host/Components/Repository/Repository.cs b/src/Tgstation.Server.Host/Components/Repository/Repository.cs
index e1ed76a4c9..a6408f3c74 100644
--- a/src/Tgstation.Server.Host/Components/Repository/Repository.cs
+++ b/src/Tgstation.Server.Host/Components/Repository/Repository.cs
@@ -130,11 +130,12 @@ namespace Tgstation.Server.Host.Components.Repository
if (disposed)
return;
- logger.LogTrace("Disposing...");
disposed = true;
- repository.Dispose();
- onDispose();
}
+
+ logger.LogTrace("Disposing...");
+ repository.Dispose();
+ onDispose();
}
void GetRepositoryOwnerName(string remote, out string owner, out string name)
diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs
index 5274cadd2d..d8c806cf74 100644
--- a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs
+++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs
@@ -549,6 +549,12 @@ namespace Tgstation.Server.Host.Components.Watchdog
///
public void SetHighPriority() => process.SetHighPriority();
+ ///
+ public void Suspend() => process.Suspend();
+
+ ///
+ public void Resume() => process.Resume();
+
///
public void ReplaceDmbProvider(IDmbProvider dmbProvider)
{
diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs
index 795ba9c2e2..652b372efa 100644
--- a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs
+++ b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs
@@ -120,7 +120,6 @@ namespace Tgstation.Server.Host.Components.Watchdog
if (pendingSwappable != null)
{
Logger.LogTrace("Replacing activeSwappable with pendingSwappable");
-
Server.ReplaceDmbProvider(pendingSwappable);
activeSwappable = pendingSwappable;
pendingSwappable = null;
@@ -139,7 +138,9 @@ namespace Tgstation.Server.Host.Components.Watchdog
windowsProvider = new WindowsSwappableDmbProvider(compileJobProvider, ioManager, symlinkFactory);
Logger.LogDebug("Swapping to compile job {0}...", windowsProvider.CompileJob.Id);
+ Server.Suspend();
await windowsProvider.MakeActive(cancellationToken).ConfigureAwait(false);
+ Server.Resume();
}
catch
{
diff --git a/src/Tgstation.Server.Host/NativeMethods.cs b/src/Tgstation.Server.Host/NativeMethods.cs
index 3686e79f9e..98a92c2eac 100644
--- a/src/Tgstation.Server.Host/NativeMethods.cs
+++ b/src/Tgstation.Server.Host/NativeMethods.cs
@@ -24,6 +24,14 @@ namespace Tgstation.Server.Host
AllowUnprivilegedCreate = 2
}
+ ///
+ /// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686769(v=vs.85).aspx
+ ///
+ public enum ThreadAccess : int
+ {
+ SuspendResume = 0x0002,
+ }
+
///
/// See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowthreadprocessid
///
@@ -70,5 +78,29 @@ namespace Tgstation.Server.Host
///
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, CreateSymbolicLinkFlags dwFlags);
+
+ ///
+ /// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684335(v=vs.85).aspx
+ ///
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
+
+ ///
+ /// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx
+ ///
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern bool CloseHandle(IntPtr hObject);
+
+ ///
+ /// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686345(v=vs.85).aspx
+ ///
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern uint SuspendThread(IntPtr hThread);
+
+ ///
+ /// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms685086(v=vs.85).aspx
+ ///
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern uint ResumeThread(IntPtr hThread);
}
}
diff --git a/src/Tgstation.Server.Host/System/IProcessBase.cs b/src/Tgstation.Server.Host/System/IProcessBase.cs
index 63d414e554..a9013d567a 100644
--- a/src/Tgstation.Server.Host/System/IProcessBase.cs
+++ b/src/Tgstation.Server.Host/System/IProcessBase.cs
@@ -17,5 +17,15 @@ namespace Tgstation.Server.Host.System
/// Set's the owned to
///
void SetHighPriority();
+
+ ///
+ /// Suspends the process.
+ ///
+ void Suspend();
+
+ ///
+ /// Resumes the process.
+ ///
+ void Resume();
}
}
diff --git a/src/Tgstation.Server.Host/System/Process.cs b/src/Tgstation.Server.Host/System/Process.cs
index d3738e94b0..c064733d1f 100644
--- a/src/Tgstation.Server.Host/System/Process.cs
+++ b/src/Tgstation.Server.Host/System/Process.cs
@@ -1,5 +1,7 @@
-using Microsoft.Extensions.Logging;
+using BetterWin32Errors;
+using Microsoft.Extensions.Logging;
using System;
+using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
@@ -133,13 +135,65 @@ namespace Tgstation.Server.Host.System
{
try
{
- handle.PriorityClass = global::System.Diagnostics.ProcessPriorityClass.AboveNormal;
- logger.LogTrace("Set to above normal priority", handle.Id);
+ handle.PriorityClass = ProcessPriorityClass.AboveNormal;
+ logger.LogTrace("Set PID {0} to above normal priority", Id);
}
catch (Exception e)
{
logger.LogWarning("Unable to raise process priority! Exception: {0}", e);
}
}
+
+ ///
+ public void Suspend()
+ {
+ try
+ {
+ foreach (ProcessThread thread in handle.Threads)
+ {
+ var pOpenThread = NativeMethods.OpenThread(NativeMethods.ThreadAccess.SuspendResume, false, (uint)thread.Id);
+ if (pOpenThread == IntPtr.Zero)
+ continue;
+
+ if (NativeMethods.SuspendThread(pOpenThread) == UInt32.MaxValue)
+ throw new Win32Exception();
+
+ NativeMethods.CloseHandle(pOpenThread);
+ }
+
+ logger.LogTrace("Suspended PID {0}", Id);
+ }
+ catch (Exception e)
+ {
+ logger.LogError(e, "Failed to suspend PID {0}!", Id);
+ throw;
+ }
+ }
+
+ ///
+ public void Resume()
+ {
+ try
+ {
+ foreach (ProcessThread thread in handle.Threads)
+ {
+ var pOpenThread = NativeMethods.OpenThread(NativeMethods.ThreadAccess.SuspendResume, false, (uint)thread.Id);
+ if (pOpenThread == IntPtr.Zero)
+ continue;
+
+ if (NativeMethods.ResumeThread(pOpenThread) == UInt32.MaxValue)
+ throw new Win32Exception();
+
+ NativeMethods.CloseHandle(pOpenThread);
+ }
+
+ logger.LogTrace("Resumed PID {0}", Id);
+ }
+ catch (Exception e)
+ {
+ logger.LogError(e, "Failed to resume PID {0}!", Id);
+ throw;
+ }
+ }
}
}