Пример #1
0
        // return: true ==> throw an OCE(externalCT)
        //         false ==> thrown an AggregateException(ex).
        private static bool ThrowAnOCE(Exception ex, CancellationState cancellationState)
        {
            // if the query has been canceled and the exception represents this, we want to throw OCE
            // but otherwise we want to throw an AggregateException to mimic normal Plinq operation
            // See QueryTaskGroupState.WaitAll for the main plinq exception handling logic.

            // check for co-operative cancellation.
#if PFX_LEGACY_3_5
            OperationCanceledException2 cancelEx = ex as OperationCanceledException2;
#else
            OperationCanceledException cancelEx = ex as OperationCanceledException;
#endif
            if (cancelEx != null &&
                cancelEx.CancellationToken == cancellationState.ExternalCancellationToken &&
                cancellationState.ExternalCancellationToken.IsCancellationRequested)
            {
                return(true);  // let the OCE(extCT) be rethrown.
            }

            // check for external cancellation which triggered the mergedToken.
            if (cancelEx != null &&
                cancelEx.CancellationToken == cancellationState.MergedCancellationToken &&
                cancellationState.MergedCancellationToken.IsCancellationRequested &&
                cancellationState.ExternalCancellationToken.IsCancellationRequested)
            {
                return(true);  // convert internal cancellation back to OCE(extCT).
            }

            return(false);
        }
        //---------------------------------------------------------------------------------------
        // Executes the entire query tree, and aggregates the intermediate results into the
        // final result based on the binary operators and final reduction.
        //
        // Return Value:
        //     The single result of aggregation.
        //

        internal TResult Aggregate()
        {
            TResult   tr;
            Exception toThrow = null;

            try
            {
                tr = InternalAggregate(ref toThrow);
            }
            catch (ThreadAbortException)
            {
                // Do not wrap ThreadAbortExceptions
                throw;
            }
            catch (Exception ex)
            {
                // If the exception is not an aggregate, we must wrap it up and throw that instead.
                if (!(ex is AggregateException))
                {
                    //
                    // Special case: if the query has been canceled, we do not want to wrap the
                    // OperationCanceledException with an AggregateException.
                    //
                    // The query has been canceled iff these conditions hold:
                    // -  The exception thrown is OperationCanceledException
                    // -  We find the external CancellationToken for this query in the OperationCanceledException
                    // -  The externalToken is actually in the canceled state.
#if PFX_LEGACY_3_5
                    OperationCanceledException2 cancelEx = ex as OperationCanceledException2;
#else
                    OperationCanceledException cancelEx = ex as OperationCanceledException;
#endif
                    if (cancelEx != null &&
                        cancelEx.CancellationToken == SpecifiedQuerySettings.CancellationState.ExternalCancellationToken &&
                        SpecifiedQuerySettings.CancellationState.ExternalCancellationToken.IsCancellationRequested)
                    {
                        throw;
                    }

                    throw new AggregateException(ex);
                }

                // Else, just rethrow the current active exception.
                throw;
            }

            // If the aggregation requested that we throw a singular exception, throw it.
            if (toThrow != null)
            {
                throw toThrow;
            }

            return(tr);
        }
Пример #3
0
        //-----------------------------------------------------------------------------------
        // The implementation of the Work API just enumerates the producer's data, and
        // enqueues it into the consumer channel. Well, really, it just defers to extension
        // points (below) implemented by subclasses to do these things.
        //

        protected override void Work()
        {
            try
            {
                // Defer to the base class for the actual work.
                SpoolingWork();
            }
            catch (Exception ex)
            {
#if PFX_LEGACY_3_5
                OperationCanceledException2 oce = ex as OperationCanceledException2;
#else
                OperationCanceledException oce = ex as OperationCanceledException;
#endif
                if (oce != null &&
                    oce.CancellationToken == m_groupState.CancellationState.MergedCancellationToken &&
                    m_groupState.CancellationState.MergedCancellationToken.IsCancellationRequested)
                {
                    //an expected internal cancellation has occurred.  suppress this exception.
                }
                else
                {
                    // TPL will catch and store the exception on the task object. We'll then later
                    // turn around and wait on it, having the effect of propagating it. In the meantime,
                    // we want to cooperative cancel all workers.
                    m_groupState.CancellationState.InternalCancellationTokenSource.Cancel();

                    // And then repropagate to let TPL catch it.
                    throw;
                }
            }
            finally
            {
                SpoolingFinally(); //dispose resources etc.
            }
        }
