/// <summary> /// Adds a new wait for the specified <paramref name="key"/> and with the specified <paramref name="timeout"/>. /// </summary> /// <typeparam name="T">The wait result type.</typeparam> /// <param name="key">A unique WaitKey for the wait.</param> /// <param name="timeout">The wait timeout.</param> /// <param name="cancellationToken">The cancellation token for the wait.</param> /// <returns>A Task representing the wait.</returns> public Task <T> Wait <T>(WaitKey key, int?timeout = null, CancellationToken?cancellationToken = null) { timeout = timeout ?? DefaultTimeout; var wait = new PendingWait() { TaskCompletionSource = new TaskCompletionSource <T>(TaskCreationOptions.RunContinuationsAsynchronously), DateTime = DateTime.UtcNow, TimeoutAfter = (int)timeout, CancellationToken = cancellationToken, }; var recordLock = Locks.GetOrAdd(key, new ReaderWriterLockSlim()); recordLock.EnterReadLock(); try { Waits.AddOrUpdate(key, new ConcurrentQueue <PendingWait>(new[] { wait }), (_, queue) => { queue.Enqueue(wait); return(queue); }); } finally { recordLock.ExitReadLock(); } return(((TaskCompletionSource <T>)wait.TaskCompletionSource).Task); }
/// <summary> /// Adds a new wait for the specified <paramref name="key"/> and with the specified <paramref name="timeout"/>. /// </summary> /// <typeparam name="T">The wait result type.</typeparam> /// <param name="key">A unique WaitKey for the wait.</param> /// <param name="timeout">The wait timeout.</param> /// <param name="cancellationToken">The cancellation token for the wait.</param> /// <returns>A Task representing the wait.</returns> public Task <T> Wait <T>(WaitKey key, int?timeout = null, CancellationToken?cancellationToken = null) { timeout = timeout ?? DefaultTimeout; var wait = new PendingWait() { TaskCompletionSource = new TaskCompletionSource <T>(TaskCreationOptions.RunContinuationsAsynchronously), DateTime = DateTime.UtcNow, TimeoutAfter = (int)timeout, CancellationToken = cancellationToken, }; Waits.AddOrUpdate(key, new ConcurrentQueue <PendingWait>(new[] { wait }), (_, queue) => { queue.Enqueue(wait); return(queue); }); return(((TaskCompletionSource <T>)wait.TaskCompletionSource).Task); }
/// <summary> /// Adds a new wait for the specified <paramref name="key"/> and with the specified <paramref name="timeout"/>. /// </summary> /// <typeparam name="T">The wait result type.</typeparam> /// <param name="key">A unique WaitKey for the wait.</param> /// <param name="timeout">The wait timeout, in milliseconds.</param> /// <param name="cancellationToken">The cancellation token for the wait.</param> /// <returns>A Task representing the wait.</returns> public Task <T> Wait <T>(WaitKey key, int?timeout = null, CancellationToken?cancellationToken = null) { timeout ??= DefaultTimeout; cancellationToken ??= CancellationToken.None; var taskCompletionSource = new TaskCompletionSource <T>(TaskCreationOptions.RunContinuationsAsynchronously); var wait = new PendingWait( taskCompletionSource, timeout.Value, cancelAction: () => Cancel(key), timeoutAction: () => Timeout(key), cancellationToken.Value); // obtain a read lock for the key. this is necessary to prevent this code from adding a wait to the ConcurrentQueue // while the containing dictionary entry is being cleaned up in Disposition(), effectively discarding the new wait. #pragma warning disable IDE0067, CA2000 // Dispose objects before losing scope var recordLock = Locks.GetOrAdd(key, new ReaderWriterLockSlim()); #pragma warning restore IDE0067, CA2000 // Dispose objects before losing scope recordLock.EnterReadLock(); try { Waits.AddOrUpdate(key, new ConcurrentQueue <PendingWait>(new[] { wait }), (_, queue) => { queue.Enqueue(wait); return(queue); }); } finally { recordLock.ExitReadLock(); } // defer registration to prevent the wait from being dispositioned prior to being successfully queued this is a // concern if we are given a timeout of 0, or a cancellation token which is already cancelled wait.Register(); return(((TaskCompletionSource <T>)wait.TaskCompletionSource).Task); }