Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/Microsoft.VisualStudio.Threading/AsyncAutoResetEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

Expand Down Expand Up @@ -162,7 +160,7 @@ private void OnCancellationRequest(object state)
/// <summary>
/// Tracks someone waiting for a signal from the event.
/// </summary>
private class WaiterCompletionSource : TaskCompletionSourceWithoutInlining<EmptyStruct>
private class WaiterCompletionSource : TaskCompletionSource<EmptyStruct>
{
/// <summary>
/// Initializes a new instance of the <see cref="WaiterCompletionSource"/> class.
Expand All @@ -171,7 +169,7 @@ private class WaiterCompletionSource : TaskCompletionSourceWithoutInlining<Empty
/// <param name="allowInliningContinuations"><see langword="true" /> to allow continuations to be inlined upon the completer's callstack.</param>
/// <param name="cancellationToken">The cancellation token associated with the waiter.</param>
internal WaiterCompletionSource(AsyncAutoResetEvent owner, bool allowInliningContinuations, CancellationToken cancellationToken)
: base(allowInliningContinuations)
: base(allowInliningContinuations ? TaskCreationOptions.None : TaskCreationOptions.RunContinuationsAsynchronously)
{
this.CancellationToken = cancellationToken;
this.Registration = cancellationToken.Register(NullableHelpers.AsNullableArgAction(owner.onCancellationRequestHandler), this);
Expand Down
98 changes: 19 additions & 79 deletions src/Microsoft.VisualStudio.Threading/AsyncManualResetEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -20,9 +17,9 @@ namespace Microsoft.VisualStudio.Threading;
public class AsyncManualResetEvent
{
/// <summary>
/// Whether the task completion source should allow executing continuations synchronously.
/// Options to use while creating the <see cref="TaskCompletionSource{TResult}"/> to return from <see cref="WaitAsync()"/>.
/// </summary>
private readonly bool allowInliningAwaiters;
private readonly TaskCreationOptions options;

/// <summary>
/// The object to lock when accessing fields.
Expand All @@ -36,39 +33,22 @@ public class AsyncManualResetEvent
/// This should not need the volatile modifier because it is
/// always accessed within a lock.
/// </devremarks>
private TaskCompletionSourceWithoutInlining<EmptyStruct> taskCompletionSource;

/// <summary>
/// A flag indicating whether the event is signaled.
/// When this is set to true, it's possible that
/// <see cref="taskCompletionSource"/>.Task.IsCompleted is still false
/// if the completion has been scheduled asynchronously.
/// Thus, this field should be the definitive answer as to whether
/// the event is signaled because it is synchronously updated.
/// </summary>
/// <devremarks>
/// This should not need the volatile modifier because it is
/// always accessed within a lock.
/// </devremarks>
private bool isSet;
private TaskCompletionSource<EmptyStruct> taskCompletionSource;

/// <summary>
/// Initializes a new instance of the <see cref="AsyncManualResetEvent"/> class.
/// </summary>
/// <param name="initialState">A value indicating whether the event should be initially signaled.</param>
/// <param name="allowInliningAwaiters">
/// A value indicating whether to allow <see cref="WaitAsync()"/> callers' continuations to execute
/// on the thread that calls <see cref="SetAsync()"/> before the call returns.
/// <see cref="SetAsync()"/> callers should not hold private locks if this value is <see langword="true" /> to avoid deadlocks.
/// When <see langword="false" />, the task returned from <see cref="WaitAsync()"/> may not have fully transitioned to
/// its completed state by the time <see cref="SetAsync()"/> returns to its caller.
/// on the thread that calls <see cref="Set()"/> before the call returns.
/// <see cref="Set()"/> callers should not hold private locks if this value is <see langword="true" /> to avoid deadlocks.
/// </param>
public AsyncManualResetEvent(bool initialState = false, bool allowInliningAwaiters = false)
{
this.allowInliningAwaiters = allowInliningAwaiters;
this.options = allowInliningAwaiters ? TaskCreationOptions.None : TaskCreationOptions.RunContinuationsAsynchronously;

this.taskCompletionSource = this.CreateTaskSource();
this.isSet = initialState;
this.taskCompletionSource = new(this.options);
if (initialState)
{
this.taskCompletionSource.SetResult(EmptyStruct.Instance);
Expand All @@ -84,7 +64,7 @@ public bool IsSet
{
lock (this.syncObject)
{
return this.isSet;
return this.taskCompletionSource.Task.IsCompleted;
}
}
}
Expand Down Expand Up @@ -112,40 +92,21 @@ public Task WaitAsync()
/// </summary>
/// <returns>A task that completes when the signal has been set.</returns>
/// <remarks>
/// <para>
/// On .NET versions prior to 4.6:
/// This method may return before the signal set has propagated (so <see cref="IsSet"/> may return <see langword="false" /> for a bit more if called immediately).
/// The returned task completes when the signal has definitely been set.
/// </para>
/// <para>
/// On .NET 4.6 and later:
/// This method is not asynchronous. The returned Task is always completed.
/// </para>
/// </remarks>
[Obsolete("Use Set() instead."), EditorBrowsable(EditorBrowsableState.Never)]
public Task SetAsync()
{
TaskCompletionSourceWithoutInlining<EmptyStruct>? tcs = null;
bool transitionRequired = false;
TaskCompletionSource<EmptyStruct>? tcs = null;
lock (this.syncObject)
{
transitionRequired = !this.isSet;
tcs = this.taskCompletionSource;
this.isSet = true;
}

// Snap the Task that is exposed to the outside so we return that one.
// Once we complete the TaskCompletionSourceWithoutInlinining's task,
// the Task property will return the inner Task.
// SetAsync should return the same Task that WaitAsync callers would have observed previously.
Task result = tcs.Task;

if (transitionRequired)
{
tcs.TrySetResult(default(EmptyStruct));
}
tcs.TrySetResult(default);

Comment thread
AArnott marked this conversation as resolved.
return result;
// SetAsync should return the same Task that WaitAsync callers would have observed previously.
return tcs.Task;
}

/// <summary>
Expand All @@ -165,10 +126,9 @@ public void Reset()
{
lock (this.syncObject)
{
if (this.isSet)
if (this.taskCompletionSource.Task.IsCompleted)
{
this.taskCompletionSource = this.CreateTaskSource();
this.isSet = false;
this.taskCompletionSource = new(this.options);
}
}
}
Expand All @@ -178,20 +138,12 @@ public void Reset()
/// </summary>
/// <returns>A task that completes when the signal has been set.</returns>
/// <remarks>
/// <para>
/// On .NET versions prior to 4.6:
/// This method may return before the signal set has propagated (so <see cref="IsSet"/> may return <see langword="false" /> for a bit more if called immediately).
/// The returned task completes when the signal has definitely been set.
/// </para>
/// <para>
/// On .NET 4.6 and later:
/// This method is not asynchronous. The returned Task is always completed.
/// </para>
/// </remarks>
[Obsolete("Use PulseAll() instead."), EditorBrowsable(EditorBrowsableState.Never)]
public Task PulseAllAsync()
{
TaskCompletionSourceWithoutInlining<EmptyStruct>? tcs = null;
TaskCompletionSource<EmptyStruct>? tcs = null;
lock (this.syncObject)
{
// Atomically replace the completion source with a new, uncompleted source
Expand All @@ -200,17 +152,13 @@ public Task PulseAllAsync()
// continue to return completed Tasks due to a Pulse method which should
// execute instantaneously.
tcs = this.taskCompletionSource;
this.taskCompletionSource = this.CreateTaskSource();
this.isSet = false;
this.taskCompletionSource = new(this.options);
}

// Snap the Task that is exposed to the outside so we return that one.
// Once we complete the TaskCompletionSourceWithoutInlinining's task,
// the Task property will return the inner Task.
tcs.TrySetResult(default);

// PulseAllAsync should return the same Task that WaitAsync callers would have observed previously.
Task result = tcs.Task;
tcs.TrySetResult(default(EmptyStruct));
return result;
return tcs.Task;
}

/// <summary>
Expand All @@ -231,12 +179,4 @@ public TaskAwaiter GetAwaiter()
{
return this.WaitAsync().GetAwaiter();
}

/// <summary>
/// Creates a new TaskCompletionSource to represent an unset event.
/// </summary>
private TaskCompletionSourceWithoutInlining<EmptyStruct> CreateTaskSource()
{
return new TaskCompletionSourceWithoutInlining<EmptyStruct>(this.allowInliningAwaiters);
}
}
4 changes: 1 addition & 3 deletions src/Microsoft.VisualStudio.Threading/AsyncQueue`1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

Expand Down Expand Up @@ -298,7 +296,7 @@ public T Peek()
this.FreeCanceledDequeuers();
}

var waiterTcs = new TaskCompletionSourceWithoutInlining<T>(allowInliningContinuations: false);
var waiterTcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
waiterTcs.AttachCancellation(cancellationToken, this);
this.dequeuingWaiters.Enqueue(waiterTcs);
return waiterTcs.Task;
Expand Down
16 changes: 8 additions & 8 deletions src/Microsoft.VisualStudio.Threading/JoinableTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -867,28 +867,28 @@ internal void Post(SendOrPostCallback d, object? state, bool mainThreadAffinitiz
}

/// <summary>
/// Instantiate a <see cref="TaskCompletionSourceWithoutInlining{T}"/> that can track the ultimate result of <see cref="initialDelegate" />.
/// Instantiate a <see cref="TaskCompletionSource{T}"/> that can track the ultimate result of <see cref="initialDelegate" />.
/// </summary>
/// <returns>The new task completion source.</returns>
/// <remarks>
/// The implementation should be sure to instantiate a <see cref="TaskCompletionSource{TResult}"/> that will
/// NOT inline continuations, since we'll be completing this ourselves, potentially while holding a private lock.
/// </remarks>
internal virtual object CreateTaskCompletionSource() => new TaskCompletionSourceWithoutInlining<EmptyStruct>(allowInliningContinuations: false);
internal virtual object CreateTaskCompletionSource() => new TaskCompletionSource<EmptyStruct>(TaskCreationOptions.RunContinuationsAsynchronously);

/// <summary>
/// Retrieves the <see cref="TaskCompletionSourceWithoutInlining{T}.Task"/> from a <see cref="TaskCompletionSourceWithoutInlining{T}"/>.
/// Retrieves the <see cref="TaskCompletionSource{T}.Task"/> from a <see cref="TaskCompletionSource{T}"/>.
/// </summary>
/// <param name="taskCompletionSource">The task completion source.</param>
/// <returns>The <see cref="System.Threading.Tasks.Task"/> that will complete with this <see cref="TaskCompletionSourceWithoutInlining{T}"/>.</returns>
internal virtual Task GetTaskFromCompletionSource(object taskCompletionSource) => ((TaskCompletionSourceWithoutInlining<EmptyStruct>)taskCompletionSource).Task;
/// <returns>The <see cref="System.Threading.Tasks.Task"/> that will complete with this <see cref="TaskCompletionSource{T}"/>.</returns>
internal virtual Task GetTaskFromCompletionSource(object taskCompletionSource) => ((TaskCompletionSource<EmptyStruct>)taskCompletionSource).Task;

/// <summary>
/// Completes a <see cref="TaskCompletionSourceWithoutInlining{T}"/>.
/// Completes a <see cref="TaskCompletionSource{T}"/>.
/// </summary>
/// <param name="wrappedTask">The task to read a result from.</param>
/// <param name="taskCompletionSource">The <see cref="TaskCompletionSourceWithoutInlining{T}"/> created earlier with <see cref="CreateTaskCompletionSource()"/> to apply the result to.</param>
internal virtual void CompleteTaskSourceFromWrappedTask(Task wrappedTask, object taskCompletionSource) => wrappedTask.ApplyResultTo((TaskCompletionSourceWithoutInlining<EmptyStruct>)taskCompletionSource);
/// <param name="taskCompletionSource">The <see cref="TaskCompletionSource{T}"/> created earlier with <see cref="CreateTaskCompletionSource()"/> to apply the result to.</param>
internal virtual void CompleteTaskSourceFromWrappedTask(Task wrappedTask, object taskCompletionSource) => wrappedTask.ApplyResultTo((TaskCompletionSource<EmptyStruct>)taskCompletionSource);

internal void SetWrappedTask(Task wrappedTask)
{
Expand Down
8 changes: 2 additions & 6 deletions src/Microsoft.VisualStudio.Threading/JoinableTask`1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

Expand Down Expand Up @@ -135,10 +131,10 @@ static async Task<T> JoinSlowAsync(JoinableTask<T> me, bool continueOnCapturedCo
}

/// <inheritdoc/>
internal override object CreateTaskCompletionSource() => new TaskCompletionSourceWithoutInlining<T>(allowInliningContinuations: false);
internal override object CreateTaskCompletionSource() => new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);

/// <inheritdoc/>
internal override Task GetTaskFromCompletionSource(object taskCompletionSource) => ((TaskCompletionSourceWithoutInlining<T>)taskCompletionSource).Task;
internal override Task GetTaskFromCompletionSource(object taskCompletionSource) => ((TaskCompletionSource<T>)taskCompletionSource).Task;

/// <inheritdoc/>
internal override void CompleteTaskSourceFromWrappedTask(Task wrappedTask, object taskCompletionSource) => ((Task<T>)wrappedTask).ApplyResultTo((TaskCompletionSource<T>)taskCompletionSource);
Expand Down

This file was deleted.

Loading
Loading