protected void UnregisterCancellation() { _TokenSource?.Dispose(); _TokenSource = null; _Token = default; #if NETSTANDARD2_0 // This may block if SwitchToCancelled is running _Registration.Dispose(); OnCompletedCleanup(); #else // This will not block if SwitchToCancelled is running, but may continue later _Awaiter = _Registration.DisposeAsync().ConfigureAwait(false).GetAwaiter(); if (_Awaiter.IsCompleted) { OnCompletedCleanup(); } else { _Awaiter.OnCompleted(_CompletedCleanup); } #endif }
public async ValueTask DisposeAsync() { _asyncEventStream._sequencer.RemoveGatingSequence(_sequence); await _cancellationTokenRegistration.DisposeAsync().ConfigureAwait(false); _linkedTokenSource.Dispose(); }
public static void CancellationTokenRegistration_DisposeAsyncRemovesDelegate() { var cts = new CancellationTokenSource(); bool invoked = false; CancellationTokenRegistration ctr = cts.Token.Register(() => invoked = true); Assert.True(ctr.DisposeAsync().IsCompletedSuccessfully); cts.Cancel(); Assert.False(invoked); }
/// <summary> /// Allows awaiting a <see cref="WaitHandle"/> which /// <inheritdoc cref="WaitHandle"/> /// </summary> /// <param name="handle">The <see cref="WaitHandle"/> to await.</param> /// <param name="timeout">The timeout period in milliseconds to return false if timed out. /// <code> /// // In order to use timeouts and infinitely wait till a resource is free use /// (int)timeout.Infinite /// </code></param> /// <param name="token">The cancellation token to use to throw a <see cref="TaskCanceledException"/> /// if this token gets cancelled</param> /// <returns>True if the handle is free, false if it is not</returns> /// <exception cref="TaskCanceledException">Throws if the cancellation token is invoked</exception> /// <example> /// handle.WaitHandleAsync((int)Timeout.Infinite, cancellationToken); /// </example> public static async Task <bool> WaitHandleAsync(this WaitHandle handle, int timeout, CancellationToken?token) { // Create a handle that awaits the original wait handle RegisteredWaitHandle registeredWaitHandle = null; // Store the token CancellationTokenRegistration?tokenRegistration = null; try { // Create a new task completion source to await TaskCompletionSource <bool> taskCompletionSource = new TaskCompletionSource <bool>(); /* Use RegisterWaitForSingleObject so we get a callback * once the wait handle has finished, and set the taskCompletionSource result in that callback. */ registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject( // The handle to wait handle, // When it is finished, set the taskCompletionSource (state, timedOut) => ((TaskCompletionSource <bool>)state) .TrySetResult(!timedOut), // Pass the taskCompletion source as the state so we don't have a reference to the parent // taskCompletionSource (optimization) taskCompletionSource, // Set timeout if passed in timeout, // Run once don't keep resetting timeout true); /* * Register to run the action and set the taskCompletionSource as cancelled * if the cancellation token itself is cancelled; which will throw a TaskCancelledException * up to the caller. */ if (token.HasValue) { tokenRegistration = token.Value.Register((state) => ((TaskCompletionSource <bool>)state) .TrySetCanceled(), taskCompletionSource); } // Await the handle or the cancellation token return(await taskCompletionSource.Task); } finally { // Clean up registered wait handle registeredWaitHandle?.Unregister(null); // Dispose of the token we had to create to register for the cancellation token callback tokenRegistration?.DisposeAsync(); } }
public ValueTask DisposeAsync() { // Deadline registration needs to be disposed with DisposeAsync, and the task completed // before the lock can be disposed. // Awaiting deadline registration and deadline task ensures it has finished running, so there is // no way for deadline logic to attempt to wait on a disposed lock. var disposeTask = _deadlineExceededRegistration.DisposeAsync(); if (disposeTask.IsCompletedSuccessfully && (_deadlineExceededTask == null || _deadlineExceededTask.IsCompletedSuccessfully)) { DisposeCore(); return(default);
internal async ValueTask CompleteWritesAsync(CancellationToken cancellationToken = default) { ValueTask task = _writer.CompleteAsync(); CancellationTokenRegistration registration = task.IsCompleted || !cancellationToken.CanBeCanceled ? default : cancellationToken.UnsafeRegister(o => ((PipeWriter)o !).CancelPendingFlush(), _writer); try { await task.ConfigureAwait(false); } finally { await registration.DisposeAsync().ConfigureAwait(false); } }
/// <summary> /// Due to the asynchronous design of LibVLC and the initial LibVLCSharp APIs, the C# functions returned immediately as they /// act as "command sender" essentially, without waiting for state changes. For consumers interested in getting notified of state /// changes triggered by their function calls, they could register to libvlc events manually (e.g. subscribe to the Playing event, /// call Play(), see your event handler invoked as the Playing event fires). /// Now thanks to this, we can offer Async versions of the APIs which actually wait for state changes (e.g. only return when the native /// event confirming state changes has fired). It may take a while and many users do not actually care about it. But some do. /// </summary> /// <typeparam name="T">the return type of the API call</typeparam> /// <param name="nativeCall">the native P/Invoke mapping</param> /// <param name="sub">the subscribe action delegate</param> /// <param name="unsub">the unsubscribe action delegate</param> /// <param name="tcs">the task completion source to wait for the event to fire</param> /// <param name="ctr">the cancellation token registration to dispose once the operation is over</param> /// <returns>The task result of the type of the API</returns> internal static async Task <T> InternalAsync <T>(Action nativeCall, Action sub, Action unsub, TaskCompletionSource <T> tcs, CancellationTokenRegistration ctr = default) { try { sub(); nativeCall(); return(await tcs.Task.ConfigureAwait(false)); } finally { unsub(); #if NETFRAMEWORK || NETSTANDARD2_0 || UWP ctr.Dispose(); #else await ctr.DisposeAsync(); #endif } }
public static async Task CancellationTokenRegistration_DisposeAsyncDuringCancellation_SuccessfullyRemovedIfNotYetInvoked() { var ctr0running = new ManualResetEventSlim(); var ctr2blocked = new ManualResetEventSlim(); var ctr2running = new ManualResetEventSlim(); var cts = new CancellationTokenSource(); CancellationTokenRegistration ctr0 = cts.Token.Register(() => ctr0running.Set()); bool ctr1Invoked = false; CancellationTokenRegistration ctr1 = cts.Token.Register(() => ctr1Invoked = true); CancellationTokenRegistration ctr2 = cts.Token.Register(() => { ctr2running.Set(); ctr2blocked.Wait(); }); // Cancel. This will trigger ctr2 to run, then ctr1, then ctr0. Task ignored = Task.Run(() => cts.Cancel()); ctr2running.Wait(); // wait for ctr2 to start running ValueTask vt2 = ctr2.DisposeAsync(); Assert.False(vt2.IsCompleted); // Now that ctr2 is running, unregister ctr1. This should succeed // and ctr1 should not run. Assert.True(ctr1.DisposeAsync().IsCompletedSuccessfully); // Allow ctr2 to continue. ctr1 should not run. ctr0 should, so wait for it. ctr2blocked.Set(); ctr0running.Wait(); await ctr0.DisposeAsync(); Assert.False(ctr1Invoked); await vt2; }
/// <summary> /// Asynchronously waits for the wait handle. /// </summary> /// <param name="handle">The handle.</param> /// <param name="timeout">The timeout.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns><c>true</c> if the timeout has not been reached, <c>false</c> otherwise.</returns> public static async ValueTask <bool> WaitOneAsync( this WaitHandle handle, TimeSpan timeout, CancellationToken cancellationToken) { RegisteredWaitHandle? registeredHandle = null; CancellationTokenRegistration tokenRegistration = default; try { var tcs = new TaskCompletionSource <bool>(); registeredHandle = ThreadPool.RegisterWaitForSingleObject( handle, ( state, timedOut) => ((TaskCompletionSource <bool>)state !).TrySetResult(!timedOut), tcs, timeout, true); tokenRegistration = cancellationToken.Register( state => state.TrySetCanceled(), tcs); return(await tcs.Task); } finally { registeredHandle?.Unregister(null); #if NETSTANDARD21_OR_GREATER await tokenRegistration.DisposeAsync(); #else tokenRegistration.Dispose(); #endif } }
public static async Task CancellationTokenRegistration_DisposeAsyncWhileCallbackRunning_WaitsForCallbackToComplete() { using (var barrier = new Barrier(2)) { var cts = new CancellationTokenSource(); CancellationTokenRegistration ctr = cts.Token.Register(() => { barrier.SignalAndWait(); barrier.SignalAndWait(); }); Task ignored = Task.Run(() => cts.Cancel()); barrier.SignalAndWait(); ValueTask vt = ctr.DisposeAsync(); await Task.Delay(1); Assert.False(vt.IsCompleted); barrier.SignalAndWait(); await vt; } }
public static async Task <ProcessResult> RunAsTaskAsync(this ProcessStartInfo psi, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var logs = new List <ProcessOutput>(); int exitCode; using (var process = new Process()) { process.StartInfo = psi; if (psi.RedirectStandardError) { process.ErrorDataReceived += (sender, e) => { if (e.Data != null) { lock (logs) { logs.Add(new ProcessOutput(ProcessOutputType.StandardError, e.Data)); } } }; } if (psi.RedirectStandardOutput) { process.OutputDataReceived += (sender, e) => { if (e.Data != null) { lock (logs) { logs.Add(new ProcessOutput(ProcessOutputType.StandardOutput, e.Data)); } } }; } if (!process.Start()) { throw new Win32Exception("Cannot start the process"); } if (psi.RedirectStandardError) { process.BeginErrorReadLine(); } if (psi.RedirectStandardOutput) { process.BeginOutputReadLine(); } if (psi.RedirectStandardInput) { process.StandardInput.Close(); } CancellationTokenRegistration registration = default; try { if (cancellationToken.CanBeCanceled && !process.HasExited) { registration = cancellationToken.Register(() => { try { process.Kill(entireProcessTree: true); } catch (InvalidOperationException) { try { // Try to at least kill the root process process.Kill(); } catch (InvalidOperationException) { } } }); } await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false); process.WaitForExit(); // https://github.com/dotnet/runtime/issues/42556 } finally { await registration.DisposeAsync().ConfigureAwait(false); } exitCode = process.ExitCode; } cancellationToken.ThrowIfCancellationRequested(); return(new ProcessResult(exitCode, logs)); }
/// <inheritdoc /> public ValueTask DisposeAsync() { return(_lifetimeAppStopRegistration.DisposeAsync()); }