diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 12404b0..d5c367e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: # Builds for Debug and Release configurations configuration: [Debug, Release] # Builds for Ubuntu, Windows, and macOS - os: [ubuntu-latest, windows-latest, macOS-latest, macos-14] + os: [ubuntu-latest, windows-latest, macOS-latest] fail-fast: false runs-on: ${{ matrix.os }} diff --git a/paket.dependencies b/paket.dependencies index fba2022..3203573 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -21,7 +21,7 @@ group Test nuget TimeProviderExtensions nuget YoloDev.Expecto.TestSdk >= 0.14.2 nuget Microsoft.NET.Test.Sdk >= 17.7.2 - nuget FSharp.Control.TaskSeq + nuget FSharp.Control.TaskSeq 0.4.0 // [ FAKE GROUP ] group Build @@ -41,5 +41,5 @@ group Build nuget Fake.BuildServer.GitHubActions ~> 6 nuget Argu nuget Octokit >= 0.50 - nuget MSBuild.StructuredLogger 2.1.858 + nuget MSBuild.StructuredLogger 2.2.243 diff --git a/paket.lock b/paket.lock index cbac881..03b69f4 100644 --- a/paket.lock +++ b/paket.lock @@ -283,7 +283,7 @@ NUGET System.Security.Principal.Windows (>= 5.0) - restriction: || (&& (>= monoandroid) (< netstandard1.3)) (&& (< monoandroid) (>= netcoreapp2.0)) (>= monotouch) (&& (< net46) (< netcoreapp2.0) (>= netstandard2.0)) (>= net461) (>= netcoreapp2.1) (>= uap10.1) (>= xamarinios) (>= xamarinmac) (>= xamarintvos) (>= xamarinwatchos) Microsoft.Win32.SystemEvents (7.0) - restriction: >= net6.0 Mono.Posix.NETStandard (1.0) - restriction: >= netstandard2.0 - MSBuild.StructuredLogger (2.1.858) + MSBuild.StructuredLogger (2.2.243) Microsoft.Build.Framework (>= 17.5) - restriction: >= netstandard2.0 Microsoft.Build.Utilities.Core (>= 17.5) - restriction: >= netstandard2.0 Newtonsoft.Json (13.0.3) - restriction: >= netstandard2.0 @@ -369,8 +369,8 @@ NUGET FsCheck (>= 2.16.5 < 3.0) - restriction: >= net6.0 FsCheck (2.16.6) - restriction: >= net6.0 FSharp.Core (>= 4.2.3) - restriction: || (>= net452) (>= netstandard1.6) - FSharp.Control.TaskSeq (0.3) - FSharp.Core (>= 6.0.2) - restriction: >= netstandard2.1 + FSharp.Control.TaskSeq (0.4) + FSharp.Core (>= 6.0.1) - restriction: >= netstandard2.1 FSharp.Core (7.0.401) - restriction: >= netstandard2.1 Microsoft.Bcl.AsyncInterfaces (8.0) System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (>= net462) (&& (>= netstandard2.0) (< netstandard2.1)) diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index e7a7266..dadfdcc 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -3,5 +3,6 @@ false true + $(NoWarn);FS0044 diff --git a/tests/IcedTasks.Tests/AsyncExTests.fs b/tests/IcedTasks.Tests/AsyncExTests.fs index dd13630..8e0930e 100644 --- a/tests/IcedTasks.Tests/AsyncExTests.fs +++ b/tests/IcedTasks.Tests/AsyncExTests.fs @@ -690,7 +690,7 @@ module AsyncExTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) let! actual = asyncEx { @@ -719,7 +719,7 @@ module AsyncExTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) use cts = new CancellationTokenSource() @@ -754,7 +754,7 @@ module AsyncExTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) use cts = new CancellationTokenSource() @@ -776,6 +776,47 @@ module AsyncExTests = } } + +#if TEST_NETSTANDARD2_1 || TEST_NET6_0_OR_GREATER + testCaseAsync "TaskSeq receives CancellationToken" + <| async { + + do! + asyncEx { + + let loops = 1 + + let mutable CancellationToken = CancellationToken.None + + let asyncSeq = + taskSeq { + for i in 0..loops do + do! + asyncEx { + let! ct = Async.CancellationToken + CancellationToken <- ct + } + + yield () + } + + use cts = new CancellationTokenSource() + + let actual = + asyncEx { + for i in asyncSeq do + do! Task.Yield() + } + + do! + Async.StartAsTask(actual, cancellationToken = cts.Token) + |> Async.AwaitTask + + Expect.equal CancellationToken cts.Token "" + } + } +#endif + ] ] diff --git a/tests/IcedTasks.Tests/CancellablePoolingValueTaskTests.fs b/tests/IcedTasks.Tests/CancellablePoolingValueTaskTests.fs index 5809886..05e25f1 100644 --- a/tests/IcedTasks.Tests/CancellablePoolingValueTaskTests.fs +++ b/tests/IcedTasks.Tests/CancellablePoolingValueTaskTests.fs @@ -2,6 +2,7 @@ namespace IcedTasks.Tests open System open Expecto +open FSharp.Control open System.Threading open System.Threading.Tasks open IcedTasks @@ -801,7 +802,7 @@ module CancellablePoolingValueTaskTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (fun _ -> valueTask { do! Task.Yield() }) let! actual = cancellablePoolingValueTask { @@ -832,7 +833,7 @@ module CancellablePoolingValueTaskTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (fun _ -> valueTask { do! Task.Yield() }) use cts = new CancellationTokenSource() @@ -865,7 +866,7 @@ module CancellablePoolingValueTaskTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (fun _ -> valueTask { do! Task.Yield() }) use cts = new CancellationTokenSource() @@ -884,6 +885,41 @@ module CancellablePoolingValueTaskTests = "" } } + testCaseAsync "TaskSeq receives CancellationToken" + <| async { + + do! + cancellablePoolingValueTask { + + let loops = 1 + + let mutable CancellationToken = CancellationToken.None + + let asyncSeq = + taskSeq { + for i in 0..loops do + do! + cancellablePoolingValueTask { + let! ct = CancellableTask.getCancellationToken () + CancellationToken <- ct + } + + yield () + } + + use cts = new CancellationTokenSource() + + let actual = + cancellablePoolingValueTask { + for i in asyncSeq do + do! Task.Yield() + } + + do! actual cts.Token + + Expect.equal CancellationToken cts.Token "" + } + } ] diff --git a/tests/IcedTasks.Tests/CancellableTaskTests.fs b/tests/IcedTasks.Tests/CancellableTaskTests.fs index 54a1b12..2651be2 100644 --- a/tests/IcedTasks.Tests/CancellableTaskTests.fs +++ b/tests/IcedTasks.Tests/CancellableTaskTests.fs @@ -2,9 +2,10 @@ namespace IcedTasks.Tests open System open Expecto +open IcedTasks +open FSharp.Control open System.Threading open System.Threading.Tasks -open IcedTasks module CancellableTaskTests = open System.Collections.Concurrent @@ -763,7 +764,7 @@ module CancellableTaskTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) let! actual = cancellableTask { @@ -793,7 +794,7 @@ module CancellableTaskTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) use cts = new CancellationTokenSource() @@ -827,7 +828,7 @@ module CancellableTaskTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) use cts = new CancellationTokenSource() @@ -845,8 +846,44 @@ module CancellableTaskTests = cts.Token "" } + } +#if TEST_NETSTANDARD2_1 || TEST_NET6_0_OR_GREATER + testCaseAsync "TaskSeq receives CancellationToken" + <| async { + + do! + cancellableTask { + + let loops = 1 + + let mutable CancellationToken = CancellationToken.None + + let asyncSeq = + taskSeq { + for i in 0..loops do + do! + cancellableTask { + let! ct = CancellableTask.getCancellationToken () + CancellationToken <- ct + } + yield () + } + + use cts = new CancellationTokenSource() + + let actual = + cancellableTask { + for i in asyncSeq do + do! Task.Yield() + } + + do! actual cts.Token + + Expect.equal CancellationToken cts.Token "" + } } +#endif ] testList "MergeSources" [ diff --git a/tests/IcedTasks.Tests/CancellableValueTaskTests.fs b/tests/IcedTasks.Tests/CancellableValueTaskTests.fs index c2d289f..48bf7ff 100644 --- a/tests/IcedTasks.Tests/CancellableValueTaskTests.fs +++ b/tests/IcedTasks.Tests/CancellableValueTaskTests.fs @@ -6,6 +6,7 @@ open System.Threading open System.Threading.Tasks open IcedTasks open System.Collections.Generic +open FSharp.Control module CancellableValueTaskTests = open TimeProviderExtensions @@ -801,7 +802,7 @@ module CancellableValueTaskTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) let! actual = cancellableValueTask { @@ -831,7 +832,7 @@ module CancellableValueTaskTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) use cts = new CancellationTokenSource() @@ -864,7 +865,7 @@ module CancellableValueTaskTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) use cts = new CancellationTokenSource() @@ -884,6 +885,46 @@ module CancellableValueTaskTests = } } +#if TEST_NETSTANDARD2_1 || TEST_NET6_0_OR_GREATER + testCaseAsync "TaskSeq receives CancellationToken" + <| async { + + do! + cancellableValueTask { + + let loops = 1 + + let mutable CancellationToken = CancellationToken.None + + let asyncSeq = + taskSeq { + for i in 0..loops do + do! + cancellableValueTask { + let! ct = + CancellableValueTask.getCancellationToken () + + CancellationToken <- ct + } + + yield () + } + + use cts = new CancellationTokenSource() + + let actual = + cancellableValueTask { + for i in asyncSeq do + do! Task.Yield() + } + + do! actual cts.Token + + Expect.equal CancellationToken cts.Token "" + } + } +#endif + ] testList "MergeSources" [ diff --git a/tests/IcedTasks.Tests/Expect.fs b/tests/IcedTasks.Tests/Expect.fs index f2bc584..fdbcdd0 100644 --- a/tests/IcedTasks.Tests/Expect.fs +++ b/tests/IcedTasks.Tests/Expect.fs @@ -179,7 +179,7 @@ module AsyncEnumerable = member this.MoveNextAsync() = moveNext () member this.DisposeAsync() = dispose () - type AsyncEnumerable<'T>(e: IEnumerable<'T>, beforeMoveNext: Func<_, ValueTask>) = + type AsyncEnumerable<'T>(e: IEnumerable<'T>, beforeMoveNext: Func<_, ValueTask>) = let mutable lastEnumerator = None member this.LastEnumerator = lastEnumerator @@ -211,3 +211,64 @@ module AsyncEnumerable = let forXtoY<'T> x y beforeMoveNext = AsyncEnumerable([ x..y ], Func<_, _>(beforeMoveNext)) + +#if TEST_NETSTANDARD2_1 || TEST_NET6_0_OR_GREATER + +[] +module AsyncEnumerableExtensions = + open FSharp.Control + open Microsoft.FSharp.Core.CompilerServices + + type TaskSeqBuilder with + + member inline _.Bind + ( + [] task: CancellableTask<'T>, + continuation: ('T -> ResumableTSC<'U>) + ) = + ResumableTSC<'U>(fun sm -> + let mutable awaiter = + task sm.Data.cancellationToken + |> Awaitable.GetTaskAwaiter + + let mutable __stack_fin = true + + if not (Awaiter.IsCompleted awaiter) then + let __stack_yield_fin = ResumableCode.Yield().Invoke(&sm) + __stack_fin <- __stack_yield_fin + + if __stack_fin then + let result = Awaiter.GetResult awaiter + (continuation result).Invoke(&sm) + else + sm.Data.awaiter <- awaiter + sm.Data.current <- ValueNone + false + ) + + member inline _.Bind + ( + [] task: CancellableValueTask<'T>, + continuation: ('T -> ResumableTSC<'U>) + ) = + ResumableTSC<'U>(fun sm -> + let mutable awaiter = + task sm.Data.cancellationToken + |> Awaitable.GetAwaiter + + let mutable __stack_fin = true + + if not (Awaiter.IsCompleted awaiter) then + let __stack_yield_fin = ResumableCode.Yield().Invoke(&sm) + __stack_fin <- __stack_yield_fin + + if __stack_fin then + let result = Awaiter.GetResult awaiter + (continuation result).Invoke(&sm) + else + sm.Data.awaiter <- awaiter + sm.Data.current <- ValueNone + false + ) + +#endif diff --git a/tests/IcedTasks.Tests/PoolingValueTaskDynamicTests.fs b/tests/IcedTasks.Tests/PoolingValueTaskDynamicTests.fs index 2eeae9d..3b3056d 100644 --- a/tests/IcedTasks.Tests/PoolingValueTaskDynamicTests.fs +++ b/tests/IcedTasks.Tests/PoolingValueTaskDynamicTests.fs @@ -9,6 +9,7 @@ open System.Threading.Tasks open IcedTasks open System.Collections.Generic + module PoolingValueTaskDynamicTests = open System.Runtime.CompilerServices open System.Collections.Generic @@ -611,7 +612,7 @@ module PoolingValueTaskDynamicTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) let! actual = dPoolingValueTask { diff --git a/tests/IcedTasks.Tests/PoolingValueTaskTests.fs b/tests/IcedTasks.Tests/PoolingValueTaskTests.fs index 31d4e2b..ed51fc0 100644 --- a/tests/IcedTasks.Tests/PoolingValueTaskTests.fs +++ b/tests/IcedTasks.Tests/PoolingValueTaskTests.fs @@ -596,7 +596,7 @@ module PoolingValueTaskTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) let! actual = poolingValueTask { diff --git a/tests/IcedTasks.Tests/TaskBackgroundTests.fs b/tests/IcedTasks.Tests/TaskBackgroundTests.fs index f1e3636..83cde23 100644 --- a/tests/IcedTasks.Tests/TaskBackgroundTests.fs +++ b/tests/IcedTasks.Tests/TaskBackgroundTests.fs @@ -598,7 +598,7 @@ module TaskBackgroundTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) let! actual = backgroundTask { diff --git a/tests/IcedTasks.Tests/TaskDynamicTests.fs b/tests/IcedTasks.Tests/TaskDynamicTests.fs index dbac837..2c75964 100644 --- a/tests/IcedTasks.Tests/TaskDynamicTests.fs +++ b/tests/IcedTasks.Tests/TaskDynamicTests.fs @@ -610,7 +610,7 @@ module TaskDynamicTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) let! actual = dTask { diff --git a/tests/IcedTasks.Tests/TaskTests.fs b/tests/IcedTasks.Tests/TaskTests.fs index c0c3797..889a35b 100644 --- a/tests/IcedTasks.Tests/TaskTests.fs +++ b/tests/IcedTasks.Tests/TaskTests.fs @@ -598,7 +598,7 @@ module TaskTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) let! actual = task { diff --git a/tests/IcedTasks.Tests/ValueTaskDynamicTests.fs b/tests/IcedTasks.Tests/ValueTaskDynamicTests.fs index fcf3c5f..9ca74fb 100644 --- a/tests/IcedTasks.Tests/ValueTaskDynamicTests.fs +++ b/tests/IcedTasks.Tests/ValueTaskDynamicTests.fs @@ -609,7 +609,7 @@ module ValueTaskDynamicTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) let! actual = dValueTask { diff --git a/tests/IcedTasks.Tests/ValueTaskTests.fs b/tests/IcedTasks.Tests/ValueTaskTests.fs index d97ae5c..2c13a92 100644 --- a/tests/IcedTasks.Tests/ValueTaskTests.fs +++ b/tests/IcedTasks.Tests/ValueTaskTests.fs @@ -597,7 +597,7 @@ module ValueTaskTests = AsyncEnumerable.forXtoY 0 loops - (fun _ -> valueTaskUnit { do! Task.Yield() }) + (cancellableValueTask { do! Task.Yield() }) let! actual = valueTask {