@@ -4409,6 +4409,7 @@ private void AssertReaderState(bool requireData, bool permitAsync, int? columnIn
4409
4409
public override Task < bool > NextResultAsync ( CancellationToken cancellationToken )
4410
4410
{
4411
4411
using ( TryEventScope . Create ( "SqlDataReader.NextResultAsync | API | Object Id {0}" , ObjectID ) )
4412
+ using ( var registrationHolder = new DisposableTemporaryOnStack < CancellationTokenRegistration > ( ) )
4412
4413
{
4413
4414
TaskCompletionSource < bool > source = new TaskCompletionSource < bool > ( ) ;
4414
4415
@@ -4418,15 +4419,14 @@ public override Task<bool> NextResultAsync(CancellationToken cancellationToken)
4418
4419
return source . Task ;
4419
4420
}
4420
4421
4421
- CancellationTokenRegistration registration = default ;
4422
4422
if ( cancellationToken . CanBeCanceled )
4423
4423
{
4424
4424
if ( cancellationToken . IsCancellationRequested )
4425
4425
{
4426
4426
source . SetCanceled ( ) ;
4427
4427
return source . Task ;
4428
4428
}
4429
- registration = cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ;
4429
+ registrationHolder . Set ( cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ) ;
4430
4430
}
4431
4431
4432
4432
Task original = Interlocked . CompareExchange ( ref _currentTask , source . Task , null ) ;
@@ -4444,7 +4444,7 @@ public override Task<bool> NextResultAsync(CancellationToken cancellationToken)
4444
4444
return source . Task ;
4445
4445
}
4446
4446
4447
- return InvokeAsyncCall ( new HasNextResultAsyncCallContext ( this , source , registration ) ) ;
4447
+ return InvokeAsyncCall ( new HasNextResultAsyncCallContext ( this , source , registrationHolder . Take ( ) ) ) ;
4448
4448
}
4449
4449
}
4450
4450
@@ -4739,17 +4739,17 @@ out bytesRead
4739
4739
public override Task < bool > ReadAsync ( CancellationToken cancellationToken )
4740
4740
{
4741
4741
using ( TryEventScope . Create ( "SqlDataReader.ReadAsync | API | Object Id {0}" , ObjectID ) )
4742
+ using ( var registrationHolder = new DisposableTemporaryOnStack < CancellationTokenRegistration > ( ) )
4742
4743
{
4743
4744
if ( IsClosed )
4744
4745
{
4745
4746
return Task . FromException < bool > ( ADP . ExceptionWithStackTrace ( ADP . DataReaderClosed ( ) ) ) ;
4746
4747
}
4747
4748
4748
4749
// Register first to catch any already expired tokens to be able to trigger cancellation event.
4749
- CancellationTokenRegistration registration = default ;
4750
4750
if ( cancellationToken . CanBeCanceled )
4751
4751
{
4752
- registration = cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ;
4752
+ registrationHolder . Set ( cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ) ;
4753
4753
}
4754
4754
4755
4755
// If user's token is canceled, return a canceled task
@@ -4862,7 +4862,7 @@ public override Task<bool> ReadAsync(CancellationToken cancellationToken)
4862
4862
4863
4863
Debug . Assert ( context . Reader == null && context . Source == null && context . Disposable == default , "cached ReadAsyncCallContext was not properly disposed" ) ;
4864
4864
4865
- context . Set ( this , source , registration ) ;
4865
+ context . Set ( this , source , registrationHolder . Take ( ) ) ;
4866
4866
context . _hasMoreData = more ;
4867
4867
context . _hasReadRowToken = rowTokenRead ;
4868
4868
@@ -5000,49 +5000,51 @@ override public Task<bool> IsDBNullAsync(int i, CancellationToken cancellationTo
5000
5000
return Task . FromException < bool > ( ex ) ;
5001
5001
}
5002
5002
5003
- // Setup and check for pending task
5004
- TaskCompletionSource < bool > source = new TaskCompletionSource < bool > ( ) ;
5005
- Task original = Interlocked . CompareExchange ( ref _currentTask , source . Task , null ) ;
5006
- if ( original != null )
5003
+ using ( var registrationHolder = new DisposableTemporaryOnStack < CancellationTokenRegistration > ( ) )
5007
5004
{
5008
- source . SetException ( ADP . ExceptionWithStackTrace ( ADP . AsyncOperationPending ( ) ) ) ;
5009
- return source . Task ;
5010
- }
5005
+ // Setup and check for pending task
5006
+ TaskCompletionSource < bool > source = new TaskCompletionSource < bool > ( ) ;
5007
+ Task original = Interlocked . CompareExchange ( ref _currentTask , source . Task , null ) ;
5008
+ if ( original != null )
5009
+ {
5010
+ source . SetException ( ADP . ExceptionWithStackTrace ( ADP . AsyncOperationPending ( ) ) ) ;
5011
+ return source . Task ;
5012
+ }
5011
5013
5012
- // Check if cancellation due to close is requested (this needs to be done after setting _currentTask)
5013
- if ( _cancelAsyncOnCloseToken . IsCancellationRequested )
5014
- {
5015
- source . SetCanceled ( ) ;
5016
- _currentTask = null ;
5017
- return source . Task ;
5018
- }
5014
+ // Check if cancellation due to close is requested (this needs to be done after setting _currentTask)
5015
+ if ( _cancelAsyncOnCloseToken . IsCancellationRequested )
5016
+ {
5017
+ source . SetCanceled ( ) ;
5018
+ _currentTask = null ;
5019
+ return source . Task ;
5020
+ }
5019
5021
5020
- // Setup cancellations
5021
- CancellationTokenRegistration registration = default ;
5022
- if ( cancellationToken . CanBeCanceled )
5023
- {
5024
- registration = cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ;
5025
- }
5022
+ // Setup cancellations
5023
+ if ( cancellationToken . CanBeCanceled )
5024
+ {
5025
+ registrationHolder . Set ( cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ) ;
5026
+ }
5026
5027
5027
- IsDBNullAsyncCallContext context = null ;
5028
- if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection )
5029
- {
5030
- context = Interlocked . Exchange ( ref sqlInternalConnection . CachedDataReaderIsDBNullContext , null ) ;
5031
- }
5032
- if ( context is null )
5033
- {
5034
- context = new IsDBNullAsyncCallContext ( ) ;
5035
- }
5028
+ IsDBNullAsyncCallContext context = null ;
5029
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection )
5030
+ {
5031
+ context = Interlocked . Exchange ( ref sqlInternalConnection . CachedDataReaderIsDBNullContext , null ) ;
5032
+ }
5033
+ if ( context is null )
5034
+ {
5035
+ context = new IsDBNullAsyncCallContext ( ) ;
5036
+ }
5036
5037
5037
- Debug . Assert ( context . Reader == null && context . Source == null && context . Disposable == default , "cached ISDBNullAsync context not properly disposed" ) ;
5038
+ Debug . Assert ( context . Reader == null && context . Source == null && context . Disposable == default , "cached ISDBNullAsync context not properly disposed" ) ;
5038
5039
5039
- context . Set ( this , source , registration ) ;
5040
- context . _columnIndex = i ;
5040
+ context . Set ( this , source , registrationHolder . Take ( ) ) ;
5041
+ context . _columnIndex = i ;
5041
5042
5042
- // Setup async
5043
- PrepareAsyncInvocation ( useSnapshot : true ) ;
5043
+ // Setup async
5044
+ PrepareAsyncInvocation ( useSnapshot : true ) ;
5044
5045
5045
- return InvokeAsyncCall ( context ) ;
5046
+ return InvokeAsyncCall ( context ) ;
5047
+ }
5046
5048
}
5047
5049
}
5048
5050
@@ -5147,37 +5149,39 @@ override public Task<T> GetFieldValueAsync<T>(int i, CancellationToken cancellat
5147
5149
return Task . FromException < T > ( ex ) ;
5148
5150
}
5149
5151
5150
- // Setup and check for pending task
5151
- TaskCompletionSource < T > source = new TaskCompletionSource < T > ( ) ;
5152
- Task original = Interlocked . CompareExchange ( ref _currentTask , source . Task , null ) ;
5153
- if ( original != null )
5152
+ using ( var registrationHolder = new DisposableTemporaryOnStack < CancellationTokenRegistration > ( ) )
5154
5153
{
5155
- source . SetException ( ADP . ExceptionWithStackTrace ( ADP . AsyncOperationPending ( ) ) ) ;
5156
- return source . Task ;
5157
- }
5154
+ // Setup and check for pending task
5155
+ TaskCompletionSource < T > source = new TaskCompletionSource < T > ( ) ;
5156
+ Task original = Interlocked . CompareExchange ( ref _currentTask , source . Task , null ) ;
5157
+ if ( original != null )
5158
+ {
5159
+ source . SetException ( ADP . ExceptionWithStackTrace ( ADP . AsyncOperationPending ( ) ) ) ;
5160
+ return source . Task ;
5161
+ }
5158
5162
5159
- // Check if cancellation due to close is requested (this needs to be done after setting _currentTask)
5160
- if ( _cancelAsyncOnCloseToken . IsCancellationRequested )
5161
- {
5162
- source . SetCanceled ( ) ;
5163
- _currentTask = null ;
5164
- return source . Task ;
5165
- }
5163
+ // Check if cancellation due to close is requested (this needs to be done after setting _currentTask)
5164
+ if ( _cancelAsyncOnCloseToken . IsCancellationRequested )
5165
+ {
5166
+ source . SetCanceled ( ) ;
5167
+ _currentTask = null ;
5168
+ return source . Task ;
5169
+ }
5166
5170
5167
- // Setup cancellations
5168
- CancellationTokenRegistration registration = default ;
5169
- if ( cancellationToken . CanBeCanceled )
5170
- {
5171
- registration = cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ;
5172
- }
5171
+ // Setup cancellations
5172
+ if ( cancellationToken . CanBeCanceled )
5173
+ {
5174
+ registrationHolder . Set ( cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ) ;
5175
+ }
5173
5176
5174
- // Setup async
5175
- PrepareAsyncInvocation ( useSnapshot : true ) ;
5177
+ // Setup async
5178
+ PrepareAsyncInvocation ( useSnapshot : true ) ;
5176
5179
5177
- GetFieldValueAsyncCallContext < T > context = new GetFieldValueAsyncCallContext < T > ( this , source , registration ) ;
5178
- context . _columnIndex = i ;
5180
+ GetFieldValueAsyncCallContext < T > context = new GetFieldValueAsyncCallContext < T > ( this , source , registrationHolder . Take ( ) ) ;
5181
+ context . _columnIndex = i ;
5179
5182
5180
- return InvokeAsyncCall ( context ) ;
5183
+ return InvokeAsyncCall ( context ) ;
5184
+ }
5181
5185
}
5182
5186
5183
5187
private static Task < T > GetFieldValueAsyncExecute < T > ( Task task , object state )
0 commit comments