[PTRun] Gracefully shutdown all threads when exiting (#20450)

This commit is contained in:
Andrey Nekrasov
2022-09-12 13:20:24 +03:00
committed by GitHub
parent 73590c3ea9
commit 38e7b3b7ae
4 changed files with 31 additions and 26 deletions

View File

@@ -31,7 +31,10 @@ namespace PowerLauncher
{ {
public static PublicAPIInstance API { get; private set; } public static PublicAPIInstance API { get; private set; }
public static CancellationTokenSource NativeThreadCTS { get; private set; }
private static bool _disposed; private static bool _disposed;
private PowerToysRunSettings _settings; private PowerToysRunSettings _settings;
private MainViewModel _mainVM; private MainViewModel _mainVM;
private MainWindow _mainWindow; private MainWindow _mainWindow;
@@ -46,6 +49,8 @@ namespace PowerLauncher
[STAThread] [STAThread]
public static void Main() public static void Main()
{ {
NativeThreadCTS = new CancellationTokenSource();
Log.Info($"Starting PowerToys Run with PID={Environment.ProcessId}", typeof(App)); Log.Info($"Starting PowerToys Run with PID={Environment.ProcessId}", typeof(App));
int powerToysPid = GetPowerToysPId(); int powerToysPid = GetPowerToysPId();
if (powerToysPid != 0) if (powerToysPid != 0)
@@ -67,15 +72,13 @@ namespace PowerLauncher
using (var application = new App()) using (var application = new App())
{ {
application.InitializeComponent(); application.InitializeComponent();
new Thread(() => NativeEventWaiter.WaitForEventLoop(
Constants.RunExitEvent(),
() =>
{ {
var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.RunExitEvent()); Log.Warn("RunExitEvent was signaled. Exiting PowerToys", typeof(App));
if (eventHandle.WaitOne()) ExitPowerToys(application);
{ }, NativeThreadCTS.Token);
Log.Warn("RunExitEvent was signaled. Exiting PowerToys", typeof(App));
ExitPowerToys(application);
}
}).Start();
if (powerToysPid != 0) if (powerToysPid != 0)
{ {
@@ -118,8 +121,8 @@ namespace PowerLauncher
StringMatcher.Instance = _stringMatcher; StringMatcher.Instance = _stringMatcher;
_stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision; _stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision;
_mainVM = new MainViewModel(_settings); _mainVM = new MainViewModel(_settings, NativeThreadCTS.Token);
_mainWindow = new MainWindow(_settings, _mainVM); _mainWindow = new MainWindow(_settings, _mainVM, NativeThreadCTS.Token);
API = new PublicAPIInstance(_settingsVM, _mainVM, _themeManager); API = new PublicAPIInstance(_settingsVM, _mainVM, _themeManager);
_settingsReader = new SettingsReader(_settings, _themeManager); _settingsReader = new SettingsReader(_settings, _themeManager);
_settingsReader.ReadSettings(); _settingsReader.ReadSettings();
@@ -154,14 +157,7 @@ namespace PowerLauncher
{ {
SingleInstance<App>.SingleInstanceMutex.Close(); SingleInstance<App>.SingleInstanceMutex.Close();
try app.Dispatcher.Invoke(() => app.Shutdown());
{
app.Dispose();
}
finally
{
Environment.Exit(0);
}
} }
private static int GetPowerToysPId() private static int GetPowerToysPId()
@@ -194,6 +190,7 @@ namespace PowerLauncher
Current.Exit += (s, e) => Current.Exit += (s, e) =>
{ {
NativeThreadCTS.Cancel();
Log.Info("Application.Current.Exit", GetType()); Log.Info("Application.Current.Exit", GetType());
Dispose(); Dispose();
}; };

View File

@@ -12,18 +12,22 @@ namespace PowerLauncher.Helper
{ {
public static class NativeEventWaiter public static class NativeEventWaiter
{ {
public static void WaitForEventLoop(string eventName, Action callback) public static void WaitForEventLoop(string eventName, Action callback, CancellationToken cancel)
{ {
new Thread(() => new Thread(() =>
{ {
var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
while (true) while (true)
{ {
if (eventHandle.WaitOne()) if (WaitHandle.WaitAny(new WaitHandle[] { cancel.WaitHandle, eventHandle }) == 1)
{ {
Log.Info($"Successfully waited for {eventName}", MethodBase.GetCurrentMethod().DeclaringType); Log.Info($"Successfully waited for {eventName}", MethodBase.GetCurrentMethod().DeclaringType);
Application.Current.Dispatcher.Invoke(callback); Application.Current.Dispatcher.Invoke(callback);
} }
else
{
return;
}
} }
}).Start(); }).Start();
} }

View File

@@ -20,6 +20,7 @@ using PowerLauncher.Plugin;
using PowerLauncher.Telemetry.Events; using PowerLauncher.Telemetry.Events;
using PowerLauncher.ViewModel; using PowerLauncher.ViewModel;
using Wox.Infrastructure.UserSettings; using Wox.Infrastructure.UserSettings;
using CancellationToken = System.Threading.CancellationToken;
using KeyEventArgs = System.Windows.Input.KeyEventArgs; using KeyEventArgs = System.Windows.Input.KeyEventArgs;
using Log = Wox.Plugin.Logger.Log; using Log = Wox.Plugin.Logger.Log;
using Screen = System.Windows.Forms.Screen; using Screen = System.Windows.Forms.Screen;
@@ -30,6 +31,7 @@ namespace PowerLauncher
{ {
private readonly PowerToysRunSettings _settings; private readonly PowerToysRunSettings _settings;
private readonly MainViewModel _viewModel; private readonly MainViewModel _viewModel;
private readonly CancellationToken _nativeWaiterCancelToken;
private bool _isTextSetProgrammatically; private bool _isTextSetProgrammatically;
private bool _deletePressed; private bool _deletePressed;
private HwndSource _hwndSource; private HwndSource _hwndSource;
@@ -38,18 +40,19 @@ namespace PowerLauncher
private bool _disposedValue; private bool _disposedValue;
private IDisposable _reactiveSubscription; private IDisposable _reactiveSubscription;
public MainWindow(PowerToysRunSettings settings, MainViewModel mainVM) public MainWindow(PowerToysRunSettings settings, MainViewModel mainVM, CancellationToken nativeWaiterCancelToken)
: this() : this()
{ {
DataContext = mainVM; DataContext = mainVM;
_viewModel = mainVM; _viewModel = mainVM;
_nativeWaiterCancelToken = nativeWaiterCancelToken;
_settings = settings; _settings = settings;
InitializeComponent(); InitializeComponent();
_firstDeleteTimer.Elapsed += CheckForFirstDelete; _firstDeleteTimer.Elapsed += CheckForFirstDelete;
_firstDeleteTimer.Interval = 1000; _firstDeleteTimer.Interval = 1000;
NativeEventWaiter.WaitForEventLoop(Constants.RunSendSettingsTelemetryEvent(), SendSettingsTelemetry); NativeEventWaiter.WaitForEventLoop(Constants.RunSendSettingsTelemetryEvent(), SendSettingsTelemetry, _nativeWaiterCancelToken);
} }
private void SendSettingsTelemetry() private void SendSettingsTelemetry()

View File

@@ -48,6 +48,7 @@ namespace PowerLauncher.ViewModel
private CancellationTokenSource _updateSource; private CancellationTokenSource _updateSource;
private CancellationToken _updateToken; private CancellationToken _updateToken;
private CancellationToken _nativeWaiterCancelToken;
private bool _saved; private bool _saved;
private ushort _hotkeyHandle; private ushort _hotkeyHandle;
@@ -59,7 +60,7 @@ namespace PowerLauncher.ViewModel
internal HotkeyManager HotkeyManager { get; private set; } internal HotkeyManager HotkeyManager { get; private set; }
public MainViewModel(PowerToysRunSettings settings) public MainViewModel(PowerToysRunSettings settings, CancellationToken nativeThreadCancelToken)
{ {
_saved = false; _saved = false;
_queryTextBeforeLeaveResults = string.Empty; _queryTextBeforeLeaveResults = string.Empty;
@@ -67,7 +68,7 @@ namespace PowerLauncher.ViewModel
_disposed = false; _disposed = false;
_settings = settings ?? throw new ArgumentNullException(nameof(settings)); _settings = settings ?? throw new ArgumentNullException(nameof(settings));
_nativeWaiterCancelToken = nativeThreadCancelToken;
_historyItemsStorage = new WoxJsonStorage<QueryHistory>(); _historyItemsStorage = new WoxJsonStorage<QueryHistory>();
_userSelectedRecordStorage = new WoxJsonStorage<UserSelectedRecord>(); _userSelectedRecordStorage = new WoxJsonStorage<UserSelectedRecord>();
_history = _historyItemsStorage.Load(); _history = _historyItemsStorage.Load();
@@ -92,12 +93,12 @@ namespace PowerLauncher.ViewModel
Log.Info("RegisterHotkey()", GetType()); Log.Info("RegisterHotkey()", GetType());
// Allow OOBE to call PowerToys Run. // Allow OOBE to call PowerToys Run.
NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherSharedEvent(), OnHotkey); NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherSharedEvent(), OnHotkey, _nativeWaiterCancelToken);
if (_settings.StartedFromPowerToysRunner) if (_settings.StartedFromPowerToysRunner)
{ {
// Allow runner to call PowerToys Run from the centralized keyboard hook. // Allow runner to call PowerToys Run from the centralized keyboard hook.
NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherCentralizedHookSharedEvent(), OnCentralizedKeyboardHookHotKey); NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherCentralizedHookSharedEvent(), OnCentralizedKeyboardHookHotKey, _nativeWaiterCancelToken);
} }
_settings.PropertyChanged += (s, e) => _settings.PropertyChanged += (s, e) =>