@@ -4397,6 +4397,7 @@ private void AssertReaderState(bool requireData, bool permitAsync, int? columnIn
43974397 public override Task < bool > NextResultAsync ( CancellationToken cancellationToken )
43984398 {
43994399 using ( TryEventScope . Create ( "SqlDataReader.NextResultAsync | API | Object Id {0}" , ObjectID ) )
4400+ using ( var registrationHolder = new DisposableTemporaryOnStack < CancellationTokenRegistration > ( ) )
44004401 {
44014402 TaskCompletionSource < bool > source = new TaskCompletionSource < bool > ( ) ;
44024403
@@ -4406,15 +4407,14 @@ public override Task<bool> NextResultAsync(CancellationToken cancellationToken)
44064407 return source . Task ;
44074408 }
44084409
4409- IDisposable registration = null ;
44104410 if ( cancellationToken . CanBeCanceled )
44114411 {
44124412 if ( cancellationToken . IsCancellationRequested )
44134413 {
44144414 source . SetCanceled ( ) ;
44154415 return source . Task ;
44164416 }
4417- registration = cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ;
4417+ registrationHolder . Set ( cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ) ;
44184418 }
44194419
44204420 Task original = Interlocked . CompareExchange ( ref _currentTask , source . Task , null ) ;
@@ -4432,7 +4432,7 @@ public override Task<bool> NextResultAsync(CancellationToken cancellationToken)
44324432 return source . Task ;
44334433 }
44344434
4435- return InvokeAsyncCall ( new HasNextResultAsyncCallContext ( this , source , registration ) ) ;
4435+ return InvokeAsyncCall ( new HasNextResultAsyncCallContext ( this , source , registrationHolder . Take ( ) ) ) ;
44364436 }
44374437 }
44384438
@@ -4727,17 +4727,17 @@ out bytesRead
47274727 public override Task < bool > ReadAsync ( CancellationToken cancellationToken )
47284728 {
47294729 using ( TryEventScope . Create ( "SqlDataReader.ReadAsync | API | Object Id {0}" , ObjectID ) )
4730+ using ( var registrationHolder = new DisposableTemporaryOnStack < CancellationTokenRegistration > ( ) )
47304731 {
47314732 if ( IsClosed )
47324733 {
47334734 return Task . FromException < bool > ( ADP . ExceptionWithStackTrace ( ADP . DataReaderClosed ( ) ) ) ;
47344735 }
47354736
47364737 // Register first to catch any already expired tokens to be able to trigger cancellation event.
4737- IDisposable registration = null ;
47384738 if ( cancellationToken . CanBeCanceled )
47394739 {
4740- registration = cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ;
4740+ registrationHolder . Set ( cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ) ;
47414741 }
47424742
47434743 // If user's token is canceled, return a canceled task
@@ -4850,7 +4850,7 @@ public override Task<bool> ReadAsync(CancellationToken cancellationToken)
48504850
48514851 Debug . Assert ( context . Reader == null && context . Source == null && context . Disposable == null , "cached ReadAsyncCallContext was not properly disposed" ) ;
48524852
4853- context . Set ( this , source , registration ) ;
4853+ context . Set ( this , source , registrationHolder . Take ( ) ) ;
48544854 context . _hasMoreData = more ;
48554855 context . _hasReadRowToken = rowTokenRead ;
48564856
@@ -4988,49 +4988,51 @@ override public Task<bool> IsDBNullAsync(int i, CancellationToken cancellationTo
49884988 return Task . FromException < bool > ( ex ) ;
49894989 }
49904990
4991- // Setup and check for pending task
4992- TaskCompletionSource < bool > source = new TaskCompletionSource < bool > ( ) ;
4993- Task original = Interlocked . CompareExchange ( ref _currentTask , source . Task , null ) ;
4994- if ( original != null )
4991+ using ( var registrationHolder = new DisposableTemporaryOnStack < CancellationTokenRegistration > ( ) )
49954992 {
4996- source . SetException ( ADP . ExceptionWithStackTrace ( ADP . AsyncOperationPending ( ) ) ) ;
4997- return source . Task ;
4998- }
4993+ // Setup and check for pending task
4994+ TaskCompletionSource < bool > source = new TaskCompletionSource < bool > ( ) ;
4995+ Task original = Interlocked . CompareExchange ( ref _currentTask , source . Task , null ) ;
4996+ if ( original != null )
4997+ {
4998+ source . SetException ( ADP . ExceptionWithStackTrace ( ADP . AsyncOperationPending ( ) ) ) ;
4999+ return source . Task ;
5000+ }
49995001
5000- // Check if cancellation due to close is requested (this needs to be done after setting _currentTask)
5001- if ( _cancelAsyncOnCloseToken . IsCancellationRequested )
5002- {
5003- source . SetCanceled ( ) ;
5004- _currentTask = null ;
5005- return source . Task ;
5006- }
5002+ // Check if cancellation due to close is requested (this needs to be done after setting _currentTask)
5003+ if ( _cancelAsyncOnCloseToken . IsCancellationRequested )
5004+ {
5005+ source . SetCanceled ( ) ;
5006+ _currentTask = null ;
5007+ return source . Task ;
5008+ }
50075009
5008- // Setup cancellations
5009- IDisposable registration = null ;
5010- if ( cancellationToken . CanBeCanceled )
5011- {
5012- registration = cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ;
5013- }
5010+ // Setup cancellations
5011+ if ( cancellationToken . CanBeCanceled )
5012+ {
5013+ registrationHolder . Set ( cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ) ;
5014+ }
50145015
5015- IsDBNullAsyncCallContext context = null ;
5016- if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection )
5017- {
5018- context = Interlocked . Exchange ( ref sqlInternalConnection . CachedDataReaderIsDBNullContext , null ) ;
5019- }
5020- if ( context is null )
5021- {
5022- context = new IsDBNullAsyncCallContext ( ) ;
5023- }
5016+ IsDBNullAsyncCallContext context = null ;
5017+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection )
5018+ {
5019+ context = Interlocked . Exchange ( ref sqlInternalConnection . CachedDataReaderIsDBNullContext , null ) ;
5020+ }
5021+ if ( context is null )
5022+ {
5023+ context = new IsDBNullAsyncCallContext ( ) ;
5024+ }
50245025
5025- Debug . Assert ( context . Reader == null && context . Source == null && context . Disposable == null , "cached ISDBNullAsync context not properly disposed" ) ;
5026+ Debug . Assert ( context . Reader == null && context . Source == null && context . Disposable == default , "cached ISDBNullAsync context not properly disposed" ) ;
50265027
5027- context . Set ( this , source , registration ) ;
5028- context . _columnIndex = i ;
5028+ context . Set ( this , source , registrationHolder . Take ( ) ) ;
5029+ context . _columnIndex = i ;
50295030
5030- // Setup async
5031- PrepareAsyncInvocation ( useSnapshot : true ) ;
5031+ // Setup async
5032+ PrepareAsyncInvocation ( useSnapshot : true ) ;
50325033
5033- return InvokeAsyncCall ( context ) ;
5034+ return InvokeAsyncCall ( context ) ;
5035+ }
50345036 }
50355037 }
50365038
@@ -5135,37 +5137,39 @@ override public Task<T> GetFieldValueAsync<T>(int i, CancellationToken cancellat
51355137 return Task . FromException < T > ( ex ) ;
51365138 }
51375139
5138- // Setup and check for pending task
5139- TaskCompletionSource < T > source = new TaskCompletionSource < T > ( ) ;
5140- Task original = Interlocked . CompareExchange ( ref _currentTask , source . Task , null ) ;
5141- if ( original != null )
5140+ using ( var registrationHolder = new DisposableTemporaryOnStack < CancellationTokenRegistration > ( ) )
51425141 {
5143- source . SetException ( ADP . ExceptionWithStackTrace ( ADP . AsyncOperationPending ( ) ) ) ;
5144- return source . Task ;
5145- }
5142+ // Setup and check for pending task
5143+ TaskCompletionSource < T > source = new TaskCompletionSource < T > ( ) ;
5144+ Task original = Interlocked . CompareExchange ( ref _currentTask , source . Task , null ) ;
5145+ if ( original != null )
5146+ {
5147+ source . SetException ( ADP . ExceptionWithStackTrace ( ADP . AsyncOperationPending ( ) ) ) ;
5148+ return source . Task ;
5149+ }
51465150
5147- // Check if cancellation due to close is requested (this needs to be done after setting _currentTask)
5148- if ( _cancelAsyncOnCloseToken . IsCancellationRequested )
5149- {
5150- source . SetCanceled ( ) ;
5151- _currentTask = null ;
5152- return source . Task ;
5153- }
5151+ // Check if cancellation due to close is requested (this needs to be done after setting _currentTask)
5152+ if ( _cancelAsyncOnCloseToken . IsCancellationRequested )
5153+ {
5154+ source . SetCanceled ( ) ;
5155+ _currentTask = null ;
5156+ return source . Task ;
5157+ }
51545158
5155- // Setup cancellations
5156- IDisposable registration = null ;
5157- if ( cancellationToken . CanBeCanceled )
5158- {
5159- registration = cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ;
5160- }
5159+ // Setup cancellations
5160+ if ( cancellationToken . CanBeCanceled )
5161+ {
5162+ registrationHolder . Set ( cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ) ;
5163+ }
51615164
5162- // Setup async
5163- PrepareAsyncInvocation ( useSnapshot : true ) ;
5165+ // Setup async
5166+ PrepareAsyncInvocation ( useSnapshot : true ) ;
51645167
5165- GetFieldValueAsyncCallContext < T > context = new GetFieldValueAsyncCallContext < T > ( this , source , registration ) ;
5166- context . _columnIndex = i ;
5168+ GetFieldValueAsyncCallContext < T > context = new GetFieldValueAsyncCallContext < T > ( this , source , registrationHolder . Take ( ) ) ;
5169+ context . _columnIndex = i ;
51675170
5168- return InvokeAsyncCall ( context ) ;
5171+ return InvokeAsyncCall ( context ) ;
5172+ }
51695173 }
51705174
51715175 private static Task < T > GetFieldValueAsyncExecute < T > ( Task task , object state )
0 commit comments