Пример #1
0
        /// <summary>
        /// Requests access to the lock.
        /// </summary>
        /// <param name="timeout">A timeout for waiting for the lock.</param>
        /// <param name="cancellationToken">A token whose cancellation signals lost interest in the lock.</param>
        /// <returns>
        /// A task whose result is a releaser that should be disposed to release the lock.
        /// This task may be canceled if <paramref name="cancellationToken"/> is signaled or <paramref name="timeout"/> expires.
        /// </returns>
        /// <exception cref="OperationCanceledException">Thrown when <paramref name="cancellationToken"/> is canceled or the <paramref name="timeout"/> expires before semaphore access is granted.</exception>
        /// <exception cref="ObjectDisposedException">Thrown when this semaphore is disposed before semaphore access is granted.</exception>
        public Task <Releaser> EnterAsync(TimeSpan timeout, CancellationToken cancellationToken = default)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(Task.FromCanceled <Releaser>(cancellationToken));
            }

            lock (this.syncObject)
            {
                if (this.disposed)
                {
                    return(DisposedReleaserTask);
                }

                if (this.CurrentCount > 0)
                {
                    this.CurrentCount--;
                    return(this.uncontestedReleaser);
                }
                else if (timeout == TimeSpan.Zero)
                {
                    return(CanceledReleaser);
                }
                else
                {
                    WaiterInfo info = new WaiterInfo(this, cancellationToken);
                    LinkedListNode <WaiterInfo>?node = this.GetNode(info);

                    // Careful: consider that if the token was cancelled just now (after we checked it on entry to this method)
                    // or the timeout expires,
                    // then this Register method may *inline* the handler we give it, reversing the apparent order of execution with respect to
                    // the code that follows this Register call.
                    info.CancellationTokenRegistration = cancellationToken.Register(s => CancellationHandler(s), info);
                    if (timeout != Timeout.InfiniteTimeSpan)
                    {
                        info.TimerTokenSource = new Timer(s => CancellationHandler(s), info, checked ((int)timeout.TotalMilliseconds), Timeout.Infinite);
                    }

                    // Only add to the queue if cancellation hasn't already happened.
                    if (!info.Trigger.Task.IsCanceled)
                    {
                        this.waiters.AddLast(node);
                        info.Node = node;
                    }
                    else
                    {
                        // Make sure we don't leak the Timer if cancellation happened before we created it.
                        info.Cleanup();

                        // Also recycle the unused node.
                        this.RecycleNode(node);
                    }

                    return(info.Trigger.Task);
                }
            }
        }
Пример #2
0
        private LinkedListNode <WaiterInfo> GetNode(WaiterInfo info)
        {
            Assumes.True(Monitor.IsEntered(this.syncObject));
            if (this.nodePool.Count > 0)
            {
                LinkedListNode <WaiterInfo?>?node = this.nodePool.Pop();
                node.Value = info;
                return(node !);
            }

            return(new LinkedListNode <WaiterInfo>(info));
        }