Skip to content

Commit 81055cf

Browse files
Fix async deadlock issue when sending attention fails due to network failure (#1766)
1 parent f2517d4 commit 81055cf

File tree

2 files changed

+24
-22
lines changed

2 files changed

+24
-22
lines changed

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

+13-12
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public TimeoutState(int value)
5555

5656
private const int AttentionTimeoutSeconds = 5;
5757

58-
private static readonly ContextCallback s_readAdyncCallbackComplete = ReadAsyncCallbackComplete;
58+
private static readonly ContextCallback s_readAsyncCallbackComplete = ReadAsyncCallbackComplete;
5959

6060
// Ticks to consider a connection "good" after a successful I/O (10,000 ticks = 1 ms)
6161
// The resolution of the timer is typically in the range 10 to 16 milliseconds according to msdn.
@@ -2337,9 +2337,9 @@ private void OnTimeoutAsync(object state)
23372337
}
23382338
}
23392339

2340-
private bool OnTimeoutSync()
2340+
private bool OnTimeoutSync(bool asyncClose = false)
23412341
{
2342-
return OnTimeoutCore(TimeoutState.Running, TimeoutState.ExpiredSync);
2342+
return OnTimeoutCore(TimeoutState.Running, TimeoutState.ExpiredSync, asyncClose);
23432343
}
23442344

