-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Its possible for the single instance manager to throw an exception on shutdown, when it was disposed on an other thread, the the original `StartApplication` was made. We don't want to force the user to keep track of the correct threads themself, so we pass all calls to the mutex to a separate thread. This however imposes an other issue for special cases like our unut tests. Using a dedicated thread for locking is no problem, as long as one process only ever attempts to lock the mutex once. But when we try to lock multiple times from from within the same application, like we do in our unit tests, then we run into the situation, that we lock twice on the smae threads, which will succeed both times, since a mutex is reentrant. This however is not thre result we want, as we still want the second call to fail and signal, that there is already a running instance. To circumvent this issue, we track how often the lock has been taken, and release it immedialty, when the count is greater than 1. After that we can pretent, the lock failed, and the rest works fine again
- Loading branch information
Showing
2 changed files
with
127 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.Collections.Generic; | ||
using System.Threading.Tasks; | ||
|
||
namespace SingleInstanceManager | ||
{ | ||
internal class MutexContext : IDisposable | ||
{ | ||
private readonly BlockingCollection<Action> _pendingOperations = new BlockingCollection<Action>(); | ||
|
||
public MutexContext() | ||
{ | ||
Task.Factory.StartNew( | ||
ProcessLoop, | ||
default, | ||
TaskCreationOptions.LongRunning | TaskCreationOptions.DenyChildAttach, | ||
TaskScheduler.Default); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
_pendingOperations.CompleteAdding(); | ||
} | ||
|
||
public void Execute(Action? action) | ||
{ | ||
TaskCompletionSource<object?> tcs = new TaskCompletionSource<object?>(); | ||
|
||
void ExecuteInternal() | ||
{ | ||
try | ||
{ | ||
action?.Invoke(); | ||
tcs.SetResult(null); | ||
} | ||
catch (Exception e) | ||
{ | ||
tcs.SetException(e); | ||
} | ||
} | ||
|
||
_pendingOperations.Add(ExecuteInternal); | ||
tcs.Task.GetAwaiter().GetResult(); | ||
} | ||
|
||
public T Execute<T>(Func<T> action) | ||
{ | ||
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>(); | ||
|
||
void ExecuteInternal() | ||
{ | ||
try | ||
{ | ||
T result = action.Invoke(); | ||
tcs.SetResult(result); | ||
} | ||
catch (Exception e) | ||
{ | ||
tcs.SetException(e); | ||
} | ||
} | ||
|
||
_pendingOperations.Add(ExecuteInternal); | ||
return tcs.Task.GetAwaiter().GetResult(); | ||
} | ||
|
||
private void ProcessLoop() | ||
{ | ||
IEnumerable<Action> operations = _pendingOperations.GetConsumingEnumerable(); | ||
foreach (Action operation in operations) | ||
{ | ||
operation.Invoke(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters