Exemplo n.º 1
0
        private void Release()
        {
            WaiterInfo?info = null;

            lock (this.syncObject)
            {
                if (this.CurrentCount++ == 0)
                {
                    // We loop because the First node may have been canceled.
                    while (this.waiters.First is { } head)
                    {
                        // Remove the head of the queue.
                        this.waiters.RemoveFirst();
                        info = head.Value;
                        this.RecycleNode(head);

                        if (info.Trigger.TrySetResult(new Releaser(this)))
                        {
                            // We successfully let someone enter the semaphore.
                            this.CurrentCount--;

                            // We've filled the one slot available in the semaphore. Stop looking for more.
                            break;
                        }
                    }
                }
            }

            // Release memory related to cancellation handling.
            info?.Cleanup();
        }
Exemplo n.º 2
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);
                }
            }
        }