23452345
/// <summary>
@@ -2348,8 +2348,9 @@ private bool OnTimeoutSync()
23482348
/// </summary>
23492349
/// <param name="expectedState">the state that is the expected current state, state will change only if this is correct</param>
23502350
/// <param name="targetState">the state that will be changed to if the expected state is correct</param>
2351+
/// <param name="asyncClose">any close action to be taken by an async task to avoid deadlock.</param>
23512352
/// <returns>boolean value indicating whether the call changed the timeout state</returns>
2352-
private bool OnTimeoutCore(int expectedState, int targetState)
2353+
private bool OnTimeoutCore(int expectedState, int targetState, bool asyncClose = false)
23532354
{
23542355
Debug.Assert(targetState == TimeoutState.ExpiredAsync || targetState == TimeoutState.ExpiredSync, "OnTimeoutCore must have an expiry state as the targetState");
23552356

@@ -2382,7 +2383,7 @@ private bool OnTimeoutCore(int expectedState, int targetState)
23822383
{
23832384
try
23842385
{
2385-
SendAttention(mustTakeWriteLock: true);
2386+
SendAttention(mustTakeWriteLock: true, asyncClose);
23862387
}
23872388
catch (Exception e)
23882389
{
@@ -2927,7 +2928,7 @@ public void ReadAsyncCallback(IntPtr key, PacketHandle packet, uint error)
29272928
// synchrnously and then call OnTimeoutSync to force an atomic change of state.
29282929
if (TimeoutHasExpired)
29292930
{
2930-
OnTimeoutSync();
2931+
OnTimeoutSync(true);
29312932
}
29322933

29332934
// try to change to the stopped state but only do so if currently in the running state
@@ -2958,7 +2959,7 @@ public void ReadAsyncCallback(IntPtr key, PacketHandle packet, uint error)
29582959
{
29592960
if (_executionContext != null)
29602961
{
2961-
ExecutionContext.Run(_executionContext, s_readAdyncCallbackComplete, source);
2962+
ExecutionContext.Run(_executionContext, s_readAsyncCallbackComplete, source);
29622963
}
29632964
else
29642965
{
@@ -3441,7 +3442,7 @@ private void CancelWritePacket()
34413442

34423443
#pragma warning disable 0420 // a reference to a volatile field will not be treated as volatile
34433444

3444-
private Task SNIWritePacket(PacketHandle packet, out uint sniError, bool canAccumulate, bool callerHasConnectionLock)
3445+
private Task SNIWritePacket(PacketHandle packet, out uint sniError, bool canAccumulate, bool callerHasConnectionLock, bool asyncClose = false)
34453446
{
34463447
// Check for a stored exception
34473448
var delayedException = Interlocked.Exchange(ref _delayedWriteAsyncCallbackException, null);
@@ -3534,7 +3535,7 @@ private Task SNIWritePacket(PacketHandle packet, out uint sniError, bool canAccu
35343535
{
35353536
SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObject.SNIWritePacket | Info | State Object Id {0}, Write async returned error code {1}", _objectID, (int)error);
35363537
AddError(_parser.ProcessSNIError(this));
3537-
ThrowExceptionAndWarning();
3538+
ThrowExceptionAndWarning(false, asyncClose);
35383539
}
35393540
AssertValidState();
35403541
completion.SetResult(null);
@@ -3569,7 +3570,7 @@ private Task SNIWritePacket(PacketHandle packet, out uint sniError, bool canAccu
35693570
{
35703571
SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObject.SNIWritePacket | Info | State Object Id {0}, Write async returned error code {1}", _objectID, (int)sniError);
35713572
AddError(_parser.ProcessSNIError(this));
3572-
ThrowExceptionAndWarning(callerHasConnectionLock);
3573+
ThrowExceptionAndWarning(callerHasConnectionLock, asyncClose);
35733574
}
35743575
AssertValidState();
35753576
}
@@ -3581,7 +3582,7 @@ private Task SNIWritePacket(PacketHandle packet, out uint sniError, bool canAccu
35813582
internal abstract uint WritePacket(PacketHandle packet, bool sync);
35823583

35833584
// Sends an attention signal - executing thread will consume attn.
3584-
internal void SendAttention(bool mustTakeWriteLock = false)
3585+
internal void SendAttention(bool mustTakeWriteLock = false, bool asyncClose = false)
35853586
{
35863587
if (!_attentionSent)
35873588
{
@@ -3623,7 +3624,7 @@ internal void SendAttention(bool mustTakeWriteLock = false)
36233624

36243625
uint sniError;
36253626
_parser._asyncWrite = false; // stop async write
3626-
SNIWritePacket(attnPacket, out sniError, canAccumulate: false, callerHasConnectionLock: false);
3627+
SNIWritePacket(attnPacket, out sniError, canAccumulate: false, callerHasConnectionLock: false, asyncClose);
36273628
SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObject.SendAttention | Info | State Object Id {0}, Sent Attention.", _objectID);
36283629
}
36293630
finally

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

+11-10
Original file line numberDiff line numberDiff line change
@@ -2401,9 +2401,9 @@ private void OnTimeoutAsync(object state)
24012401
}
24022402
}
24032403

2404-
private bool OnTimeoutSync()
2404+
private bool OnTimeoutSync(bool asyncClose = false)
24052405
{
2406-
return OnTimeoutCore(TimeoutState.Running, TimeoutState.ExpiredSync);
2406+
return OnTimeoutCore(TimeoutState.Running, TimeoutState.ExpiredSync, asyncClose);
24072407
}
24082408

24092409
/// <summary>
@@ -2412,8 +2412,9 @@ private bool OnTimeoutSync()
24122412
/// </summary>
24132413
/// <param name="expectedState">the state that is the expected current state, state will change only if this is correct</param>
24142414
/// <param name="targetState">the state that will be changed to if the expected state is correct</param>
2415+
/// <param name="asyncClose">any close action to be taken by an async task to avoid deadlock.</param>
24152416
/// <returns>boolean value indicating whether the call changed the timeout state</returns>
2416-
private bool OnTimeoutCore(int expectedState, int targetState)
2417+
private bool OnTimeoutCore(int expectedState, int targetState, bool asyncClose = false)
24172418
{
24182419
Debug.Assert(targetState == TimeoutState.ExpiredAsync || targetState == TimeoutState.ExpiredSync, "OnTimeoutCore must have an expiry state as the targetState");
24192420

@@ -2447,7 +2448,7 @@ private bool OnTimeoutCore(int expectedState, int targetState)
24472448
{
24482449
try
24492450
{
2450-
SendAttention(mustTakeWriteLock: true);
2451+
SendAttention(mustTakeWriteLock: true, asyncClose);
24512452
}
24522453
catch (Exception e)
24532454
{
@@ -2988,7 +2989,7 @@ public void ReadAsyncCallback(IntPtr key, IntPtr packet, UInt32 error)
29882989
// synchrnously and then call OnTimeoutSync to force an atomic change of state.
29892990
if (TimeoutHasExpired)
29902991
{
2991-
OnTimeoutSync();
2992+
OnTimeoutSync(asyncClose: true);
29922993
}
29932994

29942995
// try to change to the stopped state but only do so if currently in the running state
@@ -3475,7 +3476,7 @@ private void CancelWritePacket()
34753476

34763477
#pragma warning disable 420 // a reference to a volatile field will not be treated as volatile
34773478

3478-
private Task SNIWritePacket(SNIHandle handle, SNIPacket packet, out UInt32 sniError, bool canAccumulate, bool callerHasConnectionLock)
3479+
private Task SNIWritePacket(SNIHandle handle, SNIPacket packet, out UInt32 sniError, bool canAccumulate, bool callerHasConnectionLock, bool asyncClose = false)
34793480
{
34803481
// Check for a stored exception
34813482
var delayedException = Interlocked.Exchange(ref _delayedWriteAsyncCallbackException, null);
@@ -3566,7 +3567,7 @@ private Task SNIWritePacket(SNIHandle handle, SNIPacket packet, out UInt32 sniEr
35663567
SqlClientEventSource.Log.TryTraceEvent("<sc.TdsParser.WritePacket|Info> write async returned error code {0}", (int)error);
35673568

35683569
AddError(_parser.ProcessSNIError(this));
3569-
ThrowExceptionAndWarning();
3570+
ThrowExceptionAndWarning(false, asyncClose);
35703571
}
35713572
AssertValidState();
35723573
completion.SetResult(null);
@@ -3603,7 +3604,7 @@ private Task SNIWritePacket(SNIHandle handle, SNIPacket packet, out UInt32 sniEr
36033604
{
36043605
SqlClientEventSource.Log.TryTraceEvent("<sc.TdsParser.WritePacket|Info> write async returned error code {0}", (int)sniError);
36053606
AddError(_parser.ProcessSNIError(this));
3606-
ThrowExceptionAndWarning(callerHasConnectionLock);
3607+
ThrowExceptionAndWarning(callerHasConnectionLock, false);
36073608
}
36083609
AssertValidState();
36093610
}
@@ -3613,7 +3614,7 @@ private Task SNIWritePacket(SNIHandle handle, SNIPacket packet, out UInt32 sniEr
36133614
#pragma warning restore 420
36143615

36153616
// Sends an attention signal - executing thread will consume attn.
3616-
internal void SendAttention(bool mustTakeWriteLock = false)
3617+
internal void SendAttention(bool mustTakeWriteLock = false, bool asyncClose = false)
36173618
{
36183619
if (!_attentionSent)
36193620
{
@@ -3660,7 +3661,7 @@ internal void SendAttention(bool mustTakeWriteLock = false)
36603661

36613662
UInt32 sniError;
36623663
_parser._asyncWrite = false; // stop async write
3663-
SNIWritePacket(Handle, attnPacket, out sniError, canAccumulate: false, callerHasConnectionLock: false);
3664+
SNIWritePacket(Handle, attnPacket, out sniError, canAccumulate: false, callerHasConnectionLock: false, asyncClose);
36643665
SqlClientEventSource.Log.TryTraceEvent("<sc.TdsParser.SendAttention|{0}> Send Attention ASync.", "Info");
36653666
}
36663667
finally

0 commit comments

Comments
 (0)