Use AsyncMutex on HWI
MODIFY
WalletWasabi.Gui/Tabs/WalletManager/WalletManagerViewModel.cs
WalletWasabi.Gui/Tabs/WalletManager/WalletManagerViewModel.cs
@@ -113,7 +113,7 @@ namespace WalletWasabi.Gui.Tabs.WalletManager
{
try
{
- if (LoadWalletViewModelDesktop.IsWalletOpened || LoadWalletViewModelHardware.IsWalletOpened)
+ if (LoadWalletViewModelDesktop.IsWalletOpened || LoadWalletViewModelHardware.IsWalletOpened || HwiProcessManager.HwiPath is null)
{
continue; // Will wait 3sec, because of the finally.
}

MODIFY
WalletWasabi/Hwi/HwiProcessManager.cs
WalletWasabi/Hwi/HwiProcessManager.cs
@@ -21,9 +21,8 @@ namespace WalletWasabi.Hwi
/// </summary>
public static class HwiProcessManager
{
- public const string MutexName = "Global\\FE5C8D9C-E000-495F-8175-A8713E449B2E";
private static Random Random { get; } = new Random();
- public static AsyncLock AsyncLock { get; } = new AsyncLock();
+ public static AsyncMutex AsyncMutex { get; } = new AsyncMutex("hwi");
public static string HwiPath { get; private set; }
public static Network Network { get; private set; }
@@ -133,43 +132,10 @@ namespace WalletWasabi.Hwi
public static async Task<JToken> SendCommandAsync(string command, CancellationToken cancellationToken, bool isMutexPriority = false)
{
try
- {
- 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), cancellationToken);
- }
- 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
+ var delay = isMutexPriority ? (100 + rand) : (1000 + rand);
+ using (await AsyncMutex.LockAsync(cancellationToken, delay)) // 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.)
{
if (!File.Exists(HwiPath))
{
@@ -207,46 +173,6 @@ namespace WalletWasabi.Hwi
}
return jToken;
-
- //if (command == "enumerate")
- //{
- // //string response = "[{\"fingerprint\": \"8038ecd9\", \"serial_number\": \"205A32753042\", \"type\": \"coldcard\", \"path\": \"0001:0005:00\"},{\"fingerprint\": \"8338ecd9\", \"serial_number\": \"205A32753042\", \"type\": \"keepkey\", \"path\": \"0001:0005:00\"},{\"fingerprint\": \"8038ecd2\", \"serial_number\": \"205A32753042\", \"type\": \"coldcard\", \"path\": \"0001:0005:00\"}]";
- // string response = "[{\"fingerprint\": \"8038ecd9\", \"serial_number\": \"205A32753042\", \"type\": \"coldcard\", \"path\": \"0001:0005:00\"},{\"fingerprint\": \"8338ecd9\", \"serial_number\": \"205A32753042\", \"type\": \"keepkey\", \"path\": \"0001:0005:00\"}]";
- // var jToken = JToken.Parse(response);
- // return jToken;
- //}
- //if (command.Contains("getxpub", StringComparison.OrdinalIgnoreCase))
- //{
- // string response = "{\"xpub\": \"xpub6DP9afdc7qsz7s7mwAvciAR2dV6vPC3gyiQbqKDzDcPAq3UQChKPimHc3uCYfTTkpoXdwRTFnVTBdFpM9ysbf6KV34uMqkD3zXr6FzkJtcB\"}";
- // var jToken = JToken.Parse(response);
- // return jToken;
- //}
- //else
- //{
- // string response = await process.StandardOutput.ReadToEndAsync();
- // var jToken = JToken.Parse(response);
- // string err = null;
- // try
- // {
- // JObject json = jToken as JObject;
- // err = json.Value<string>("error");
- // }
- // catch (Exception ex)
- // {
- // Logger.LogTrace(ex, nameof(HwiProcessManager));
- // }
- // if (err != null)
- // {
- // throw new IOException(err);
- // }
-
- // return jToken;
- //}
- }
- }
- finally
- {
- mutex.ReleaseMutex();
}
}
}

MODIFY
WalletWasabi/Nito/AsyncEx/AsyncMutex.cs
WalletWasabi/Nito/AsyncEx/AsyncMutex.cs
@@ -11,7 +11,6 @@ namespace Nito.AsyncEx
public class AsyncMutex
{
private string ShortName { get; set; }
- private int PollInterval { get; }
private string FullName { get; set; }
private Mutex Mutex { get; set; }
@@ -24,7 +23,7 @@ namespace Nito.AsyncEx
private static object AsyncLocksLock { get; } = new object();
- public AsyncMutex(string name, int pollInterval = 100)
+ public AsyncMutex(string name)
{
// https://docs.microsoft.com/en-us/dotnet/api/system.threading.mutex?view=netframework-4.8
// On a server that is running Terminal Services, a named system mutex can have two levels of visibility.
@@ -36,7 +35,6 @@ namespace Nito.AsyncEx
// and both are visible to all processes in the terminal server session.
// That is, the prefix names "Global\" and "Local\" describe the scope of the mutex name relative to terminal server sessions, not relative to processes.
ShortName = name;
- PollInterval = pollInterval;
FullName = $"Global\\4AA0E5A2-A94F-4B92-B962-F2BBC7A68323-{name}";
Mutex = null;
@@ -55,7 +53,7 @@ namespace Nito.AsyncEx
}
}
- public async Task<IDisposable> LockAsync(CancellationToken cancellationToken = default)
+ public async Task<IDisposable> LockAsync(CancellationToken cancellationToken = default, int pollInterval = 100)
{
Exception inner = null;
@@ -75,7 +73,7 @@ namespace Nito.AsyncEx
{
return new Key(this);
}
- await Task.Delay(PollInterval, cancellationToken);
+ await Task.Delay(pollInterval, cancellationToken);
}
// Let it go.
}
@@ -97,9 +95,7 @@ namespace Nito.AsyncEx
// Let it go.
}
- Mutex?.Dispose();
- Mutex = null;
- AsyncLock.ReleaseLock();
+ ReleaseLock();
throw new IOException($"Couldn't acquire system wide mutex on {ShortName}", inner);
}

Use AsyncMutex on HWI