/// <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); } } }
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)); }