Repository: microsoft/terminal (language: cpp)

Issue:
AtlasEngine: Exiting OpenConsole in Debug builds throws an exception
### Steps to reproduce

* Set `HKEY_CURRENT_USER\Console\UseDx` to `2`
* Run `OpenConsole.exe`
* Exit

### Expected Behavior

No exceptions.

### Actual Behavior

![image](https://user-images.githubusercontent.com/2256941/152832989-092d7b81-1586-442f-b53b-816c73a30e67.png)


```
D3D11 WARNING: Process is terminating. Using simple reporting. Please call ReportLiveObjects() at runtime for standard reporting. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11: **BREAK** enabled for the previous message, which was: [ WARNING STATE_CREATION #0: UNKNOWN ]
An interface [0000021167E04968] was created but not released. Use 'dps 0000021167E04908' to view its allocation stack.
Object type: ID2D1SolidColorBrush
	Color: r=1.00 g=1.00 b=1.00 a=1.00
	Outstanding reference count: 1

An interface [0000021167DF85A8] was created but not released. Use 'dps 0000021167DF8548' to view its allocation stack.
Object type: ID2D1DeviceContext
	Outstanding reference count: 1

An interface [0000021167D71F18] was created but not released. Use 'dps 0000021167D71EB8' to view its allocation stack.
Object type: ID2D1Factory
	Factory type: D2D1_FACTORY_TYPE_SINGLE_THREADED
	Outstanding reference count: 5

D2D DEBUG ERROR - Memory leaks detected.
```


Discussion / hints:

Fix patch:
diff --git a/src/interactivity/base/ServiceLocator.cpp b/src/interactivity/base/ServiceLocator.cpp
index 3bc691abdc6..49a89e3e81c 100644
--- a/src/interactivity/base/ServiceLocator.cpp
+++ b/src/interactivity/base/ServiceLocator.cpp
@@ -17,14 +17,13 @@ using namespace Microsoft::Console::Interactivity;
 std::unique_ptr<IInteractivityFactory> ServiceLocator::s_interactivityFactory;
 std::unique_ptr<IConsoleControl> ServiceLocator::s_consoleControl;
 std::unique_ptr<IConsoleInputThread> ServiceLocator::s_consoleInputThread;
+std::unique_ptr<IConsoleWindow> ServiceLocator::s_consoleWindow;
 std::unique_ptr<IWindowMetrics> ServiceLocator::s_windowMetrics;
 std::unique_ptr<IAccessibilityNotifier> ServiceLocator::s_accessibilityNotifier;
 std::unique_ptr<IHighDpiApi> ServiceLocator::s_highDpiApi;
 std::unique_ptr<ISystemConfigurationProvider> ServiceLocator::s_systemConfigurationProvider;
 std::unique_ptr<IInputServices> ServiceLocator::s_inputServices;
 
-IConsoleWindow* ServiceLocator::s_consoleWindow = nullptr;
-
 Globals ServiceLocator::s_globals;
 
 bool ServiceLocator::s_pseudoWindowInitialized = false;
@@ -45,6 +44,10 @@ wil::unique_hwnd ServiceLocator::s_pseudoWindow = nullptr;
         s_globals.pRender->TriggerTeardown();
     }
 
+    // By locking the console, we ensure no background tasks are accessing the
+    // classes we're going to destruct down below (for instance: CursorBlinker).
+    s_globals.getConsoleInformation().LockConsole();
+
     // A History Lesson from MSFT: 13576341:
     // We introduced RundownAndExit to give services that hold onto important handles
     // an opportunity to let those go when we decide to exit from the console for various reasons.
@@ -62,10 +65,9 @@ wil::unique_hwnd ServiceLocator::s_pseudoWindow = nullptr;
 
     // TODO: MSFT: 14397093 - Expand graceful rundown beyond just the Hot Bug input services case.
 
-    if (s_inputServices.get() != nullptr)
-    {
-        s_inputServices.reset(nullptr);
-    }
+    delete s_globals.pRender;
+    s_inputServices.reset(nullptr);
+    s_consoleWindow.reset(nullptr);
 
     ExitProcess(hr);
 }
@@ -154,7 +156,7 @@ wil::unique_hwnd ServiceLocator::s_pseudoWindow = nullptr;
     }
     else
     {
-        s_consoleWindow = window;
+        s_consoleWindow.reset(window);
     }
 
     return status;
@@ -166,7 +168,7 @@ wil::unique_hwnd ServiceLocator::s_pseudoWindow = nullptr;
 
 IConsoleWindow* ServiceLocator::LocateConsoleWindow()
 {
-    return s_consoleWindow;
+    return s_consoleWindow.get();
 }
 
 IConsoleControl* ServiceLocator::LocateConsoleControl()
