Skip to content

Commit 6190851

Browse files
committed
Merging of TdsParserStateObject.ReadAsyncCallback (note: ports parts of dotnet#378 and dotnet#528 to netfx).
1 parent df6e07b commit 6190851

File tree

3 files changed

+158
-291
lines changed

3 files changed

+158
-291
lines changed

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

Lines changed: 0 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ internal abstract partial class TdsParserStateObject
1919
{
2020
private static bool UseManagedSNI => TdsParserStateObjectFactory.UseManagedSNI;
2121

22-
private static readonly ContextCallback s_readAsyncCallbackComplete = ReadAsyncCallbackComplete;
23-
2422
// Timeout variables
2523
private readonly WeakReference _cancellationOwner = new WeakReference(null);
2624

@@ -394,157 +392,8 @@ private void SetBufferSecureStrings()
394392
public void ReadAsyncCallback(PacketHandle packet, uint error) =>
395393
ReadAsyncCallback(IntPtr.Zero, packet, error);
396394

397-
public void ReadAsyncCallback(IntPtr key, PacketHandle packet, uint error)
398-
{
399-
// Key never used.
400-
// Note - it's possible that when native calls managed that an asynchronous exception
401-
// could occur in the native->managed transition, which would
402-
// have two impacts:
403-
// 1) user event not called
404-
// 2) DecrementPendingCallbacks not called, which would mean this object would be leaked due
405-
// to the outstanding GCRoot until AppDomain.Unload.
406-
// We live with the above for the time being due to the constraints of the current
407-
// reliability infrastructure provided by the CLR.
408-
SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObject.ReadAsyncCallback | Info | State Object Id {0}, received error {1} on idle connection", _objectID, (int)error);
409-
410-
TaskCompletionSource<object> source = _networkPacketTaskSource;
411-
#if DEBUG
412-
if ((s_forcePendingReadsToWaitForUser) && (_realNetworkPacketTaskSource != null))
413-
{
414-
source = _realNetworkPacketTaskSource;
415-
}
416-
#endif
417-
418-
// The mars physical connection can get a callback
419-
// with a packet but no result after the connection is closed.
420-
if (source == null && _parser._pMarsPhysicalConObj == this)
421-
{
422-
return;
423-
}
424-
425-
bool processFinallyBlock = true;
426-
try
427-
{
428-
Debug.Assert(CheckPacket(packet, source) && source != null, "AsyncResult null on callback");
429-
430-
if (_parser.MARSOn)
431-
{
432-
// Only take reset lock on MARS and Async.
433-
CheckSetResetConnectionState(error, CallbackType.Read);
434-
}
435-
436-
ChangeNetworkPacketTimeout(Timeout.Infinite, Timeout.Infinite);
437-
438-
// The timer thread may be unreliable under high contention scenarios. It cannot be
439-
// assumed that the timeout has happened on the timer thread callback. Check the timeout
440-
// synchrnously and then call OnTimeoutSync to force an atomic change of state.
441-
if (TimeoutHasExpired)
442-
{
443-
OnTimeoutSync();
444-
}
445-
446-
// try to change to the stopped state but only do so if currently in the running state
447-
// and use cmpexch so that all changes out of the running state are atomic
448-
int previousState = Interlocked.CompareExchange(ref _timeoutState, TimeoutState.Stopped, TimeoutState.Running);
449-
450-
// if the state is anything other than running then this query has reached an end so
451-
// set the correlation _timeoutIdentityValue to 0 to prevent late callbacks executing
452-
if (_timeoutState != TimeoutState.Running)
453-
{
454-
_timeoutIdentityValue = 0;
455-
}
456-
457-
ProcessSniPacket(packet, error);
458-
}
459-
catch (Exception e)
460-
{
461-
processFinallyBlock = ADP.IsCatchableExceptionType(e);
462-
throw;
463-
}
464-
finally
465-
{
466-
// pendingCallbacks may be 2 after decrementing, this indicates that a fatal timeout is occurring, and therefore we shouldn't complete the task
467-
int pendingCallbacks = DecrementPendingCallbacks(false); // may dispose of GC handle.
468-
if ((processFinallyBlock) && (source != null) && (pendingCallbacks < 2))
469-
{
470-
if (error == 0)
471-
{
472-
if (_executionContext != null)
473-
{
474-
ExecutionContext.Run(_executionContext, s_readAsyncCallbackComplete, source);
475-
}
476-
else
477-
{
478-
source.TrySetResult(null);
479-
}
480-
}
481-
else
482-
{
483-
if (_executionContext != null)
484-
{
485-
ExecutionContext.Run(_executionContext, state => ReadAsyncCallbackCaptureException((TaskCompletionSource<object>)state), source);
486-
}
487-
else
488-
{
489-
ReadAsyncCallbackCaptureException(source);
490-
}
491-
}
492-
}
493-
494-
AssertValidState();
495-
}
496-
}
497-
498-
private static void ReadAsyncCallbackComplete(object state)
499-
{
500-
TaskCompletionSource<object> source = (TaskCompletionSource<object>)state;
501-
source.TrySetResult(null);
502-
}
503-
504395
protected abstract bool CheckPacket(PacketHandle packet, TaskCompletionSource<object> source);
505396

506-
private void ReadAsyncCallbackCaptureException(TaskCompletionSource<object> source)
507-
{
508-
bool captureSuccess = false;
509-
try
510-
{
511-
if (_hasErrorOrWarning)
512-
{
513-
// Do the close on another thread, since we don't want to block the callback thread
514-
ThrowExceptionAndWarning(asyncClose: true);
515-
}
516-
else if ((_parser.State == TdsParserState.Closed) || (_parser.State == TdsParserState.Broken))
517-
{
518-
// Connection was closed by another thread before we parsed the packet, so no error was added to the collection
519-
throw ADP.ClosedConnectionError();
520-
}
521-
}
522-
catch (Exception ex)
523-
{
524-
if (source.TrySetException(ex))
525-
{
526-
// There was an exception, and it was successfully stored in the task
527-
captureSuccess = true;
528-
}
529-
}
530-
531-
if (!captureSuccess)
532-
{
533-
// Either there was no exception, or the task was already completed
534-
// This is unusual, but possible if a fatal timeout occurred on another thread (which should mean that the connection is now broken)
535-
Debug.Assert(_parser.State == TdsParserState.Broken || _parser.State == TdsParserState.Closed || _parser.Connection.IsConnectionDoomed, "Failed to capture exception while the connection was still healthy");
536-
537-
// The safest thing to do is to ensure that the connection is broken and attempt to cancel the task
538-
// This must be done from another thread to not block the callback thread
539-
Task.Factory.StartNew(() =>
540-
{
541-
_parser.State = TdsParserState.Broken;
542-
_parser.Connection.BreakConnection();
543-
source.TrySetCanceled();
544-
});
545-
}
546-
}
547-
548397
public void WriteAsyncCallback(PacketHandle packet, uint sniError) =>
549398
WriteAsyncCallback(IntPtr.Zero, packet, sniError);
550399

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

Lines changed: 2 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,8 @@ private IntPtr ReadSyncOverAsync(int timeout, out uint error)
464464

465465
private bool IsValidPacket(IntPtr packet) => packet != IntPtr.Zero;
466466

467+
private bool CheckPacket(IntPtr packet, TaskCompletionSource<object> source) => IntPtr.Zero == packet || IntPtr.Zero != packet && source != null;
468+
467469
private void ReleasePacket(IntPtr packet) => SNINativeMethodWrapper.SNIPacketRelease(packet);
468470

469471
private IntPtr ReadAsync(SessionHandle handle, out uint error)
@@ -484,146 +486,6 @@ private uint SNIPacketGetData(IntPtr packet, byte[] _inBuff, ref uint dataSize)
484486
return SNINativeMethodWrapper.SNIPacketGetData(packet, _inBuff, ref dataSize);
485487
}
486488

487-
public void ReadAsyncCallback(IntPtr key, IntPtr packet, uint error)
488-
{ // Key never used.
489-
// Note - it's possible that when native calls managed that an asynchronous exception
490-
// could occur in the native->managed transition, which would
491-
// have two impacts:
492-
// 1) user event not called
493-
// 2) DecrementPendingCallbacks not called, which would mean this object would be leaked due
494-
// to the outstanding GCRoot until AppDomain.Unload.
495-
// We live with the above for the time being due to the constraints of the current
496-
// reliability infrastructure provided by the CLR.
497-
498-
TaskCompletionSource<object> source = _networkPacketTaskSource;
499-
#if DEBUG
500-
if ((s_forcePendingReadsToWaitForUser) && (_realNetworkPacketTaskSource != null))
501-
{
502-
source = _realNetworkPacketTaskSource;
503-
}
504-
#endif
505-
506-
// The mars physical connection can get a callback
507-
// with a packet but no result after the connection is closed.
508-
if (source == null && _parser._pMarsPhysicalConObj == this)
509-
{
510-
return;
511-
}
512-
513-
RuntimeHelpers.PrepareConstrainedRegions();
514-
bool processFinallyBlock = true;
515-
try
516-
{
517-
Debug.Assert(IntPtr.Zero == packet || IntPtr.Zero != packet && source != null, "AsyncResult null on callback");
518-
if (_parser.MARSOn)
519-
{ // Only take reset lock on MARS and Async.
520-
CheckSetResetConnectionState(error, CallbackType.Read);
521-
}
522-
523-
ChangeNetworkPacketTimeout(Timeout.Infinite, Timeout.Infinite);
524-
525-
// The timer thread may be unreliable under high contention scenarios. It cannot be
526-
// assumed that the timeout has happened on the timer thread callback. Check the timeout
527-
// synchrnously and then call OnTimeoutSync to force an atomic change of state.
528-
if (TimeoutHasExpired)
529-
{
530-
OnTimeoutSync();
531-
}
532-
533-
// try to change to the stopped state but only do so if currently in the running state
534-
// and use cmpexch so that all changes out of the running state are atomic
535-
int previousState = Interlocked.CompareExchange(ref _timeoutState, TimeoutState.Stopped, TimeoutState.Running);
536-
537-
// if the state is anything other than running then this query has reached an end so
538-
// set the correlation _timeoutIdentityValue to 0 to prevent late callbacks executing
539-
if (_timeoutState != TimeoutState.Running)
540-
{
541-
_timeoutIdentityValue = 0;
542-
}
543-
544-
ProcessSniPacket(packet, error);
545-
}
546-
catch (Exception e)
547-
{
548-
processFinallyBlock = ADP.IsCatchableExceptionType(e);
549-
throw;
550-
}
551-
finally
552-
{
553-
// pendingCallbacks may be 2 after decrementing, this indicates that a fatal timeout is occuring, and therefore we shouldn't complete the task
554-
int pendingCallbacks = DecrementPendingCallbacks(false); // may dispose of GC handle.
555-
if ((processFinallyBlock) && (source != null) && (pendingCallbacks < 2))
556-
{
557-
if (error == 0)
558-
{
559-
if (_executionContext != null)
560-
{
561-
ExecutionContext.Run(_executionContext, (state) => source.TrySetResult(null), null);
562-
}
563-
else
564-
{
565-
source.TrySetResult(null);
566-
}
567-
}
568-
else
569-
{
570-
if (_executionContext != null)
571-
{
572-
ExecutionContext.Run(_executionContext, (state) => ReadAsyncCallbackCaptureException(source), null);
573-
}
574-
else
575-
{
576-
ReadAsyncCallbackCaptureException(source);
577-
}
578-
}
579-
}
580-
581-
AssertValidState();
582-
}
583-
}
584-
585-
private void ReadAsyncCallbackCaptureException(TaskCompletionSource<object> source)
586-
{
587-
bool captureSuccess = false;
588-
try
589-
{
590-
if (_hasErrorOrWarning)
591-
{
592-
// Do the close on another thread, since we don't want to block the callback thread
593-
ThrowExceptionAndWarning(asyncClose: true);
594-
}
595-
else if ((_parser.State == TdsParserState.Closed) || (_parser.State == TdsParserState.Broken))
596-
{
597-
// Connection was closed by another thread before we parsed the packet, so no error was added to the collection
598-
throw ADP.ClosedConnectionError();
599-
}
600-
}
601-
catch (Exception ex)
602-
{
603-
if (source.TrySetException(ex))
604-
{
605-
// There was an exception, and it was successfully stored in the task
606-
captureSuccess = true;
607-
}
608-
}
609-
610-
if (!captureSuccess)
611-
{
612-
// Either there was no exception, or the task was already completed
613-
// This is unusual, but possible if a fatal timeout occurred on another thread (which should mean that the connection is now broken)
614-
Debug.Assert(_parser.State == TdsParserState.Broken || _parser.State == TdsParserState.Closed || _parser.Connection.IsConnectionDoomed, "Failed to capture exception while the connection was still healthy");
615-
616-
// The safest thing to do is to ensure that the connection is broken and attempt to cancel the task
617-
// This must be done from another thread to not block the callback thread
618-
Task.Factory.StartNew(() =>
619-
{
620-
_parser.State = TdsParserState.Broken;
621-
_parser.Connection.BreakConnection();
622-
source.TrySetCanceled();
623-
});
624-
}
625-
}
626-
627489
#pragma warning disable 420 // a reference to a volatile field will not be treated as volatile
628490

629491
public void WriteAsyncCallback(IntPtr key, IntPtr packet, uint sniError)

0 commit comments

Comments
 (0)