Skip to content

Commit ff34a53

Browse files
committed
POC Runtime Async
1 parent 0ffed55 commit ff34a53

File tree

6 files changed

+1062
-2
lines changed

6 files changed

+1062
-2
lines changed

src/IcedTasks/AutoOpens.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,7 @@ module AutoOpens =
1818
[<assembly: AutoOpen("IcedTasks.CancellableValueTasks")>]
1919
[<assembly: AutoOpen("IcedTasks.CancellablePoolingValueTasks")>]
2020
[<assembly: AutoOpen("IcedTasks.CancellableTasks")>]
21+
#if NET10_0_OR_GREATER
22+
[<assembly: AutoOpen("IcedTasks.TaskBase_Net10")>]
23+
#endif
2124
do ()

src/IcedTasks/IcedTasks.fsproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFrameworks>net9.0;net6.0;netstandard2.1;netstandard2.0;</TargetFrameworks>
3+
<TargetFrameworks>net10.0;net9.0;net6.0;netstandard2.1;netstandard2.0;</TargetFrameworks>
44
<NoWarn>$(NoWarn);FS3513</NoWarn>
55
</PropertyGroup>
66
<PropertyGroup>
@@ -12,6 +12,7 @@
1212
<Compile Include="Nullness.fs" />
1313
<Compile Include="TaskLike.fs" />
1414
<Compile Include="TaskBuilderBase.fs" />
15+
<Compile Include="TaskBuilderBase_Net10.fs" Condition="$(NET10_0_OR_GREATER) == 'true'"/>
1516
<Compile Include="ValueTask.fs" />
1617
<Compile Include="PoolingValueTask.fs" />
1718
<Compile Include="ValueTaskUnit.fs" />
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
namespace IcedTasks.TaskBase_Net10
2+
3+
open IcedTasks.Nullness
4+
open IcedTasks.TaskLike
5+
open System.Threading.Tasks
6+
open System.Runtime.CompilerServices
7+
open Microsoft.FSharp.Core.CompilerServices
8+
open System.Collections.Generic
9+
10+
module internal AsyncHelpers =
11+
12+
let inline awaitAwaiter awaiter =
13+
if not (Awaiter.IsCompleted awaiter) then
14+
AsyncHelpers.UnsafeAwaitAwaiter awaiter
15+
16+
Awaiter.GetResult awaiter
17+
18+
let inline awaitAwaitable awaiter =
19+
awaitAwaiter (Awaitable.GetAwaiter awaiter)
20+
21+
type TaskBuilderBaseRuntime() =
22+
23+
24+
member inline _.Return(value: 'T) = ValueTask.FromResult value
25+
26+
// [<NoEagerConstraintApplication>]
27+
member inline _.Bind(awaiter, [<InlineIfLambda>] continuation) =
28+
AsyncHelpers.awaitAwaiter awaiter
29+
|> continuation
30+
31+
32+
// [<NoEagerConstraintApplication>]
33+
member inline this.ReturnFrom(awaiter) = this.Bind(awaiter, this.Return)
34+
35+
member inline _.Zero() = ValueTask<_>()
36+
37+
member inline _.Delay([<InlineIfLambda>] f: unit -> 'a) = f
38+
39+
member inline this.BindReturn(awaiter, [<InlineIfLambda>] mapper) =
40+
this.Bind(awaiter, (fun v -> this.Return(mapper v)))
41+
42+
// [<NoEagerConstraintApplication>]
43+
member inline this.MergeSources(awaiter1, awaiter2) =
44+
this.Bind(awaiter1, fun v1 -> this.Bind(awaiter2, fun v2 -> this.Return(struct (v1, v2))))
45+
|> Awaitable.GetAwaiter
46+
47+
member inline this.Combine(awaiter1, [<InlineIfLambda>] continuation) =
48+
this.Bind(Awaitable.GetAwaiter awaiter1, fun _ -> continuation ())
49+
50+
member inline this.While
51+
([<InlineIfLambda>] guard: unit -> bool, [<InlineIfLambda>] body: unit -> 'a)
52+
=
53+
while guard () do
54+
body ()
55+
|> AsyncHelpers.awaitAwaitable
56+
57+
this.Zero()
58+
59+
member inline this.For(sequence: seq<'T>, [<InlineIfLambda>] body: 'T -> 'a) =
60+
for x in sequence do
61+
body x
62+
|> AsyncHelpers.awaitAwaitable
63+
64+
this.Zero()
65+
66+
67+
member inline this.TryFinallyAsync
68+
([<InlineIfLambda>] awaiter, [<InlineIfLambda>] compensation: unit -> ValueTask)
69+
=
70+
try
71+
this.Bind(Awaitable.GetAwaiter(awaiter ()), this.Return)
72+
finally
73+
compensation ()
74+
|> AsyncHelpers.awaitAwaitable
75+
76+
member inline this.TryFinally
77+
([<InlineIfLambda>] awaiter, [<InlineIfLambda>] compensation: unit -> unit)
78+
=
79+
try
80+
this.Bind(Awaitable.GetAwaiter(awaiter ()), this.Return)
81+
finally
82+
compensation ()
83+
84+
member inline this.TryWith
85+
([<InlineIfLambda>] awaiter, [<InlineIfLambda>] catchHandler: exn -> ValueTask<'a>)
86+
=
87+
try
88+
this.Bind(Awaitable.GetAwaiter(awaiter ()), this.Return)
89+
with ex ->
90+
catchHandler ex
91+
92+
93+
member inline this.Using
94+
(resource: #IAsyncDisposableNull, [<InlineIfLambda>] binder: #IAsyncDisposableNull -> 'a)
95+
=
96+
let inline disposeAsync (resource: #IAsyncDisposableNull) =
97+
if not (isNull resource) then
98+
resource.DisposeAsync()
99+
else
100+
ValueTask.CompletedTask
101+
102+
this.TryFinallyAsync((fun () -> binder resource), (fun () -> disposeAsync resource))
103+
104+
105+
member inline this.For(sequence: IAsyncEnumerable<'T>, [<InlineIfLambda>] body: 'T -> 'a) =
106+
107+
this.Using(
108+
sequence.GetAsyncEnumerator(),
109+
fun enumerator ->
110+
while enumerator.MoveNextAsync()
111+
|> AsyncHelpers.awaitAwaitable do
112+
body enumerator.Current
113+
|> AsyncHelpers.awaitAwaitable
114+
115+
this.Zero()
116+
)
117+
118+
119+
[<AutoOpen>]
120+
module LowPriority =
121+
// let task2 = TaskBuilderBaseRuntime()
122+
123+
type TaskBuilderBaseRuntime with
124+
125+
126+
/// <summary>Allows the computation expression to turn other types into CancellationToken -> 'Awaiter</summary>
127+
///
128+
/// <remarks>This is the identify function.</remarks>
129+
///
130+
/// <returns>'Awaiter</returns>
131+
// [<NoEagerConstraintApplication>]
132+
member inline _.Source<'TResult1, 'Awaiter when Awaiter<'Awaiter, 'TResult1>>
133+
(awaiter: 'Awaiter)
134+
: 'Awaiter =
135+
awaiter
136+
137+
/// <summary>Allows the computation expression to turn other types into 'Awaiter</summary>
138+
///
139+
/// <remarks>This turns a ^Awaitable into a 'Awaiter.</remarks>
140+
///
141+
/// <returns>'Awaiter</returns>
142+
// [<NoEagerConstraintApplication>]
143+
member inline _.Source<'Awaitable, 'TResult1, 'TResult2, 'Awaiter, 'TOverall
144+
when Awaitable<'Awaitable, 'Awaiter, 'TResult1>>
145+
(awaitable: 'Awaitable)
146+
: 'Awaiter =
147+
Awaitable.GetAwaiter awaitable
148+
149+
member inline _.Source(seq: #IAsyncEnumerable<_>) = seq
150+
151+
[<AutoOpen>]
152+
module HighPriority =
153+
154+
type TaskBuilderBaseRuntime with
155+
156+
member inline this.Using
157+
(resource: #IDisposableNull, [<InlineIfLambda>] binder: #IDisposableNull -> 'a)
158+
=
159+
let inline dispose (resource: #IDisposableNull) =
160+
if not (isNull resource) then
161+
resource.Dispose()
162+
163+
this.TryFinally((fun () -> binder resource), (fun () -> dispose resource))
164+
165+
166+
/// <summary>Allows the computation expression to turn other types into 'Awaiter</summary>
167+
///
168+
/// <remarks>This turns a Task&lt;'T&gt; into a 'Awaiter.</remarks>
169+
///
170+
/// <returns>'Awaiter</returns>
171+
member inline _.Source(task: Task<'T>) = Awaitable.GetTaskAwaiter task
172+
173+
174+
/// <summary>Allows the computation expression to turn other types into 'Awaiter</summary>
175+
///
176+
/// <remarks>This turns a Task&lt;'T&gt; into a 'Awaiter.</remarks>
177+
///
178+
/// <returns>'Awaiter</returns>
179+
member inline _.Source(task: System.Func<Task<'T>>) =
180+
Awaitable.GetTaskAwaiter(task.Invoke())
181+
182+
member inline x.Source(async: Async<'T>) =
183+
async
184+
|> Async.StartImmediateAsTask
185+
|> x.Source
186+
187+
member inline _.Source(seq: #seq<_>) = seq
188+
189+
190+
type TaskBuilderRuntime() =
191+
inherit TaskBuilderBaseRuntime()
192+
193+
member inline this.Run([<InlineIfLambda>] f: unit -> ValueTask<'T>) : Task<'T> = f().AsTask()
194+
195+
196+
/// <summary>Allows the computation expression to turn other types into 'Awaiter</summary>
197+
///
198+
/// <remarks>This turns a Task&lt;'T&gt; into a 'Awaiter.</remarks>
199+
///
200+
/// <returns>'Awaiter</returns>
201+
member inline _.Source(task: Task<'T>) = Awaitable.GetTaskAwaiter task
202+
203+
204+
type ValueTaskBuilderRuntime() =
205+
inherit TaskBuilderBaseRuntime()
206+
207+
member inline this.Run([<InlineIfLambda>] f: unit -> ValueTask<'T>) : ValueTask<'T> = f ()
208+
209+
210+
/// <summary>Allows the computation expression to turn other types into 'Awaiter</summary>
211+
///
212+
/// <remarks>This turns a Task&lt;'T&gt; into a 'Awaiter.</remarks>
213+
///
214+
/// <returns>'Awaiter</returns>
215+
member inline _.Source(task: ValueTask<'T>) = Awaitable.GetAwaiter task
216+
217+
218+
module Testing =
219+
let taskRuntime = TaskBuilderRuntime()
220+
221+
let foo () =
222+
taskRuntime {
223+
let! x = Task.FromResult 42
224+
and! y = ValueTask.FromResult 42
225+
and! a = ValueTask.FromResult 42
226+
and! b = Task.FromResult 42
227+
and! c = ValueTask.FromResult 42
228+
and! z = Task.Run(fun () -> 42)
229+
230+
do! Task.Yield()
231+
232+
if true then
233+
do! ValueTask.CompletedTask
234+
235+
let mutable i = 0
236+
237+
while i < 10 do
238+
do! ValueTask.CompletedTask
239+
i <- i + 1
240+
241+
for j in 1..5 do
242+
do! ValueTask.CompletedTask
243+
244+
245+
return! ValueTask.FromResult(x + y + z)
246+
}

tests/IcedTasks.Tests/Expect.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ module Expect =
8787
}
8888

8989
match thrown with
90-
| ValueSome e when e.GetType().IsAssignableFrom typeof<'texn> ->
90+
| ValueSome e when not (e.GetType().IsAssignableFrom typeof<'texn>) ->
9191
failtestf
9292
"%s. Expected f to throw an exn of type %s, but one of type %s was thrown."
9393
message

tests/IcedTasks.Tests/IcedTasks.Tests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<Compile Include="Expect.fs" />
1313
<Compile Include="AsyncExTests.fs" />
1414
<Compile Include="TaskTests.fs" />
15+
<Compile Include="TaskTests_Net10.fs" Condition="$(NET10_0_OR_GREATER) == 'true'" />
1516
<Compile Include="TaskBackgroundTests.fs" />
1617
<Compile Include="TaskDynamicTests.fs" />
1718
<Compile Include="ValueTaskTests.fs" />

0 commit comments

Comments
 (0)