Handle multiple processes
MODIFY
WalletWasabi.Gui/Tabs/WalletManager/WalletManagerViewModel.cs
WalletWasabi.Gui/Tabs/WalletManager/WalletManagerViewModel.cs
@@ -101,12 +101,13 @@ namespace WalletWasabi.Gui.Tabs.WalletManager
}
private CancellationTokenSource HardwareWalletRefreshCancel { get; }
- private bool HwTabSelectedOnce { get; set; } = false;
+ private bool HwTabAutomaticallySelectedOnce { get; set; } = false;
private async Task RefreshHardwareWalletListAsync()
{
try
{
+ int waitTime = 3000;
while (!HardwareWalletRefreshCancel.IsCancellationRequested)
{
try
@@ -116,11 +117,14 @@ namespace WalletWasabi.Gui.Tabs.WalletManager
var hwis = await HwiProcessManager.EnumerateAsync();
LoadWalletViewModelHardware.TryRefreshHardwareWallets(hwis);
- if (hwis.Any() && !HwTabSelectedOnce)
+ if (hwis.Any())
+ {
+ waitTime = 7000;
+ if (!HwTabAutomaticallySelectedOnce)
{
try
{
- HwTabSelectedOnce = true;
+ HwTabAutomaticallySelectedOnce = true;
SelectHardwareWallet();
}
catch (Exception ex)
@@ -129,13 +133,18 @@ namespace WalletWasabi.Gui.Tabs.WalletManager
}
}
}
+ else
+ {
+ waitTime = 3000;
+ }
+ }
catch (Exception ex)
{
- Logger.LogError<WalletManagerViewModel>(ex);
+ Logger.LogWarning<WalletManagerViewModel>(ex);
}
finally
{
- await Task.Delay(3000, HardwareWalletRefreshCancel.Token);
+ await Task.Delay(waitTime, HardwareWalletRefreshCancel.Token);
}
}
}

MODIFY
WalletWasabi/Hwi/HwiProcessManager.cs
WalletWasabi/Hwi/HwiProcessManager.cs
@@ -21,6 +21,7 @@ namespace WalletWasabi.Hwi
/// </summary>
public static class HwiProcessManager
{
+ public const string MutexName = "Global\\FE5C8D9C-E000-495F-8175-A8713E449B2E";
public static string HwiPath { get; private set; }
public static AsyncLock AsyncLock { get; }
public static Network Network { get; private set; }
@@ -35,7 +36,7 @@ namespace WalletWasabi.Hwi
{
var psbtString = psbt.ToBase64();
var networkString = Network == Network.Main ? "" : "--testnet";
- JToken jtok = await SendCommandAsync($"{networkString} --device-type \"{hardwareWalletInfo.Type.ToString().ToLowerInvariant()}\" --device-path \"{hardwareWalletInfo.Path}\" signtx {psbtString}");
+ JToken jtok = await SendCommandAsync($"{networkString} --device-type \"{hardwareWalletInfo.Type.ToString().ToLowerInvariant()}\" --device-path \"{hardwareWalletInfo.Path}\" signtx {psbtString}", isMutexPriority: true);
JObject json = jtok as JObject;
var signedPsbtString = json.Value<string>("psbt");
var signedPsbt = PSBT.Parse(signedPsbtString, Network);
@@ -86,9 +87,46 @@ namespace WalletWasabi.Hwi
return hwis;
}
- public static async Task<JToken> SendCommandAsync(string command)
+ private static Random Random { get; } = new Random();
+
+ public static async Task<JToken> SendCommandAsync(string command, bool isMutexPriority = false)
{
using (await AsyncLock.LockAsync())
+ using (var mutex = new Mutex(false, MutexName)) // It could be even improved more if this Mutex would also look at which hardware wallet the operation is going towards (enumerate sends to all.)
+ {
+ var mutexAcquired = false;
+ try
+ {
+ // acquire the mutex (or timeout after 60 seconds)
+ // will return false if it timed out
+ var start = DateTime.Now;
+ while (DateTime.Now - start > TimeSpan.FromSeconds(90))
+ {
+ mutexAcquired = mutex.WaitOne(1);
+ if (!mutexAcquired)
+ {
+ var rand = Random.Next(1, 100);
+ await Task.Delay(isMutexPriority ? (100 + rand) : (1000 + rand));
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ catch (AbandonedMutexException)
+ {
+ // abandoned mutexes are still acquired, we just need
+ // to handle the exception and treat it as acquisition
+ mutexAcquired = true;
+ }
+
+ if (mutexAcquired == false)
+ {
+ throw new IOException("Couldn't acquire Mutex on the file.");
+ }
+
+ try
{
if (!File.Exists(HwiPath))
{
@@ -163,6 +201,11 @@ namespace WalletWasabi.Hwi
//}
}
}
+ finally
+ {
+ mutex.ReleaseMutex();
+ }
+ }
}
public static async Task EnsureHwiInstalledAsync(string dataDir, Network network, bool logFound = true)

Handle multiple processes