3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
8 // CancellationState.cs
10 // <OWNER>Microsoft</OWNER>
12 // A bag of cancellation-related items that are passed around as a group.
14 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
17 using System
.Collections
.Generic
;
18 using System
.Threading
;
20 using System
.Core
; // for System.Core.SR
23 namespace System
.Linq
.Parallel
25 internal class CancellationState
27 // a cancellation signal that can be set internally to prompt early query termination.
28 internal CancellationTokenSource InternalCancellationTokenSource
;
30 // the external cancellationToken that the user sets to ask for the query to terminate early.
31 // this has to be tracked explicitly so that an OCE(externalToken) can be thrown as the query
32 // execution unravels.
33 internal CancellationToken ExternalCancellationToken
;
35 // A combined token Source for internal/external cancellation, defining the total cancellation state.
36 internal CancellationTokenSource MergedCancellationTokenSource
;
38 // A combined token for internal/external cancellation, defining the total cancellation state.
39 internal CancellationToken MergedCancellationToken
43 if( MergedCancellationTokenSource
!= null)
44 return MergedCancellationTokenSource
.Token
;
46 return new CancellationToken(false);
50 // A shared boolean flag to track whether a query-opening-enumerator dispose has occured.
51 internal Shared
<bool> TopLevelDisposedFlag
;
53 internal CancellationState(CancellationToken externalCancellationToken
)
55 ExternalCancellationToken
= externalCancellationToken
;
56 TopLevelDisposedFlag
= new Shared
<bool>(false); //it would always be initialised to false, so no harm doing it here and avoid #if around constructors.
60 /// Poll frequency (number of loops per cancellation check) for situations where per-1-loop testing is too high an overhead.
62 internal const int POLL_INTERVAL
= 63; //must be of the form (2^n)-1.
64 // The two main situations requiring POLL_INTERVAL are:
65 // 1. inner loops of sorting/merging operations
66 // 2. tight loops that perform very little work per MoveNext call.
67 // Testing has shown both situations have similar requirements and can share the same constant for polling interval.
69 // Because the poll checks are per-N loops, if there are delays in user code, they may affect cancellation timeliness.
70 // Guidance is that all user-delegates should perform cancellation checks at least every 1ms.
72 // Inner loop code should poll once per n loop, typically via:
73 // if ((i++ & CancellationState.POLL_INTERVAL) == 0)
74 // CancellationState.ThrowIfCanceled(m_cancellationToken);
75 // (Note, this only behaves as expected if FREQ is of the form (2^n)-1
78 /// Throws an OCE if the merged token has been canceled.
80 /// <param name="token">A token to check for cancelation.</param>
81 internal static void ThrowIfCanceled(CancellationToken token
)
83 if (token
.IsCancellationRequested
)
84 throw new OperationCanceledException(token
);
87 // Test if external cancellation was requested and occured, and if so throw a standardize OCE with standardized message
88 internal static void ThrowWithStandardMessageIfCanceled(CancellationToken externalCancellationToken
)
90 if (externalCancellationToken
.IsCancellationRequested
)
92 string oceMessage
= SR
.GetString(SR
.PLINQ_ExternalCancellationRequested
);
93 throw new OperationCanceledException(oceMessage
, externalCancellationToken
);