Skip to content

Commit

Permalink
Remove shutdown handling to make behavior deterministic
Browse files Browse the repository at this point in the history
  • Loading branch information
lonitra committed Oct 7, 2024
1 parent 428aa3b commit c4817f2
Show file tree
Hide file tree
Showing 2 changed files with 5 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -693,8 +693,6 @@ private unsafe void Initialize()
hInstance, IntPtr.Zero);
}
}

AppDomain.CurrentDomain.ProcessExit += new EventHandler(Shutdown);
}

/// <summary>
Expand Down Expand Up @@ -1070,67 +1068,6 @@ private static void RemoveEventHandler(object key, Delegate? value)
}
}

private static void Shutdown()
{
if (s_systemEvents is null)
{
return;
}

lock (s_procLockObject)
{
if (s_systemEvents is null)
{
return;
}

// If we are using system events from another thread, request that it terminate
if (s_windowThread is not null)
{
#if DEBUG
unsafe
{
int pid;
int thread = Interop.User32.GetWindowThreadProcessId(s_systemEvents._windowHandle, &pid);
Debug.Assert(thread != Interop.Kernel32.GetCurrentThreadId(), "Don't call Shutdown on the system events thread");

}
#endif
// The handle could be valid, Zero or invalid depending on the state of the thread
// that is processing the messages. We optimistically expect it to be valid to
// notify the thread to shutdown. The Zero or invalid values should be present
// only when the thread is already shutting down due to external factors.
if (s_systemEvents._windowHandle != IntPtr.Zero)
{
InvokeOnEventsThread(() => Interop.User32.PostQuitMessage(0));
}

// Don't wait for the SystemEvents thread to finish to avoid blocking the Finalizer thread.
// When the main thread kicks off shutdown, the Finalizer thread will raise a ProcessExit event,
// which will callback to here waiting for this method to finish.
// This occurs before AppDomain.IsFinalizingForUnload or Environment.HasShutdownStarted is set to true.
// Because of this, any code that needs a response from the main thread will not know main thread will not respond
// since it is in the middle of shutdown. This can cause synchronous callbacks to deadlock.
// For example, in https://github.com/dotnet/winforms/issues/11944, WindowsFormsSynchronizationContext will block the
// SystemEvents thread to wait for tasks to complete on the main thread. It cannot see that main thread is trying to
// shut down, but is stuck waiting for the Finalizer thread to finish, which is waiting for SystemEvents thread to finish.
}
else
{
s_systemEvents.Dispose();
s_systemEvents = null;
}
}
}

#if FEATURE_CER
[PrePrepareMethod]
#endif
private static void Shutdown(object? sender, EventArgs e)
{
Shutdown();
}

/// <summary>
/// A standard Win32 window proc for our broadcast window.
/// </summary>
Expand Down Expand Up @@ -1258,8 +1195,8 @@ private IntPtr WindowProc(IntPtr hWnd, int msg, nint wParam, nint lParam)
}

/// <summary>
/// This is the method that runs our window thread. This method
/// creates a window and spins up a message loop. The window
/// This is the method that runs our window thread. This method
/// creates a window and spins up a message loop. The window
/// is made visible with a size of 0, 0, so that it will trap
/// global broadcast messages.
/// </summary>
Expand All @@ -1272,7 +1209,7 @@ private void WindowThreadProc()

if (_windowHandle != IntPtr.Zero)
{
Interop.User32.MSG msg = default(Interop.User32.MSG);
Interop.User32.MSG msg = default;

while (Interop.User32.GetMessageW(ref msg, _windowHandle, 0, 0) > 0)
{
Expand All @@ -1285,11 +1222,11 @@ private void WindowThreadProc()
}
catch (Exception e)
{
// In case something very very wrong happend during the creation action.
// In case something very very wrong happened during the creation action.
// This will unblock the calling thread.
s_eventWindowReady!.Set();

if (!((e is ThreadInterruptedException) || (e is ThreadAbortException)))
if (e is not (ThreadInterruptedException or ThreadAbortException))
{
Debug.Fail("Unexpected thread exception in system events window thread proc", e.ToString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ public void ShutdownThroughRestartManager()
// Fake Restart Manager behavior by sending external WM_CLOSE message
SendMessage(Interop.User32.WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
// Emulate calling the Shutdown event
var shutdownMethod = typeof(SystemEvents).GetMethod("Shutdown", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic, null, new Type[0], null);
Assert.NotNull(shutdownMethod);
shutdownMethod.Invoke(null, null);
}).Dispose();
}

Expand Down

0 comments on commit c4817f2

Please sign in to comment.