Пример #4
0
        //-----------------------------------------------------------------------------------
        // Marks the end of a query's execution, waiting for all tasks to finish and
        // propagating any relevant exceptions.  Note that the full set of tasks must have
        // been initialized (with SetTask) before calling this.
        //

        internal void QueryEnd(bool userInitiatedDispose)
        {
            Contract.Assert(m_rootTask != null);
            //Contract.Assert(Task.Current == null || (Task.Current != m_rootTask && Task.Current.Parent != m_rootTask));

            if (Interlocked.Exchange(ref m_alreadyEnded, 1) == 0)
            {
                // There are four cases:
                // Case #1: Wait produced an exception that is not OCE(ct), or an AggregateException which is not full of OCE(ct) ==>  We rethrow.
                // Case #2: External cancellation has been requested ==> we'll manually throw OCE(externalToken).
                // Case #3a: We are servicing a call to Dispose() (and possibly also external cancellation has been requested).. simply return. See bug 695173
                // Case #3b: The enumerator has already been disposed (and possibly also external cancellation was requested).  Throw an ODE.
                // Case #4: No exceptions or explicit call to Dispose() by this caller ==> we just return.

                // See also "InlinedAggregationOperator" which duplicates some of this logic for the aggregators.
                // See also "QueryOpeningEnumerator" which duplicates some of this logic.
                // See also "ExceptionAggregator" which duplicates some of this logic.

                try
                {
                    // Wait for all the tasks to complete
                    // If any of the tasks ended in the Faulted stated, an AggregateException will be thrown.
                    m_rootTask.Wait();
                }
                catch (AggregateException ae)
                {
                    AggregateException flattenedAE = ae.Flatten();
                    bool allOCEsOnTrackedExternalCancellationToken = true;
                    for (int i = 0; i < flattenedAE.InnerExceptions.Count; i++)
                    {
#if PFX_LEGACY_3_5
                        OperationCanceledException2 oce = flattenedAE.InnerExceptions[i] as OperationCanceledException2;
#else
                        OperationCanceledException oce = flattenedAE.InnerExceptions[i] as OperationCanceledException;
#endif

                        // we only let it pass through iff:
                        // it is not null, not default, and matches the exact token we were given as being the external token
                        // and the external Token is actually canceled (ie not a spoof OCE(extCT) for a non-canceled extCT)
                        if (oce == null ||
                            !oce.CancellationToken.IsCancellationRequested ||
                            oce.CancellationToken != m_cancellationState.ExternalCancellationToken)
                        {
                            allOCEsOnTrackedExternalCancellationToken = false;
                            break;
                        }
                    }

                    // if all the exceptions were OCE(externalToken), then we will propogate only a single OCE(externalToken) below
                    // otherwise, we flatten the aggregate (because the WaitAll above already aggregated) and rethrow.
                    if (!allOCEsOnTrackedExternalCancellationToken)
                    {
                        throw flattenedAE;  // Case #1
                    }
                }
                finally
                {
                    m_rootTask.Dispose();
                }

                if (m_cancellationState.MergedCancellationToken.IsCancellationRequested)
                {
                    // cancellation has occured but no user-delegate exceptions were detected

                    // NOTE: it is important that we see other state variables correctly here, and that
                    // read-reordering hasn't played havoc.
                    // This is OK because
                    //   1. all the state writes (eg in the Initiate* methods) are volatile writes (standard .NET MM)
                    //   2. tokenCancellationRequested is backed by a volatile field, hence the reads below
                    //   won't get reordered about the read of token.IsCancellationRequested.

                    // If the query has already been disposed, we don't want to throw an OCE (this is a fix for bug 695173.)
                    if (!m_cancellationState.TopLevelDisposedFlag.Value)
                    {
                        CancellationState.ThrowWithStandardMessageIfCanceled(m_cancellationState.ExternalCancellationToken); // Case #2
                    }

                    //otherwise, given that there were no user-delegate exceptions (they would have been rethrown above),
                    //the only remaining situation is user-initiated dispose.
                    Contract.Assert(m_cancellationState.TopLevelDisposedFlag.Value);

                    // If we aren't actively disposing, that means somebody else previously disposed
                    // of the enumerator. We must throw an ObjectDisposedException.
                    if (!userInitiatedDispose)
                    {
                        throw new ObjectDisposedException("enumerator", "PLINQ_DisposeRequested"); // Case #3
                    }
                }

                // Case #4. nothing to do.
            }
        }