diff --git a/src/interactivity/inc/ServiceLocator.hpp b/src/interactivity/inc/ServiceLocator.hpp
index 41e27bcdf9f..cbcbdd8f0b0 100644
--- a/src/interactivity/inc/ServiceLocator.hpp
+++ b/src/interactivity/inc/ServiceLocator.hpp
@@ -61,7 +61,7 @@ namespace Microsoft::Console::Interactivity
         template<typename T>
         static T* LocateConsoleWindow()
         {
-            return static_cast<T*>(s_consoleWindow);
+            return static_cast<T*>(s_consoleWindow.get());
         }
 
         static IWindowMetrics* LocateWindowMetrics();
@@ -106,7 +106,7 @@ namespace Microsoft::Console::Interactivity
         // TODO: MSFT 15344939 - some implementations of IConsoleWindow are currently singleton
         // classes so we can't own a pointer to them here. fix this so s_consoleWindow can follow the
         // pattern of the rest of the service interface pointers.
-        static IConsoleWindow* s_consoleWindow;
+        static std::unique_ptr<IConsoleWindow> s_consoleWindow;
         static std::unique_ptr<IWindowMetrics> s_windowMetrics;
         static std::unique_ptr<IHighDpiApi> s_highDpiApi;
         static std::unique_ptr<ISystemConfigurationProvider> s_systemConfigurationProvider;
diff --git a/src/interactivity/win32/window.cpp b/src/interactivity/win32/window.cpp
index e47a2ef03bd..5b72012c706 100644
--- a/src/interactivity/win32/window.cpp
+++ b/src/interactivity/win32/window.cpp
@@ -51,7 +51,6 @@ using namespace Microsoft::Console::Interactivity;
 using namespace Microsoft::Console::Render;
 
 ATOM Window::s_atomWindowClass = 0;
-Window* Window::s_Instance = nullptr;
 
 Window::Window() :
     _fIsInFullscreen(false),
@@ -68,10 +67,13 @@ Window::Window() :
 
 Window::~Window()
 {
-    if (ServiceLocator::LocateGlobals().pRender != nullptr)
-    {
-        delete ServiceLocator::LocateGlobals().pRender;
-    }
+    delete pGdiEngine;
+#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
+    delete pDxEngine;
+#endif
+#if TIL_FEATURE_ATLASENGINE_ENABLED
+    delete pAtlasEngine;
+#endif
 }
 
 // Routine Description:
@@ -98,7 +100,6 @@ Window::~Window()
 
             if (NT_SUCCESS(status))
             {
-                Window::s_Instance = pNewWindow;
                 LOG_IF_FAILED(ServiceLocator::SetConsoleWindowInstance(pNewWindow));
             }
         }
@@ -210,13 +211,6 @@ void Window::_UpdateSystemMetrics() const
     _UpdateSystemMetrics();
 
     const auto useDx = pSettings->GetUseDx();
-    GdiEngine* pGdiEngine = nullptr;
-#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
-    DxEngine* pDxEngine = nullptr;
-#endif
-#if TIL_FEATURE_ATLASENGINE_ENABLED
-    AtlasEngine* pAtlasEngine = nullptr;
-#endif
     try
     {
         switch (useDx)
diff --git a/src/interactivity/win32/window.hpp b/src/interactivity/win32/window.hpp
index 8e1cdef53eb..cb4742aefca 100644
--- a/src/interactivity/win32/window.hpp
+++ b/src/interactivity/win32/window.hpp
@@ -16,6 +16,13 @@ Author(s):
 
 #include "../inc/IConsoleWindow.hpp"
 
+namespace Microsoft::Console::Render
+{
+    class AtlasEngine;
+    class DxEngine;
+    class GdiEngine;
+}
+
 namespace Microsoft::Console::Interactivity::Win32
 {
     class WindowUiaProvider;
@@ -105,7 +112,14 @@ namespace Microsoft::Console::Interactivity::Win32
         Settings* _pSettings;
 
         HWND _hWnd;
-        static Window* s_Instance;
+
+        Render::GdiEngine* pGdiEngine = nullptr;
+#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
+        Render::DxEngine* pDxEngine = nullptr;
+#endif
+#if TIL_FEATURE_ATLASENGINE_ENABLED
+        Render::AtlasEngine* pAtlasEngine = nullptr;
+#endif
 
         [[nodiscard]] NTSTATUS _InternalSetWindowSize();
         void _UpdateWindowSize(const SIZE sizeNew);


Test patch: