提交 ab2a8d11 编写于 作者: J jaredpar

While inspecting AnalyzerDriver last night I noticed that it would potentially...

While inspecting AnalyzerDriver last night I noticed that it would potentially deadlock if these methods were called on one of the event queues.  Looking through our code base I noticed these methods were never invoked and after discussion with Neal and Sri decided to remove them vs. trying to account for their usage in AnalyzerDriver. (changeset 1390239)
上级 cad10413
......@@ -34,14 +34,6 @@ public void EnqueueAfterComplete()
Assert.Throws(typeof(InvalidOperationException), () => queue.Enqueue(42));
}
[Fact]
public void EnqueueAfterSetException()
{
var queue = new AsyncQueue<int>();
queue.SetException(new Exception());
Assert.Throws(typeof(InvalidOperationException), () => queue.Enqueue(42));
}
[Fact]
public void TryEnqueueAfterComplete()
{
......@@ -51,72 +43,6 @@ public void TryEnqueueAfterComplete()
Assert.False(queue.TryEnqueue(42));
}
[Fact]
public void TryEnqueueAfterSetException()
{
var queue = new AsyncQueue<int>();
Assert.True(queue.TryEnqueue(42));
queue.SetException(new Exception());
Assert.False(queue.TryEnqueue(42));
}
[Fact]
public async Task SetException()
{
var queue = new AsyncQueue<int>();
var exception = new Exception();
queue.SetException(exception);
Assert.True(queue.IsCompleted);
var threw = false;
try
{
await queue.WhenCompletedAsync.ConfigureAwait(false);
}
catch (Exception ex)
{
Assert.Same(exception, ex);
threw = true;
}
Assert.True(threw);
}
[Fact]
public void SetExceptionAfterCompleted()
{
var queue = new AsyncQueue<int>();
queue.Complete();
Assert.Throws(typeof(InvalidOperationException), () => queue.SetException(new Exception()));
}
[Fact]
public async Task SetExceptionThenDequeue()
{
var queue = new AsyncQueue<int>();
var exception = new Exception();
queue.SetException(exception);
var threw = false;
try
{
await queue.DequeueAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
Assert.Same(exception, ex);
threw = true;
}
Assert.True(threw);
}
[Fact]
public void TrySetException()
{
var queue = new AsyncQueue<int>();
Assert.True(queue.TrySetException(new Exception()));
Assert.False(queue.TrySetException(new Exception()));
}
[Fact]
public async Task DequeueThenEnqueue()
{
......@@ -148,30 +74,6 @@ public async Task DequeueManyThenEnqueueMany()
}
}
[Fact]
public async Task DequeueThenSetException()
{
var queue = new AsyncQueue<int>();
var task = queue.DequeueAsync();
Assert.False(task.IsCompleted);
var exception = new Exception();
queue.SetException(exception);
var threw = false;
try
{
await task.ConfigureAwait(false);
}
catch (Exception ex)
{
Assert.Same(exception, ex);
threw = true;
}
Assert.True(threw);
}
[Fact]
public async Task DequeueThenComplete()
{
......@@ -241,38 +143,6 @@ public async Task DequeueAfterCompleteWithData()
Assert.True(threw);
}
[Fact]
public async Task DequeueAfterSetExceptionWithData()
{
var queue = new AsyncQueue<int>();
queue.Enqueue(42);
queue.SetException(new InvalidOperationException());
var threw = false;
try
{
await queue.WhenCompletedAsync.ConfigureAwait(false);
}
catch (InvalidOperationException)
{
threw = true;
}
Assert.True(threw);
Assert.Equal(42, await queue.DequeueAsync().ConfigureAwait(false));
threw = false;
try
{
await queue.DequeueAsync().ConfigureAwait(false);
}
catch (InvalidOperationException)
{
threw = true;
}
Assert.True(threw);
}
[Fact]
public void DequeueAsyncWithCancellation()
{
......@@ -322,50 +192,6 @@ public async Task TaskCompletesAsyncWithComplete()
Assert.True(task.IsCompleted);
}
[Fact]
public async Task TaskCompletesAsyncWithSetException()
{
var queue = new AsyncQueue<int>();
var tcs = new TaskCompletionSource<bool>();
var task = queue.DequeueAsync().ContinueWith(
t =>
{
try
{
tcs.Task.Wait();
}
catch
{
}
return 0;
},
default(CancellationToken),
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
queue.SetException(new Exception());
Assert.False(queue.WhenCompletedAsync.IsCompleted);
tcs.SetResult(true);
var threw = false;
try
{
await queue.WhenCompletedAsync.ConfigureAwait(false);
}
catch
{
threw = true;
}
// The AsyncQueue<T>.Task property won't complete until all of the
// exitsing DequeueAsync values have also completed.
Assert.True(threw);
Assert.True(task.IsCompleted);
}
[Fact]
public void TryDequeue()
{
......@@ -393,28 +219,5 @@ public async Task TryDequeueAfterComplete()
Assert.True(queue.TryDequeue(out value));
Assert.Equal(13, value);
}
[Fact]
public async Task TryDequeueAfterSetException()
{
var queue = new AsyncQueue<int> ();
queue.Enqueue(13);
queue.SetException(new Exception());
var threw = false;
try
{
await queue.WhenCompletedAsync.ConfigureAwait(false);
}
catch
{
threw = true;
}
Assert.True(threw);
int value;
Assert.True(queue.TryDequeue(out value));
Assert.Equal(13, value);
}
}
}
......@@ -17,21 +17,13 @@ namespace Microsoft.CodeAnalysis.Diagnostics
/// <typeparam name="TElement">The type of values kept by the queue.</typeparam>
public sealed class AsyncQueue<TElement>
{
private enum State : byte
{
Active,
Completed,
CompletedWithException,
}
private readonly TaskCompletionSource<bool> whenCompleted = new TaskCompletionSource<bool>();
// Note: All of the below fields are accessed in parallel and may only be accessed
// when protected by lock (SyncObject)
private readonly Queue<TElement> data = new Queue<TElement>();
private Queue<TaskCompletionSource<TElement>> waiters;
private Exception thrown;
private State state = State.Active;
private bool completed;
private object SyncObject
{
......@@ -52,13 +44,6 @@ public int Count
}
}
/// <summary>
/// Initializes a new instance of the <see cref="AsyncQueue{TElement}"/> class.
/// </summary>
public AsyncQueue()
{
}
/// <summary>
/// Adds an element to the tail of the queue. This method will throw if the queue
/// is completed.
......@@ -88,7 +73,7 @@ private bool EnqueueCore(TElement value)
TaskCompletionSource<TElement> waiter;
lock (SyncObject)
{
if (this.state != State.Active)
if (this.completed)
{
return false;
}
......@@ -107,69 +92,6 @@ private bool EnqueueCore(TElement value)
return true;
}
/// <summary>
/// Sets the queue to a completed state with the provided <parameref name="exception"/>. All
/// outstanding and future <see cref="AsyncQueue{TElement}.DequeueAsync"/> Task values will
/// be resolved to this value.
/// </summary>
/// <exception cref="InvalidOperationException">The queue is already completed.</exception>
/// <param name="exception">The exception to be associated with this queue.</param>
public void SetException(Exception exception)
{
if (!SetExceptionCore(exception))
{
throw new InvalidOperationException($"Cannot call ${nameof(SetException)} when the queue is already completed.");
}
}
/// <summary>
/// This has the same effect as <see cref="AsyncQueue{TElement}.SetException"/> except
/// it will not throw an exception if the queue is in a completed state.
/// </summary>
/// <param name="exception">The exception to be associated with this queue.</param>
/// <returns>Whether or not the operation succeeded.</returns>
public bool TrySetException(Exception exception)
{
return SetExceptionCore(exception);
}
private bool SetExceptionCore(Exception exception)
{
if (exception == null)
{
throw new ArgumentNullException(nameof(exception));
}
Queue<TaskCompletionSource<TElement>> existingWaiters;
lock (SyncObject)
{
if (this.state != State.Active)
{
return false;
}
existingWaiters = this.waiters;
this.thrown = exception;
this.state = State.CompletedWithException;
this.waiters = null;
}
Task.Run(() =>
{
if (existingWaiters != null)
{
foreach (var tcs in existingWaiters)
{
tcs.SetException(exception);
}
}
this.whenCompleted.SetException(exception);
});
return true;
}
/// <summary>
/// Attempts to dequeue an existing item and return whether or not it was available.
/// </summary>
......@@ -189,9 +111,7 @@ public bool TryDequeue(out TElement d)
}
/// <summary>
/// Gets a value indicating whether the queue has completed. This is true
/// when the method <see cref="AsyncQueue{TElement}.Complete"/>, <see cref="AsyncQueue{TElement}.SetException(Exception)" />
/// or their Try variants have been called.
/// Gets a value indicating whether the queue has completed.
/// </summary>
public bool IsCompleted
{
......@@ -199,7 +119,7 @@ public bool IsCompleted
{
lock (SyncObject)
{
return this.state != State.Active;
return this.completed;
}
}
}
......@@ -232,14 +152,13 @@ private bool CompleteCore()
Queue<TaskCompletionSource<TElement>> existingWaiters;
lock (SyncObject)
{
if (this.state != State.Active)
if (this.completed)
{
return false;
}
Debug.Assert(this.thrown == null);
existingWaiters = this.waiters;
this.state = State.Completed;
this.completed = true;
this.waiters = null;
}
......@@ -260,9 +179,8 @@ private bool CompleteCore()
}
/// <summary>
/// Gets a task that transitions to a completed state when <see cref="Complete"/>,
/// <see cref="SetException"/> or their Try variants is called. This transition
/// will not happen synchronously.
/// Gets a task that transitions to a completed state when <see cref="Complete"/> or
/// <see cref="TryComplete"/> is called. This transition will not happen synchronously.
///
/// This Task will not complete until it has completed all existing values returned
/// from <see cref="DequeueAsync"/>.
......@@ -278,9 +196,7 @@ public Task WhenCompletedAsync
/// <summary>
/// Gets a task whose result is the element at the head of the queue. If the queue
/// is empty, the returned task waits for an element to be enqueued. If <see cref="Complete"/>
/// is called before an element becomes available, the returned task is cancelled. If
/// <see cref="SetException"/> is called before an element becomes available, the
/// returned task is resolved to that exception.
/// is called before an element becomes available, the returned task is cancelled.
/// </summary>
public Task<TElement> DequeueAsync(CancellationToken cancellationToken = default(CancellationToken))
{
......@@ -317,37 +233,22 @@ private Task<TElement> DequeueAsyncCore()
return Task.FromResult(this.data.Dequeue());
}
switch (this.state)
if (this.completed)
{
case State.Active:
{
var waiter = new TaskCompletionSource<TElement>();
if (this.waiters == null)
{
this.waiters = new Queue<TaskCompletionSource<TElement>>();
}
this.waiters.Enqueue(waiter);
return waiter.Task;
}
case State.Completed:
{
var tcs = new TaskCompletionSource<TElement>();
tcs.SetCanceled();
return tcs.Task;
}
case State.CompletedWithException:
{
Debug.Assert(this.thrown != null);
var tcs = new TaskCompletionSource<TElement>();
tcs.SetException(this.thrown);
return tcs.Task;
}
var tcs = new TaskCompletionSource<TElement>();
tcs.SetCanceled();
return tcs.Task;
}
else
{
if (this.waiters == null)
{
this.waiters = new Queue<TaskCompletionSource<TElement>>();
}
default:
throw ExceptionUtilities.Unreachable;
var waiter = new TaskCompletionSource<TElement>();
this.waiters.Enqueue(waiter);
return waiter.Task;
}
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册