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) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } } catch (OperationCanceledException oce) if (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; } }
public static Task SafeStartNew( this TaskFactory factory, Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler) { Action wrapped = () => { try { action(); } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }; // The one and only place we can call StartNew(). return(factory.StartNew(wrapped, cancellationToken, creationOptions, scheduler)); }
public static Task <TResult> SafeStartNew <TResult>( this TaskFactory factory, Func <TResult> func, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler) { Func <TResult> wrapped = () => { try { return(func()); } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }; // The one and only place we can call StartNew<>(). return(factory.StartNew(wrapped, cancellationToken, creationOptions, scheduler)); }