Skip to content

Commit 92faac2

Browse files
committed
Handling cancellation tokens with explicit cancellation (DNET-1001).
1 parent 61a808b commit 92faac2

27 files changed

+383
-211
lines changed

Provider/src/FirebirdSql.Data.FirebirdClient.Tests/FbCommandTests.cs

Lines changed: 98 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using System.Text;
2222
using System.Threading;
2323
using System.Threading.Tasks;
24+
using FirebirdSql.Data.Common;
2425
using FirebirdSql.Data.TestsBase;
2526
using NUnit.Framework;
2627

@@ -30,6 +31,16 @@ namespace FirebirdSql.Data.FirebirdClient.Tests
3031
[TestFixtureSource(typeof(FbServerTypeTestFixtureSource), nameof(FbServerTypeTestFixtureSource.Embedded))]
3132
public class FbCommandTests : FbTestsBase
3233
{
34+
const string FiniteInfiniteLoopCommand =
35+
@"execute block as
36+
declare variable start_time timestamp;
37+
begin
38+
start_time = cast('now' as timestamp);
39+
while (datediff(second from start_time to cast('now' as timestamp)) <= 10) do
40+
begin
41+
end
42+
end";
43+
3344
public FbCommandTests(FbServerType serverType, bool compression, FbWireCrypt wireCrypt)
3445
: base(serverType, compression, wireCrypt)
3546
{ }
@@ -559,45 +570,6 @@ public async Task ReadingCharOctetsTest()
559570
}
560571
}
561572

562-
[Test]
563-
public async Task CommandCancellationTest()
564-
{
565-
if (!await EnsureVersion(new Version(2, 5, 0, 0)))
566-
return;
567-
568-
var cancelled = false;
569-
await using (var cmd = Connection.CreateCommand())
570-
{
571-
cmd.CommandText =
572-
@"execute block as
573-
declare variable start_time timestamp;
574-
begin
575-
start_time = cast('now' as timestamp);
576-
while (datediff(second from start_time to cast('now' as timestamp)) <= 10) do
577-
begin
578-
end
579-
end";
580-
async Task Execute()
581-
{
582-
await Task.Yield();
583-
try
584-
{
585-
await cmd.ExecuteNonQueryAsync();
586-
}
587-
catch (FbException ex)
588-
{
589-
cancelled = "HY008" == ex.SQLSTATE;
590-
}
591-
}
592-
var executeTask = Execute();
593-
Thread.Sleep(2000);
594-
cmd.Cancel();
595-
Thread.Sleep(2000);
596-
await executeTask;
597-
}
598-
Assert.IsTrue(cancelled);
599-
}
600-
601573
[Test]
602574
public async Task GetCommandPlanTest()
603575
{
@@ -714,5 +686,92 @@ public async Task ExecuteNonQueryReturnsMinusOneOnNonInsertUpdateDelete()
714686
Assert.AreEqual(-1, ra);
715687
}
716688
}
689+
690+
[Test]
691+
public async Task CommandCancellationDirectTest()
692+
{
693+
if (!await EnsureVersion(new Version(2, 5, 0, 0)))
694+
return;
695+
696+
await using (var cmd = Connection.CreateCommand())
697+
{
698+
cmd.CommandText = FiniteInfiniteLoopCommand;
699+
async Task Execute()
700+
{
701+
await Task.Yield();
702+
await cmd.ExecuteNonQueryAsync();
703+
}
704+
var executeTask = Execute();
705+
Thread.Sleep(2000);
706+
cmd.Cancel();
707+
Thread.Sleep(2000);
708+
Assert.ThrowsAsync<OperationCanceledException>(async () => await executeTask);
709+
}
710+
}
711+
712+
[Test]
713+
public async Task CommandCancellationCancellationTokenTest()
714+
{
715+
if (!await EnsureVersion(new Version(2, 5, 0, 0)))
716+
return;
717+
718+
using (var cts = new CancellationTokenSource())
719+
{
720+
await using (var cmd = Connection.CreateCommand())
721+
{
722+
cmd.CommandText = FiniteInfiniteLoopCommand;
723+
async Task Execute(CancellationToken cancellationToken)
724+
{
725+
await Task.Yield();
726+
await cmd.ExecuteNonQueryAsync(cancellationToken);
727+
}
728+
var executeTask = Execute(cts.Token);
729+
Thread.Sleep(2000);
730+
cts.Cancel();
731+
Thread.Sleep(2000);
732+
Assert.ThrowsAsync<OperationCanceledException>(async () => await executeTask);
733+
}
734+
}
735+
}
736+
737+
[Test]
738+
public async Task CommandUsableAfterCancellationTest()
739+
{
740+
if (!await EnsureVersion(new Version(2, 5, 0, 0)))
741+
return;
742+
743+
using (var cts = new CancellationTokenSource())
744+
{
745+
await using (var cmd = Connection.CreateCommand())
746+
{
747+
cmd.CommandText = FiniteInfiniteLoopCommand;
748+
async Task Execute(CancellationToken cancellationToken)
749+
{
750+
await Task.Yield();
751+
await cmd.ExecuteNonQueryAsync(cancellationToken);
752+
}
753+
var executeTask = Execute(cts.Token);
754+
Thread.Sleep(2000);
755+
cts.Cancel();
756+
Thread.Sleep(2000);
757+
try
758+
{
759+
await executeTask;
760+
}
761+
catch (OperationCanceledException)
762+
{ }
763+
cmd.CommandText = "select 1 from rdb$database union all select 6 from rdb$database";
764+
await using (var reader = await cmd.ExecuteReaderAsync())
765+
{
766+
var result = new List<int>();
767+
while (await reader.ReadAsync())
768+
{
769+
result.Add(reader.GetInt32(0));
770+
}
771+
CollectionAssert.AreEqual(new[] { 1, 6 }, result);
772+
}
773+
}
774+
}
775+
}
717776
}
718777
}

