public void AsyncLock_Locked_PreventsLockUntilUnlocked() { AsyncContext.Run(async () => { var mutex = new AsyncLock(); var task1HasLock = new TaskCompletionSource(); var task1Continue = new TaskCompletionSource(); Task<IDisposable> task1LockTask = null; var task1 = Task.Run(async () => { task1LockTask = mutex.LockAsync(); using (await task1LockTask) { task1HasLock.SetResult(); await task1Continue.Task; } }); await task1HasLock.Task; Task<IDisposable> task2LockTask = null; var task2Start = Task.Factory.StartNew(async () => { task2LockTask = mutex.LockAsync(); await task2LockTask; }); var task2 = await task2Start; Assert.IsFalse(task2.IsCompleted); task1Continue.SetResult(); await task2; }); }
public void AsyncLock_Locked_PreventsLockUntilUnlocked() { Test.Async(async () => { var mutex = new AsyncLock(); var task1HasLock = new TaskCompletionSource(); var task1Continue = new TaskCompletionSource(); var task1 = TaskShim.Run(async () => { using (await mutex.LockAsync()) { task1HasLock.SetResult(); await task1Continue.Task; } }); await task1HasLock.Task; var task2Start = Task.Factory.StartNew(async () => { await mutex.LockAsync(); }); var task2 = await task2Start; Assert.IsFalse(task2.IsCompleted); task1Continue.SetResult(); await task2; }); }
public void MultipleWaits_NotifyAll_AllAreCompleted() { AsyncContext.Run(async () => { var mutex = new AsyncLock(); var cv = new AsyncConditionVariable(mutex); var key1 = await mutex.LockAsync(); var task1 = cv.WaitAsync(); var __ = task1.ContinueWith(_ => key1.Dispose()); var key2 = await mutex.LockAsync(); var task2 = cv.WaitAsync(); var ___ = task2.ContinueWith(_ => key2.Dispose()); await Task.Run(async () => { using (await mutex.LockAsync()) { cv.NotifyAll(); } }); await task1; await task2; }); }
/// <summary> /// Authenticates with the server and creates all the necessary components. /// All of them should be initialized inside the synchronized block and MUST NOT call any method on <see cref="SpotifySession">this</see> object. /// </summary> /// <param name="credentials"><see cref="Spotify.LoginCredentials"/></param> /// <exception cref="MercuryException"></exception> private async Task Authenticate( [NotNull] Spotify.LoginCredentials credentials, [NotNull] WebsocketHandler handler) { AuthenticatePartial(credentials, false); using (await authLock.LockAsync()) { //Initialize Services mercuryClient = new MercuryClient(this); eventService = new EventService(this); tokenProvider = new TokenProvider(this); apiClient = await ApiClient.BuildApiClient(this); dealer = new DealerClient(this, handler); //_audioKeyManager = new AudioKeyManager(this); // _channelManager = new ChannelManager(this); // _cdn = new CdnManager(this); // _cache = new CacheManager(_inner.Conf); // _contentFeeder = new PlayableContentFeeder(this); authLockEventWaitHandle.Set(); } receiver = new Receiver(this); eventService.Language(inner.PreferredLocale); TimeProvider.Init(this); Debug.WriteLine($"Authenticated as {apWelcome.CanonicalUsername}!"); Mercury().InterestedIn("spotify:user:attributes:update", this); Dealer().AddMessageListener(this, "hm://connect-state/v1/connect/logout"); }
public void WaitAsync_Notified_IsCompleted() { Test.Async(async () => { var mutex = new AsyncLock(); var cv = new AsyncConditionVariable(mutex); await mutex.LockAsync(); var task = cv.WaitAsync(); await TaskShim.Run(async () => { using (await mutex.LockAsync()) { cv.Notify(); } }); await task; }); }
public void AsyncLock_Unlocked_SynchronouslyPermitsLock() { var mutex = new AsyncLock(); var lockTask = mutex.LockAsync(); Assert.IsTrue(lockTask.IsCompleted); Assert.IsFalse(lockTask.IsFaulted); Assert.IsFalse(lockTask.IsCanceled); }
public void AsyncLock_DoubleDispose_OnlyPermitsOneTask() { AsyncContext.Run(async () => { var mutex = new AsyncLock(); var task1HasLock = new TaskCompletionSource(); var task1Continue = new TaskCompletionSource(); Task<IDisposable> lockTask = null; await Task.Run(async () => { lockTask = mutex.LockAsync(); var key = await lockTask; key.Dispose(); key.Dispose(); }); Task<IDisposable> task1LockTask = null; var task1 = Task.Run(async () => { task1LockTask = mutex.LockAsync(); using (await task1LockTask) { task1HasLock.SetResult(); await task1Continue.Task; } }); await task1HasLock.Task; Task<IDisposable> task2LockTask = null; var task2Start = Task.Factory.StartNew(async () => { task2LockTask = mutex.LockAsync(); await task2LockTask; }); var task2 = await task2Start; Assert.IsFalse(task2.IsCompleted); task1Continue.SetResult(); await task2; }); }
public void WaitAsync_AfterNotify_IsNotCompleted() { Test.Async(async () => { var mutex = new AsyncLock(); var cv = new AsyncConditionVariable(mutex); await TaskShim.Run(async () => { using (await mutex.LockAsync()) { cv.Notify(); } }); await mutex.LockAsync(); var task = cv.WaitAsync(); await AssertEx.NeverCompletesAsync(task); }); }
public void WaitAsync_WithoutNotify_IsNotCompleted() { Test.Async(async () => { var mutex = new AsyncLock(); var cv = new AsyncConditionVariable(mutex); await mutex.LockAsync(); var task = cv.WaitAsync(); await AssertEx.NeverCompletesAsync(task); }); }
public void AsyncLock_DoubleDispose_OnlyPermitsOneTask() { Test.Async(async () => { var mutex = new AsyncLock(); var task1HasLock = new TaskCompletionSource(); var task1Continue = new TaskCompletionSource(); await TaskShim.Run(async () => { var key = await mutex.LockAsync(); key.Dispose(); key.Dispose(); }); var task1 = TaskShim.Run(async () => { using (await mutex.LockAsync()) { task1HasLock.SetResult(); await task1Continue.Task; } }); await task1HasLock.Task; var task2Start = Task.Factory.StartNew(async () => { await mutex.LockAsync(); }); var task2 = await task2Start; Assert.IsFalse(task2.IsCompleted); task1Continue.SetResult(); await task2; }); }
/// <summary> /// The Lock mechanism designed for standard using blocks. This lock is thread and interprocess safe. /// You can create and use it from anywhere. /// </summary> /// <param name="pollInterval">The frequency of polling the termination of the mutex-thread.</param> /// <returns>The IDisposable await-able Task.</returns> public async Task <IDisposable> LockAsync(CancellationToken cancellationToken = default, int pollInterval = 100) { Exception inner = null; try { if (IsQuitPending) { throw new OperationCanceledException($"{nameof(AsyncMutex)}.{nameof(AsyncMutex.LockAsync)} failed because quit is pending on: {ShortName}."); } // Local lock for thread safety. await AsyncLock.LockAsync(cancellationToken).ConfigureAwait(false); if (IsAlive) { throw new InvalidOperationException("Thread should not be alive."); } MutexThread = new Thread(new ParameterizedThreadStart(HoldLock)) { Name = $"{nameof(MutexThread)}" }; MutexThread.Start(cancellationToken); ChangeStatus(AsyncLockStatus.StatusAcquiring, AsyncLockStatus.StatusReady); // Thread is running from now. try { // Create the mutex and acquire it. await SetCommandAsync(1, cancellationToken, pollInterval).ConfigureAwait(false); } catch (Exception ex) { Logger.LogWarning(ex); // If the thread is still alive we must stop it StopThread(); ChangeStatus(AsyncLockStatus.StatusReady, AsyncLockStatus.StatusAcquiring); throw ex; } ChangeStatus(AsyncLockStatus.StatusAcquired, AsyncLockStatus.StatusAcquiring); return(new Key(this)); } catch (TaskCanceledException) { // Let it go. } catch (AbandonedMutexException) { // abandoned mutexes are still acquired, we just need // to handle the exception and treat it as acquisition Logger.LogWarning($"{nameof(AbandonedMutexException)} in {ShortName}."); return(new Key(this)); } catch (Exception ex) { Logger.LogError($"{ex.ToTypeMessageString()} in {ShortName}."); inner = ex; // Let it go. } // Release the local lock. AsyncLock.ReleaseLock(); throw new IOException($"Could not acquire system wide mutex on {ShortName}.", inner); }
public void AsyncLock_CanceledTooLate_StillTakesLock() { AsyncContext.Run(async () => { var mutex = new AsyncLock(); var cts = new CancellationTokenSource(); Task<IDisposable> initialLockTask, cancelableLockTask; initialLockTask = mutex.LockAsync(); using (await initialLockTask) { cancelableLockTask = mutex.LockAsync(cts.Token); } var key = await cancelableLockTask; cts.Cancel(); var nextLocker = mutex.LockAsync(); Assert.IsFalse(nextLocker.IsCompleted); key.Dispose(); await nextLocker; }); }
public void AsyncLock_CanceledLock_ThrowsException() { AsyncContext.Run(async () => { var mutex = new AsyncLock(); var cts = new CancellationTokenSource(); var initialLockTask = mutex.LockAsync(); await initialLockTask; var canceledLockTask = mutex.LockAsync(cts.Token); cts.Cancel(); await AssertEx.ThrowsExceptionAsync<OperationCanceledException>(canceledLockTask); }); }
public void AsyncLock_CancelledLock_LeavesLockUnlocked() { AsyncContext.Run(async () => { var mutex = new AsyncLock(); var cts = new CancellationTokenSource(); var taskReady = new TaskCompletionSource(); Task<IDisposable> initialLockTask = null, lockTask = null; initialLockTask = mutex.LockAsync(); var unlock = await initialLockTask; var task = Task.Run(async () => { lockTask = mutex.LockAsync(cts.Token); taskReady.SetResult(); await lockTask; }); await taskReady.Task; cts.Cancel(); await AssertEx.ThrowsExceptionAsync<OperationCanceledException>(task); Assert.IsTrue(task.IsCanceled); unlock.Dispose(); var finalLockTask = mutex.LockAsync(); await finalLockTask; }); }
public void AsyncLock_PreCancelled_Locked_SynchronouslyCancels() { var mutex = new AsyncLock(); var lockTask = mutex.LockAsync(); var token = new CancellationToken(true); var task = mutex.LockAsync(token); Assert.IsTrue(task.IsCompleted); Assert.IsTrue(task.IsCanceled); Assert.IsFalse(task.IsFaulted); }
public void AsyncLock_CanceledTooLate_StillTakesLock() { Test.Async(async () => { var mutex = new AsyncLock(); var cts = new CancellationTokenSource(); AwaitableDisposable<IDisposable> cancelableLockTask; using (await mutex.LockAsync()) { cancelableLockTask = mutex.LockAsync(cts.Token); } var key = await cancelableLockTask; cts.Cancel(); var nextLocker = mutex.LockAsync().AsTask(); Assert.IsFalse(nextLocker.IsCompleted); key.Dispose(); await nextLocker; }); }
/// <summary> /// Asynchronously enters the monitor. Returns a disposable that leaves the monitor when disposed. /// </summary> /// <param name="cancellationToken">The cancellation token used to cancel the enter. If this is already set, then this method will attempt to enter the monitor immediately (succeeding if the monitor is currently available).</param> /// <returns>A disposable that leaves the monitor when disposed.</returns> public AwaitableDisposable <IDisposable> EnterAsync(CancellationToken cancellationToken) { return(_asyncLock.LockAsync(cancellationToken)); }
public void MultipleWaits_Notify_OneIsCompleted() { Test.Async(async () => { var mutex = new AsyncLock(); var cv = new AsyncConditionVariable(mutex); var key = await mutex.LockAsync(); var task1 = cv.WaitAsync(); var __ = task1.ContinueWith(_ => key.Dispose()); await mutex.LockAsync(); var task2 = cv.WaitAsync(); await TaskShim.Run(async () => { using (await mutex.LockAsync()) { cv.Notify(); } }); await task1; await AssertEx.NeverCompletesAsync(task2); }); }
public void AsyncLock_Locked_OnlyPermitsOneLockerAtATime() { Test.Async(async () => { var mutex = new AsyncLock(); var task1HasLock = new TaskCompletionSource(); var task1Continue = new TaskCompletionSource(); var task2HasLock = new TaskCompletionSource(); var task2Continue = new TaskCompletionSource(); var task1 = TaskShim.Run(async () => { using (await mutex.LockAsync()) { task1HasLock.SetResult(); await task1Continue.Task; } }); await task1HasLock.Task; var task2Start = Task.Factory.StartNew(async () => { using (await mutex.LockAsync()) { task2HasLock.SetResult(); await task2Continue.Task; } }); var task2 = await task2Start; var task3Start = Task.Factory.StartNew(async () => { await mutex.LockAsync(); }); var task3 = await task3Start; task1Continue.SetResult(); await task2HasLock.Task; Assert.IsFalse(task3.IsCompleted); task2Continue.SetResult(); await task2; await task3; }); }
public void AsyncLock_Locked_OnlyPermitsOneLockerAtATime() { AsyncContext.Run(async () => { var mutex = new AsyncLock(); var task1HasLock = new TaskCompletionSource(); var task1Continue = new TaskCompletionSource(); var task2HasLock = new TaskCompletionSource(); var task2Continue = new TaskCompletionSource(); Task<IDisposable> task1LockTask = null, task2LockTask = null, task3LockTask = null; var task1 = Task.Run(async () => { task1LockTask = mutex.LockAsync(); using (await task1LockTask) { task1HasLock.SetResult(); await task1Continue.Task; } }); await task1HasLock.Task; var task2Start = Task.Factory.StartNew(async () => { task2LockTask = mutex.LockAsync(); using (await task2LockTask) { task2HasLock.SetResult(); await task2Continue.Task; } }); var task2 = await task2Start; var task3Start = Task.Factory.StartNew(async () => { task3LockTask = mutex.LockAsync(); await task3LockTask; }); var task3 = await task3Start; task1Continue.SetResult(); await task2HasLock.Task; Assert.IsFalse(task3.IsCompleted); task2Continue.SetResult(); await task2; await task3; }); }
public async Task AsyncLock_DoesNotPermitRecursiveWaits() { var mutex = new AsyncLock(); var key = await mutex.LockAsync(); var waiter = mutex.LockAsync().AsTask(); Assert.IsFalse(waiter.Wait(1000)); key.Dispose(); }
public void AsyncLock_CanceledLock_ThrowsException() { Test.Async(async () => { var mutex = new AsyncLock(); var cts = new CancellationTokenSource(); await mutex.LockAsync(); var canceledLockTask = mutex.LockAsync(cts.Token).AsTask(); cts.Cancel(); await AssertEx.ThrowsExceptionAsync<OperationCanceledException>(canceledLockTask); }); }