/// <summary> /// Try to skip to the head of the queue. /// </summary> /// <param name="AHandle"> /// The output <see cref="SynchronizationHandle"/> for the skipped turn. /// </param> /// <returns> /// <see langword="true"/> if skipping was successful and <paramref name="AHandle"/> is /// live; otherwise <see langword="false"/>. /// </returns> /// <remarks> /// <para> /// Skipping means entering an empty queue to immediately start a turn. Internally, the /// caller enters the queue regardless of state but cancels any wait immediately, reporting /// failure instead. /// </para> /// <para> /// When the result is <see langword="true"/>, <paramref name="AHandle"/> is acquired and /// live and must be disposed! /// </para> /// </remarks> public bool TrySkip(out SynchronizationHandle AHandle) { AHandle = null; if (IsDisposed) { // Cannot skip a disposed queue. return(false); } // Make a handle to end this turn. var handle = new Handle(); // Update the tail to reserve our position whilst getting our immediate predecessor. // NOTE: Exception cannot be thrown. // ReSharper disable once ExceptionNotDocumented var predecessor = Interlocked.Exchange(ref FTail, handle); if (predecessor.IsReleased) { // Successfully skipped to the head of the queue. AHandle = handle; return(true); } // Immediately bypass to cede our turn. #pragma warning disable 4014 predecessor.Turn.WaitAsync() .ContinueWith( A => { if (A.IsFaulted) { handle.Fault(); } else { handle.Dispose(); } }, TaskContinuationOptions.ExecuteSynchronously ); #pragma warning restore 4014 return(false); }
/// <inheritdoc /> /// <exception cref="OperationCanceledException"> /// <paramref name="ACancel"/> was canceled. /// </exception> /// <exception cref="ObjectDisposedException">This instance is disposed.</exception> public async Task <SynchronizationHandle> WaitAsync(CancellationToken ACancel) { ThrowIfDisposed(); ACancel.ThrowIfCancellationRequested(); var id = TaskContext.CurrentId; // Strengthen the lock to prevent release (temporarily). Strengthen(); if (FOwner == id) { // Re-enter the lock. return(new Handle(this)); } // The lock is either unacquired or acquired by some other context. Task <SynchronizationHandle> turn; try { // Acquire an awaitable to await our turn. turn = FQueue.WaitAsync(ACancel); } finally { // Weak the lock to allow release again. Weaken(); } // Await our turn. FHead = await turn; // We now have exclusive access. FOwner = id; Strengthen(); return(new Handle(this)); }