Skip to content

Commit 41af8ac

Browse files
committed
implement on netfx
1 parent 598d450 commit 41af8ac

File tree

2 files changed

+67
-85
lines changed

2 files changed

+67
-85
lines changed

src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs

+18-33
Original file line numberDiff line numberDiff line change
@@ -1757,19 +1757,13 @@ private int EndExecuteNonQueryAsync(IAsyncResult asyncResult)
17571757
else
17581758
{
17591759
ThrowIfReconnectionHasBeenCanceled();
1760-
// lock on _stateObj prevents races with close/cancel.
1761-
// If we have already initiate the End call internally, we have already done that, so no point doing it again.
1762-
if (!_internalEndExecuteInitiated)
1760+
if (!_internalEndExecuteInitiated && _stateObj != null)
17631761
{
1764-
lock (_stateObj)
1765-
{
1766-
return EndExecuteNonQueryInternal(asyncResult);
1767-
}
1768-
}
1769-
else
1770-
{
1771-
return EndExecuteNonQueryInternal(asyncResult);
1762+
// call SetCancelStateClosed on the stateobject to ensure that cancel cannot
1763+
// happen after we have changed started the end processing
1764+
_stateObj.SetCancelStateClosed();
17721765
}
1766+
return EndExecuteNonQueryInternal(asyncResult);
17731767
}
17741768
}
17751769

@@ -2270,19 +2264,14 @@ private XmlReader EndExecuteXmlReaderAsync(IAsyncResult asyncResult)
22702264
else
22712265
{
22722266
ThrowIfReconnectionHasBeenCanceled();
2273-
// lock on _stateObj prevents races with close/cancel.
2274-
// If we have already initiate the End call internally, we have already done that, so no point doing it again.
2275-
if (!_internalEndExecuteInitiated)
2276-
{
2277-
lock (_stateObj)
2278-
{
2279-
return EndExecuteXmlReaderInternal(asyncResult);
2280-
}
2281-
}
2282-
else
2267+
if (!_internalEndExecuteInitiated && _stateObj != null)
22832268
{
2284-
return EndExecuteXmlReaderInternal(asyncResult);
2269+
// call SetCancelStateClosed on the stateobject to ensure that cancel cannot
2270+
// happen after we have changed started the end processing
2271+
_stateObj.SetCancelStateClosed();
22852272
}
2273+
2274+
return EndExecuteXmlReaderInternal(asyncResult);
22862275
}
22872276
}
22882277

@@ -2532,19 +2521,15 @@ private SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult)
25322521
else
25332522
{
25342523
ThrowIfReconnectionHasBeenCanceled();
2535-
// lock on _stateObj prevents races with close/cancel.
2536-
// If we have already initiate the End call internally, we have already done that, so no point doing it again.
2537-
if (!_internalEndExecuteInitiated)
2538-
{
2539-
lock (_stateObj)
2540-
{
2541-
return EndExecuteReaderInternal(asyncResult);
2542-
}
2543-
}
2544-
else
2524+
2525+
if (!_internalEndExecuteInitiated && _stateObj != null)
25452526
{
2546-
return EndExecuteReaderInternal(asyncResult);
2527+
// call SetCancelStateClosed on the stateobject to ensure that cancel cannot happen after
2528+
// we have changed started the end processing
2529+
_stateObj.SetCancelStateClosed();
25472530
}
2531+
2532+
return EndExecuteReaderInternal(asyncResult);
25482533
}
25492534
}
25502535

src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs

+49-52
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,17 @@ internal int ObjectID
153153
// 2) post first packet write, but before session return - a call to cancel will send an
154154
// attention to the server
155155
// 3) post session close - no attention is allowed
156-
private bool _cancelled;
157156
private const int _waitForCancellationLockPollTimeout = 100;
158157

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+
159167
// This variable is used to prevent sending an attention by another thread that is not the
160168
// current owner of the stateObj. I currently do not know how this can happen. Mark added
161169
// 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)
650658
Debug.Assert(result == 1, "invalid deactivate count");
651659
}
652660

661+
internal bool SetCancelStateClosed()
662+
{
663+
return Interlocked.CompareExchange(ref _cancelState, CancelState.Unset, CancelState.Closed) == CancelState.Unset;
664+
}
665+
653666
// This method is only called by the command or datareader as a result of a user initiated
654667
// cancel request.
655668
internal void Cancel(int objectID)
656669
{
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+
)
659675
{
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))
662681
{
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))
673687
{
674-
_cancelled = true;
675-
676-
if (_pendingData && !_attentionSent)
688+
try
677689
{
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)
681692
{
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)
692702
{
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;
701704
}
705+
_parser.Connection._parserLock.Release();
702706
}
703707
}
704708
}
705709
}
706710
}
707711
}
708-
finally
709-
{
710-
if (hasLock)
711-
{
712-
Monitor.Exit(this);
713-
}
714-
}
715712
}
716713

717714
// CancelRequest - use to cancel while writing a request to the server
@@ -804,7 +801,7 @@ private void ResetCancelAndProcessAttention()
804801
lock (this)
805802
{
806803
// Reset cancel state.
807-
_cancelled = false;
804+
_cancelState = CancelState.Unset;
808805
_allowObjectID = -1;
809806

810807
if (_attentionSent)
@@ -1108,10 +1105,10 @@ internal Task ExecuteFlush()
11081105
{
11091106
lock (this)
11101107
{
1111-
if (_cancelled && 1 == _outputPacketNumber)
1108+
if (_cancelState != CancelState.Unset && 1 == _outputPacketNumber)
11121109
{
11131110
ResetBuffer();
1114-
_cancelled = false;
1111+
_cancelState = CancelState.Unset;
11151112
throw SQL.OperationCancelled();
11161113
}
11171114
else
@@ -3397,7 +3394,7 @@ internal Task WritePacket(byte flushMode, bool canAccumulate = false)
33973394
byte packetNumber = _outputPacketNumber;
33983395

33993396
// 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);
34013398
if (willCancel)
34023399
{
34033400
status = TdsEnums.ST_EOM | TdsEnums.ST_IGNORE;
@@ -3446,7 +3443,7 @@ internal Task WritePacket(byte flushMode, bool canAccumulate = false)
34463443

34473444
private void CancelWritePacket()
34483445
{
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");
34503447

34513448
_parser.Connection.ThreadHasParserLockForClose = true; // In case of error, let the connection know that we are holding the lock
34523449
try
@@ -4128,7 +4125,7 @@ internal void AssertStateIsClean()
41284125
Debug.Assert(_delayedWriteAsyncCallbackException == null, "StateObj has an unobserved exceptions from an async write");
41294126
// Attention\Cancellation\Timeouts
41304127
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");
41324129
Debug.Assert(_timeoutState == TimeoutState.Stopped, "StateObj still has internal timeout set");
41334130
// Errors and Warnings
41344131
Debug.Assert(!_hasErrorOrWarning, "StateObj still has stored errors or warnings");

0 commit comments

Comments
 (0)