提交 f43398f3 编写于 作者: C CyrusNajmabadi

Move to TaskCompletionSource in AsyncLazy now that we're no longer on .net 4.5

上级 42bb20e5
......@@ -101,7 +101,6 @@ public AsyncLazy(Func<CancellationToken, Task<T>> asynchronousComputeFunction, b
public AsyncLazy(Func<CancellationToken, Task<T>> asynchronousComputeFunction, Func<CancellationToken, T> synchronousComputeFunction, bool cacheResult)
{
Contract.ThrowIfNull(asynchronousComputeFunction);
_asynchronousComputeFunction = asynchronousComputeFunction;
_synchronousComputeFunction = synchronousComputeFunction;
_cacheResult = cacheResult;
......@@ -262,10 +261,7 @@ public override T GetValue(CancellationToken cancellationToken)
catch (Exception ex)
{
// We faulted for some unknown reason. We should simply fault everything.
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
tcs.SetException(ex);
CompleteWithTask(tcs.Task, CancellationToken.None);
CompleteWithTask(Task.FromException<T>(ex), CancellationToken.None);
throw;
}
......@@ -379,11 +375,7 @@ private void StartAsynchronousComputation(AsynchronousComputationToStart computa
//
// Note: we want to do this before we do the .ContinueWith below. That way,
// when the async call to CompleteWithTask runs, it sees that we've already
// completed and can bail immediately. If we were to do this after we
// kicked off the async work, then we'd have the chance that both would
// run concurrently and we'd have a higher change of hitting the race condition
// of calling AsyncMethodBuilder.SetResult simultaneously (and thus having
// the InvalidOperationException that we have to ignore).
// completed and can bail immediately.
if (requestToCompleteSynchronously != null && task.IsCompleted)
{
using (TakeLock(CancellationToken.None))
......@@ -533,25 +525,15 @@ private sealed class Request
private CancellationToken _cancellationToken;
private CancellationTokenRegistration _cancellationTokenRegistration;
// We use a AsyncTaskMethodBuilder so we have the ability to cancel the task with a given cancellation token
// TODO: remove this once we're on .NET 4.6 and can move back to using TaskCompletionSource.
// WARNING: this is a mutable struct, and thus cannot be made readonly
private AsyncTaskMethodBuilder<T> _taskBuilder;
private TaskCompletionSource<T> _taskCompletionSource;
public Request()
{
// .Task on AsyncTaskMethodBuilder is lazily created in a non-synchronized way, so we must request it
// once before we start doing fancy stuff
var ignored = _taskBuilder.Task;
_taskCompletionSource = new TaskCompletionSource<T>();
}
public Task<T> Task
{
get
{
return _taskBuilder.Task;
}
}
public Task<T> Task => _taskCompletionSource.Task;
public void RegisterForCancellation(Action<object> callback, CancellationToken cancellationToken)
{
......@@ -575,31 +557,24 @@ public void CompleteFromTaskSynchronously(Task<T> task)
// is already completed. The belief is that the race is somewhere between rare to impossible, and
// so we'll do a quick check to see if the task is already completed or otherwise just give it a shot
// and catch it if it fails
if (_taskBuilder.Task.IsCompleted)
if (this.Task.IsCompleted)
{
return;
}
try
// As an optimization, we'll cancel the request even we did get a value for it.
// That way things abort sooner.
if (task.IsCanceled || _cancellationToken.IsCancellationRequested)
{
// As an optimization, we'll cancel the request even we did get a value for it.
// That way things abort sooner.
if (task.IsCanceled || _cancellationToken.IsCancellationRequested)
{
CancelSynchronously();
}
else if (task.IsFaulted)
{
_taskBuilder.SetException(task.Exception);
}
else
{
_taskBuilder.SetResult(task.Result);
}
CancelSynchronously();
}
catch (InvalidOperationException)
else if (task.IsFaulted)
{
// Something else beat us to setting the state, so bail
_taskCompletionSource.TrySetException(task.Exception);
}
else
{
_taskCompletionSource.TrySetResult(task.Result);
}
_cancellationTokenRegistration.Dispose();
......@@ -609,29 +584,14 @@ public void CancelAsynchronously()
{
// Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool
// to avoid inline running of other operations.
System.Threading.Tasks.Task.Factory.StartNew(CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
System.Threading.Tasks.Task.Factory.StartNew(
CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
}
private void CancelSynchronously()
{
// AsyncTaskMethodBuilder doesn't give us Try* methods, and the Set methods may throw if the task
// is already completed. The belief is that the race is somewhere between rare to impossible, and
// so we'll do a quick check to see if the task is already completed or otherwise just give it a shot
// and catch it if it fails
if (_taskBuilder.Task.IsCompleted)
{
return;
}
try
{
_taskBuilder.SetException(new OperationCanceledException(_cancellationToken));
}
catch (InvalidOperationException)
{
// Something else beat us to setting the state, so bail
}
_taskCompletionSource.TrySetCanceled(_cancellationToken);
}
}
}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册