Provider/src/FirebirdSql.Data.FirebirdClient.Tests/FbDataReaderTests.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
using System;
1919
using System.Data;
20+
using System.Threading;
2021
using System.Threading.Tasks;
2122
using FirebirdSql.Data.TestsBase;
2223
using NUnit.Framework;
@@ -452,5 +453,48 @@ public async Task DNET749_CommandBehaviorCloseConnectionStackOverflow()
452453
}
453454
}
454455
}
456+
457+
[Test]
458+
public async Task ReadCancellation()
459+
{
460+
if (!await EnsureVersion(new Version(2, 5, 0, 0)))
461+
return;
462+
463+
using (var cts = new CancellationTokenSource())
464+
{
465+
await using (var cmd = Connection.CreateCommand())
466+
{
467+
cmd.FetchSize = 1;
468+
cmd.CommandText =
469+
@"execute block
470+
returns (i int)
471+
as
472+
declare variable start_time timestamp;
473+
begin
474+
i = 0;
475+
while (i < 100) do
476+
begin
477+
i = i + 1;
478+
suspend;
479+
480+
start_time = cast('now' as timestamp);
481+
while (datediff(second from start_time to cast('now' as timestamp)) <= 2) do
482+
begin
483+
end
484+
end
485+
end";
486+
await using (var reader = await cmd.ExecuteReaderAsync())
487+
{
488+
await reader.ReadAsync(cts.Token);
489+
cts.CancelAfter(100);
490+
Assert.ThrowsAsync<OperationCanceledException>(async () =>
491+
{
492+
while (await reader.ReadAsync(cts.Token))
493+
{ }
494+
});
495+
}
496+
}
497+
}
498+
}
455499
}
456500
}

Provider/src/FirebirdSql.Data.FirebirdClient/Client/Native/FesDatabase.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,12 @@ public override Task CancelOperation(int kind, AsyncWrappingCommonArgs async)
218218

219219
_fbClient.fb_cancel_operation(localStatusVector, ref _handle, kind);
220220

221-
ProcessStatusVector(localStatusVector);
221+
try
222+
{
223+
ProcessStatusVector(localStatusVector);
224+
}
225+
catch (IscException ex) when (ex.ErrorCode == IscCodes.isc_nothing_to_cancel)
226+
{ }
222227

223228
return Task.CompletedTask;
224229
}

0 commit comments

Comments
 (0)