Ensure single application instance
MODIFY
WalletWasabi.Gui/CommandLine/MixerCommand.cs
WalletWasabi.Gui/CommandLine/MixerCommand.cs
@@ -36,6 +36,8 @@ namespace WalletWasabi.Gui.CommandLine
var error = false;
try
{
+ await Global.EnsureSingleInstanceAsync();
+
var extra = Options.Parse(args);
if (ShowHelp)
{

MODIFY
WalletWasabi.Gui/Global.cs
WalletWasabi.Gui/Global.cs
@@ -77,6 +77,8 @@ namespace WalletWasabi.Gui
public static JsonRpcServer RpcServer { get; private set; }
+ private static IDisposable SingleApplicationLockHolder { get; set; }
+
public Global()
{
using (BenchmarkLogger.Measure())
@@ -617,6 +619,22 @@ namespace WalletWasabi.Gui
Logger.LogInfo($"Transaction Notification ({notificationType}): {title} - {message} - {e.Transaction.GetHash()}");
}
+ public static async Task EnsureSingleInstanceAsync()
+ {
+ using CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.Zero);
+
+ // The disposal of this mutex handled by AsyncMutex.WaitForAllMutexToCloseAsync().
+ var mutex = new AsyncMutex("WalletWasabiSingleInstance");
+ try
+ {
+ SingleApplicationLockHolder = await mutex.LockAsync(cts.Token).ConfigureAwait(false);
+ }
+ catch (IOException)
+ {
+ throw new InvalidOperationException("Wasabi is already running!");
+ }
+ }
+
/// <summary>
/// 0: nobody called
/// 1: somebody called
@@ -772,6 +790,15 @@ namespace WalletWasabi.Gui
cache.Dispose();
}
+ try
+ {
+ SingleApplicationLockHolder?.Dispose();
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError($"Error during the disposal of {nameof(SingleApplicationLockHolder)}: {ex}");
+ }
+
if (AsyncMutex.IsAny)
{
try

MODIFY
WalletWasabi.Gui/Program.cs
WalletWasabi.Gui/Program.cs
@@ -52,7 +52,7 @@ namespace WalletWasabi.Gui
finally
{
MainWindowViewModel.Instance?.Dispose();
- Global.DisposeAsync().GetAwaiter().GetResult();
+ Global?.DisposeAsync().GetAwaiter().GetResult();
AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException -= TaskScheduler_UnobservedTaskException;
@@ -68,6 +68,8 @@ namespace WalletWasabi.Gui
AvalonStudio.Extensibility.Theme.ColorTheme.LoadTheme(AvalonStudio.Extensibility.Theme.ColorTheme.VisualStudioDark);
MainWindowViewModel.Instance = new MainWindowViewModel();
+ await Global.EnsureSingleInstanceAsync();
+
await Global.InitializeNoWalletAsync();
MainWindowViewModel.Instance.Initialize();

Ensure single application instance