From 8c6c1ef0a3665f78a2e8961fa8fe2f2300e2dbbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20M=C3=A1rkus?= Date: Thu, 18 Jan 2024 12:28:51 +0100 Subject: [PATCH 01/14] Upgrade to .NET 8 --- .../Lombiq.NodeJs.Extensions.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lombiq.NodeJs.Extensions.Tests/Lombiq.NodeJs.Extensions.Tests.csproj b/Lombiq.NodeJs.Extensions.Tests/Lombiq.NodeJs.Extensions.Tests.csproj index a3bfcf56..f3b53381 100644 --- a/Lombiq.NodeJs.Extensions.Tests/Lombiq.NodeJs.Extensions.Tests.csproj +++ b/Lombiq.NodeJs.Extensions.Tests/Lombiq.NodeJs.Extensions.Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 From 038b00b15592d2e596a015d4b40085bbeaaf0da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20M=C3=A1rkus?= Date: Thu, 18 Jan 2024 17:58:18 +0100 Subject: [PATCH 02/14] Addressing IDE0290 and other warnings. --- .../CustomExecTasks/ExclusiveMutex.cs | 18 ++++++------------ .../CustomExecTasks/SharedMutex.cs | 18 ++++++------------ 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs index fe65b4a9..3c153719 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs @@ -4,20 +4,14 @@ namespace Lombiq.NodeJs.Extensions.CustomExecTasks; -public class ExclusiveMutex +public class ExclusiveMutex(string mutexName, TimeSpan timeout) { - private readonly string _mutexName; - private readonly TimeSpan _timeout; + private readonly string _mutexName = mutexName; + private readonly TimeSpan _timeout = timeout; public int RetryIntervalMs { get; set; } = 100; public int WaitTimeMs { get; set; } = 1000; - public ExclusiveMutex(string mutexName, TimeSpan timeout) - { - _mutexName = mutexName; - _timeout = timeout; - } - public bool Execute( Func functionToExecute, Action logWait = null, Action logError = null) { @@ -34,7 +28,7 @@ public bool Execute( try { logWait?.Invoke( - "Acquired exclusive access to {0} after {1}.", new object[] { _mutexName, stopwatch.Elapsed }); + "Acquired exclusive access to {0} after {1}.", [_mutexName, stopwatch.Elapsed]); return functionToExecute(); } finally @@ -44,11 +38,11 @@ public bool Execute( } } - logWait?.Invoke("#{0} Waiting for exclusive access to {1}.", new object[] { count++, _mutexName }); + logWait?.Invoke("#{0} Waiting for exclusive access to {1}.", [count++, _mutexName]); Thread.Sleep(RetryIntervalMs); } - logError?.Invoke("Failed to acquire exclusive access {0} in {1}.", new object[] { _mutexName, _timeout }); + logError?.Invoke("Failed to acquire exclusive access {0} in {1}.", [_mutexName, _timeout]); return false; } } diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs index 4b9fee7f..fa9aacc3 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs @@ -4,19 +4,13 @@ namespace Lombiq.NodeJs.Extensions.CustomExecTasks; -public class SharedMutex +public class SharedMutex(string mutexName, TimeSpan timeout) { - private readonly string _mutexName; - private readonly TimeSpan _timeout; + private readonly string _mutexName = mutexName; + private readonly TimeSpan _timeout = timeout; public int RetryIntervalMs { get; set; } = 100; - public SharedMutex(string mutexName, TimeSpan timeout) - { - _mutexName = mutexName; - _timeout = timeout; - } - public bool Execute( Func functionToExecute, Action logWait = null, Action logError = null) { @@ -32,18 +26,18 @@ public bool Execute( // it is currently not "locked", i.e. in exclusive usage. mutex.ReleaseMutex(); - logWait?.Invoke("Acquired shared access to {0} in {1}.", new object[] { _mutexName, stopwatch.Elapsed }); + logWait?.Invoke("Acquired shared access to {0} in {1}.", [_mutexName, stopwatch.Elapsed]); return functionToExecute(); } } - logWait?.Invoke("#{0} Waiting for shared access to {1}.", new object[] { count++, _mutexName }); + logWait?.Invoke("#{0} Waiting for shared access to {1}.", [count++, _mutexName]); Thread.Sleep(RetryIntervalMs); } - logError?.Invoke("Failed to acquire {0} in {1}.", new object[] { _mutexName, _timeout }); + logError?.Invoke("Failed to acquire {0} in {1}.", [_mutexName, _timeout]); return false; } } From 1847f740bdfcde73ce5b3a7db45aad7f60b2b9d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20M=C3=A1rkus?= Date: Sat, 20 Jan 2024 12:16:07 +0100 Subject: [PATCH 03/14] Addressing warnings. --- .../MutexReaderWriterTest.cs | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/Lombiq.NodeJs.Extensions.Tests/MutexReaderWriterTest.cs b/Lombiq.NodeJs.Extensions.Tests/MutexReaderWriterTest.cs index d7f44f00..636a23a1 100644 --- a/Lombiq.NodeJs.Extensions.Tests/MutexReaderWriterTest.cs +++ b/Lombiq.NodeJs.Extensions.Tests/MutexReaderWriterTest.cs @@ -11,7 +11,7 @@ namespace Lombiq.NodeJs.Extensions.Tests; -public class MutexReaderWriterTest +public class MutexReaderWriterTest(ITestOutputHelper testOutputHelper) { private const string MutexName = nameof(MutexReaderWriterTest); private const int ReaderWriterCount = 10; @@ -22,10 +22,6 @@ public class MutexReaderWriterTest // of this limit is to assert that all threads will successfully run within a predictable, constant time. private const int MaxWaitTimeMs = 15000; - private readonly ITestOutputHelper _testOutputHelper; - - public MutexReaderWriterTest(ITestOutputHelper testOutputHelper) => _testOutputHelper = testOutputHelper; - [Fact] public Task SyncReadersAndWriters() { @@ -44,7 +40,7 @@ public Task SyncReadersAndWriters() private Action CreateReaderAction(int actionIndex, TimeSpan timeout) => () => { - _testOutputHelper.WriteLine("-> Reader {0}", actionIndex); + testOutputHelper.WriteLine("-> Reader {0}", actionIndex); // Add some random wait time to mix reader and writer threads. Thread.Sleep(RandomNumberGenerator.GetInt32(1000)); @@ -52,20 +48,20 @@ private Action CreateReaderAction(int actionIndex, TimeSpan timeout) => () => .Execute( () => { - _testOutputHelper.WriteLine(" - Reader {0} executing", actionIndex); + testOutputHelper.WriteLine(" - Reader {0} executing", actionIndex); Thread.Sleep(GetRandomTimeoutAround(ReaderExecutionTimeMs)); return true; }, - (message, mutexName) => _testOutputHelper.WriteLine( + (message, mutexName) => testOutputHelper.WriteLine( " - Reader {0} waiting: {1}", actionIndex, string.Format(CultureInfo.InvariantCulture, message, mutexName))) .ShouldBeTrue(); - _testOutputHelper.WriteLine("<- Reader {0}", actionIndex); + testOutputHelper.WriteLine("<- Reader {0}", actionIndex); }; private Action CreateWriterAction(int actionIndex, TimeSpan timeout) => () => { - _testOutputHelper.WriteLine("-> Writer {0}", actionIndex); + testOutputHelper.WriteLine("-> Writer {0}", actionIndex); // Add some random wait time to mix reader and writer threads. Thread.Sleep(RandomNumberGenerator.GetInt32(1000)); @@ -73,15 +69,15 @@ private Action CreateWriterAction(int actionIndex, TimeSpan timeout) => () => .Execute( () => { - _testOutputHelper.WriteLine(" + Writer {0} executing", 0); + testOutputHelper.WriteLine(" + Writer {0} executing", 0); Thread.Sleep(GetRandomTimeoutAround(WriterExecutionTimeMs)); return true; }, - (message, mutexName) => _testOutputHelper.WriteLine( + (message, mutexName) => testOutputHelper.WriteLine( " + Writer {0} waiting: {1}", actionIndex, string.Format(CultureInfo.InvariantCulture, message, mutexName))) .ShouldBeTrue(); - _testOutputHelper.WriteLine("<- Writer {0}", actionIndex); + testOutputHelper.WriteLine("<- Writer {0}", actionIndex); }; private static int GetRandomTimeoutAround(int timeoutMs) => From f7697fe46dc74ccd973fb707ed790f24126ae802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20M=C3=A1rkus?= Date: Mon, 29 Jan 2024 14:07:59 +0100 Subject: [PATCH 04/14] Removing fields. --- .../CustomExecTasks/SharedMutex.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs index fa9aacc3..cf44c6b6 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs @@ -6,9 +6,6 @@ namespace Lombiq.NodeJs.Extensions.CustomExecTasks; public class SharedMutex(string mutexName, TimeSpan timeout) { - private readonly string _mutexName = mutexName; - private readonly TimeSpan _timeout = timeout; - public int RetryIntervalMs { get; set; } = 100; public bool Execute( @@ -16,9 +13,9 @@ public bool Execute( { var count = 1; var stopwatch = Stopwatch.StartNew(); - while (stopwatch.Elapsed <= _timeout) + while (stopwatch.Elapsed <= timeout) { - using (var mutex = new Mutex(initiallyOwned: false, _mutexName)) + using (var mutex = new Mutex(initiallyOwned: false, mutexName)) { if (mutex.WaitOne(0)) { @@ -26,18 +23,18 @@ public bool Execute( // it is currently not "locked", i.e. in exclusive usage. mutex.ReleaseMutex(); - logWait?.Invoke("Acquired shared access to {0} in {1}.", [_mutexName, stopwatch.Elapsed]); + logWait?.Invoke("Acquired shared access to {0} in {1}.", [mutexName, stopwatch.Elapsed]); return functionToExecute(); } } - logWait?.Invoke("#{0} Waiting for shared access to {1}.", [count++, _mutexName]); + logWait?.Invoke("#{0} Waiting for shared access to {1}.", [count++, mutexName]); Thread.Sleep(RetryIntervalMs); } - logError?.Invoke("Failed to acquire {0} in {1}.", [_mutexName, _timeout]); + logError?.Invoke("Failed to acquire {0} in {1}.", [mutexName, timeout]); return false; } } From ba3e490e6d82898c2a8b4ff69fb8df4089a6dc3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20M=C3=A1rkus?= Date: Mon, 29 Jan 2024 14:08:47 +0100 Subject: [PATCH 05/14] Removing fields. --- .../CustomExecTasks/ExclusiveMutex.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs index 3c153719..0a3a28d1 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs @@ -6,9 +6,6 @@ namespace Lombiq.NodeJs.Extensions.CustomExecTasks; public class ExclusiveMutex(string mutexName, TimeSpan timeout) { - private readonly string _mutexName = mutexName; - private readonly TimeSpan _timeout = timeout; - public int RetryIntervalMs { get; set; } = 100; public int WaitTimeMs { get; set; } = 1000; @@ -17,9 +14,9 @@ public bool Execute( { var count = 1; var stopwatch = Stopwatch.StartNew(); - while (stopwatch.Elapsed <= _timeout) + while (stopwatch.Elapsed <= timeout) { - using (var mutex = new Mutex(initiallyOwned: false, _mutexName, out var createdNew)) + using (var mutex = new Mutex(initiallyOwned: false, mutexName, out var createdNew)) { // We only try to acquire the mutex in case it was freshly created, because that means that no other // processes are currently using it, including in a shared way. @@ -28,7 +25,7 @@ public bool Execute( try { logWait?.Invoke( - "Acquired exclusive access to {0} after {1}.", [_mutexName, stopwatch.Elapsed]); + "Acquired exclusive access to {0} after {1}.", [mutexName, stopwatch.Elapsed]); return functionToExecute(); } finally @@ -38,11 +35,11 @@ public bool Execute( } } - logWait?.Invoke("#{0} Waiting for exclusive access to {1}.", [count++, _mutexName]); + logWait?.Invoke("#{0} Waiting for exclusive access to {1}.", [count++, mutexName]); Thread.Sleep(RetryIntervalMs); } - logError?.Invoke("Failed to acquire exclusive access {0} in {1}.", [_mutexName, _timeout]); + logError?.Invoke("Failed to acquire exclusive access {0} in {1}.", [mutexName, timeout]); return false; } } From 4c24f20815fc09dc814db6b297dfb68761d8fe07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20M=C3=A1rkus?= Date: Tue, 30 Jan 2024 10:41:30 +0100 Subject: [PATCH 06/14] Revert "Addressing IDE0290 and other warnings." This reverts commit 038b00b15592d2e596a015d4b40085bbeaaf0da5. # Conflicts: # Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs # Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs Revert "Addressing warnings." This reverts commit 1847f740bdfcde73ce5b3a7db45aad7f60b2b9d4. --- .../MutexReaderWriterTest.cs | 22 +++++++++++-------- .../CustomExecTasks/ExclusiveMutex.cs | 16 ++++++++++---- .../CustomExecTasks/SharedMutex.cs | 16 ++++++++++---- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/Lombiq.NodeJs.Extensions.Tests/MutexReaderWriterTest.cs b/Lombiq.NodeJs.Extensions.Tests/MutexReaderWriterTest.cs index 636a23a1..d7f44f00 100644 --- a/Lombiq.NodeJs.Extensions.Tests/MutexReaderWriterTest.cs +++ b/Lombiq.NodeJs.Extensions.Tests/MutexReaderWriterTest.cs @@ -11,7 +11,7 @@ namespace Lombiq.NodeJs.Extensions.Tests; -public class MutexReaderWriterTest(ITestOutputHelper testOutputHelper) +public class MutexReaderWriterTest { private const string MutexName = nameof(MutexReaderWriterTest); private const int ReaderWriterCount = 10; @@ -22,6 +22,10 @@ public class MutexReaderWriterTest(ITestOutputHelper testOutputHelper) // of this limit is to assert that all threads will successfully run within a predictable, constant time. private const int MaxWaitTimeMs = 15000; + private readonly ITestOutputHelper _testOutputHelper; + + public MutexReaderWriterTest(ITestOutputHelper testOutputHelper) => _testOutputHelper = testOutputHelper; + [Fact] public Task SyncReadersAndWriters() { @@ -40,7 +44,7 @@ public Task SyncReadersAndWriters() private Action CreateReaderAction(int actionIndex, TimeSpan timeout) => () => { - testOutputHelper.WriteLine("-> Reader {0}", actionIndex); + _testOutputHelper.WriteLine("-> Reader {0}", actionIndex); // Add some random wait time to mix reader and writer threads. Thread.Sleep(RandomNumberGenerator.GetInt32(1000)); @@ -48,20 +52,20 @@ private Action CreateReaderAction(int actionIndex, TimeSpan timeout) => () => .Execute( () => { - testOutputHelper.WriteLine(" - Reader {0} executing", actionIndex); + _testOutputHelper.WriteLine(" - Reader {0} executing", actionIndex); Thread.Sleep(GetRandomTimeoutAround(ReaderExecutionTimeMs)); return true; }, - (message, mutexName) => testOutputHelper.WriteLine( + (message, mutexName) => _testOutputHelper.WriteLine( " - Reader {0} waiting: {1}", actionIndex, string.Format(CultureInfo.InvariantCulture, message, mutexName))) .ShouldBeTrue(); - testOutputHelper.WriteLine("<- Reader {0}", actionIndex); + _testOutputHelper.WriteLine("<- Reader {0}", actionIndex); }; private Action CreateWriterAction(int actionIndex, TimeSpan timeout) => () => { - testOutputHelper.WriteLine("-> Writer {0}", actionIndex); + _testOutputHelper.WriteLine("-> Writer {0}", actionIndex); // Add some random wait time to mix reader and writer threads. Thread.Sleep(RandomNumberGenerator.GetInt32(1000)); @@ -69,15 +73,15 @@ private Action CreateWriterAction(int actionIndex, TimeSpan timeout) => () => .Execute( () => { - testOutputHelper.WriteLine(" + Writer {0} executing", 0); + _testOutputHelper.WriteLine(" + Writer {0} executing", 0); Thread.Sleep(GetRandomTimeoutAround(WriterExecutionTimeMs)); return true; }, - (message, mutexName) => testOutputHelper.WriteLine( + (message, mutexName) => _testOutputHelper.WriteLine( " + Writer {0} waiting: {1}", actionIndex, string.Format(CultureInfo.InvariantCulture, message, mutexName))) .ShouldBeTrue(); - testOutputHelper.WriteLine("<- Writer {0}", actionIndex); + _testOutputHelper.WriteLine("<- Writer {0}", actionIndex); }; private static int GetRandomTimeoutAround(int timeoutMs) => diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs index 0a3a28d1..d1f73785 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs @@ -4,11 +4,19 @@ namespace Lombiq.NodeJs.Extensions.CustomExecTasks; -public class ExclusiveMutex(string mutexName, TimeSpan timeout) +public class ExclusiveMutex { + private readonly string _mutexName; + private readonly TimeSpan _timeout; public int RetryIntervalMs { get; set; } = 100; public int WaitTimeMs { get; set; } = 1000; + public ExclusiveMutex(string mutexName, TimeSpan timeout) + { + _mutexName = mutexName; + _timeout = timeout; + } + public bool Execute( Func functionToExecute, Action logWait = null, Action logError = null) { @@ -25,7 +33,7 @@ public bool Execute( try { logWait?.Invoke( - "Acquired exclusive access to {0} after {1}.", [mutexName, stopwatch.Elapsed]); + "Acquired exclusive access to {0} after {1}.", new object[] { _mutexName, stopwatch.Elapsed }); return functionToExecute(); } finally @@ -35,11 +43,11 @@ public bool Execute( } } - logWait?.Invoke("#{0} Waiting for exclusive access to {1}.", [count++, mutexName]); + logWait?.Invoke("#{0} Waiting for exclusive access to {1}.", new object[] { count++, _mutexName }); Thread.Sleep(RetryIntervalMs); } - logError?.Invoke("Failed to acquire exclusive access {0} in {1}.", [mutexName, timeout]); + logError?.Invoke("Failed to acquire exclusive access {0} in {1}.", new object[] { _mutexName, _timeout }); return false; } } diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs index cf44c6b6..1d91d8fb 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs @@ -4,10 +4,18 @@ namespace Lombiq.NodeJs.Extensions.CustomExecTasks; -public class SharedMutex(string mutexName, TimeSpan timeout) +public class SharedMutex { + private readonly string _mutexName; + private readonly TimeSpan _timeout; public int RetryIntervalMs { get; set; } = 100; + public SharedMutex(string mutexName, TimeSpan timeout) + { + _mutexName = mutexName; + _timeout = timeout; + } + public bool Execute( Func functionToExecute, Action logWait = null, Action logError = null) { @@ -23,18 +31,18 @@ public bool Execute( // it is currently not "locked", i.e. in exclusive usage. mutex.ReleaseMutex(); - logWait?.Invoke("Acquired shared access to {0} in {1}.", [mutexName, stopwatch.Elapsed]); + logWait?.Invoke("Acquired shared access to {0} in {1}.", new object[] { _mutexName, stopwatch.Elapsed }); return functionToExecute(); } } - logWait?.Invoke("#{0} Waiting for shared access to {1}.", [count++, mutexName]); + logWait?.Invoke("#{0} Waiting for shared access to {1}.", new object[] { count++, _mutexName }); Thread.Sleep(RetryIntervalMs); } - logError?.Invoke("Failed to acquire {0} in {1}.", [mutexName, timeout]); + logError?.Invoke("Failed to acquire {0} in {1}.", new object[] { _mutexName, _timeout }); return false; } } From a9cd268c6423a5f4f4b34a5573d4d6cc98e3499b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20M=C3=A1rkus?= Date: Tue, 30 Jan 2024 10:43:12 +0100 Subject: [PATCH 07/14] Fixing. --- .../CustomExecTasks/ExclusiveMutex.cs | 10 +++++----- .../CustomExecTasks/SharedMutex.cs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs index d1f73785..aa079cf0 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs @@ -22,9 +22,9 @@ public bool Execute( { var count = 1; var stopwatch = Stopwatch.StartNew(); - while (stopwatch.Elapsed <= timeout) + while (stopwatch.Elapsed <= _timeout) { - using (var mutex = new Mutex(initiallyOwned: false, mutexName, out var createdNew)) + using (var mutex = new Mutex(initiallyOwned: false, _mutexName, out var createdNew)) { // We only try to acquire the mutex in case it was freshly created, because that means that no other // processes are currently using it, including in a shared way. @@ -33,7 +33,7 @@ public bool Execute( try { logWait?.Invoke( - "Acquired exclusive access to {0} after {1}.", new object[] { _mutexName, stopwatch.Elapsed }); + "Acquired exclusive access to {0} after {1}.", [_mutexName, stopwatch.Elapsed]); return functionToExecute(); } finally @@ -43,11 +43,11 @@ public bool Execute( } } - logWait?.Invoke("#{0} Waiting for exclusive access to {1}.", new object[] { count++, _mutexName }); + logWait?.Invoke("#{0} Waiting for exclusive access to {1}.", [count++, _mutexName]); Thread.Sleep(RetryIntervalMs); } - logError?.Invoke("Failed to acquire exclusive access {0} in {1}.", new object[] { _mutexName, _timeout }); + logError?.Invoke("Failed to acquire exclusive access {0} in {1}.", [_mutexName, _timeout]); return false; } } diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs index 1d91d8fb..a303b7a5 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs @@ -21,9 +21,9 @@ public bool Execute( { var count = 1; var stopwatch = Stopwatch.StartNew(); - while (stopwatch.Elapsed <= timeout) + while (stopwatch.Elapsed <= _timeout) { - using (var mutex = new Mutex(initiallyOwned: false, mutexName)) + using (var mutex = new Mutex(initiallyOwned: false, _mutexName)) { if (mutex.WaitOne(0)) { @@ -31,18 +31,18 @@ public bool Execute( // it is currently not "locked", i.e. in exclusive usage. mutex.ReleaseMutex(); - logWait?.Invoke("Acquired shared access to {0} in {1}.", new object[] { _mutexName, stopwatch.Elapsed }); + logWait?.Invoke("Acquired shared access to {0} in {1}.", [_mutexName, stopwatch.Elapsed]); return functionToExecute(); } } - logWait?.Invoke("#{0} Waiting for shared access to {1}.", new object[] { count++, _mutexName }); + logWait?.Invoke("#{0} Waiting for shared access to {1}.", [count++, _mutexName]); Thread.Sleep(RetryIntervalMs); } - logError?.Invoke("Failed to acquire {0} in {1}.", new object[] { _mutexName, _timeout }); + logError?.Invoke("Failed to acquire {0} in {1}.", [_mutexName, _timeout]); return false; } } From 6a70c6e634e0494b8847452aac0262356534b2a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20M=C3=A1rkus?= Date: Wed, 31 Jan 2024 14:33:11 +0100 Subject: [PATCH 08/14] Adding line. --- Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs index aa079cf0..7fd6cbc1 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs @@ -8,6 +8,7 @@ public class ExclusiveMutex { private readonly string _mutexName; private readonly TimeSpan _timeout; + public int RetryIntervalMs { get; set; } = 100; public int WaitTimeMs { get; set; } = 1000; From 16e602053944a4a1a90ceae2c81f5e869a3c6ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20M=C3=A1rkus?= Date: Wed, 31 Jan 2024 14:33:42 +0100 Subject: [PATCH 09/14] Adding line. --- Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs index a303b7a5..3804a3ec 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs @@ -8,6 +8,7 @@ public class SharedMutex { private readonly string _mutexName; private readonly TimeSpan _timeout; + public int RetryIntervalMs { get; set; } = 100; public SharedMutex(string mutexName, TimeSpan timeout) From abd32e5a810ad893adda4af9ec50d6099a283c2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20M=C3=A1rkus?= Date: Wed, 14 Feb 2024 14:25:02 +0100 Subject: [PATCH 10/14] Using LangVersion 7.3 in NetStandard2.0 NodeJsExtensions. --- .../CustomExecTasks/ExclusiveMutex.cs | 71 +++++----- .../CustomExecTasks/ExecWithMutex.cs | 122 ++++++++++-------- .../CustomExecTasks/MutexAccess.cs | 13 +- .../CustomExecTasks/SharedMutex.cs | 61 ++++----- .../Lombiq.NodeJs.Extensions.csproj | 1 + 5 files changed, 140 insertions(+), 128 deletions(-) diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs index 7fd6cbc1..6fc8fc9d 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExclusiveMutex.cs @@ -2,53 +2,54 @@ using System.Diagnostics; using System.Threading; -namespace Lombiq.NodeJs.Extensions.CustomExecTasks; - -public class ExclusiveMutex +namespace Lombiq.NodeJs.Extensions.CustomExecTasks { - private readonly string _mutexName; - private readonly TimeSpan _timeout; + public class ExclusiveMutex + { + private readonly string _mutexName; + private readonly TimeSpan _timeout; - public int RetryIntervalMs { get; set; } = 100; - public int WaitTimeMs { get; set; } = 1000; + public int RetryIntervalMs { get; set; } = 100; + public int WaitTimeMs { get; set; } = 1000; - public ExclusiveMutex(string mutexName, TimeSpan timeout) - { - _mutexName = mutexName; - _timeout = timeout; - } + public ExclusiveMutex(string mutexName, TimeSpan timeout) + { + _mutexName = mutexName; + _timeout = timeout; + } - public bool Execute( - Func functionToExecute, Action logWait = null, Action logError = null) - { - var count = 1; - var stopwatch = Stopwatch.StartNew(); - while (stopwatch.Elapsed <= _timeout) + public bool Execute( + Func functionToExecute, Action logWait = null, Action logError = null) { - using (var mutex = new Mutex(initiallyOwned: false, _mutexName, out var createdNew)) + var count = 1; + var stopwatch = Stopwatch.StartNew(); + while (stopwatch.Elapsed <= _timeout) { - // We only try to acquire the mutex in case it was freshly created, because that means that no other - // processes are currently using it, including in a shared way. - if (createdNew && mutex.WaitOne(WaitTimeMs)) + using (var mutex = new Mutex(initiallyOwned: false, _mutexName, out var createdNew)) { - try - { - logWait?.Invoke( - "Acquired exclusive access to {0} after {1}.", [_mutexName, stopwatch.Elapsed]); - return functionToExecute(); - } - finally + // We only try to acquire the mutex in case it was freshly created, because that means that no other + // processes are currently using it, including in a shared way. + if (createdNew && mutex.WaitOne(WaitTimeMs)) { - mutex.ReleaseMutex(); + try + { + logWait?.Invoke( + "Acquired exclusive access to {0} after {1}.", new object[] { _mutexName, stopwatch.Elapsed }); + return functionToExecute(); + } + finally + { + mutex.ReleaseMutex(); + } } } + + logWait?.Invoke("#{0} Waiting for exclusive access to {1}.", new object[] { count++, _mutexName }); + Thread.Sleep(RetryIntervalMs); } - logWait?.Invoke("#{0} Waiting for exclusive access to {1}.", [count++, _mutexName]); - Thread.Sleep(RetryIntervalMs); + logError?.Invoke("Failed to acquire exclusive access {0} in {1}.", new object[] { _mutexName, _timeout }); + return false; } - - logError?.Invoke("Failed to acquire exclusive access {0} in {1}.", [_mutexName, _timeout]); - return false; } } diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExecWithMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExecWithMutex.cs index 70762f2e..63a6a461 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExecWithMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExecWithMutex.cs @@ -3,69 +3,77 @@ using System; using System.Threading; -namespace Lombiq.NodeJs.Extensions.CustomExecTasks; - -/// -/// An Exec task, wrapped around a critical section. -/// -/// -/// -/// We want to synchronize many shared readers and exclusive writers. Here's how to do that using a . -/// -/// -/// -/// -/// Any reader process simply creates a Mutex instance without calling WaitOne() for the time of execution. This will -/// signal to the writer, that at least one reader is already relying on that Mutex. At the same, this approach still -/// allows other readers to proceed in parallel. To assure that no writer is currently using the Mutex, any reader will -/// call WaitOne(0) on it. If that call fails, it means that a writer is currently using the Mutex, and we have to wait. -/// -/// -/// -/// -/// The writer process first creates a Mutex instance and checks whether it was created just now. If yes, it will -/// process to "lock" the Mutex by calling WaitOne() on it. If not, it means that a reader is currently processing and -/// the writer needs to retry (after a short wait time). -/// -/// -/// -/// -public class ExecWithMutex : Exec +namespace Lombiq.NodeJs.Extensions.CustomExecTasks { - public MutexAccess MutexAccessToUse => - Enum.TryParse(Access, ignoreCase: true, out MutexAccess access) ? access : MutexAccess.Undefined; - - public TimeSpan TimeoutSpan => TimeSpan.FromSeconds(TimeoutSeconds); - /// - /// Gets or sets the mutex name. + /// An Exec task, wrapped around a critical section. /// - [Required] - public string MutexName { get; set; } + /// + /// + /// We want to synchronize many shared readers and exclusive writers. Here's how to do that using a . + /// + /// + /// + /// + /// Any reader process simply creates a Mutex instance without calling WaitOne() for the time of execution. This will + /// signal to the writer, that at least one reader is already relying on that Mutex. At the same, this approach still + /// allows other readers to proceed in parallel. To assure that no writer is currently using the Mutex, any reader will + /// call WaitOne(0) on it. If that call fails, it means that a writer is currently using the Mutex, and we have to wait. + /// + /// + /// + /// + /// The writer process first creates a Mutex instance and checks whether it was created just now. If yes, it will + /// process to "lock" the Mutex by calling WaitOne() on it. If not, it means that a reader is currently processing and + /// the writer needs to retry (after a short wait time). + /// + /// + /// + /// + public class ExecWithMutex : Exec + { + public MutexAccess MutexAccessToUse => + Enum.TryParse(Access, ignoreCase: true, out MutexAccess access) ? access : MutexAccess.Undefined; - /// - /// Gets or sets the level to use on this Mutex. - /// - [Required] - public string Access { get; set; } + public TimeSpan TimeoutSpan => TimeSpan.FromSeconds(TimeoutSeconds); - /// - /// Gets or sets the maximum number of seconds that any thread should wait for the given mutex. - /// - [Required] - public int TimeoutSeconds { get; set; } + /// + /// Gets or sets the mutex name. + /// + [Required] + public string MutexName { get; set; } - /// - public override bool Execute() => MutexAccessToUse switch - { - MutexAccess.Shared => new SharedMutex(MutexName, TimeoutSpan) - .Execute(base.Execute, Log.LogMessage, Log.LogError), + /// + /// Gets or sets the level to use on this Mutex. + /// + [Required] + public string Access { get; set; } + + /// + /// Gets or sets the maximum number of seconds that any thread should wait for the given mutex. + /// + [Required] + public int TimeoutSeconds { get; set; } - MutexAccess.Exclusive => new ExclusiveMutex(MutexName, TimeoutSpan) - .Execute(base.Execute, Log.LogMessage, Log.LogError), + /// + public override bool Execute() + { + switch (MutexAccessToUse) + { + case MutexAccess.Shared: + return new SharedMutex(MutexName, TimeoutSpan) + .Execute(base.Execute, Log.LogMessage, Log.LogError); - _ => throw new ArgumentException( - $"The \"{nameof(Access)}\" attribute on the {nameof(ExecWithMutex)} task needs to be set to either " + - $"\"{nameof(MutexAccess.Shared)}\" or \"{nameof(MutexAccess.Exclusive)}\"!"), - }; + case MutexAccess.Exclusive: + return new ExclusiveMutex(MutexName, TimeoutSpan) + .Execute(base.Execute, Log.LogMessage, Log.LogError); + case MutexAccess.Undefined: + return false; + default: + throw new ArgumentException( + $"The \"{nameof(Access)}\" attribute on the {nameof(ExecWithMutex)} task needs to be set to either " + + $"\"{nameof(MutexAccess.Shared)}\" or \"{nameof(MutexAccess.Exclusive)}\"!"); + } + } + } } diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/MutexAccess.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/MutexAccess.cs index 0a6ebdf6..7c9f9107 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/MutexAccess.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/MutexAccess.cs @@ -1,8 +1,9 @@ -namespace Lombiq.NodeJs.Extensions.CustomExecTasks; - -public enum MutexAccess +namespace Lombiq.NodeJs.Extensions.CustomExecTasks { - Undefined, - Exclusive, - Shared, + public enum MutexAccess + { + Undefined, + Exclusive, + Shared, + } } diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs index 3804a3ec..66405396 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs @@ -2,48 +2,49 @@ using System.Diagnostics; using System.Threading; -namespace Lombiq.NodeJs.Extensions.CustomExecTasks; - -public class SharedMutex +namespace Lombiq.NodeJs.Extensions.CustomExecTasks { - private readonly string _mutexName; - private readonly TimeSpan _timeout; + public class SharedMutex + { + private readonly string _mutexName; + private readonly TimeSpan _timeout; - public int RetryIntervalMs { get; set; } = 100; + public int RetryIntervalMs { get; set; } = 100; - public SharedMutex(string mutexName, TimeSpan timeout) - { - _mutexName = mutexName; - _timeout = timeout; - } + public SharedMutex(string mutexName, TimeSpan timeout) + { + _mutexName = mutexName; + _timeout = timeout; + } - public bool Execute( - Func functionToExecute, Action logWait = null, Action logError = null) - { - var count = 1; - var stopwatch = Stopwatch.StartNew(); - while (stopwatch.Elapsed <= _timeout) + public bool Execute( + Func functionToExecute, Action logWait = null, Action logError = null) { - using (var mutex = new Mutex(initiallyOwned: false, _mutexName)) + var count = 1; + var stopwatch = Stopwatch.StartNew(); + while (stopwatch.Elapsed <= _timeout) { - if (mutex.WaitOne(0)) + using (var mutex = new Mutex(initiallyOwned: false, _mutexName)) { - // Release the mutex asap because we don't need it for execution. We only needed it to verify that - // it is currently not "locked", i.e. in exclusive usage. - mutex.ReleaseMutex(); + if (mutex.WaitOne(0)) + { + // Release the mutex asap because we don't need it for execution. We only needed it to verify that + // it is currently not "locked", i.e. in exclusive usage. + mutex.ReleaseMutex(); - logWait?.Invoke("Acquired shared access to {0} in {1}.", [_mutexName, stopwatch.Elapsed]); + logWait?.Invoke("Acquired shared access to {0} in {1}.", new object[] { _mutexName, stopwatch.Elapsed }); - return functionToExecute(); + return functionToExecute(); + } } - } - logWait?.Invoke("#{0} Waiting for shared access to {1}.", [count++, _mutexName]); + logWait?.Invoke("#{0} Waiting for shared access to {1}.", new object[] { count++, _mutexName }); - Thread.Sleep(RetryIntervalMs); - } + Thread.Sleep(RetryIntervalMs); + } - logError?.Invoke("Failed to acquire {0} in {1}.", [_mutexName, _timeout]); - return false; + logError?.Invoke("Failed to acquire {0} in {1}.", new object[] { _mutexName, _timeout }); + return false; + } } } diff --git a/Lombiq.NodeJs.Extensions/Lombiq.NodeJs.Extensions.csproj b/Lombiq.NodeJs.Extensions/Lombiq.NodeJs.Extensions.csproj index 676dfe4f..ac2248cb 100644 --- a/Lombiq.NodeJs.Extensions/Lombiq.NodeJs.Extensions.csproj +++ b/Lombiq.NodeJs.Extensions/Lombiq.NodeJs.Extensions.csproj @@ -2,6 +2,7 @@ netstandard2.0 + 7.3 .git*;$(DefaultItemExcludes) false From 134c058d47b2c4ea69ea7937de0038cbf270a163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20M=C3=A1rkus?= Date: Wed, 14 Feb 2024 17:07:29 +0100 Subject: [PATCH 11/14] Adding LangVersion to Samples project aswell. --- .../Lombiq.NodeJs.Extensions.Samples.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Lombiq.NodeJs.Extensions.Samples/Lombiq.NodeJs.Extensions.Samples.csproj b/Lombiq.NodeJs.Extensions.Samples/Lombiq.NodeJs.Extensions.Samples.csproj index d81e1f7e..94896187 100644 --- a/Lombiq.NodeJs.Extensions.Samples/Lombiq.NodeJs.Extensions.Samples.csproj +++ b/Lombiq.NodeJs.Extensions.Samples/Lombiq.NodeJs.Extensions.Samples.csproj @@ -4,6 +4,7 @@ netstandard2.0 + 7.3 $(DefaultItemExcludes);.git*;node_modules\** From 953d31c53af2bd5c75a306f35d80a5b54956258b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20M=C3=A1rkus?= Date: Sat, 17 Feb 2024 12:19:17 +0100 Subject: [PATCH 12/14] Updating NodeJs.Extensions. --- .../Lombiq.NodeJs.Extensions.Samples.NuGet.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lombiq.NodeJs.Extensions.Samples.NuGet/Lombiq.NodeJs.Extensions.Samples.NuGet.csproj b/Lombiq.NodeJs.Extensions.Samples.NuGet/Lombiq.NodeJs.Extensions.Samples.NuGet.csproj index 0c3ad9c5..075391a0 100644 --- a/Lombiq.NodeJs.Extensions.Samples.NuGet/Lombiq.NodeJs.Extensions.Samples.NuGet.csproj +++ b/Lombiq.NodeJs.Extensions.Samples.NuGet/Lombiq.NodeJs.Extensions.Samples.NuGet.csproj @@ -19,7 +19,7 @@ - + From 11a1fcdd397ab0eeccab4dae3c6097a4f5cc7ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Tue, 20 Feb 2024 15:10:10 +0100 Subject: [PATCH 13/14] Comment about why we set LangVersion --- .../Lombiq.NodeJs.Extensions.Samples.csproj | 1 + Lombiq.NodeJs.Extensions/Lombiq.NodeJs.Extensions.csproj | 1 + 2 files changed, 2 insertions(+) diff --git a/Lombiq.NodeJs.Extensions.Samples/Lombiq.NodeJs.Extensions.Samples.csproj b/Lombiq.NodeJs.Extensions.Samples/Lombiq.NodeJs.Extensions.Samples.csproj index 94896187..3e40474f 100644 --- a/Lombiq.NodeJs.Extensions.Samples/Lombiq.NodeJs.Extensions.Samples.csproj +++ b/Lombiq.NodeJs.Extensions.Samples/Lombiq.NodeJs.Extensions.Samples.csproj @@ -4,6 +4,7 @@ netstandard2.0 + 7.3 $(DefaultItemExcludes);.git*;node_modules\** diff --git a/Lombiq.NodeJs.Extensions/Lombiq.NodeJs.Extensions.csproj b/Lombiq.NodeJs.Extensions/Lombiq.NodeJs.Extensions.csproj index ac2248cb..c524f8bf 100644 --- a/Lombiq.NodeJs.Extensions/Lombiq.NodeJs.Extensions.csproj +++ b/Lombiq.NodeJs.Extensions/Lombiq.NodeJs.Extensions.csproj @@ -2,6 +2,7 @@ netstandard2.0 + 7.3 .git*;$(DefaultItemExcludes) false From 96c28d79684081e0a6f66add637ad556de5ed5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Tue, 20 Feb 2024 15:10:16 +0100 Subject: [PATCH 14/14] Formatting --- .../CustomExecTasks/ExecWithMutex.cs | 13 +++++++------ .../CustomExecTasks/SharedMutex.cs | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExecWithMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExecWithMutex.cs index 63a6a461..b4fd08c7 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/ExecWithMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/ExecWithMutex.cs @@ -15,17 +15,18 @@ namespace Lombiq.NodeJs.Extensions.CustomExecTasks /// /// /// - /// Any reader process simply creates a Mutex instance without calling WaitOne() for the time of execution. This will - /// signal to the writer, that at least one reader is already relying on that Mutex. At the same, this approach still - /// allows other readers to proceed in parallel. To assure that no writer is currently using the Mutex, any reader will - /// call WaitOne(0) on it. If that call fails, it means that a writer is currently using the Mutex, and we have to wait. + /// Any reader process simply creates a Mutex instance without calling WaitOne() for the time of execution. This + /// will signal to the writer, that at least one reader is already relying on that Mutex. At the same, this approach + /// still allows other readers to proceed in parallel. To assure that no writer is currently using the Mutex, any + /// reader will call WaitOne(0) on it. If that call fails, it means that a writer is currently using the Mutex, and + /// we have to wait. /// /// /// /// /// The writer process first creates a Mutex instance and checks whether it was created just now. If yes, it will - /// process to "lock" the Mutex by calling WaitOne() on it. If not, it means that a reader is currently processing and - /// the writer needs to retry (after a short wait time). + /// process to "lock" the Mutex by calling WaitOne() on it. If not, it means that a reader is currently processing + /// and the writer needs to retry (after a short wait time). /// /// /// diff --git a/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs b/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs index 66405396..6b2ccf78 100644 --- a/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs +++ b/Lombiq.NodeJs.Extensions/CustomExecTasks/SharedMutex.cs @@ -28,8 +28,8 @@ public bool Execute( { if (mutex.WaitOne(0)) { - // Release the mutex asap because we don't need it for execution. We only needed it to verify that - // it is currently not "locked", i.e. in exclusive usage. + // Release the mutex asap because we don't need it for execution. We only needed it to verify + // that it is currently not "locked", i.e. in exclusive usage. mutex.ReleaseMutex(); logWait?.Invoke("Acquired shared access to {0} in {1}.", new object[] { _mutexName, stopwatch.Elapsed });