Ejemplo n.º 1
0
        /// <summary>
        /// Attempts to enter the lock with concurrent access where all
        /// readers can execute concurrently with respect to each other.
        /// </summary>
        /// <param name="timeout">The amount of time to wait before timing out.</param>
        /// <returns>The token used to control the duration of entry.</returns>
        /// <exception cref="TimeoutException">The lock could not be entered before the <paramref name="timeout"/> expired.</exception>
        public async Task <IDisposable> TryEnterReadLockAsync(TimeSpan timeout)
        {
            LockEntry lockEntry = new LockEntry();

            try
            {
                Task readerTask = lockEntry.WhenReleased();

                // The exclusive lock must be obtained to update the reader list,
                // but it's also necessary so that the reader will wait on any
                // writers that entered before it
                using (await ReaderListLock.TryEnterAsync(timeout).ConfigureAwait(false))
                    ReaderList.Add(readerTask);

                // Prevent the reader list from growing indefinitely
                _ = readerTask.ContinueWith(async _ =>
                {
                    using (await ReaderListLock.EnterAsync().ConfigureAwait(false))
                        ReaderList.Remove(readerTask);
                });

                return(lockEntry);
            }
            catch
            {
                // Make sure to dispose the lock entry since
                // it's not being returned to the caller
                lockEntry.Dispose();
                throw;
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Attempts to obtain exclusive access to the lock.
        /// </summary>
        /// <param name="timeout">The amount of time to wait before failing to take the lock.</param>
        /// <returns>
        /// A task that, if cancelled, indicates the lock was not taken,
        /// and must be awaited to obtain the token that will release the
        /// lock on <see cref="IDisposable.Dispose"/>.
        /// </returns>
        /// <exception cref="TaskCanceledException">The <paramref name="timeout"/> expires before the lock could be taken.</exception>
        /// <remarks>
        /// <para>
        /// The following illustrates an example of using try-catch to detect a failure to take the lock.
        /// </para>
        ///
        /// <code>
        /// AsyncLock asyncLock = new AsyncLock();
        ///
        /// try
        /// {
        ///     using IDisposable token = await asyncLock.TryEnterAsync();
        ///     // Critical region
        /// }
        /// catch (TaskCanceledException)
        /// {
        ///     // Lock failed
        /// }
        /// </code>
        ///
        /// <para>
        /// The following illustrates an example of using <see cref="Task.ContinueWith{TResult}(Func{Task, TResult})"/>
        /// to detect a failure to take the lock.
        /// </para>
        ///
        /// <code>
        /// AsyncLock asyncLock = new AsyncLock();
        ///
        /// await asyncLock.TryEnterAsync().ContinueWith(async tokenTask =>
        /// {
        ///     if (tokenTask.IsCanceled)
        ///     {
        ///         // Lock failed
        ///         return;
        ///     }
        ///
        ///     using IDisposable token = await tokenTask;
        ///     // Critical region
        /// }).Unwrap();
        /// </code>
        /// </remarks>
        public async Task <IDisposable> TryEnterAsync(TimeSpan timeout)
        {
            LockEntry lockEntry = new LockEntry();

            try
            {
                Task timeoutTask      = Task.Delay(timeout);
                Task lockReleasedTask = Interlocked.Exchange(ref m_task, lockEntry.WhenReleased());
                await Task.WhenAny(timeoutTask, lockReleasedTask).ConfigureAwait(false);

                if (!lockReleasedTask.IsCompleted)
                {
                    throw new TaskCanceledException("Timed out while attempting to get exclusive access to async lock.");
                }

                return(lockEntry);
            }
            catch
            {
                // Make sure to dispose the lock entry since
                // it's not being returned to the caller
                lockEntry.Dispose();
                throw;
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Attempts to enter the lock with exclusive access where no other
        /// readers or writers can execute concurrently with the writer.
        /// </summary>
        /// <param name="timeout">The amount of time to wait before timing out.</param>
        /// <returns>The token used to control the duration of entry.</returns>
        /// <exception cref="TimeoutException">The lock could not be entered before the <paramref name="timeout"/> expired.</exception>
        public async Task <IDisposable> TryEnterWriteLockAsync(TimeSpan timeout)
        {
            LockEntry lockEntry = new LockEntry();

            try
            {
                // The writer must maintain exclusive access until the write operation is complete
                IDisposable readerListToken = await ReaderListLock.TryEnterAsync(timeout).ConfigureAwait(false);

                _ = lockEntry.WhenReleased().ContinueWith(_ => readerListToken.Dispose());

                Task timeoutTask = Task.Delay(timeout);
                Task readerTask  = Task.WhenAll(ReaderList);
                await Task.WhenAny(timeoutTask, readerTask).ConfigureAwait(false);

                if (!readerTask.IsCompleted)
                {
                    throw new TaskCanceledException("Timed out waiting for readers to complete.");
                }

                // Completed readers will eventually remove themselves,
                // but may as well remove them all here
                ReaderList.Clear();
                return(lockEntry);
            }
            catch
            {
                // Make sure to dispose the lock entry since
                // it's not being returned to the caller
                lockEntry.Dispose();
                throw;
            }
        }