Beispiel #1
0
        public override Task <T> GetValueAsync(CancellationToken cancellationToken)
        {
            // Optimization: if we're already cancelled, do not pass go
            if (cancellationToken.IsCancellationRequested)
            {
                return(Task.FromCanceled <T>(cancellationToken));
            }

            // Avoid taking the lock if a cached value is available
            var cachedResult = _cachedResult;

            if (cachedResult != null)
            {
                return(cachedResult);
            }

            Request request;
            AsynchronousComputationToStart?newAsynchronousComputation = null;

            using (TakeLock(cancellationToken))
            {
                // If cached, get immediately
                if (_cachedResult != null)
                {
                    return(_cachedResult);
                }

                request = CreateNewRequest_NoLock();

                // If we have either synchronous or asynchronous work current in flight, we don't need to do anything.
                // Otherwise, we shall start an asynchronous computation for this
                if (!_computationActive)
                {
                    newAsynchronousComputation = RegisterAsynchronousComputation_NoLock();
                }
            }

            // We now have the request counted for, register for cancellation. It is critical this is
            // done outside the lock, as our registration may immediately fire and we want to avoid the
            // reentrancy
            request.RegisterForCancellation(OnAsynchronousRequestCancelled, cancellationToken);

            if (newAsynchronousComputation != null)
            {
                StartAsynchronousComputation(newAsynchronousComputation.Value, requestToCompleteSynchronously: request, callerCancellationToken: cancellationToken);
            }

            return(request.Task);
        }
Beispiel #2
0
        public override T GetValue(CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            Request request = null;
            AsynchronousComputationToStart?newAsynchronousComputation = null;

            using (TakeLock(cancellationToken))
            {
                // If cached, get immediately
                if (_cachedResult != null)
                {
                    return(_cachedResult.Result);
                }

                // If there is an existing computation active, we'll just create another request
                if (_computationActive)
                {
                    request = CreateNewRequest_NoLock();
                }
                else if (_synchronousComputeFunction == null)
                {
                    // A synchronous request, but we have no synchronous function. Start off the async work
                    request = CreateNewRequest_NoLock();

                    newAsynchronousComputation = RegisterAsynchronousComputation_NoLock();
                }
                else
                {
                    // We will do the computation here
                    _computationActive = true;
                }
            }

            // If we simply created a new asynchronous request, so wait for it. Yes, we're blocking the thread
            // but we don't want multiple threads attempting to compute the same thing.
            if (request != null)
            {
                request.RegisterForCancellation(OnAsynchronousRequestCancelled, cancellationToken);

                // Since we already registered for cancellation, it's possible that the registration has
                // cancelled this new computation if we were the only requester.
                if (newAsynchronousComputation != null)
                {
                    StartAsynchronousComputation(newAsynchronousComputation.Value, requestToCompleteSynchronously: request, callerCancellationToken: cancellationToken);
                }

                // The reason we have synchronous codepaths in AsyncLazy is to support the synchronous requests for syntax trees
                // that we may get from the compiler. Thus, it's entirely possible that this will be requested by the compiler or
                // an analyzer on the background thread when another part of the IDE is requesting the same tree asynchronously.
                // In that case we block the synchronous request on the asynchronous request, since that's better than alternatives.
                return(request.Task.WaitAndGetResult_CanCallOnBackground(cancellationToken));
            }
            else
            {
                T result;

                // We are the active computation, so let's go ahead and compute.
                try
                {
                    result = _synchronousComputeFunction(cancellationToken);
                }
                catch (OperationCanceledException)
                {
                    // This cancelled for some reason. We don't care why, but
                    // it means anybody else waiting for this result isn't going to get it
                    // from us.
                    using (TakeLock(CancellationToken.None))
                    {
                        _computationActive = false;

                        if (_requests != null)
                        {
                            // There's a possible improvement here: there might be another synchronous caller who
                            // also wants the value. We might consider stealing their thread rather than punting
                            // to the thread pool.
                            newAsynchronousComputation = RegisterAsynchronousComputation_NoLock();
                        }
                    }

                    if (newAsynchronousComputation != null)
                    {
                        StartAsynchronousComputation(newAsynchronousComputation.Value, requestToCompleteSynchronously: null, callerCancellationToken: cancellationToken);
                    }

                    throw;
                }
                catch (Exception ex)
                {
                    // We faulted for some unknown reason. We should simply fault everything.
                    CompleteWithTask(Task.FromException <T>(ex), CancellationToken.None);
                    throw;
                }

                // We have a value, so complete
                CompleteWithTask(Task.FromResult(result), CancellationToken.None);

                // Optimization: if they did cancel and the computation never observed it, let's throw so we don't keep
                // processing a value somebody never wanted
                cancellationToken.ThrowIfCancellationRequested();

                return(result);
            }
        }
Beispiel #3
0
        public override T GetValue(CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            Request request = null;
            AsynchronousComputationToStart?newAsynchronousComputation = null;

            using (TakeLock(cancellationToken))
            {
                // If cached, get immediately
                if (_cachedResult != null)
                {
                    return(_cachedResult.Result);
                }

                // If there is an existing computation active, we'll just create another request
                if (_computationActive)
                {
                    request = CreateNewRequest_NoLock();
                }
                else if (_synchronousComputeFunction == null)
                {
                    // A synchronous request, but we have no synchronous function. Start off the async work
                    request = CreateNewRequest_NoLock();

                    newAsynchronousComputation = RegisterAsynchronousComputation_NoLock();
                }
                else
                {
                    // We will do the computation here
                    _computationActive = true;
                }
            }

            // If we simply created a new asynchronous request, so wait for it. Yes, we're blocking the thread
            // but we don't want multiple threads attempting to compute the same thing.
            if (request != null)
            {
                request.RegisterForCancellation(OnAsynchronousRequestCancelled, cancellationToken);

                // Since we already registered for cancellation, it's possible that the registration has
                // cancelled this new computation if we were the only requestor.
                if (newAsynchronousComputation != null)
                {
                    StartAsynchronousComputation(newAsynchronousComputation.Value, requestToCompleteSynchronously: request);
                }

                return(request.Task.WaitAndGetResult(cancellationToken));
            }
            else
            {
                T result;

                // We are the active computation, so let's go ahead and compute.
                try
                {
                    result = _synchronousComputeFunction(cancellationToken);
                }
                catch (OperationCanceledException)
                {
                    // This cancelled for some reason. We don't care why, but
                    // it means anybody else waiting for this result isn't going to get it
                    // from us.
                    using (TakeLock(CancellationToken.None))
                    {
                        _computationActive = false;

                        if (_requests != null)
                        {
                            // There's a possible improvement here: there might be another synchronous caller who
                            // also wants the value. We might consider stealing their thread rather than punting
                            // to the thread pool.
                            newAsynchronousComputation = RegisterAsynchronousComputation_NoLock();
                        }
                    }

                    if (newAsynchronousComputation != null)
                    {
                        StartAsynchronousComputation(newAsynchronousComputation.Value, requestToCompleteSynchronously: null);
                    }

                    throw;
                }
                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);

                    throw;
                }

                // We have a value, so complete
                CompleteWithTask(Task.FromResult(result), CancellationToken.None);

                return(result);
            }
        }