@@ -153,9 +153,17 @@ internal int ObjectID
153
153
// 2) post first packet write, but before session return - a call to cancel will send an
154
154
// attention to the server
155
155
// 3) post session close - no attention is allowed
156
- private bool _cancelled ;
157
156
private const int _waitForCancellationLockPollTimeout = 100 ;
158
157
158
+ private static class CancelState
159
+ {
160
+ public const int Unset = 0 ;
161
+ public const int Closed = 1 ;
162
+ public const int Cancelled = 2 ;
163
+ }
164
+
165
+ private int _cancelState ;
166
+
159
167
// This variable is used to prevent sending an attention by another thread that is not the
160
168
// current owner of the stateObj. I currently do not know how this can happen. Mark added
161
169
// the code but does not remember either. At some point, we need to research killing this
@@ -650,68 +658,57 @@ internal void Activate(object owner)
650
658
Debug . Assert ( result == 1 , "invalid deactivate count" ) ;
651
659
}
652
660
661
+ internal bool SetCancelStateClosed ( )
662
+ {
663
+ return Interlocked . CompareExchange ( ref _cancelState , CancelState . Unset , CancelState . Closed ) == CancelState . Unset ;
664
+ }
665
+
653
666
// This method is only called by the command or datareader as a result of a user initiated
654
667
// cancel request.
655
668
internal void Cancel ( int objectID )
656
669
{
657
- bool hasLock = false ;
658
- try
670
+ // Keep looping until we either grabbed the lock (and therefore sent attention) or the connection closes\breaks
671
+ if (
672
+ ( _parser . State != TdsParserState . Closed ) && ( _parser . State != TdsParserState . Broken ) &&
673
+ Interlocked . CompareExchange ( ref _cancelState , CancelState . Unset , CancelState . Cancelled ) == CancelState . Unset
674
+ )
659
675
{
660
- // Keep looping until we either grabbed the lock (and therefore sent attention) or the connection closes\breaks
661
- while ( ( ! hasLock ) && ( _parser . State != TdsParserState . Closed ) && ( _parser . State != TdsParserState . Broken ) )
676
+ // don't allow objectID -1 since it is reserved for 'not associated with a command'
677
+ // yes, the 2^32-1 comand won't cancel - but it also won't cancel when we don't want it
678
+ if (
679
+ ( objectID == _allowObjectID ) &&
680
+ ( objectID != - 1 ) )
662
681
{
663
-
664
- Monitor . TryEnter ( this , _waitForCancellationLockPollTimeout , ref hasLock ) ;
665
- if ( hasLock )
666
- { // Lock for the time being - since we need to synchronize the attention send.
667
- // At some point in the future, I hope to remove this.
668
- // This lock is also protecting against concurrent close and async continuations
669
-
670
- // don't allow objectID -1 since it is reserved for 'not associated with a command'
671
- // yes, the 2^32-1 comand won't cancel - but it also won't cancel when we don't want it
672
- if ( ( ! _cancelled ) && ( objectID == _allowObjectID ) && ( objectID != - 1 ) )
682
+ if ( _pendingData && ! _attentionSent )
683
+ {
684
+ bool hasParserLock = false ;
685
+ // Keep looping until we have the parser lock (and so are allowed to write), or the conneciton closes\breaks
686
+ while ( ( ! hasParserLock ) && ( _parser . State != TdsParserState . Closed ) && ( _parser . State != TdsParserState . Broken ) )
673
687
{
674
- _cancelled = true ;
675
-
676
- if ( _pendingData && ! _attentionSent )
688
+ try
677
689
{
678
- bool hasParserLock = false ;
679
- // Keep looping until we have the parser lock (and so are allowed to write), or the conneciton closes\breaks
680
- while ( ( ! hasParserLock ) && ( _parser . State != TdsParserState . Closed ) && ( _parser . State != TdsParserState . Broken ) )
690
+ _parser . Connection . _parserLock . Wait ( canReleaseFromAnyThread : false , timeout : _waitForCancellationLockPollTimeout , lockTaken : ref hasParserLock ) ;
691
+ if ( hasParserLock )
681
692
{
682
- try
683
- {
684
- _parser . Connection . _parserLock . Wait ( canReleaseFromAnyThread : false , timeout : _waitForCancellationLockPollTimeout , lockTaken : ref hasParserLock ) ;
685
- if ( hasParserLock )
686
- {
687
- _parser . Connection . ThreadHasParserLockForClose = true ;
688
- SendAttention ( ) ;
689
- }
690
- }
691
- finally
693
+ _parser . Connection . ThreadHasParserLockForClose = true ;
694
+ SendAttention ( ) ;
695
+ }
696
+ }
697
+ finally
698
+ {
699
+ if ( hasParserLock )
700
+ {
701
+ if ( _parser . Connection . ThreadHasParserLockForClose )
692
702
{
693
- if ( hasParserLock )
694
- {
695
- if ( _parser . Connection . ThreadHasParserLockForClose )
696
- {
697
- _parser . Connection . ThreadHasParserLockForClose = false ;
698
- }
699
- _parser . Connection . _parserLock . Release ( ) ;
700
- }
703
+ _parser . Connection . ThreadHasParserLockForClose = false ;
701
704
}
705
+ _parser . Connection . _parserLock . Release ( ) ;
702
706
}
703
707
}
704
708
}
705
709
}
706
710
}
707
711
}
708
- finally
709
- {
710
- if ( hasLock )
711
- {
712
- Monitor . Exit ( this ) ;
713
- }
714
- }
715
712
}
716
713
717
714
// CancelRequest - use to cancel while writing a request to the server
@@ -804,7 +801,7 @@ private void ResetCancelAndProcessAttention()
804
801
lock ( this )
805
802
{
806
803
// Reset cancel state.
807
- _cancelled = false ;
804
+ _cancelState = CancelState . Unset ;
808
805
_allowObjectID = - 1 ;
809
806
810
807
if ( _attentionSent )
@@ -1108,10 +1105,10 @@ internal Task ExecuteFlush()
1108
1105
{
1109
1106
lock ( this )
1110
1107
{
1111
- if ( _cancelled && 1 == _outputPacketNumber )
1108
+ if ( _cancelState != CancelState . Unset && 1 == _outputPacketNumber )
1112
1109
{
1113
1110
ResetBuffer ( ) ;
1114
- _cancelled = false ;
1111
+ _cancelState = CancelState . Unset ;
1115
1112
throw SQL . OperationCancelled ( ) ;
1116
1113
}
1117
1114
else
@@ -3397,7 +3394,7 @@ internal Task WritePacket(byte flushMode, bool canAccumulate = false)
3397
3394
byte packetNumber = _outputPacketNumber;
3398
3395
3399
3396
// Set Status byte based whether this is end of message or not
3400
- bool willCancel = ( _cancelled ) && ( _parser . _asyncWrite ) ;
3397
+ bool willCancel = ( _cancelState != CancelState . Unset ) && ( _parser . _asyncWrite ) ;
3401
3398
if ( willCancel )
3402
3399
{
3403
3400
status = TdsEnums . ST_EOM | TdsEnums . ST_IGNORE ;
@@ -3446,7 +3443,7 @@ internal Task WritePacket(byte flushMode, bool canAccumulate = false)
3446
3443
3447
3444
private void CancelWritePacket ( )
3448
3445
{
3449
- Debug . Assert ( _cancelled , "Should not call CancelWritePacket if _cancelled is not set" ) ;
3446
+ Debug . Assert ( _cancelState != CancelState . Unset , "Should not call CancelWritePacket if _cancelled is not set" ) ;
3450
3447
3451
3448
_parser. Connection . ThreadHasParserLockForClose = true ; // In case of error, let the connection know that we are holding the lock
3452
3449
try
@@ -4128,7 +4125,7 @@ internal void AssertStateIsClean()
4128
4125
Debug . Assert ( _delayedWriteAsyncCallbackException == null , "StateObj has an unobserved exceptions from an async write" ) ;
4129
4126
// Attention\Cancellation\Timeouts
4130
4127
Debug . Assert ( ! _attentionReceived && ! _attentionSent && ! _attentionSending , $ "StateObj is still dealing with attention: Sent: { _attentionSent } , Received: { _attentionReceived } , Sending: { _attentionSending } ") ;
4131
- Debug . Assert ( ! _cancelled , "StateObj still has cancellation set" ) ;
4128
+ Debug . Assert ( _cancelState == CancelState . Unset , "StateObj still has cancellation set" ) ;
4132
4129
Debug . Assert ( _timeoutState == TimeoutState . Stopped , "StateObj still has internal timeout set" ) ;
4133
4130
// Errors and Warnings
4134
4131
Debug . Assert ( ! _hasErrorOrWarning , "StateObj still has stored errors or warnings" ) ;
0 commit comments