Skip to content

Commit 760510c

Browse files
authored
Add | Adding disposable stack temp ref struct and use (#1818)
1 parent f684185 commit 760510c

File tree

5 files changed

+174
-120
lines changed

5 files changed

+174
-120
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj

+3
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@
109109
<Compile Include="..\..\src\Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs">
110110
<Link>Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs</Link>
111111
</Compile>
112+
<Compile Include="..\..\src\Microsoft\Data\SqlClient\DisposableTemporaryOnStack.cs">
113+
<Link>Microsoft\Data\SqlClient\DisposableTemporaryOnStack.cs</Link>
114+
</Compile>
112115
<Compile Include="..\..\src\Microsoft\Data\SqlClient\EnclaveDelegate.cs">
113116
<Link>Microsoft\Data\SqlClient\EnclaveDelegate.cs</Link>
114117
</Compile>

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

+70-66
Original file line numberDiff line numberDiff line change
@@ -4409,6 +4409,7 @@ private void AssertReaderState(bool requireData, bool permitAsync, int? columnIn
44094409
public override Task<bool> NextResultAsync(CancellationToken cancellationToken)
44104410
{
44114411
using (TryEventScope.Create("SqlDataReader.NextResultAsync | API | Object Id {0}", ObjectID))
4412+
using (var registrationHolder = new DisposableTemporaryOnStack<CancellationTokenRegistration>())
44124413
{
44134414
TaskCompletionSource<bool> source = new TaskCompletionSource<bool>();
44144415

@@ -4418,15 +4419,14 @@ public override Task<bool> NextResultAsync(CancellationToken cancellationToken)
44184419
return source.Task;
44194420
}
44204421

4421-
CancellationTokenRegistration registration = default;
44224422
if (cancellationToken.CanBeCanceled)
44234423
{
44244424
if (cancellationToken.IsCancellationRequested)
44254425
{
44264426
source.SetCanceled();
44274427
return source.Task;
44284428
}
4429-
registration = cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command);
4429+
registrationHolder.Set(cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command));
44304430
}
44314431

44324432
Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null);
@@ -4444,7 +4444,7 @@ public override Task<bool> NextResultAsync(CancellationToken cancellationToken)
44444444
return source.Task;
44454445
}
44464446

4447-
return InvokeAsyncCall(new HasNextResultAsyncCallContext(this, source, registration));
4447+
return InvokeAsyncCall(new HasNextResultAsyncCallContext(this, source, registrationHolder.Take()));
44484448
}
44494449
}
44504450

@@ -4739,17 +4739,17 @@ out bytesRead
47394739
public override Task<bool> ReadAsync(CancellationToken cancellationToken)
47404740
{
47414741
using (TryEventScope.Create("SqlDataReader.ReadAsync | API | Object Id {0}", ObjectID))
4742+
using (var registrationHolder = new DisposableTemporaryOnStack<CancellationTokenRegistration>())
47424743
{
47434744
if (IsClosed)
47444745
{
47454746
return Task.FromException<bool>(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed()));
47464747
}
47474748

47484749
// Register first to catch any already expired tokens to be able to trigger cancellation event.
4749-
CancellationTokenRegistration registration = default;
47504750
if (cancellationToken.CanBeCanceled)
47514751
{
4752-
registration = cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command);
4752+
registrationHolder.Set(cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command));
47534753
}
47544754

47554755
// If user's token is canceled, return a canceled task
@@ -4862,7 +4862,7 @@ public override Task<bool> ReadAsync(CancellationToken cancellationToken)
48624862

48634863
Debug.Assert(context.Reader == null && context.Source == null && context.Disposable == default, "cached ReadAsyncCallContext was not properly disposed");
48644864

4865-
context.Set(this, source, registration);
4865+
context.Set(this, source, registrationHolder.Take());
48664866
context._hasMoreData = more;
48674867
context._hasReadRowToken = rowTokenRead;
48684868

@@ -5000,49 +5000,51 @@ override public Task<bool> IsDBNullAsync(int i, CancellationToken cancellationTo
50005000
return Task.FromException<bool>(ex);
50015001
}
50025002

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>())
50075004
{
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+
}
50115013

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+
}
50195021

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+
}
50265027

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+
}
50365037

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");
50385039

5039-
context.Set(this, source, registration);
5040-
context._columnIndex = i;
5040+
context.Set(this, source, registrationHolder.Take());
5041+
context._columnIndex = i;
50415042

5042-
// Setup async
5043-
PrepareAsyncInvocation(useSnapshot: true);
5043+
// Setup async
5044+
PrepareAsyncInvocation(useSnapshot: true);
50445045

5045-
return InvokeAsyncCall(context);
5046+
return InvokeAsyncCall(context);
5047+
}
50465048
}
50475049
}
50485050

@@ -5147,37 +5149,39 @@ override public Task<T> GetFieldValueAsync<T>(int i, CancellationToken cancellat
51475149
return Task.FromException<T>(ex);
51485150
}
51495151

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>())
51545153
{
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+
}
51585162

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+
}
51665170

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+
}
51735176

5174-
// Setup async
5175-
PrepareAsyncInvocation(useSnapshot: true);
5177+
// Setup async
5178+
PrepareAsyncInvocation(useSnapshot: true);
51765179

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;
51795182

5180-
return InvokeAsyncCall(context);
5183+
return InvokeAsyncCall(context);
5184+
}
51815185
}
51825186

51835187
private static Task<T> GetFieldValueAsyncExecute<T>(Task task, object state)

src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj

+3
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@
185185
<Compile Include="..\..\src\Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs">
186186
<Link>Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs</Link>
187187
</Compile>
188+
<Compile Include="..\..\src\Microsoft\Data\SqlClient\DisposableTemporaryOnStack.cs">
189+
<Link>Microsoft\Data\SqlClient\DisposableTemporaryOnStack.cs</Link>
190+
</Compile>
188191
<Compile Include="..\..\src\Microsoft\Data\SqlClient\EnclaveDelegate.cs">
189192
<Link>Microsoft\Data\SqlClient\EnclaveDelegate.cs</Link>
190193
</Compile>

0 commit comments

Comments
 (0)