Beispiel #1
0
        private void StartAsynchronousComputation(AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously, CancellationToken callerCancellationToken)
        {
            var cancellationToken = computationToStart.CancellationTokenSource.Token;

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                // DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function
                // runs unsynchronized, it's possible that during this function this request
                // might be cancelled, and then a whole additional request might start and
                // complete inline, and cache the result. By grabbing state before we check
                // the cancellation token, we can be assured that we are only operating on
                // a state that was complete.
                try
                {
                    // We avoid creating a full closure just to pass the token along
                    // Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline
                    // the continuation if asynchronousComputeFunction completes synchronously
                    var task = computationToStart.AsynchronousComputeFunction(cancellationToken);

                    task.ContinueWith(
                        (t, s) => CompleteWithTask(t, ((CancellationTokenSource)s).Token),
                        computationToStart.CancellationTokenSource,
                        cancellationToken,
                        TaskContinuationOptions.ExecuteSynchronously,
                        TaskScheduler.Default);

                    if (requestToCompleteSynchronously != null && task.IsCompleted)
                    {
                        using (TakeLock(CancellationToken.None))
                        {
                            task = GetCachedValueAndCacheThisValueIfNoneCached_NoLock(task);
                        }

                        // It's safe to synchronously complete this task, since the Task object hasn't been returned
                        // to the caller of GetValueAsync yet
                        requestToCompleteSynchronously.CompleteFromTaskSynchronously(task);
                    }
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
            catch (OperationCanceledException oce) when(CrashIfCanceledWithDifferentToken(oce, cancellationToken))
            {
                // The underlying computation cancelled with the correct token, but we must ourselves ensure that the caller
                // on our stack gets an OperationCanceledException thrown with the right token
                callerCancellationToken.ThrowIfCancellationRequested();

                // We can only be here if the computation was cancelled, which means all requests for the value
                // must have been cancelled. Therefore, the ThrowIfCancellationRequested above must have thrown
                // because that token from the requestor was cancelled.
                throw ExceptionUtilities.Unreachable;
            }
        }
Beispiel #2
0
        private void StartAsynchronousComputation(AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously)
        {
            var cancellationToken = computationToStart.CancellationTokenSource.Token;

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                // DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function
                // runs unsynchronized, it's possible that during this function this request
                // might be cancelled, and then a whole additional request might start and
                // complete inline, and cache the result. By grabbing state before we check
                // the cancellation token, we can be assured that we are only operating on
                // a state that was complete.
                try
                {
                    // We avoid creating a full closure just to pass the token along
                    // Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline
                    // the continuation if asynchronousComputeFunction completes synchronously
                    var task = computationToStart.AsynchronousComputeFunction(cancellationToken);

                    task.ContinueWith(
                        (t, s) => CompleteWithTask(t, ((CancellationTokenSource)s).Token),
                        computationToStart.CancellationTokenSource,
                        cancellationToken,
                        TaskContinuationOptions.ExecuteSynchronously,
                        TaskScheduler.Default);

                    if (requestToCompleteSynchronously != null && task.IsCompleted)
                    {
                        using (TakeLock(CancellationToken.None))
                        {
                            task = GetCachedValueAndCacheThisValueIfNoneCached_NoLock(task);
                        }

                        requestToCompleteSynchronously.CompleteFromTaskSynchronously(task);
                    }
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
            catch (OperationCanceledException oce) when(CrashIfCanceledWithDifferentToken(oce, cancellationToken))
            {
                // As long as it's the right token, this means that our thread was the first thread
                // to start an asynchronous computation, but the requestor cancelled as we were starting up
                // the computation.
                throw ExceptionUtilities.Unreachable;
            }
        }
Beispiel #3
0
        private void StartAsynchronousComputation(AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously, CancellationToken callerCancellationToken)
        {
            var cancellationToken = computationToStart.CancellationTokenSource.Token;

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                // DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function
                // runs unsynchronized, it's possible that during this function this request
                // might be cancelled, and then a whole additional request might start and
                // complete inline, and cache the result. By grabbing state before we check
                // the cancellation token, we can be assured that we are only operating on
                // a state that was complete.
                try
                {
                    var task = computationToStart.AsynchronousComputeFunction(cancellationToken);

                    // As an optimization, if the task is already completed, mark the
                    // request as being completed as well.
                    //
                    // 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).
                    if (requestToCompleteSynchronously != null && task.IsCompleted)
                    {
                        using (TakeLock(CancellationToken.None))
                        {
                            task = GetCachedValueAndCacheThisValueIfNoneCached_NoLock(task);
                        }

                        // It's safe to synchronously complete this task, since the Task object hasn't been returned
                        // to the caller of GetValueAsync yet
                        requestToCompleteSynchronously.CompleteFromTaskSynchronously(task);
                    }

                    // We avoid creating a full closure just to pass the token along
                    // Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline
                    // the continuation if asynchronousComputeFunction completes synchronously
                    task.ContinueWith(
                        (t, s) => CompleteWithTask(t, ((CancellationTokenSource)s).Token),
                        computationToStart.CancellationTokenSource,
                        cancellationToken,
                        TaskContinuationOptions.ExecuteSynchronously,
                        TaskScheduler.Default);
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
            catch (OperationCanceledException oce) when(CrashIfCanceledWithDifferentToken(oce, cancellationToken))
            {
                // The underlying computation cancelled with the correct token, but we must ourselves ensure that the caller
                // on our stack gets an OperationCanceledException thrown with the right token
                callerCancellationToken.ThrowIfCancellationRequested();

                // We can only be here if the computation was cancelled, which means all requests for the value
                // must have been cancelled. Therefore, the ThrowIfCancellationRequested above must have thrown
                // because that token from the requester was cancelled.
                throw ExceptionUtilities.Unreachable;
            }
        }