├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── Directory.Build.props
├── FSharp.Control.FusionTasks.Tests.CS
├── FSharp.Control.FusionTasks.Tests.CS.csproj
└── TaskExtensionsTests.cs
├── FSharp.Control.FusionTasks.Tests.FS
├── AsyncSequneceTests.fs
├── BasisTests.fs
├── ConfiguredTests.fs
├── FSharp.Control.FusionTasks.Tests.FS.fsproj
└── OperatorTests.fs
├── FSharp.Control.FusionTasks.Tests.Utilities
├── AsyncDisposableFactory.cs
├── AsyncEnumerableFactory.cs
├── FSharp.Control.FusionTasks.Tests.Utilities.csproj
└── ThreadBoundSynchronizationContext.cs
├── FSharp.Control.FusionTasks.sln
├── FSharp.Control.FusionTasks.sln.licenseheader
├── FSharp.Control.FusionTasks
├── AsyncCompletionSource.fs
├── AsyncExtensions.fs
├── Awaiters.fs
├── CommonAssemblyInfo.fs
├── FSharp.Control.FusionTasks.fsproj
├── Infrastructures.fs
├── TaskExtensions.fs
└── Utilities.fs
├── Images
├── FSharp.Control.FusionTasks.100.png
├── FSharp.Control.FusionTasks.128.png
├── FSharp.Control.FusionTasks.design
└── linqpad5.png
├── LICENSE.txt
├── NuGet.Config
├── README.md
├── build-nupkg.bat
└── build-nupkg.sh
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: .NET
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - uses: actions/checkout@v2
11 | with:
12 | fetch-depth: 0
13 | # lfs: true
14 | # https://stackoverflow.com/questions/61463578/github-actions-actions-checkoutv2-lfs-true-flag-not-converting-pointers-to-act
15 | #- name: Checkout LFS objects
16 | # run: git lfs checkout
17 |
18 | - name: Extract branch name
19 | id: extract_branch_name
20 | # if: startsWith( github.ref, 'refs/tags/' )
21 | run: |
22 | export branch_name=`git name-rev --name-only --exclude=tags/* HEAD`
23 | echo "Detected current branch: $branch_name"
24 | echo "::set-output name=branch_name::$branch_name"
25 |
26 | # ----------------------------------------------------------
27 |
28 | - name: Setup .NET Core 2.2
29 | uses: actions/setup-dotnet@v1
30 | with:
31 | dotnet-version: 2.2.*
32 | - name: Setup .NET Core 3.1
33 | uses: actions/setup-dotnet@v1
34 | with:
35 | dotnet-version: 3.1.*
36 | - name: Setup .NET 5
37 | uses: actions/setup-dotnet@v1
38 | with:
39 | dotnet-version: 5.0.*
40 | - name: Setup .NET 6
41 | uses: actions/setup-dotnet@v1
42 | with:
43 | dotnet-version: 6.0.*
44 |
45 | # ----------------------------------------------------------
46 |
47 | - name: Setup NuGet package reference
48 | run: dotnet nuget add source ${{secrets.GH_LOCAL_NUGET_URL}} -n ref1 -u ${{secrets.GH_LOCAL_NUGET_USER}} -p ${{secrets.GH_LOCAL_NUGET_PASSWORD}} --store-password-in-clear-text --configfile NuGet.Config
49 |
50 | # ----------------------------------------------------------
51 |
52 | - name: Build
53 | run: dotnet build -p:Configuration=Release -p:BuildIdentifier=${GITHUB_RUN_NUMBER}
54 |
55 | # ----------------------------------------------------------
56 |
57 | - name: Test
58 | run: dotnet test --verbosity normal -p:Configuration=Release -p:BuildIdentifier=${GITHUB_RUN_NUMBER}
59 | timeout-minutes: 2
60 |
61 | # ----------------------------------------------------------
62 |
63 | - name: Build packages
64 | run: dotnet pack -p:Configuration=Release -p:BuildIdentifier=${GITHUB_RUN_NUMBER} -o artifacts
65 |
66 | # ----------------------------------------------------------
67 |
68 | - name: Deploy NuGet package (devel/ref1)
69 | if: startsWith( github.ref, 'refs/tags/' )
70 | run: dotnet nuget push artifacts/FSharp.Control.FusionTasks.*.nupkg --source ref1
71 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 |
24 | # Visual Studio 2015 cache/options directory
25 | .vs/
26 | # Uncomment if you have tasks that create the project's static files in wwwroot
27 | #wwwroot/
28 |
29 | # MSTest test Results
30 | [Tt]est[Rr]esult*/
31 | [Bb]uild[Ll]og.*
32 |
33 | # NUNIT
34 | *.VisualState.xml
35 | TestResult.xml
36 |
37 | # Build Results of an ATL Project
38 | [Dd]ebugPS/
39 | [Rr]eleasePS/
40 | dlldata.c
41 |
42 | # DNX
43 | project.lock.json
44 | artifacts/
45 |
46 | *_i.c
47 | *_p.c
48 | *_i.h
49 | *.ilk
50 | *.meta
51 | *.obj
52 | *.pch
53 | *.pdb
54 | *.pgc
55 | *.pgd
56 | *.rsp
57 | *.sbr
58 | *.tlb
59 | *.tli
60 | *.tlh
61 | *.tmp
62 | *.tmp_proj
63 | *.log
64 | *.vspscc
65 | *.vssscc
66 | .builds
67 | *.pidb
68 | *.svclog
69 | *.scc
70 |
71 | # Chutzpah Test files
72 | _Chutzpah*
73 |
74 | # Visual C++ cache files
75 | ipch/
76 | *.aps
77 | *.ncb
78 | *.opendb
79 | *.opensdf
80 | *.sdf
81 | *.cachefile
82 |
83 | # Visual Studio profiler
84 | *.psess
85 | *.vsp
86 | *.vspx
87 | *.sap
88 |
89 | # TFS 2012 Local Workspace
90 | $tf/
91 |
92 | # Guidance Automation Toolkit
93 | *.gpState
94 |
95 | # ReSharper is a .NET coding add-in
96 | _ReSharper*/
97 | *.[Rr]e[Ss]harper
98 | *.DotSettings.user
99 |
100 | # JustCode is a .NET coding add-in
101 | .JustCode
102 |
103 | # TeamCity is a build add-in
104 | _TeamCity*
105 |
106 | # DotCover is a Code Coverage Tool
107 | *.dotCover
108 |
109 | # NCrunch
110 | _NCrunch_*
111 | .*crunch*.local.xml
112 | nCrunchTemp_*
113 |
114 | # MightyMoose
115 | *.mm.*
116 | AutoTest.Net/
117 |
118 | # Web workbench (sass)
119 | .sass-cache/
120 |
121 | # Installshield output folder
122 | [Ee]xpress/
123 |
124 | # DocProject is a documentation generator add-in
125 | DocProject/buildhelp/
126 | DocProject/Help/*.HxT
127 | DocProject/Help/*.HxC
128 | DocProject/Help/*.hhc
129 | DocProject/Help/*.hhk
130 | DocProject/Help/*.hhp
131 | DocProject/Help/Html2
132 | DocProject/Help/html
133 |
134 | # Click-Once directory
135 | publish/
136 |
137 | # Publish Web Output
138 | *.[Pp]ublish.xml
139 | *.azurePubxml
140 | # TODO: Comment the next line if you want to checkin your web deploy settings
141 | # but database connection strings (with potential passwords) will be unencrypted
142 | *.pubxml
143 | *.publishproj
144 |
145 | # NuGet Packages
146 | *.nupkg
147 | # The packages folder can be ignored because of Package Restore
148 | **/packages/*
149 | # except build/, which is used as an MSBuild target.
150 | !**/packages/build/
151 | # Uncomment if necessary however generally it will be regenerated when needed
152 | #!**/packages/repositories.config
153 |
154 | # Microsoft Azure Build Output
155 | csx/
156 | *.build.csdef
157 |
158 | # Microsoft Azure Emulator
159 | ecf/
160 | rcf/
161 |
162 | # Microsoft Azure ApplicationInsights config file
163 | ApplicationInsights.config
164 |
165 | # Windows Store app package directory
166 | AppPackages/
167 | BundleArtifacts/
168 |
169 | # Visual Studio cache files
170 | # files ending in .cache can be ignored
171 | *.[Cc]ache
172 | # but keep track of directories ending in .cache
173 | !*.[Cc]ache/
174 |
175 | # Others
176 | ClientBin/
177 | ~$*
178 | *~
179 | *.dbmdl
180 | *.dbproj.schemaview
181 | *.pfx
182 | *.publishsettings
183 | node_modules/
184 | orleans.codegen.cs
185 |
186 | # RIA/Silverlight projects
187 | Generated_Code/
188 |
189 | # Backup & report files from converting an old project file
190 | # to a newer Visual Studio version. Backup files are not needed,
191 | # because we have git ;-)
192 | _UpgradeReport_Files/
193 | Backup*/
194 | UpgradeLog*.XML
195 | UpgradeLog*.htm
196 |
197 | # SQL Server files
198 | *.mdf
199 | *.ldf
200 |
201 | # Business Intelligence projects
202 | *.rdl.data
203 | *.bim.layout
204 | *.bim_*.settings
205 |
206 | # Microsoft Fakes
207 | FakesAssemblies/
208 |
209 | # GhostDoc plugin setting file
210 | *.GhostDoc.xml
211 |
212 | # Node.js Tools for Visual Studio
213 | .ntvs_analysis.dat
214 |
215 | # Visual Studio 6 build log
216 | *.plg
217 |
218 | # Visual Studio 6 workspace options file
219 | *.opt
220 |
221 | # Visual Studio LightSwitch build output
222 | **/*.HTMLClient/GeneratedArtifacts
223 | **/*.DesktopClient/GeneratedArtifacts
224 | **/*.DesktopClient/ModelManifest.xml
225 | **/*.Server/GeneratedArtifacts
226 | **/*.Server/ModelManifest.xml
227 | _Pvt_Extensions
228 |
229 | # Paket dependency manager
230 | .paket/paket.exe
231 |
232 | # FAKE - F# Make
233 | .fake/
234 |
235 | .nuget/
236 |
237 | .idea/
238 |
239 | artifacts/
240 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | latest
5 | enable
6 | AnyCPU
7 |
8 | true
9 | true
10 | false
11 | false
12 | true
13 | git
14 | https://github.com/kekyo/FSharp.Control.FusionTasks.git
15 |
16 | FlashCap
17 | false
18 | true
19 | $(NoWarn);CS1570;CS1591;CA1416
20 |
21 | FusionTasks
22 | FusionTasks
23 | FusionTasks
24 | Copyright (c) Kouji Matsui
25 | F# Async workflow <--> .NET Task easy seamless interoperability library.
26 |
27 | Kouji Matsui (@kozy_kekyo, @kekyo@mastodon.cloud)
28 | Kouji Matsui (@kozy_kekyo, @kekyo@mastodon.cloud)
29 | Apache-2.0
30 | https://github.com/kekyo/FSharp.Control.FusionTasks
31 | FSharp.Control.FusionTasks.100.png
32 | F#;FSharp;Async;Task;C#;CSharp;await;interop;seamless;computation;Builder;extension
33 | .pdb
34 | true
35 | $(NoWarn);NU1605;NU1701;NU1803
36 | false
37 |
38 |
39 |
40 | portable
41 | false
42 | false
43 | false
44 |
45 |
46 |
47 | embedded
48 | true
49 | true
50 | true
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks.Tests.CS/FSharp.Control.FusionTasks.Tests.CS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48;netcoreapp2.2;netcoreapp3.1;net5.0-windows;net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks.Tests.CS/TaskExtensionsTests.cs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | using System;
21 | using System.Collections.Generic;
22 | using System.Threading;
23 | using System.Threading.Tasks;
24 | using Microsoft.FSharp.Control;
25 | using Microsoft.FSharp.Core;
26 |
27 | using NUnit;
28 | using NUnit.Framework;
29 |
30 | namespace FSharp.Control.FusionTasks.Tests
31 | {
32 | [TestFixture]
33 | public class TaskExtensionsTests
34 | {
35 | #region Task.AsAsync
36 | private static async Task DelayAsync()
37 | {
38 | await Task.Delay(500);
39 | Console.WriteLine("AAA");
40 | }
41 |
42 | [Test]
43 | public void TaskAsAsyncTest()
44 | {
45 | var task = DelayAsync();
46 | var asy = task.AsAsync();
47 |
48 | // MSTest not supported FSharpAsync based tests, so run synchronously here.
49 | FSharpAsync.RunSynchronously(asy, FSharpOption.None, FSharpOption.None);
50 | }
51 |
52 | [Test]
53 | public void ConfiguredAsyncAwaiterAsAsyncTest()
54 | {
55 | var task = DelayAsync();
56 | var asy = task.AsAsyncConfigured(false);
57 |
58 | // MSTest not supported FSharpAsync based tests, so run synchronously here.
59 | FSharpAsync.RunSynchronously(asy, FSharpOption.None, FSharpOption.None);
60 | }
61 |
62 | private static async Task DelayAndReturnAsync()
63 | {
64 | await Task.Delay(500);
65 | return 123;
66 | }
67 |
68 | [Test]
69 | public void TaskTAsAsyncTest()
70 | {
71 | var task = DelayAndReturnAsync();
72 | var asy = task.AsAsync();
73 |
74 | // MSTest not supported FSharpAsync based tests, so run synchronously here.
75 | var result = FSharpAsync.RunSynchronously(asy, FSharpOption.None, FSharpOption.None);
76 | Assert.AreEqual(123, result);
77 | }
78 |
79 | [Test]
80 | public void ConfiguredAsyncAwaiterTAsAsyncTest()
81 | {
82 | var task = DelayAndReturnAsync();
83 | var asy = task.AsAsyncConfigured(false);
84 |
85 | // MSTest not supported FSharpAsync based tests, so run synchronously here.
86 | var result = FSharpAsync.RunSynchronously(asy, FSharpOption.None, FSharpOption.None);
87 | Assert.AreEqual(123, result);
88 | }
89 |
90 | private static ValueTask DelayAndReturnAsyncByValueTask()
91 | {
92 | return new ValueTask(DelayAndReturnAsync());
93 | }
94 |
95 | [Test]
96 | public void ValueTaskTAsAsyncTest()
97 | {
98 | var task = DelayAndReturnAsyncByValueTask();
99 | var asy = task.AsAsync();
100 |
101 | // MSTest not supported FSharpAsync based tests, so run synchronously here.
102 | var result = FSharpAsync.RunSynchronously(asy, FSharpOption.None, FSharpOption.None);
103 | Assert.AreEqual(123, result);
104 | }
105 |
106 | [Test]
107 | public void ConfiguredAsyncAwaiterTValueTaskAsAsyncTest()
108 | {
109 | var task = DelayAndReturnAsyncByValueTask();
110 | var asy = task.AsAsyncConfigured(false);
111 |
112 | // MSTest not supported FSharpAsync based tests, so run synchronously here.
113 | var result = FSharpAsync.RunSynchronously(asy, FSharpOption.None, FSharpOption.None);
114 | Assert.AreEqual(123, result);
115 | }
116 | #endregion
117 |
118 | #region FSharpAsync<'T>.AsTask
119 | [Test]
120 | public async Task AsyncAsTaskTestAsync()
121 | {
122 | var asy = FSharpAsync.Sleep(500);
123 | await asy.AsTask();
124 | }
125 |
126 | [Test]
127 | public async Task AsyncTAsTaskTestAsync()
128 | {
129 | // C# cannot create FSharpAsync<'T>, so create C#'ed Task and convert to FSharpAsync<'T>.
130 | var task = DelayAndReturnAsync();
131 | var asy = task.AsAsync();
132 | var result = await asy.AsTask();
133 |
134 | Assert.AreEqual(123, result);
135 | }
136 |
137 | [Test]
138 | public void AsyncAsTaskWithCancellationTestAsync()
139 | {
140 | var cts = new CancellationTokenSource();
141 |
142 | var asy = FSharpAsync.Sleep(500);
143 | var outerTask = asy.AsTask(cts.Token);
144 |
145 | // Force hard wait.
146 | Thread.Sleep(100);
147 |
148 | // Task cancelled.
149 | cts.Cancel();
150 |
151 | // Continuation point. (Will raise exception)
152 | Assert.ThrowsAsync(() => outerTask);
153 | }
154 |
155 | private static async Task DelayAndReturnAsync(CancellationToken token)
156 | {
157 | await Task.Delay(500, token); /* explicitly */
158 | return 123;
159 | }
160 |
161 | [Test]
162 | public void AsyncTAsTaskWithCancellationTestAsync()
163 | {
164 | // Information:
165 | // F#'s CancellationToken is managing contextual token in async computation expression.
166 | // But .NET CancellationToken is non contextual/default token, in C# too.
167 | // So no automatic implicit derive tokens, token just explicit set to any async operation:
168 | // Task.Delay(int, token) <-- DelayAndReturnAsync(token) <-- cts.Token
169 |
170 | var cts = new CancellationTokenSource();
171 |
172 | // C# cannot create FSharpAsync<'T>, so create C#'ed Task and convert to FSharpAsync<'T>.
173 | var task = DelayAndReturnAsync(/* explicitly */ cts.Token);
174 | var asy = task.AsAsync();
175 | var outerTask = asy.AsTask(cts.Token); /* explicitly, but not derived into DelayAndReturnAsync() */
176 |
177 | // Force hard wait.
178 | Thread.Sleep(100);
179 |
180 | // Task cancelled.
181 | cts.Cancel();
182 |
183 | // Continuation point. (Will raise exception)
184 | Assert.ThrowsAsync(() => outerTask);
185 | }
186 |
187 | private static ValueTask DelayAndReturnAsyncByValueTask(CancellationToken token)
188 | {
189 | return new ValueTask(DelayAndReturnAsync(token));
190 | }
191 |
192 | [Test]
193 | public void AsyncTValueTaskAsTaskWithCancellationTestAsync()
194 | {
195 | // Information:
196 | // F#'s CancellationToken is managing contextual token in async computation expression.
197 | // But .NET CancellationToken is non contextual/default token, in C# too.
198 | // So no automatic implicit derive tokens, token just explicit set to any async operation:
199 | // Task.Delay(int, token) <-- DelayAndReturnAsync(token) <-- cts.Token
200 |
201 | var cts = new CancellationTokenSource();
202 |
203 | // C# cannot create FSharpAsync<'T>, so create C#'ed Task and convert to FSharpAsync<'T>.
204 | var task = DelayAndReturnAsyncByValueTask(/* explicitly */ cts.Token);
205 | var asy = task.AsAsync();
206 | var outerTask = asy.AsTask(cts.Token); /* explicitly, but not derived into DelayAndReturnAsyncByValueTask() */
207 |
208 | // Force hard wait.
209 | Thread.Sleep(100);
210 |
211 | // Task cancelled.
212 | cts.Cancel();
213 |
214 | // Continuation point. (Will raise exception)
215 | Assert.ThrowsAsync(() => outerTask);
216 | }
217 | #endregion
218 |
219 | #region FSharpAsync<'T>.GetAwaiter
220 | [Test]
221 | public async Task AsyncGetAwaiterTestAsync()
222 | {
223 | var asy = FSharpAsync.Sleep(500);
224 | await asy;
225 | }
226 |
227 | [Test]
228 | public async Task AsyncTGetAwaiterTestAsync()
229 | {
230 | // C# cannot create FSharpAsync<'T>, so create C#'ed Task and convert to FSharpAsync<'T>.
231 | var task = DelayAndReturnAsync();
232 | var asy = task.AsAsync();
233 | var result = await asy;
234 |
235 | Assert.AreEqual(123, result);
236 | }
237 | #endregion
238 |
239 | [Test]
240 | public void HoldSynchContextTest()
241 | {
242 | var id1 = Thread.CurrentThread.ManagedThreadId;
243 | var context = new ThreadBoundSynchronizationContext();
244 | SynchronizationContext.SetSynchronizationContext(context);
245 |
246 | async Task ComputationAsync()
247 | {
248 | var idStartImmediately = Thread.CurrentThread.ManagedThreadId;
249 | Assert.AreEqual(id1, idStartImmediately);
250 |
251 | await FSharpAsync.StartImmediateAsTask(
252 | FSharpAsync.Sleep(300), FSharpOption.None);
253 | var idAwaitTask = Thread.CurrentThread.ManagedThreadId;
254 | Assert.AreEqual(id1, idAwaitTask);
255 |
256 | await FSharpAsync.Sleep(300).AsTask();
257 | var idAwaited1 = Thread.CurrentThread.ManagedThreadId;
258 | Assert.AreEqual(id1, idAwaited1);
259 |
260 | await FSharpAsync.Sleep(300).AsTask();
261 | var idAwaited2 = Thread.CurrentThread.ManagedThreadId;
262 | Assert.AreEqual(id1, idAwaited2);
263 | }
264 |
265 | context.Run(ComputationAsync());
266 | }
267 |
268 | [TestCase(true)]
269 | [TestCase(false)]
270 | public void ThreadBoundSynchronizationContextTest(bool capture)
271 | {
272 | var id1 = Thread.CurrentThread.ManagedThreadId;
273 | var context = new ThreadBoundSynchronizationContext();
274 | SynchronizationContext.SetSynchronizationContext(context);
275 |
276 | async Task ComputationAsync()
277 | {
278 | var id2 = Thread.CurrentThread.ManagedThreadId;
279 | Assert.AreEqual(id1, id2);
280 |
281 | var values = new[] { 1, 2, 3, 4, 5 };
282 | var delay = TimeSpan.FromMilliseconds(100);
283 | var results = new List();
284 |
285 | await foreach (var value in values.DelayEachAsync(delay).ConfigureAwait(capture))
286 | {
287 | var id3 = Thread.CurrentThread.ManagedThreadId;
288 | if (capture) Assert.AreEqual(id1, id3);
289 | else Assert.AreNotEqual(id1, id3);
290 |
291 | results.Add(value);
292 |
293 | await Task.Delay(delay);
294 | }
295 |
296 | var id4 = Thread.CurrentThread.ManagedThreadId;
297 | if (capture) Assert.AreEqual(id1, id4);
298 | else Assert.AreNotEqual(id1, id4);
299 |
300 | Assert.AreEqual(values, results);
301 | }
302 |
303 | context.Run(ComputationAsync());
304 | }
305 | }
306 | }
307 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks.Tests.FS/AsyncSequneceTests.fs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | namespace FSharp.Control.FusionTasks.Tests
21 |
22 | open System
23 | open System.IO
24 | open System.Diagnostics
25 | open System.Threading
26 | open System.Threading.Tasks
27 |
28 | open NUnit.Framework
29 |
30 | #nowarn "44"
31 |
32 | module AsyncSequenceTests =
33 |
34 | ////////////////////////////////////////////////////////////////////////
35 | // IAsyncEnumerable
36 |
37 | let private delay = TimeSpan.FromMilliseconds 100.0
38 |
39 | []
40 | let AsyncEnumerableIterationTest() =
41 | let computation = async {
42 | let values = [ 2; 5; 3; 7; 1 ]
43 | let results = new System.Collections.Generic.List()
44 | for value in values.DelayEachAsync(delay) do
45 | results.Add value
46 | do! Async.Sleep delay
47 | Assert.AreEqual(values, results)
48 | }
49 | computation.AsTask()
50 |
51 | []
52 | let AsyncEnumerableIterationNoDelayingBodyTest() =
53 | let computation = async {
54 | let values = [ 2; 5; 3; 7; 1 ]
55 | let results = new System.Collections.Generic.List()
56 | for value in values.DelayEachAsync(delay) do
57 | results.Add value
58 | Assert.AreEqual(values, results)
59 | }
60 | computation.AsTask()
61 |
62 | []
63 | let AsyncEnumerableSynchIterationTest() =
64 | let computation = async {
65 | let values = [ 2; 5; 3; 7; 1 ]
66 | let results = new System.Collections.Generic.List()
67 | for value in values.AsAsyncEnumerable() do
68 | results.Add value
69 | do! Async.Sleep delay
70 | Assert.AreEqual(values, results)
71 | }
72 | computation.AsTask()
73 |
74 | []
75 | let AsyncEnumerableSynchIterationNoDelayingBodyTest() =
76 | let computation = async {
77 | let values = [ 2; 5; 3; 7; 1 ]
78 | let results = new System.Collections.Generic.List()
79 | for value in values.AsAsyncEnumerable() do
80 | results.Add value
81 | Assert.AreEqual(values, results)
82 | }
83 | computation.AsTask()
84 |
85 | []
86 | let AsyncEnumerableIterationThrowingTest() =
87 | let computation = async {
88 | let values = [ 2; 5; 3; 7; 1 ]
89 | let results = new System.Collections.Generic.List()
90 | let exn = new Exception()
91 | let mutable index = 0
92 | try
93 | for value in values.DelayEachAsync(delay) do
94 | results.Add value
95 | index <- index + 1
96 | if index >= 3 then
97 | raise exn
98 | do! Async.Sleep delay
99 | Assert.Fail()
100 | with
101 | | exn2 ->
102 | Assert.AreSame(exn, exn2)
103 | Assert.AreEqual(values |> Seq.take 3, results)
104 | }
105 | computation.AsTask()
106 |
107 | []
108 | let AsyncEnumerableIterationThrowingNoDelayingBodyTest() =
109 | let computation = async {
110 | let values = [ 2; 5; 3; 7; 1 ]
111 | let results = new System.Collections.Generic.List()
112 | let exn = new Exception()
113 | let mutable index = 0
114 | try
115 | for value in values.DelayEachAsync(delay) do
116 | results.Add value
117 | index <- index + 1
118 | if index >= 3 then
119 | raise exn
120 | Assert.Fail()
121 | with
122 | | exn2 ->
123 | Assert.AreSame(exn, exn2)
124 | Assert.AreEqual(values |> Seq.take 3, results)
125 | }
126 | computation.AsTask()
127 |
128 | []
129 | let AsyncEnumerableSynchIterationThrowingTest() =
130 | let computation = async {
131 | let values = [ 2; 5; 3; 7; 1 ]
132 | let results = new System.Collections.Generic.List()
133 | let exn = new Exception()
134 | let mutable index = 0
135 | try
136 | for value in values.AsAsyncEnumerable() do
137 | results.Add value
138 | index <- index + 1
139 | if index >= 3 then
140 | raise exn
141 | do! Async.Sleep delay
142 | Assert.Fail()
143 | with
144 | | exn2 ->
145 | Assert.AreSame(exn, exn2)
146 | Assert.AreEqual(values |> Seq.take 3, results)
147 | }
148 | computation.AsTask()
149 |
150 | []
151 | let AsyncEnumerableSynchIterationThrowingNoDelayingBodyTest() =
152 | let computation = async {
153 | let values = [ 2; 5; 3; 7; 1 ]
154 | let results = new System.Collections.Generic.List()
155 | let exn = new Exception()
156 | let mutable index = 0
157 | try
158 | for value in values.AsAsyncEnumerable() do
159 | results.Add value
160 | index <- index + 1
161 | if index >= 3 then
162 | raise exn
163 | Assert.Fail()
164 | with
165 | | exn2 ->
166 | Assert.AreSame(exn, exn2)
167 | Assert.AreEqual(values |> Seq.take 3, results)
168 | }
169 | computation.AsTask()
170 |
171 | []
172 | let AsyncEnumerableIterationThrowingBeforeTest() =
173 | let computation = async {
174 | let values = [ 2; 5; 3; 7; 1 ]
175 | let results = new System.Collections.Generic.List()
176 | try
177 | for value in values.WillThrowBefore() do
178 | results.Add value
179 | do! Async.Sleep delay
180 | Assert.Fail()
181 | with
182 | | exn ->
183 | Assert.AreEqual("WillThrowBefore", exn.Message)
184 | Assert.AreEqual(0, results.Count)
185 | }
186 | computation.AsTask()
187 |
188 | []
189 | let AsyncEnumerableIterationThrowingAfterTest() =
190 | let computation = async {
191 | let values = [ 2; 5; 3; 7; 1 ]
192 | let results = new System.Collections.Generic.List()
193 | try
194 | for value in values.WillThrowAfter() do
195 | results.Add value
196 | do! Async.Sleep delay
197 | Assert.Fail()
198 | with
199 | | exn ->
200 | Assert.AreEqual("WillThrowAfter", exn.Message)
201 | Assert.AreEqual(values, results)
202 | }
203 | computation.AsTask()
204 |
205 | []
206 | let AsyncEnumerableIterationThrowingInterBeforeTest() =
207 | let computation = async {
208 | let values = [ 2; 5; 3; 7; 1 ]
209 | let results = new System.Collections.Generic.List()
210 | try
211 | for value in values.WillThrowInterBefore() do
212 | results.Add value
213 | do! Async.Sleep delay
214 | Assert.Fail()
215 | with
216 | | exn ->
217 | Assert.AreEqual("WillThrowInterBefore", exn.Message)
218 | Assert.AreEqual(0, results.Count)
219 | }
220 | computation.AsTask()
221 |
222 | []
223 | let AsyncEnumerableIterationThrowingInterAfterTest() =
224 | let computation = async {
225 | let values = [ 2; 5; 3; 7; 1 ]
226 | let results = new System.Collections.Generic.List()
227 | try
228 | for value in values.WillThrowInterAfter() do
229 | results.Add value
230 | do! Async.Sleep delay
231 | Assert.Fail()
232 | with
233 | | exn ->
234 | Assert.AreEqual("WillThrowInterAfter", exn.Message)
235 | Assert.AreEqual(values |> Seq.take 1, results)
236 | }
237 | computation.AsTask()
238 |
239 | []
240 | let AsyncEnumerableWillCallAsyncDisposeTest() =
241 | let computation = async {
242 | let values = [ 2; 5; 3; 7; 1 ]
243 | let results = new System.Collections.Generic.List()
244 | let mutable called = false
245 | for value in values.HookAsyncEnumerable(fun () ->
246 | called <- true
247 | new ValueTask()) do
248 | results.Add value
249 | do! Async.Sleep delay
250 | Assert.IsTrue(called)
251 | Assert.AreEqual(values, results)
252 | }
253 | computation.AsTask()
254 |
255 | []
256 | let AsyncEnumerableWillDelayCallAsyncDisposeTest() =
257 | let computation = async {
258 | let values = [ 2; 5; 3; 7; 1 ]
259 | let results = new System.Collections.Generic.List()
260 | let mutable called = false
261 | for value in values.HookAsyncEnumerable(fun () ->
262 | new ValueTask(
263 | Async.StartImmediateAsTask (async {
264 | do! Async.Sleep delay
265 | called <- true }))) do
266 | results.Add value
267 | do! Async.Sleep delay
268 | Assert.IsTrue(called)
269 | Assert.AreEqual(values, results)
270 | }
271 | computation.AsTask()
272 |
273 | []
274 | let AsyncEnumerableWillThrowAsyncDisposeTest() =
275 | let computation = async {
276 | let values = [ 2; 5; 3; 7; 1 ]
277 | let results = new System.Collections.Generic.List()
278 | let exn = new Exception()
279 | try
280 | for value in values.HookAsyncEnumerable(fun () -> raise exn) do
281 | results.Add value
282 | do! Async.Sleep delay
283 | Assert.Fail()
284 | with
285 | | exn2 ->
286 | Assert.AreSame(exn, exn2)
287 | Assert.AreEqual(values, results)
288 | }
289 | computation.AsTask()
290 |
291 | []
292 | let AsyncEnumerableWillDelayedThrowAsyncDisposeTest() =
293 | let computation = async {
294 | let values = [ 2; 5; 3; 7; 1 ]
295 | let results = new System.Collections.Generic.List()
296 | let exn = new Exception()
297 | try
298 | for value in values.HookAsyncEnumerable(fun () ->
299 | new ValueTask(
300 | Async.StartImmediateAsTask (async {
301 | do! Async.Sleep delay
302 | raise exn }))) do
303 | results.Add value
304 | do! Async.Sleep delay
305 | Assert.Fail()
306 | with
307 | | exn2 ->
308 | Assert.AreSame(exn, exn2)
309 | Assert.AreEqual(values, results)
310 | }
311 | computation.AsTask()
312 |
313 | []
314 | []
315 | let AsyncEnumerableIterationCaptureContextTest(capture: bool) =
316 | let context = new ThreadBoundSynchronizationContext()
317 | SynchronizationContext.SetSynchronizationContext context
318 | let computation = async {
319 | let values = [ 2; 5; 3; 7; 1 ]
320 | let results = new System.Collections.Generic.List()
321 | let tid = Thread.CurrentThread.ManagedThreadId
322 | for value in values.DelayEachAsync(delay).ConfigureAwait(capture) do
323 | let tid2 = Thread.CurrentThread.ManagedThreadId
324 | if capture then
325 | Assert.AreEqual(tid, tid2)
326 | else
327 | Assert.AreNotEqual(tid, tid2)
328 | results.Add value
329 | do! Async.Sleep delay
330 | let tid3 = Thread.CurrentThread.ManagedThreadId
331 | if capture then
332 | Assert.AreEqual(tid, tid3)
333 | else
334 | Assert.AreNotEqual(tid, tid3)
335 | Assert.AreEqual(values, results)
336 | }
337 | context.Run(computation.AsTask())
338 |
339 | ////////////////////////////////////////////////////////////////////////
340 | // IAsyncDisposable
341 |
342 | []
343 | let AsyncDisposableTest() =
344 | let mutable index = 0
345 | let computation = async {
346 | let inner = async {
347 | use _ = AsyncDisposableFactory.CreateDelegatedAsyncDisposable(fun () ->
348 | (async {
349 | do! Async.Sleep delay
350 | index <- index + 1
351 | }).AsTask())
352 | Assert.AreEqual(0, index)
353 | return ()
354 | }
355 | do! inner
356 | Assert.AreEqual(1, index)
357 | }
358 | computation.AsTask()
359 |
360 | []
361 | let AsyncDisposableThrowedTest() =
362 | let ex = new Exception()
363 | let computation = async {
364 | let inner = async {
365 | use _ = AsyncDisposableFactory.CreateDelegatedAsyncDisposable(fun () ->
366 | (async {
367 | do! Async.Sleep delay
368 | }).AsTask())
369 | raise ex
370 | return ()
371 | }
372 | try
373 | do! inner
374 | Assert.Fail()
375 | with
376 | | exn -> Assert.AreSame(ex, exn)
377 | }
378 | computation.AsTask()
379 |
380 | []
381 | let AsyncDisposableThrowedDisposingTest() =
382 | let ex = new Exception()
383 | let computation = async {
384 | let inner = async {
385 | use _ = AsyncDisposableFactory.CreateDelegatedAsyncDisposable(fun () ->
386 | (async {
387 | do! Async.Sleep delay
388 | raise ex
389 | }).AsTask())
390 | return ()
391 | }
392 | try
393 | do! inner
394 | Assert.Fail()
395 | with
396 | | exn -> Assert.AreSame(ex, exn)
397 | }
398 | computation.AsTask()
399 |
400 | //[]
401 | //let AsyncDisposableCaptureContextTest() =
402 | // In currently C#, lacks a feature for detaching synch context doing at just after asynchronous disposer.
403 | // https://github.com/dotnet/csharplang/discussions/2661
404 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks.Tests.FS/BasisTests.fs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | namespace FSharp.Control.FusionTasks.Tests
21 |
22 | open System
23 | open System.IO
24 | open System.Diagnostics
25 | open System.Threading
26 | open System.Threading.Tasks
27 |
28 | open NUnit.Framework
29 |
30 | #nowarn "44"
31 |
32 | module BasisTests =
33 |
34 | ////////////////////////////////////////////////////////////////////////
35 | // Basis
36 |
37 | []
38 | let AsyncBuilderAsAsyncFromTaskTest() =
39 | let test = async {
40 | let r = Random()
41 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
42 | do r.NextBytes data
43 | use ms = new MemoryStream()
44 | let computation = async {
45 | do! ms.WriteAsync(data, 0, data.Length)
46 | Assert.AreEqual(data, ms.ToArray())
47 | }
48 | do! computation
49 | }
50 | test.AsTask()
51 |
52 | []
53 | let AsyncBuilderAsAsyncFromTaskTTest() =
54 | let test = async {
55 | let r = Random()
56 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
57 | do r.NextBytes data
58 | let computation = async {
59 | use ms = new MemoryStream()
60 | do ms.Write(data, 0, data.Length)
61 | do ms.Position <- 0L
62 | let! length = ms.ReadAsync(data, 0, data.Length)
63 | Assert.AreEqual(data.Length, length)
64 |
65 | return ms.ToArray()
66 | }
67 | let! results = computation
68 | Assert.AreEqual(data, results)
69 | }
70 | test.AsTask()
71 |
72 | []
73 | let AsyncBuilderAsAsyncFromValueTaskTest() =
74 | let test = async {
75 | let r = Random()
76 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
77 | do r.NextBytes data
78 | use ms = new MemoryStream()
79 | let computation = async {
80 | do! new ValueTask(ms.WriteAsync(data, 0, data.Length))
81 | }
82 | do! computation
83 | Assert.AreEqual(data, ms.ToArray())
84 | }
85 | test.AsTask()
86 |
87 | []
88 | let AsyncBuilderAsAsyncFromValueTaskTTest() =
89 | let test = async {
90 | let r = Random()
91 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
92 | do r.NextBytes data
93 | let computation = async {
94 | use ms = new MemoryStream()
95 | do ms.Write(data, 0, data.Length)
96 | do ms.Position <- 0L
97 | let! length = new ValueTask(ms.ReadAsync(data, 0, data.Length))
98 | Assert.AreEqual(data.Length, length)
99 |
100 | return ms.ToArray()
101 | }
102 | let! results = computation
103 | Assert.AreEqual(data, results)
104 | }
105 | test.AsTask()
106 |
107 | []
108 | let AsyncBuilderAsAsyncVTest() =
109 | let test = async {
110 | let r = Random()
111 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
112 | do r.NextBytes data
113 | let computation = async {
114 | use ms = new MemoryStream()
115 | do! new ValueTask(ms.WriteAsync(data, 0, data.Length))
116 | do ms.Position <- 0L
117 | let length = ms.Read(data, 0, data.Length)
118 | Assert.AreEqual(data.Length, length)
119 |
120 | return ms.ToArray()
121 | }
122 | let! results = computation
123 | Assert.AreEqual(data, results)
124 | }
125 | test.AsTask()
126 |
127 | []
128 | let AsyncBuilderAsAsyncVTTest() =
129 | let test = async {
130 | let r = Random()
131 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
132 | do r.NextBytes data
133 | let computation = async {
134 | use ms = new MemoryStream()
135 | do ms.Write(data, 0, data.Length)
136 | do ms.Position <- 0L
137 | let! length = new ValueTask(ms.ReadAsync(data, 0, data.Length))
138 | Assert.AreEqual(data.Length, length)
139 | return ms.ToArray()
140 | }
141 | let! results = computation
142 | Assert.AreEqual(data, results)
143 | }
144 | test.AsTask()
145 |
146 | []
147 | let AsyncBuilderWithAsyncAndTaskCombinationTest() =
148 | let asyncGenData() = async {
149 | let r = Random()
150 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
151 | do r.NextBytes data
152 | return data
153 | }
154 | let computation = async {
155 | let! data = asyncGenData()
156 | use ms = new MemoryStream()
157 | do! ms.WriteAsync(data, 0, data.Length)
158 | Assert.AreEqual(data, ms.ToArray())
159 | }
160 | computation.AsTask()
161 |
162 | []
163 | let AsyncBuilderCompilesForInTest() =
164 | let computation = async {
165 | let mutable result = 0
166 | for i in {1..10} do
167 | result <- result + i
168 | Assert.AreEqual(55, result)
169 | }
170 |
171 | computation.AsTask()
172 |
173 | // #11
174 | //[]
175 | //[]
176 | //let AsyncBuilderCompilesIfInTest(flag, expected) =
177 | // let computation = async {
178 | // if flag
179 | // then
180 | // Assert.IsTrue flag
181 | // return ()
182 | // do! Task.Delay 100
183 | // Assert.IsFalse flag
184 | // }
185 | // computation |> Async.RunSynchronously
186 |
187 | ////////////////////////////////////////////////////////////////////////
188 | // SynchronizationContext
189 |
190 | []
191 | let HoldSynchContextTest() =
192 | let id1 = Thread.CurrentThread.ManagedThreadId
193 | let context = new ThreadBoundSynchronizationContext()
194 | SynchronizationContext.SetSynchronizationContext(context)
195 | let computation = async {
196 | let idStartImmediately = Thread.CurrentThread.ManagedThreadId
197 | Assert.AreEqual(id1, idStartImmediately)
198 |
199 | do! Task.Delay 300 |> Async.AwaitTask
200 | let idAwaitTask = Thread.CurrentThread.ManagedThreadId
201 | Assert.AreEqual(id1, idAwaitTask)
202 |
203 | do! Task.Delay 300
204 | let idAwaited1 = Thread.CurrentThread.ManagedThreadId
205 | Assert.AreEqual(id1, idAwaited1)
206 |
207 | do! Task.Delay 300
208 | let idAwaited2 = Thread.CurrentThread.ManagedThreadId
209 | Assert.AreEqual(id1, idAwaited2)
210 | }
211 | context.Run(computation.AsTask())
212 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks.Tests.FS/ConfiguredTests.fs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | namespace FSharp.Control.FusionTasks.Tests
21 |
22 | open System
23 | open System.IO
24 | open System.Diagnostics
25 | open System.Threading
26 | open System.Threading.Tasks
27 |
28 | open NUnit.Framework
29 |
30 | #nowarn "44"
31 |
32 | module ConfiguredTests =
33 |
34 | ////////////////////////////////////////////////////////////////////////
35 | // Configured async monad.
36 |
37 | []
38 | let AsyncBuilderAsAsyncCTTest() =
39 | let test = async {
40 | let r = Random()
41 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
42 | do r.NextBytes data
43 | use ms = new MemoryStream()
44 | let computation = async {
45 | do! ms.WriteAsync(data, 0, data.Length).ConfigureAwait(false)
46 | }
47 | do! computation
48 | Assert.AreEqual(data, ms.ToArray())
49 | }
50 | test.AsTask()
51 |
52 | []
53 | let AsyncBuilderAsAsyncCTTTest() =
54 | let test = async {
55 | let r = Random()
56 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
57 | do r.NextBytes data
58 | let computation = async {
59 | use ms = new MemoryStream()
60 | do ms.Write(data, 0, data.Length)
61 | do ms.Position <- 0L
62 | let! length = ms.ReadAsync(data, 0, data.Length).ConfigureAwait(false)
63 | Assert.AreEqual(data.Length, length)
64 |
65 | return ms.ToArray()
66 | }
67 | let! results = computation
68 | Assert.AreEqual(data, results)
69 | }
70 | test.AsTask()
71 |
72 | []
73 | let AsyncBuilderAsAsyncCVTTest() =
74 | let test = async {
75 | let r = Random()
76 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
77 | do r.NextBytes data
78 | use ms = new MemoryStream()
79 | let computation = async {
80 | do! (new ValueTask(ms.WriteAsync(data, 0, data.Length))).ConfigureAwait(false)
81 | }
82 | do! computation
83 | Assert.AreEqual(data, ms.ToArray())
84 | }
85 | test.AsTask()
86 |
87 | []
88 | let AsyncBuilderAsAsyncCVTTTest() =
89 | let test = async {
90 | let r = Random()
91 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
92 | do r.NextBytes data
93 | let computation = async {
94 | use ms = new MemoryStream()
95 | do ms.Write(data, 0, data.Length)
96 | do ms.Position <- 0L
97 | let! length = (new ValueTask(ms.ReadAsync(data, 0, data.Length))).ConfigureAwait(false)
98 | Assert.AreEqual(data.Length, length)
99 |
100 | return ms.ToArray()
101 | }
102 | let! results = computation
103 | Assert.AreEqual(data, results)
104 | }
105 | test.AsTask()
106 |
107 | []
108 | []
109 | let AsyncBuilderAsAsyncCTATest() =
110 | let test = async {
111 | let r = Random()
112 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
113 | do r.NextBytes data
114 | use ms = new MemoryStream()
115 | let computation = async {
116 | do! ms.WriteAsync(data, 0, data.Length).AsyncConfigure(false)
117 | }
118 | do! computation
119 | Assert.AreEqual(data, ms.ToArray())
120 | }
121 | test.AsTask()
122 |
123 | []
124 | []
125 | let AsyncBuilderAsAsyncCTATTest() =
126 | let test = async {
127 | let r = Random()
128 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
129 | do r.NextBytes data
130 | let computation = async {
131 | use ms = new MemoryStream()
132 | do ms.Write(data, 0, data.Length)
133 | do ms.Position <- 0L
134 | let! length = ms.ReadAsync(data, 0, data.Length).AsyncConfigure(false)
135 | Assert.AreEqual(data.Length, length)
136 |
137 | return ms.ToArray()
138 | }
139 | let! results = computation
140 | Assert.AreEqual(data, results)
141 | }
142 | test.AsTask()
143 |
144 | []
145 | []
146 | let AsyncBuilderAsAsyncCVTATest() =
147 | let test = async {
148 | let r = Random()
149 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
150 | do r.NextBytes data
151 | use ms = new MemoryStream()
152 | let computation = async {
153 | do! (new ValueTask(ms.WriteAsync(data, 0, data.Length))).AsyncConfigure(false)
154 | }
155 | do! computation
156 | Assert.AreEqual(data, ms.ToArray())
157 | }
158 | test.AsTask()
159 |
160 | []
161 | []
162 | let AsyncBuilderAsAsyncCVTATTest() =
163 | let test = async {
164 | let r = Random()
165 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
166 | do r.NextBytes data
167 | let computation = async {
168 | use ms = new MemoryStream()
169 | do ms.Write(data, 0, data.Length)
170 | do ms.Position <- 0L
171 | let! length = (new ValueTask(ms.ReadAsync(data, 0, data.Length))).AsyncConfigure(false)
172 | Assert.AreEqual(data.Length, length)
173 |
174 | return ms.ToArray()
175 | }
176 | let! results = computation
177 | Assert.AreEqual(data, results)
178 | }
179 | test.AsTask()
180 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks.Tests.FS/FSharp.Control.FusionTasks.Tests.FS.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48;netcoreapp2.2;netcoreapp3.1;net5.0-windows;net6.0
5 | 988;NU1803
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks.Tests.FS/OperatorTests.fs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | namespace FSharp.Control.FusionTasks.Tests
21 |
22 | open System
23 | open System.IO
24 | open System.Diagnostics
25 | open System.Threading
26 | open System.Threading.Tasks
27 |
28 | open NUnit.Framework
29 |
30 | #nowarn "44"
31 |
32 | module OperatorTests =
33 |
34 | ////////////////////////////////////////////////////////////////////////
35 | // Explicitly operators
36 |
37 | []
38 | let OperatorAsAsyncFromTaskTest() =
39 | let test = async {
40 | let r = Random()
41 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
42 | do r.NextBytes data
43 | use ms = new MemoryStream()
44 | let computation = async {
45 | do! ms.WriteAsync(data, 0, data.Length) |> Async.AsAsync
46 | Assert.AreEqual(data, ms.ToArray())
47 | }
48 | do! computation
49 | }
50 | test.AsTask()
51 |
52 | []
53 | let OperatorAsAsyncFromTaskTTest() =
54 | let test = async {
55 | let r = Random()
56 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
57 | do r.NextBytes data
58 | let computation = async {
59 | use ms = new MemoryStream()
60 | do ms.Write(data, 0, data.Length)
61 | do ms.Position <- 0L
62 | let! length = ms.ReadAsync(data, 0, data.Length) |> Async.AsAsync
63 | Assert.AreEqual(data.Length, length)
64 |
65 | return ms.ToArray()
66 | }
67 | let! results = computation
68 | Assert.AreEqual(data, results)
69 | }
70 | test.AsTask()
71 |
72 | []
73 | let OperatorAsAsyncFromValueTaskTest() =
74 | let test = async {
75 | let r = Random()
76 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
77 | do r.NextBytes data
78 | use ms = new MemoryStream()
79 | let computation = async {
80 | do! new ValueTask(ms.WriteAsync(data, 0, data.Length)) |> Async.AsAsync
81 | Assert.AreEqual(data, ms.ToArray())
82 | }
83 | do! computation
84 | }
85 | test.AsTask()
86 |
87 | []
88 | let OperatorAsAsyncFromValueTaskTTest() =
89 | let test = async {
90 | let r = Random()
91 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
92 | do r.NextBytes data
93 | let computation = async {
94 | use ms = new MemoryStream()
95 | do ms.Write(data, 0, data.Length)
96 | do ms.Position <- 0L
97 | let! length = new ValueTask(ms.ReadAsync(data, 0, data.Length)) |> Async.AsAsync
98 | Assert.AreEqual(data.Length, length)
99 |
100 | return ms.ToArray()
101 | }
102 | let! results = computation
103 | Assert.AreEqual(data, results)
104 | }
105 | test.AsTask()
106 |
107 | []
108 | let OperatorAsAsyncFromTaskConfiguredTest() =
109 | let test = async {
110 | let r = Random()
111 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
112 | do r.NextBytes data
113 | use ms = new MemoryStream()
114 | let computation = async {
115 | do! ms.WriteAsync(data, 0, data.Length).ConfigureAwait(false) |> Async.AsAsync
116 | Assert.AreEqual(data, ms.ToArray())
117 | }
118 | do! computation
119 | }
120 | test.AsTask()
121 |
122 |
123 | []
124 | let OperatorAsAsyncFromTaskTConfiguredTest() =
125 | let test = async {
126 | let r = Random()
127 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
128 | do r.NextBytes data
129 | let computation = async {
130 | use ms = new MemoryStream()
131 | do ms.Write(data, 0, data.Length)
132 | do ms.Position <- 0L
133 | let! length = ms.ReadAsync(data, 0, data.Length).ConfigureAwait(false) |> Async.AsAsync
134 | Assert.AreEqual(data.Length, length)
135 |
136 | return ms.ToArray()
137 | }
138 | let! results = computation
139 | Assert.AreEqual(data, results)
140 | }
141 | test.AsTask()
142 |
143 | []
144 | let OperatorAsAsyncFromValueTaskConfiguredTest() =
145 | let test = async {
146 | let r = Random()
147 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
148 | do r.NextBytes data
149 | use ms = new MemoryStream()
150 | let computation = async {
151 | do! (new ValueTask(ms.WriteAsync(data, 0, data.Length))).ConfigureAwait(false) |> Async.AsAsync
152 | Assert.AreEqual(data, ms.ToArray())
153 | }
154 | do! computation
155 | }
156 | test.AsTask()
157 |
158 |
159 | []
160 | let OperatorAsAsyncFromValueTaskTConfiguredTest() =
161 | let test = async {
162 | let r = Random()
163 | let data = Seq.init 100000 (fun i -> 0uy) |> Seq.toArray
164 | do r.NextBytes data
165 | let computation = async {
166 | use ms = new MemoryStream()
167 | do ms.Write(data, 0, data.Length)
168 | do ms.Position <- 0L
169 | let! length = (new ValueTask(ms.ReadAsync(data, 0, data.Length))).ConfigureAwait(false) |> Async.AsAsync
170 | Assert.AreEqual(data.Length, length)
171 |
172 | return ms.ToArray()
173 | }
174 | let! results = computation
175 | Assert.AreEqual(data, results)
176 | }
177 | test.AsTask()
178 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks.Tests.Utilities/AsyncDisposableFactory.cs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | using System;
21 | using System.Threading.Tasks;
22 |
23 | namespace FSharp.Control.FusionTasks.Tests
24 | {
25 | public static class AsyncDisposableFactory
26 | {
27 | public static IAsyncDisposable CreateDelegatedAsyncDisposable(Func disposeAsync) =>
28 | new DelegatedAsyncDisposable(disposeAsync);
29 |
30 | private sealed class DelegatedAsyncDisposable : IAsyncDisposable
31 | {
32 | private readonly Func disposeAsync;
33 |
34 | public DelegatedAsyncDisposable(Func disposeAsync) =>
35 | this.disposeAsync = disposeAsync;
36 |
37 | public ValueTask DisposeAsync() =>
38 | new ValueTask(this.disposeAsync());
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks.Tests.Utilities/AsyncEnumerableFactory.cs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | using System;
21 | using System.Collections.Generic;
22 | using System.Threading;
23 | using System.Threading.Tasks;
24 |
25 | #pragma warning disable CS1998
26 | #pragma warning disable CS0162
27 |
28 | namespace FSharp.Control.FusionTasks.Tests
29 | {
30 | public static class AsyncEnumerableFactory
31 | {
32 | public static async IAsyncEnumerable DelayEachAsync(
33 | this IEnumerable enumerable,
34 | TimeSpan delay)
35 | {
36 | foreach (var value in enumerable)
37 | {
38 | await Task.Delay(delay).ConfigureAwait(false);
39 | yield return value;
40 | }
41 | }
42 |
43 | public static async IAsyncEnumerable AsAsyncEnumerable(
44 | this IEnumerable enumerable)
45 | {
46 | foreach (var value in enumerable)
47 | {
48 | yield return value;
49 | }
50 | }
51 |
52 | public static async IAsyncEnumerable WillThrowBefore(
53 | this IEnumerable enumerable)
54 | {
55 | throw new Exception("WillThrowBefore");
56 | foreach (var value in enumerable)
57 | {
58 | yield return value;
59 | }
60 | }
61 |
62 | public static async IAsyncEnumerable WillThrowAfter(
63 | this IEnumerable enumerable)
64 | {
65 | foreach (var value in enumerable)
66 | {
67 | yield return value;
68 | }
69 | throw new Exception("WillThrowAfter");
70 | }
71 |
72 | public static async IAsyncEnumerable WillThrowInterBefore(
73 | this IEnumerable enumerable)
74 | {
75 | foreach (var value in enumerable)
76 | {
77 | throw new Exception("WillThrowInterBefore");
78 | yield return value;
79 | }
80 | }
81 |
82 | public static async IAsyncEnumerable WillThrowInterAfter(
83 | this IEnumerable enumerable)
84 | {
85 | foreach (var value in enumerable)
86 | {
87 | yield return value;
88 | throw new Exception("WillThrowInterAfter");
89 | }
90 | }
91 |
92 | public static IAsyncEnumerable HookAsyncEnumerable(
93 | this IEnumerable enumerable, Func dispose) =>
94 | new AsyncEnumerable(
95 | enumerable,
96 | enumerator => enumerator.MoveNext(),
97 | enumerator => enumerator.Current,
98 | dispose);
99 |
100 | private sealed class AsyncEnumerable : IAsyncEnumerable
101 | {
102 | private readonly IEnumerable enumerable;
103 | private readonly Func, bool> moveNext;
104 | private readonly Func, T> current;
105 | private readonly Func dispose;
106 |
107 | public AsyncEnumerable(
108 | IEnumerable enumerable,
109 | Func, bool> moveNext,
110 | Func, T> current,
111 | Func dispose)
112 | {
113 | this.enumerable = enumerable;
114 | this.moveNext = moveNext;
115 | this.current = current;
116 | this.dispose = dispose;
117 | }
118 |
119 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) =>
120 | new AsyncEnumerator(
121 | this.enumerable.GetEnumerator(),
122 | this.moveNext,
123 | this.current,
124 | this.dispose);
125 | }
126 |
127 | private sealed class AsyncEnumerator : IAsyncEnumerator
128 | {
129 | private readonly IEnumerator enumerator;
130 | private readonly Func, bool> moveNext;
131 | private readonly Func, T> current;
132 | private readonly Func dispose;
133 |
134 | public AsyncEnumerator(
135 | IEnumerator enumerator,
136 | Func, bool> moveNext,
137 | Func, T> current,
138 | Func dispose)
139 | {
140 | this.enumerator = enumerator;
141 | this.moveNext = moveNext;
142 | this.current = current;
143 | this.dispose = dispose;
144 | }
145 |
146 | public T Current =>
147 | this.current(this.enumerator);
148 |
149 | public ValueTask MoveNextAsync() =>
150 | new ValueTask(this.moveNext(this.enumerator));
151 |
152 | public ValueTask DisposeAsync() =>
153 | this.dispose();
154 | }
155 | }
156 | }
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks.Tests.Utilities/FSharp.Control.FusionTasks.Tests.Utilities.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48;netstandard2.0;netstandard2.1;net5.0;net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks.Tests.Utilities/ThreadBoundSynchronizationContext.cs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | using System;
21 | using System.Collections.Generic;
22 | using System.Threading;
23 | using System.Threading.Tasks;
24 |
25 | namespace FSharp.Control.FusionTasks.Tests
26 | {
27 | // https://github.com/kekyo/SynchContextSample
28 | public sealed class ThreadBoundSynchronizationContext :
29 | SynchronizationContext
30 | {
31 | private readonly struct Entry
32 | {
33 | public readonly SendOrPostCallback Callback;
34 | public readonly object? State;
35 |
36 | public Entry(SendOrPostCallback callback, object? state)
37 | {
38 | this.Callback = callback;
39 | this.State = state;
40 | }
41 | }
42 |
43 | private readonly Queue entries = new Queue();
44 | private int? boundThreadId;
45 | private readonly ManualResetEventSlim queued = new ManualResetEventSlim(false);
46 | private readonly ManualResetEventSlim quit = new ManualResetEventSlim(false);
47 |
48 | public void Run(Task? runTask)
49 | {
50 | this.boundThreadId = Thread.CurrentThread.ManagedThreadId;
51 |
52 | runTask?.ContinueWith(task =>
53 | {
54 | if (task.IsFaulted || task.IsCanceled)
55 | {
56 | this.Post(_ => task.Wait(), null);
57 | }
58 | else
59 | {
60 | this.Quit();
61 | }
62 | });
63 |
64 | while (true)
65 | {
66 | if (WaitHandle.WaitAny(new [] { this.queued.WaitHandle, this.quit.WaitHandle }) == 1)
67 | {
68 | break;
69 | }
70 |
71 | loop:
72 | Entry entry;
73 | lock (this.entries)
74 | {
75 | if (this.entries.Count == 0)
76 | {
77 | this.queued.Reset();
78 | continue;
79 | }
80 | entry = this.entries.Dequeue();
81 | }
82 |
83 | entry.Callback(entry.State);
84 | goto loop;
85 | }
86 | }
87 |
88 | public void Quit() =>
89 | this.quit.Set();
90 |
91 | public override void Send(SendOrPostCallback d, object? state)
92 | {
93 | lock (this.entries)
94 | {
95 | if (this.boundThreadId != Thread.CurrentThread.ManagedThreadId)
96 | {
97 | this.entries.Enqueue(new Entry(d, state));
98 | if (this.entries.Count >= 1)
99 | {
100 | this.queued.Set();
101 | }
102 | return;
103 | }
104 | }
105 |
106 | d(state);
107 | }
108 |
109 | public override void Post(SendOrPostCallback d, object? state)
110 | {
111 | lock (this.entries)
112 | {
113 | this.entries.Enqueue(new Entry(d, state));
114 | if (this.entries.Count >= 1)
115 | {
116 | this.queued.Set();
117 | }
118 | }
119 | }
120 | }
121 | }
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.3.32901.215
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BEB599F7-8021-4E01-9200-B9ABF1D9D623}"
7 | ProjectSection(SolutionItems) = preProject
8 | .gitignore = .gitignore
9 | build-nupkg.bat = build-nupkg.bat
10 | build-nupkg.sh = build-nupkg.sh
11 | .github\workflows\build.yml = .github\workflows\build.yml
12 | Directory.Build.props = Directory.Build.props
13 | FSharp.Control.FusionTasks.sln.licenseheader = FSharp.Control.FusionTasks.sln.licenseheader
14 | LICENSE.txt = LICENSE.txt
15 | NuGet.Config = NuGet.Config
16 | README.md = README.md
17 | EndProjectSection
18 | EndProject
19 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Images", "Images", "{5EA37107-7C53-4291-8EAD-543B392CE2B1}"
20 | ProjectSection(SolutionItems) = preProject
21 | Images\FSharp.Control.FusionTasks.100.png = Images\FSharp.Control.FusionTasks.100.png
22 | Images\FSharp.Control.FusionTasks.128.png = Images\FSharp.Control.FusionTasks.128.png
23 | Images\FSharp.Control.FusionTasks.design = Images\FSharp.Control.FusionTasks.design
24 | EndProjectSection
25 | EndProject
26 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Control.FusionTasks", "FSharp.Control.FusionTasks\FSharp.Control.FusionTasks.fsproj", "{43D74C62-B635-447E-B99B-7D3A588B093F}"
27 | EndProject
28 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Control.FusionTasks.Tests.FS", "FSharp.Control.FusionTasks.Tests.FS\FSharp.Control.FusionTasks.Tests.FS.fsproj", "{053C93E0-E0F1-41FB-9F29-9D9877C685DF}"
29 | EndProject
30 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.Control.FusionTasks.Tests.CS", "FSharp.Control.FusionTasks.Tests.CS\FSharp.Control.FusionTasks.Tests.CS.csproj", "{46EC46A3-A601-4B04-9CB0-0113D01E8F8A}"
31 | EndProject
32 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.Control.FusionTasks.Tests.Utilities", "FSharp.Control.FusionTasks.Tests.Utilities\FSharp.Control.FusionTasks.Tests.Utilities.csproj", "{3C19BE31-9D89-4AA9-BA88-30AC4DFEBAC0}"
33 | EndProject
34 | Global
35 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
36 | Debug|Any CPU = Debug|Any CPU
37 | Release|Any CPU = Release|Any CPU
38 | EndGlobalSection
39 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
40 | {43D74C62-B635-447E-B99B-7D3A588B093F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {43D74C62-B635-447E-B99B-7D3A588B093F}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {43D74C62-B635-447E-B99B-7D3A588B093F}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {43D74C62-B635-447E-B99B-7D3A588B093F}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {053C93E0-E0F1-41FB-9F29-9D9877C685DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {053C93E0-E0F1-41FB-9F29-9D9877C685DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {053C93E0-E0F1-41FB-9F29-9D9877C685DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {053C93E0-E0F1-41FB-9F29-9D9877C685DF}.Release|Any CPU.Build.0 = Release|Any CPU
48 | {46EC46A3-A601-4B04-9CB0-0113D01E8F8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {46EC46A3-A601-4B04-9CB0-0113D01E8F8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {46EC46A3-A601-4B04-9CB0-0113D01E8F8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
51 | {46EC46A3-A601-4B04-9CB0-0113D01E8F8A}.Release|Any CPU.Build.0 = Release|Any CPU
52 | {3C19BE31-9D89-4AA9-BA88-30AC4DFEBAC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53 | {3C19BE31-9D89-4AA9-BA88-30AC4DFEBAC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
54 | {3C19BE31-9D89-4AA9-BA88-30AC4DFEBAC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
55 | {3C19BE31-9D89-4AA9-BA88-30AC4DFEBAC0}.Release|Any CPU.Build.0 = Release|Any CPU
56 | EndGlobalSection
57 | GlobalSection(SolutionProperties) = preSolution
58 | HideSolutionNode = FALSE
59 | EndGlobalSection
60 | GlobalSection(NestedProjects) = preSolution
61 | {5EA37107-7C53-4291-8EAD-543B392CE2B1} = {BEB599F7-8021-4E01-9200-B9ABF1D9D623}
62 | EndGlobalSection
63 | GlobalSection(ExtensibilityGlobals) = postSolution
64 | SolutionGuid = {0FB2B9EE-D327-40E1-BF78-8D8F51B37A1A}
65 | EndGlobalSection
66 | EndGlobal
67 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks.sln.licenseheader:
--------------------------------------------------------------------------------
1 | extensions: designer.cs generated.cs
2 | extensions: .cs .fs .cpp .h
3 | /////////////////////////////////////////////////////////////////////////////////////////////////
4 | //
5 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
6 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
7 | //
8 | // Licensed under the Apache License, Version 2.0 (the "License");
9 | // you may not use this file except in compliance with the License.
10 | // You may obtain a copy of the License at
11 | //
12 | // http://www.apache.org/licenses/LICENSE-2.0
13 | //
14 | // Unless required by applicable law or agreed to in writing, software
15 | // distributed under the License is distributed on an "AS IS" BASIS,
16 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | // See the License for the specific language governing permissions and
18 | // limitations under the License.
19 | //
20 | /////////////////////////////////////////////////////////////////////////////////////////////////
21 |
22 | extensions: .xml .xaml .axaml .config .xsd
23 | .NET Task easy seamless interoperability library.
27 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
28 | //
29 | // Licensed under the Apache License, Version 2.0 (the "License");
30 | // you may not use this file except in compliance with the License.
31 | // You may obtain a copy of the License at
32 | //
33 | // http://www.apache.org/licenses/LICENSE-2.0
34 | //
35 | // Unless required by applicable law or agreed to in writing, software
36 | // distributed under the License is distributed on an "AS IS" BASIS,
37 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
38 | // See the License for the specific language governing permissions and
39 | // limitations under the License.
40 | //
41 | /////////////////////////////////////////////////////////////////////////////////////////////////
42 | -->
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks/AsyncCompletionSource.fs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | namespace Microsoft.FSharp.Control
21 |
22 | open System
23 |
24 | ///
25 | /// Delegation F#'s async continuation.
26 | ///
27 | ///
28 | /// Simulate TaskCompletionSource<'T> for F#'s Async<'T>.
29 | ///
30 | /// Computation result type
31 | []
32 | type AsyncCompletionSource<'T> =
33 |
34 | []
35 | val mutable private _completed : 'T -> unit
36 | []
37 | val mutable private _caught : exn -> unit
38 | []
39 | val mutable private _canceled : OperationCanceledException -> unit
40 |
41 | val private _async : Async<'T>
42 |
43 | ///
44 | /// Constructor.
45 | ///
46 | new () as this = {
47 | _async = Async.FromContinuations<'T>(fun (completed, caught, canceled) ->
48 | this._completed <- completed
49 | this._caught <- caught
50 | this._canceled <- canceled)
51 | }
52 |
53 | ///
54 | /// Target Async<'T> instance.
55 | ///
56 | member this.Async = this._async
57 |
58 | ///
59 | /// Set result value and continue continuation.
60 | ///
61 | /// Result value
62 | member this.SetResult value = this._completed value
63 |
64 | ///
65 | /// Set exception and continue continuation.
66 | ///
67 | /// Exception instance
68 | member this.SetException exn = this._caught exn
69 |
70 | ///
71 | /// Cancel async computation.
72 | ///
73 | member this.SetCanceled() =
74 | this._canceled(Utilities.createCanceledException(None))
75 |
76 | ///
77 | /// Cancel async computation.
78 | ///
79 | /// CancellationToken
80 | member this.SetCanceled token =
81 | this._canceled(Utilities.createCanceledException(Some token))
82 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks/AsyncExtensions.fs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | namespace Microsoft.FSharp.Control
21 |
22 | open System
23 | open System.Collections.Generic
24 | open System.Runtime.CompilerServices
25 | open System.Threading
26 | open System.Threading.Tasks
27 |
28 | #nowarn "44"
29 |
30 | []
31 | []
32 | module AsyncExtensions =
33 |
34 | ///////////////////////////////////////////////////////////////////////////////////
35 | // F# side Async class extensions.
36 |
37 | type Async with
38 |
39 | ///
40 | /// Seamless conversion from F# Async to .NET Task.
41 | ///
42 | /// F# Async
43 | /// Cancellation token (optional)
44 | /// .NET Task instance
45 | static member AsTask(async: Async, ?token: CancellationToken) =
46 | Infrastructures.asTaskT(async, token) :> Task
47 |
48 | ///
49 | /// Seamless conversion from F# Async to .NET Task.
50 | ///
51 | /// Computation result type
52 | /// F# Async
53 | /// Cancellation token (optional)
54 | /// .NET Task instance
55 | static member AsTask(async: Async<'T>, ?token: CancellationToken) =
56 | Infrastructures.asTaskT(async, token)
57 |
58 | ////////////////////////////////////////////////////////////////////
59 | // Operators
60 |
61 | ///
62 | /// Seamless conversion operator from .NET Task to F# Async.
63 | ///
64 | /// .NET Task instance
65 | /// F# Async
66 | static member AsAsync (task: Task) =
67 | Infrastructures.asAsync(task, None)
68 |
69 | ///
70 | /// Seamless conversion operator from .NET Task to F# Async.
71 | ///
72 | /// Computation result type
73 | /// .NET Task instance
74 | /// F# Async
75 | static member AsAsync (task: Task<'T>) =
76 | Infrastructures.asAsyncT(task, None)
77 |
78 | ///
79 | /// Seamless conversion operator from .NET ValueTask to F# Async.
80 | ///
81 | /// .NET ValueTask instance
82 | /// F# Async
83 | static member AsAsync (task: ValueTask) =
84 | Infrastructures.asAsyncV(task, None)
85 |
86 | ///
87 | /// Seamless conversion operator from .NET ValueTask to F# Async.
88 | ///
89 | /// Computation result type
90 | /// .NET ValueTask instance
91 | /// F# Async
92 | static member AsAsync (task: ValueTask<'T>) =
93 | Infrastructures.asAsyncVT(task, None)
94 |
95 | ///
96 | /// Seamless conversion operator from .NET Task to F# Async.
97 | ///
98 | /// .NET Configured Task instance
99 | /// F# Async
100 | static member AsAsync (ct: ConfiguredTaskAwaitable) =
101 | Infrastructures.asAsyncCT(ct)
102 |
103 | ///
104 | /// Seamless conversion operator from .NET Task to F# Async.
105 | ///
106 | /// Computation result type
107 | /// .NET Configured Task instance
108 | /// F# Async
109 | static member AsAsync (ctt: ConfiguredTaskAwaitable<'T>) =
110 | Infrastructures.asAsyncCTT(ctt)
111 |
112 | ///
113 | /// Seamless conversion operator from .NET ValueTask to F# Async.
114 | ///
115 | /// .NET Configured ValueTask instance
116 | /// F# Async
117 | static member AsAsync (cvt: ConfiguredValueTaskAwaitable) =
118 | Infrastructures.asAsyncCVT(cvt)
119 |
120 | ///
121 | /// Seamless conversion operator from .NET ValueTask to F# Async.
122 | ///
123 | /// Computation result type
124 | /// .NET Configured ValueTask instance
125 | /// F# Async
126 | static member AsAsync (cvtt: ConfiguredValueTaskAwaitable<'T>) =
127 | Infrastructures.asAsyncCVTT(cvtt)
128 |
129 | ///////////////////////////////////////////////////////////////////////////////////
130 | // F# side Task class extensions.
131 |
132 | type Task with
133 |
134 | ///
135 | /// Seamless conversion from .NET Task to F# Async.
136 | ///
137 | /// Cancellation token (optional)
138 | /// F# Async instance
139 | member task.AsAsync(?token: CancellationToken) =
140 | Infrastructures.asAsync(task, token)
141 |
142 | ///
143 | /// Seamless conversionable substitution Task.ConfigureAwait()
144 | ///
145 | /// True if continuation running on captured SynchronizationContext
146 | /// ConfiguredAsyncAwaitable instance
147 | []
148 | member task.AsyncConfigure(continueOnCapturedContext: bool) =
149 | ConfiguredTaskAsyncAwaitable(task.ConfigureAwait(continueOnCapturedContext))
150 |
151 | type Task<'T> with
152 |
153 | ///
154 | /// Seamless conversion from .NET Task to F# Async.
155 | ///
156 | /// Computation result type
157 | /// Cancellation token (optional)
158 | /// F# Async instance
159 | member task.AsAsync(?token: CancellationToken) =
160 | Infrastructures.asAsyncT(task, token)
161 |
162 | ///
163 | /// Seamless conversionable substitution Task.ConfigureAwait()
164 | ///
165 | /// Computation result type
166 | /// True if continuation running on captured SynchronizationContext
167 | /// ConfiguredAsyncAwaitable instance
168 | []
169 | member task.AsyncConfigure(continueOnCapturedContext: bool) =
170 | ConfiguredTaskAsyncAwaitable<'T>(task.ConfigureAwait(continueOnCapturedContext))
171 |
172 | type ValueTask with
173 |
174 | ///
175 | /// Seamless conversion from .NET ValueTask to F# Async.
176 | ///
177 | /// Cancellation token (optional)
178 | /// F# Async instance
179 | member task.AsAsync(?token: CancellationToken) =
180 | Infrastructures.asAsyncV(task, token)
181 |
182 | ///
183 | /// Seamless conversionable substitution ValueTask.ConfigureAwait()
184 | ///
185 | /// True if continuation running on captured SynchronizationContext
186 | /// ConfiguredAsyncAwaitable instance
187 | []
188 | member task.AsyncConfigure(continueOnCapturedContext: bool) =
189 | ConfiguredValueTaskAsyncAwaitable(task.ConfigureAwait(continueOnCapturedContext))
190 |
191 | type ValueTask<'T> with
192 |
193 | ///
194 | /// Seamless conversion from .NET ValueTask to F# Async.
195 | ///
196 | /// Computation result type
197 | /// Cancellation token (optional)
198 | /// F# Async instance
199 | member task.AsAsync(?token: CancellationToken) =
200 | Infrastructures.asAsyncVT(task, token)
201 |
202 | ///
203 | /// Seamless conversionable substitution ValueTask.ConfigureAwait()
204 | ///
205 | /// Computation result type
206 | /// True if continuation running on captured SynchronizationContext
207 | /// ConfiguredAsyncAwaitable instance
208 | []
209 | member task.AsyncConfigure(continueOnCapturedContext: bool) =
210 | ConfiguredValueTaskAsyncAwaitable<'T>(task.ConfigureAwait(continueOnCapturedContext))
211 |
212 | ///////////////////////////////////////////////////////////////////////////////////
213 | // F# side ConfiguredAsyncAwaitable class extensions.
214 |
215 | type ConfiguredTaskAsyncAwaitable with
216 |
217 | ///
218 | /// Seamless conversion from .NET Task to F# Async.
219 | ///
220 | /// F# Async instance.
221 | []
222 | member cta.AsAsync() =
223 | Infrastructures.asAsyncCTA(cta)
224 |
225 | type ConfiguredTaskAsyncAwaitable<'T> with
226 |
227 | ///
228 | /// Seamless conversion from .NET Task to F# Async.
229 | ///
230 | /// Computation result type
231 | /// F# Async instance.
232 | []
233 | member cta.AsAsync() =
234 | Infrastructures.asAsyncCTAT(cta)
235 |
236 | type ConfiguredValueTaskAsyncAwaitable with
237 |
238 | ///
239 | /// Seamless conversion from .NET Task to F# Async.
240 | ///
241 | /// F# Async instance.
242 | []
243 | member cta.AsAsync() =
244 | Infrastructures.asAsyncCVTA(cta)
245 |
246 | type ConfiguredValueTaskAsyncAwaitable<'T> with
247 |
248 | ///
249 | /// Seamless conversion from .NET Task to F# Async.
250 | ///
251 | /// Computation result type
252 | /// F# Async instance.
253 | []
254 | member cta.AsAsync() =
255 | Infrastructures.asAsyncCVTAT(cta)
256 |
257 | ///////////////////////////////////////////////////////////////////////////////////
258 | // F# side async computation builder extensions.
259 |
260 | type AsyncBuilder with
261 |
262 | ///
263 | /// Bypass default Async binder.
264 | ///
265 | /// F# Async instance.
266 | /// F# Async instance.
267 | member __.Source(computation: Async<'T>) =
268 | computation
269 |
270 | ///
271 | /// Seamless conversion from .NET Task to F# Async in Async workflow.
272 | ///
273 | /// .NET Task (expression result)
274 | /// F# Async instance.
275 | member __.Source(task: Task) =
276 | Infrastructures.asAsync(task, None)
277 |
278 | ///
279 | /// Seamless conversion from .NET Task to F# Async in Async workflow.
280 | ///
281 | /// Computation result type
282 | /// .NET Task<'T> (expression result)
283 | /// F# Async instance.
284 | member __.Source(task: Task<'T>) =
285 | Infrastructures.asAsyncT(task, None)
286 |
287 | ///
288 | /// Seamless conversion from .NET Task to F# Async in Async workflow.
289 | ///
290 | /// .NET ConfiguredTaskAwaitable (expr.ConfigureAwait(...))
291 | /// F# Async instance.
292 | member __.Source(ct: ConfiguredTaskAwaitable) =
293 | Infrastructures.asAsyncCT(ct)
294 |
295 | ///
296 | /// Seamless conversion from .NET Task to F# Async in Async workflow.
297 | ///
298 | /// Computation result type
299 | /// .NET ConfiguredTaskAwaitable<'T> (expr.ConfigureAwait(...))
300 | /// F# Async instance.
301 | member __.Source(ctt: ConfiguredTaskAwaitable<'T>) =
302 | Infrastructures.asAsyncCTT(ctt)
303 |
304 | ///
305 | /// Seamless conversion from .NET Task to F# Async in Async workflow.
306 | ///
307 | /// .NET ValueTask (expression result)
308 | /// F# Async instance.
309 | member __.Source(task: ValueTask) =
310 | Infrastructures.asAsyncV(task, None)
311 |
312 | ///
313 | /// Seamless conversion from .NET Task to F# Async in Async workflow.
314 | ///
315 | /// Computation result type
316 | /// .NET ValueTask<'T> (expression result)
317 | /// F# Async instance.
318 | member __.Source(task: ValueTask<'T>) =
319 | Infrastructures.asAsyncVT(task, None)
320 |
321 | ///
322 | /// Seamless conversion from .NET Task to F# Async in Async workflow.
323 | ///
324 | /// .NET ConfiguredValueTaskAwaitable (expr.ConfigureAwait(...))
325 | /// F# Async instance.
326 | member __.Source(cvt: ConfiguredValueTaskAwaitable) =
327 | Infrastructures.asAsyncCVT(cvt)
328 |
329 | ///
330 | /// Seamless conversion from .NET Task to F# Async in Async workflow.
331 | ///
332 | /// Computation result type
333 | /// .NET ConfiguredValueTaskAwaitable<'T> (expr.ConfigureAwait(...))
334 | /// F# Async instance.
335 | member __.Source(cvtt: ConfiguredValueTaskAwaitable<'T>) =
336 | Infrastructures.asAsyncCVTT(cvtt)
337 |
338 | ///
339 | /// Accept any sequence type to support `for .. in` expressions in Async workflows.
340 | ///
341 | /// The element type of the sequence
342 | /// The sequence.
343 | /// The sequence.
344 | member __.Source(s: 'E seq) =
345 | s
346 |
347 | ///
348 | /// Seamless conversion from .NET Task to F# Async in Async workflow.
349 | ///
350 | /// .NET ConfiguredTaskAsyncAwaitable (expr.AsyncConfigure(...))
351 | /// F# Async instance.
352 | []
353 | member __.Source(cta: ConfiguredTaskAsyncAwaitable) =
354 | Infrastructures.asAsyncCTA(cta)
355 |
356 | ///
357 | /// Seamless conversion from .NET Task to F# Async in Async workflow.
358 | ///
359 | /// Computation result type
360 | /// .NET ConfiguredTaskAsyncAwaitable<'T> (expr.AsyncConfigure(...))
361 | /// F# Async instance.
362 | []
363 | member __.Source(cta: ConfiguredTaskAsyncAwaitable<'T>) =
364 | Infrastructures.asAsyncCTAT(cta)
365 |
366 | ///
367 | /// Seamless conversion from .NET Task to F# Async in Async workflow.
368 | ///
369 | /// .NET ConfiguredValueTaskAsyncAwaitable (expr.AsyncConfigure(...))
370 | /// F# Async instance.
371 | []
372 | member __.Source(cta: ConfiguredValueTaskAsyncAwaitable) =
373 | Infrastructures.asAsyncCVTA(cta)
374 |
375 | ///
376 | /// Seamless conversion from .NET Task to F# Async in Async workflow.
377 | ///
378 | /// Computation result type
379 | /// .NET ConfiguredValueTaskAsyncAwaitable<'T> (expr.AsyncConfigure(...))
380 | /// F# Async instance.
381 | []
382 | member __.Source(cta: ConfiguredValueTaskAsyncAwaitable<'T>) =
383 | Infrastructures.asAsyncCVTAT(cta)
384 |
385 | #if !NET45 && !NETSTANDARD1_6 && !NETCOREAPP2_0
386 |
387 | ///
388 | /// Seamless conversion from .NET Async disposer (IAsyncDisposable) to F# Async in Async workflow.
389 | ///
390 | /// .NET IAsyncDisposable
391 | /// use expression body
392 | /// F# Async instance.
393 | member __.Using(disposable: 'T :> IAsyncDisposable, body: 'T -> Async<'R>) : Async<'R> =
394 | Infrastructures.asAsyncAD(disposable, body)
395 |
396 | ///
397 | /// Seamless conversion from .NET Async sequence (IAsyncEnumerable<'E>) to F# Async in Async workflow.
398 | ///
399 | /// The element type of the sequence
400 | /// .NET IAsyncEnumerable<'E> (expression result)
401 | /// for expression body
402 | /// F# Async instance.
403 | member __.For(enumerable: IAsyncEnumerable<'E>, body: 'E -> Async<'R>) =
404 | Infrastructures.asAsyncAE(enumerable, body, None)
405 |
406 | ///
407 | /// Seamless conversion from .NET Async sequence (IAsyncEnumerable<'E>) to F# Async in Async workflow.
408 | ///
409 | /// The element type of the sequence
410 | /// .NET IAsyncEnumerable<'E> (expression result)
411 | /// for expression body
412 | /// F# Async instance.
413 | member __.For(enumerable: ConfiguredCancelableAsyncEnumerable<'E>, body: 'E -> Async<'R>) =
414 | Infrastructures.asAsyncCCAE(enumerable, body)
415 |
416 | ///
417 | /// Accept any async sequence type to support `for .. in` expressions in Async workflows.
418 | ///
419 | /// The element type of the sequence
420 | /// .NET IAsyncEnumerable<'E> (expression result)
421 | /// The sequence.
422 | member __.Source(enumerable: IAsyncEnumerable<'E>) =
423 | enumerable
424 |
425 | ///
426 | /// Accept any async sequence type to support `for .. in` expressions in Async workflows.
427 | ///
428 | /// The element type of the sequence
429 | /// .NET ConfiguredCancelableAsyncEnumerable<'E> (expression result)
430 | /// The sequence.
431 | member __.Source(enumerable: ConfiguredCancelableAsyncEnumerable<'E>) =
432 | enumerable
433 | #endif
434 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks/Awaiters.fs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | namespace Microsoft.FSharp.Control
21 |
22 | open System
23 | open System.Threading
24 | open System.Threading.Tasks
25 | open System.Runtime.CompilerServices
26 |
27 | #nowarn "44"
28 |
29 | // Provide Awaitable/Awaiter on custom code:
30 | // PCL's Awaitable/Awaiter on "Microsoft.Runtime.CompilerServices",
31 | // but not decorated TypeForwardedTo attributes.
32 | // So if referenced Awaitable/Awaiter types, cause cannot found Awaitable/Awaiter types.
33 | // (Awaitable/Awaiter types defined on "System.Runtime.CompilerServices" real assembly)
34 | // FusionTasks redefined custom Awaitable/Awaiter types, exclude CompilerServices's type references.
35 |
36 | ///////////////////////////////////////////////////////////////////////////////////
37 | // AsyncAwaiter.
38 |
39 | ///
40 | /// F# Async's awaiter implementation. This structure using implicitly.
41 | ///
42 | []
43 | type AsyncAwaiter internal (ta: TaskAwaiter) =
44 |
45 | member __.IsCompleted = ta.IsCompleted
46 | member __.OnCompleted(continuation: Action) = ta.OnCompleted(continuation)
47 | member __.UnsafeOnCompleted(continuation: Action) = ta.UnsafeOnCompleted(continuation)
48 | member __.GetResult() = ta.GetResult()
49 |
50 | interface System.Runtime.CompilerServices.INotifyCompletion with
51 | member __.OnCompleted(continuation: Action) = ta.OnCompleted(continuation)
52 |
53 | ///
54 | /// F# Async's awaiter implementation. This structure using implicitly.
55 | ///
56 | []
57 | type AsyncAwaiter<'T> internal (ta: TaskAwaiter<'T>) =
58 |
59 | member __.IsCompleted = ta.IsCompleted
60 | member __.OnCompleted(continuation: Action) = ta.OnCompleted(continuation)
61 | member __.UnsafeOnCompleted(continuation: Action) = ta.UnsafeOnCompleted(continuation)
62 | member __.GetResult() = ta.GetResult()
63 |
64 | interface System.Runtime.CompilerServices.INotifyCompletion with
65 | member __.OnCompleted(continuation: Action) = ta.OnCompleted(continuation)
66 |
67 | ///////////////////////////////////////////////////////////////////////////////////
68 | // ConfiguredTaskAsyncAwaiter.
69 |
70 | ///
71 | /// F# Async's awaiter implementation. This structure using implicitly.
72 | ///
73 | []
74 | []
75 | type ConfiguredTaskAsyncAwaiter internal (ctacta: ConfiguredTaskAwaitable.ConfiguredTaskAwaiter) =
76 |
77 | member __.IsCompleted = ctacta.IsCompleted
78 | member __.OnCompleted(continuation: Action) = ctacta.OnCompleted(continuation)
79 | member __.UnsafeOnCompleted(continuation: Action) = ctacta.UnsafeOnCompleted(continuation)
80 | member __.GetResult() = ctacta.GetResult()
81 |
82 | interface System.Runtime.CompilerServices.INotifyCompletion with
83 | member __.OnCompleted(continuation: Action) = ctacta.OnCompleted(continuation)
84 |
85 | ///
86 | /// F# Async's awaitable implementation. This structure using implicitly.
87 | ///
88 | []
89 | []
90 | type ConfiguredTaskAsyncAwaitable internal (cta: ConfiguredTaskAwaitable) =
91 |
92 | member __.GetAwaiter() = ConfiguredTaskAsyncAwaiter(cta.GetAwaiter())
93 |
94 | ///
95 | /// F# Async's awaiter implementation. This structure using implicitly.
96 | ///
97 | []
98 | []
99 | type ConfiguredTaskAsyncAwaiter<'T> internal (ctacta: ConfiguredTaskAwaitable<'T>.ConfiguredTaskAwaiter) =
100 |
101 | member __.IsCompleted = ctacta.IsCompleted
102 | member __.OnCompleted(continuation: Action) = ctacta.OnCompleted(continuation)
103 | member __.UnsafeOnCompleted(continuation: Action) = ctacta.UnsafeOnCompleted(continuation)
104 | member __.GetResult() = ctacta.GetResult()
105 |
106 | interface System.Runtime.CompilerServices.INotifyCompletion with
107 | member __.OnCompleted(continuation: Action) = ctacta.OnCompleted(continuation)
108 |
109 | ///
110 | /// F# Async's awaitable implementation. This structure using implicitly.
111 | ///
112 | []
113 | []
114 | type ConfiguredTaskAsyncAwaitable<'T> internal (cta: ConfiguredTaskAwaitable<'T>) =
115 |
116 | member __.GetAwaiter() = ConfiguredTaskAsyncAwaiter<'T>(cta.GetAwaiter())
117 |
118 | ///////////////////////////////////////////////////////////////////////////////////
119 | // ConfiguredValueTaskAsyncAwaiter.
120 |
121 | ///
122 | /// F# Async's awaiter implementation. This structure using implicitly.
123 | ///
124 | []
125 | []
126 | type ConfiguredValueTaskAsyncAwaiter internal (ctacta: ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter) =
127 |
128 | member __.IsCompleted = ctacta.IsCompleted
129 | member __.OnCompleted(continuation: Action) = ctacta.OnCompleted(continuation)
130 | member __.UnsafeOnCompleted(continuation: Action) = ctacta.UnsafeOnCompleted(continuation)
131 | member __.GetResult() = ctacta.GetResult()
132 |
133 | interface System.Runtime.CompilerServices.INotifyCompletion with
134 | member __.OnCompleted(continuation: Action) = ctacta.OnCompleted(continuation)
135 |
136 | ///
137 | /// F# Async's awaitable implementation. This structure using implicitly.
138 | ///
139 | []
140 | []
141 | type ConfiguredValueTaskAsyncAwaitable internal (cta: ConfiguredValueTaskAwaitable) =
142 |
143 | member __.GetAwaiter() = ConfiguredValueTaskAsyncAwaiter(cta.GetAwaiter())
144 |
145 | ///
146 | /// F# Async's awaiter implementation. This structure using implicitly.
147 | ///
148 | []
149 | []
150 | type ConfiguredValueTaskAsyncAwaiter<'T> internal (ctacta: ConfiguredValueTaskAwaitable<'T>.ConfiguredValueTaskAwaiter) =
151 |
152 | member __.IsCompleted = ctacta.IsCompleted
153 | member __.OnCompleted(continuation: Action) = ctacta.OnCompleted(continuation)
154 | member __.UnsafeOnCompleted(continuation: Action) = ctacta.UnsafeOnCompleted(continuation)
155 | member __.GetResult() = ctacta.GetResult()
156 |
157 | interface System.Runtime.CompilerServices.INotifyCompletion with
158 | member __.OnCompleted(continuation: Action) = ctacta.OnCompleted(continuation)
159 |
160 | ///
161 | /// F# Async's awaitable implementation. This structure using implicitly.
162 | ///
163 | []
164 | []
165 | type ConfiguredValueTaskAsyncAwaitable<'T> internal (cta: ConfiguredValueTaskAwaitable<'T>) =
166 |
167 | member __.GetAwaiter() = ConfiguredValueTaskAsyncAwaiter<'T>(cta.GetAwaiter())
168 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks/CommonAssemblyInfo.fs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | namespace global
21 | []
22 | do()
23 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks/FSharp.Control.FusionTasks.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net45;net461;net48;netstandard1.6;netstandard2.0;netstandard2.1;netcoreapp2.0;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1;net5.0;net6.0
5 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks/Infrastructures.fs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | namespace Microsoft.FSharp.Control
21 |
22 | open System
23 | open System.Threading
24 | open System.Threading.Tasks
25 | open System.Collections.Generic
26 | open System.Runtime.CompilerServices
27 |
28 | #nowarn "44"
29 |
30 | ///////////////////////////////////////////////////////////////////////////////////
31 | // Internal implementations.
32 |
33 | module internal Infrastructures =
34 |
35 | let inline private (|IsFaulted|IsCanceled|IsCompleted|) (task: Task) =
36 | if task.IsFaulted then IsFaulted task.Exception
37 | else if task.IsCanceled then IsCanceled
38 | else IsCompleted
39 |
40 | let inline private safeToken (ct: CancellationToken option) =
41 | match ct with
42 | | Some token -> token
43 | | None -> Async.DefaultCancellationToken
44 |
45 | let asTaskT(async: Async<'T>, ct: CancellationToken option) =
46 | Async.StartImmediateAsTask(async, safeToken ct)
47 |
48 | let asValueTask(async: Async, ct: CancellationToken option) =
49 | ValueTask(Async.StartImmediateAsTask(async, safeToken ct))
50 |
51 | let asValueTaskT(async: Async<'T>, ct: CancellationToken option) =
52 | ValueTask<'T>(Async.StartImmediateAsTask(async, safeToken ct))
53 |
54 | let asAsync(task: Task, ct: CancellationToken option) =
55 | let scheduler = Utilities.getScheduler()
56 | Async.FromContinuations(
57 | fun (completed, caught, canceled) ->
58 | task.ContinueWith(
59 | new Action(fun _ ->
60 | match task with
61 | | IsFaulted exn -> caught(exn)
62 | | IsCanceled -> canceled(Utilities.createCanceledException ct) // TODO: how to extract implicit caught exceptions from task?
63 | | IsCompleted -> completed(())),
64 | safeToken ct,
65 | TaskContinuationOptions.AttachedToParent,
66 | scheduler)
67 | |> ignore)
68 |
69 | let asAsyncT(task: Task<'T>, ct: CancellationToken option) =
70 | let scheduler = Utilities.getScheduler()
71 | Async.FromContinuations(
72 | fun (completed, caught, canceled) ->
73 | task.ContinueWith(
74 | new Action>(fun _ ->
75 | match task with
76 | | IsFaulted exn -> caught(exn)
77 | | IsCanceled -> canceled(Utilities.createCanceledException ct) // TODO: how to extract implicit caught exceptions from task?
78 | | IsCompleted -> completed(task.Result)),
79 | safeToken ct,
80 | TaskContinuationOptions.AttachedToParent,
81 | scheduler)
82 | |> ignore)
83 |
84 | let asAsyncV(task: ValueTask, ct: CancellationToken option) =
85 | let scheduler = Utilities.getScheduler()
86 | Async.FromContinuations(
87 | fun (completed, caught, canceled) ->
88 | match task.IsCompletedSuccessfully with
89 | | true -> completed()
90 | | false ->
91 | let task = task.AsTask()
92 | task.ContinueWith(
93 | new Action(fun _ ->
94 | match task with
95 | | IsFaulted exn -> caught(exn)
96 | | IsCanceled -> canceled(Utilities.createCanceledException ct) // TODO: how to extract implicit caught exceptions from task?
97 | | IsCompleted -> completed()),
98 | safeToken ct,
99 | TaskContinuationOptions.AttachedToParent,
100 | scheduler)
101 | |> ignore)
102 |
103 | let asAsyncVT(task: ValueTask<'T>, ct: CancellationToken option) =
104 | let scheduler = Utilities.getScheduler()
105 | Async.FromContinuations(
106 | fun (completed, caught, canceled) ->
107 | match task.IsCompletedSuccessfully with
108 | | true -> completed(task.Result)
109 | | false ->
110 | let task = task.AsTask()
111 | task.ContinueWith(
112 | new Action>(fun _ ->
113 | match task with
114 | | IsFaulted exn -> caught(exn)
115 | | IsCanceled -> canceled(Utilities.createCanceledException ct) // TODO: how to extract implicit caught exceptions from task?
116 | | IsCompleted -> completed(task.Result)),
117 | safeToken ct,
118 | TaskContinuationOptions.AttachedToParent,
119 | scheduler)
120 | |> ignore)
121 |
122 | let asAsyncCT(ct: ConfiguredTaskAwaitable) =
123 | Async.FromContinuations(
124 | fun (completed, caught, _) ->
125 | let awaiter = ct.GetAwaiter()
126 | awaiter.OnCompleted(
127 | new Action(fun _ ->
128 | try
129 | awaiter.GetResult()
130 | completed()
131 | with exn -> caught(exn)))
132 | |> ignore)
133 |
134 | let asAsyncCTT(cta: ConfiguredTaskAwaitable<'T>) =
135 | Async.FromContinuations(
136 | fun (completed, caught, _) ->
137 | let awaiter = cta.GetAwaiter()
138 | awaiter.OnCompleted(
139 | new Action(fun _ ->
140 | try completed(awaiter.GetResult())
141 | with exn -> caught(exn)))
142 | |> ignore)
143 |
144 | let asAsyncCVT(cvt: ConfiguredValueTaskAwaitable) =
145 | Async.FromContinuations(
146 | fun (completed, caught, _) ->
147 | let awaiter = cvt.GetAwaiter()
148 | awaiter.OnCompleted(
149 | new Action(fun _ ->
150 | try completed(awaiter.GetResult())
151 | with exn -> caught(exn)))
152 | |> ignore)
153 |
154 | let asAsyncCVTT(cvtt: ConfiguredValueTaskAwaitable<'T>) =
155 | Async.FromContinuations(
156 | fun (completed, caught, _) ->
157 | let awaiter = cvtt.GetAwaiter()
158 | awaiter.OnCompleted(
159 | new Action(fun _ ->
160 | try completed(awaiter.GetResult())
161 | with exn -> caught(exn)))
162 | |> ignore)
163 |
164 | []
165 | let asAsyncCTA(cta: ConfiguredTaskAsyncAwaitable) =
166 | Async.FromContinuations(
167 | fun (completed, caught, _) ->
168 | let awaiter = cta.GetAwaiter()
169 | awaiter.OnCompleted(
170 | new Action(fun _ ->
171 | try
172 | awaiter.GetResult()
173 | completed()
174 | with exn -> caught(exn)))
175 | |> ignore)
176 |
177 | []
178 | let asAsyncCTAT(cta: ConfiguredTaskAsyncAwaitable<'T>) =
179 | Async.FromContinuations(
180 | fun (completed, caught, _) ->
181 | let awaiter = cta.GetAwaiter()
182 | awaiter.OnCompleted(
183 | new Action(fun _ ->
184 | try completed(awaiter.GetResult())
185 | with exn -> caught(exn)))
186 | |> ignore)
187 |
188 | []
189 | let asAsyncCVTA(cta: ConfiguredValueTaskAsyncAwaitable) =
190 | Async.FromContinuations(
191 | fun (completed, caught, _) ->
192 | let awaiter = cta.GetAwaiter()
193 | awaiter.OnCompleted(
194 | new Action(fun _ ->
195 | try completed(awaiter.GetResult())
196 | with exn -> caught(exn)))
197 | |> ignore)
198 |
199 | []
200 | let asAsyncCVTAT(cta: ConfiguredValueTaskAsyncAwaitable<'T>) =
201 | Async.FromContinuations(
202 | fun (completed, caught, _) ->
203 | let awaiter = cta.GetAwaiter()
204 | awaiter.OnCompleted(
205 | new Action(fun _ ->
206 | try completed(awaiter.GetResult())
207 | with exn -> caught(exn)))
208 | |> ignore)
209 |
210 | #if !NET45 && !NETSTANDARD1_6 && !NETCOREAPP2_0
211 |
212 | let private finallyAD (disposable: IAsyncDisposable) (continuation: unit -> unit) (caught: exn -> unit) =
213 | try
214 | let disposeAwaiter = disposable.DisposeAsync().GetAwaiter()
215 | if disposeAwaiter.IsCompleted then
216 | disposeAwaiter.GetResult()
217 | continuation()
218 | else
219 | disposeAwaiter.OnCompleted(
220 | fun () ->
221 | try
222 | disposeAwaiter.GetResult()
223 | continuation()
224 | with
225 | | exn -> caught exn)
226 | with
227 | | exn -> caught exn
228 |
229 | let private finallyCCAEE (disposable: ConfiguredCancelableAsyncEnumerable<'T>.Enumerator) (continuation: unit -> unit) (caught: exn -> unit) =
230 | try
231 | let disposeAwaiter = disposable.DisposeAsync().GetAwaiter()
232 | if disposeAwaiter.IsCompleted then
233 | disposeAwaiter.GetResult()
234 | continuation()
235 | else
236 | disposeAwaiter.OnCompleted(
237 | fun () ->
238 | try
239 | disposeAwaiter.GetResult()
240 | continuation()
241 | with
242 | | exn -> caught exn)
243 | with
244 | | exn -> caught exn
245 |
246 | let asAsyncAD(disposable: 'T :> IAsyncDisposable, body: 'T -> Async<'R>) =
247 |
248 | let bodyAsync = body disposable
249 |
250 | // Wrap asynchronous monad.
251 | Async.FromContinuations(
252 | fun (completed, caught, canceled) ->
253 |
254 | // Finally handlers.
255 | let completedContinuation value =
256 | finallyAD disposable (fun () -> completed value) caught
257 | let caughtContinuation exn =
258 | finallyAD disposable (fun () -> caught exn) caught
259 | let canceledContinuation exn =
260 | finallyAD disposable (fun () -> canceled exn) caught
261 |
262 | Async.StartWithContinuations(
263 | bodyAsync,
264 | // Got:
265 | completedContinuation,
266 | caughtContinuation,
267 | canceledContinuation))
268 |
269 | let asAsyncAE(enumerable: IAsyncEnumerable<'T>, body: 'T -> Async<'U>, ct: CancellationToken option) =
270 |
271 | let inline checkCancellation() =
272 | if ct.IsSome then
273 | ct.Value.ThrowIfCancellationRequested()
274 |
275 | // Check early cancellation.
276 | checkCancellation()
277 |
278 | // Get asynchronous enumerator.
279 | let enumerator = enumerable.GetAsyncEnumerator(Utilities.unwrap ct)
280 |
281 | // Wrap asynchronous monad.
282 | Async.FromContinuations(
283 | fun (completed, caught, canceled) ->
284 | let mutable finalValue = Unchecked.defaultof<'U>
285 |
286 | let rec whileLoop() =
287 |
288 | // Finally handlers.
289 | let completedContinuation value =
290 | finallyAD enumerator (fun () -> completed value) caught
291 | let caughtContinuation exn =
292 | finallyAD enumerator (fun () -> caught exn) caught
293 | let canceledContinuation exn =
294 | finallyAD enumerator (fun () -> canceled exn) caught
295 |
296 | // (Recursive) Loop main:
297 | try
298 | // Check early cancellation.
299 | checkCancellation()
300 |
301 | let moveNextAwaiter = enumerator.MoveNextAsync().GetAwaiter()
302 |
303 | // Will get result and invoke continuation.
304 | let getResultContinuation() =
305 | // Got next (asynchronous) value?
306 | let moveNextResult = moveNextAwaiter.GetResult()
307 | if moveNextResult then
308 | // Got Async<'U>
309 | let resultAsync = body enumerator.Current
310 | // Will get value asynchronously.
311 | Async.StartWithContinuations(
312 | resultAsync,
313 | // Got:
314 | (fun result ->
315 | // Save last value
316 | finalValue <- result
317 | // NOTE: Maybe will not cause stack overflow, because async workflow will be scattered recursive calls...
318 | whileLoop()),
319 | // Caught asynchronous monadic exception.
320 | caughtContinuation,
321 | // Caught asynchronous monadic cancel exception.
322 | canceledContinuation)
323 | // Didn't get next value (= finished)
324 | else
325 | // Completed totally asynchronous sequence.
326 | completedContinuation finalValue
327 |
328 | // Already completed synchronously MoveNextAsync() ?
329 | if moveNextAwaiter.IsCompleted then
330 | // Get result synchronously.
331 | getResultContinuation()
332 | else
333 | // Delay getting result.
334 | moveNextAwaiter.OnCompleted(
335 | fun () ->
336 | try
337 | getResultContinuation()
338 | with
339 | | exn -> caughtContinuation exn)
340 | with
341 | | exn -> caughtContinuation exn
342 |
343 | // Start simulated asynchronous loop.
344 | whileLoop())
345 |
346 | let asAsyncCCAE(enumerable: ConfiguredCancelableAsyncEnumerable<'T>, body: 'T -> Async<'U>) =
347 |
348 | // Get asynchronous enumerator.
349 | let enumerator = enumerable.GetAsyncEnumerator()
350 |
351 | // Wrap asynchronous monad.
352 | Async.FromContinuations(
353 | fun (completed, caught, canceled) ->
354 | let mutable finalValue = Unchecked.defaultof<'U>
355 |
356 | let rec whileLoop() =
357 |
358 | // Finally handlers.
359 | let completedContinuation value =
360 | finallyCCAEE enumerator (fun () -> completed value) caught
361 | let caughtContinuation exn =
362 | finallyCCAEE enumerator (fun () -> caught exn) caught
363 | let canceledContinuation exn =
364 | finallyCCAEE enumerator (fun () -> canceled exn) caught
365 |
366 | // (Recursive) Loop main:
367 | try
368 | let moveNextAwaiter = enumerator.MoveNextAsync().GetAwaiter()
369 |
370 | // Will get result and invoke continuation.
371 | let getResultContinuation() =
372 | // Got next (asynchronous) value?
373 | let moveNextResult = moveNextAwaiter.GetResult()
374 | if moveNextResult then
375 | // Got Async<'U>
376 | let resultAsync = body enumerator.Current
377 | // Will get value asynchronously.
378 | Async.StartWithContinuations(
379 | resultAsync,
380 | // Got:
381 | (fun result ->
382 | // Save last value
383 | finalValue <- result
384 | // NOTE: Maybe will not cause stack overflow, because async workflow will be scattered recursive calls...
385 | whileLoop()),
386 | // Caught asynchronous monadic exception.
387 | caughtContinuation,
388 | // Caught asynchronous monadic cancel exception.
389 | canceledContinuation)
390 | // Didn't get next value (= finished)
391 | else
392 | // Completed totally asynchronous sequence.
393 | completedContinuation finalValue
394 |
395 | // Already completed synchronously MoveNextAsync() ?
396 | if moveNextAwaiter.IsCompleted then
397 | // Get result synchronously.
398 | getResultContinuation()
399 | else
400 | // Delay getting result.
401 | moveNextAwaiter.OnCompleted(
402 | fun () ->
403 | try
404 | getResultContinuation()
405 | with
406 | | exn -> caughtContinuation exn)
407 | with
408 | | exn -> caughtContinuation exn
409 |
410 | // Start simulated asynchronous loop.
411 | whileLoop())
412 |
413 | #endif
414 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks/TaskExtensions.fs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | namespace System.Threading.Tasks
21 |
22 | open System.Runtime.CompilerServices
23 | open System.Threading
24 | open Microsoft.FSharp.Control
25 |
26 | #nowarn "44"
27 |
28 | ///
29 | /// Seamless conversion extensions in standard .NET Task based infrastructure.
30 | ///
31 | []
32 | type TaskExtensions =
33 |
34 | ///////////////////////////////////////////////////////////////////////////////////
35 | // .NET (C#) side Task --> Async conversion extensions.
36 |
37 | ///
38 | /// Seamless conversion from .NET Task to F# Async.
39 | ///
40 | /// .NET Task
41 | /// F# Async (FSharpAsync<Unit>)
42 | []
43 | static member AsAsync (task: Task) =
44 | Infrastructures.asAsync (task, None)
45 |
46 | ///
47 | /// Seamless conversion from .NET Task to F# Async.
48 | ///
49 | /// .NET Task
50 | /// Cancellation token
51 | /// F# Async (FSharpAsync<Unit>)
52 | []
53 | static member AsAsync (task: Task, token: CancellationToken) =
54 | Infrastructures.asAsync (task, Some token)
55 |
56 | ///
57 | /// Seamless conversion from .NET Task to F# Async.
58 | ///
59 | /// .NET Task
60 | /// True if continuation running on captured SynchronizationContext
61 | /// F# Async (FSharpAsync<Unit>)
62 | []
63 | static member AsAsyncConfigured (task: Task, continueOnCapturedContext: bool) =
64 | Infrastructures.asAsyncCTA (ConfiguredTaskAsyncAwaitable(task.ConfigureAwait(continueOnCapturedContext)))
65 |
66 | ///
67 | /// Seamless conversion from .NET Task to F# Async.
68 | ///
69 | /// Computation result type
70 | /// .NET Task<'T>
71 | /// F# Async<'T> (FSharpAsync<'T>)
72 | []
73 | static member AsAsync (task: Task<'T>) =
74 | Infrastructures.asAsyncT (task, None)
75 |
76 | ///
77 | /// Seamless conversion from .NET Task to F# Async.
78 | ///
79 | /// Computation result type
80 | /// .NET Task<'T>
81 | /// Cancellation token
82 | /// F# Async<'T> (FSharpAsync<'T>)
83 | []
84 | static member AsAsync (task: Task<'T>, token: CancellationToken) =
85 | Infrastructures.asAsyncT (task, Some token)
86 |
87 | ///
88 | /// Seamless conversion from .NET Task to F# Async.
89 | ///
90 | /// Computation result type
91 | /// .NET Task<'T>
92 | /// True if continuation running on captured SynchronizationContext
93 | /// F# Async<'T> (FSharpAsync<'T>)
94 | []
95 | static member AsAsyncConfigured (task: Task<'T>, continueOnCapturedContext: bool) =
96 | Infrastructures.asAsyncCTAT (ConfiguredTaskAsyncAwaitable<'T>(task.ConfigureAwait(continueOnCapturedContext)))
97 |
98 | ///
99 | /// Seamless conversion extensions in standard .NET ValueTask based infrastructure.
100 | ///
101 | []
102 | type ValueTaskExtensions =
103 |
104 | ///////////////////////////////////////////////////////////////////////////////////
105 | // .NET (C#) side ValueTask --> Async conversion extensions.
106 |
107 | ///
108 | /// Seamless conversion from .NET ValueTask to F# Async.
109 | ///
110 | /// .NET ValueTask
111 | /// F# Async<Unit> (FSharpAsync<Unit>)
112 | []
113 | static member AsAsync (task: ValueTask) =
114 | Infrastructures.asAsyncV (task, None)
115 |
116 | ///
117 | /// Seamless conversion from .NET ValueTask to F# Async.
118 | ///
119 | /// .NET ValueTask
120 | /// Cancellation token
121 | /// F# Async<Unit> (FSharpAsync<Unit>)
122 | []
123 | static member AsAsync (task: ValueTask, token: CancellationToken) =
124 | Infrastructures.asAsyncV (task, Some token)
125 |
126 | ///
127 | /// Seamless conversion from .NET ValueTask to F# Async.
128 | ///
129 | /// .NET ValueTask
130 | /// True if continuation running on captured SynchronizationContext
131 | /// F# Async<Unit> (FSharpAsync<Unit>)
132 | []
133 | static member AsAsyncConfigured (task: ValueTask, continueOnCapturedContext: bool) =
134 | Infrastructures.asAsyncCVTA (ConfiguredValueTaskAsyncAwaitable(task.ConfigureAwait(continueOnCapturedContext)))
135 |
136 | ///
137 | /// Seamless conversion from .NET ValueTask to F# Async.
138 | ///
139 | /// Computation result type
140 | /// .NET ValueTask<'T>
141 | /// F# Async<'T> (FSharpAsync<'T>)
142 | []
143 | static member AsAsync (task: ValueTask<'T>) =
144 | Infrastructures.asAsyncVT (task, None)
145 |
146 | ///
147 | /// Seamless conversion from .NET ValueTask to F# Async.
148 | ///
149 | /// Computation result type
150 | /// .NET ValueTask<'T>
151 | /// Cancellation token
152 | /// F# Async<'T> (FSharpAsync<'T>)
153 | []
154 | static member AsAsync (task: ValueTask<'T>, token: CancellationToken) =
155 | Infrastructures.asAsyncVT (task, Some token)
156 |
157 | ///
158 | /// Seamless conversion from .NET ValueTask to F# Async.
159 | ///
160 | /// Computation result type
161 | /// .NET ValueTask<'T>
162 | /// True if continuation running on captured SynchronizationContext
163 | /// F# Async<'T> (FSharpAsync<'T>)
164 | []
165 | static member AsAsyncConfigured (task: ValueTask<'T>, continueOnCapturedContext: bool) =
166 | Infrastructures.asAsyncCVTAT (ConfiguredValueTaskAsyncAwaitable<'T>(task.ConfigureAwait(continueOnCapturedContext)))
167 |
168 | ///////////////////////////////////////////////////////////////////////////////////
169 |
170 | namespace Microsoft.FSharp.Control
171 |
172 | open System.Runtime.CompilerServices
173 | open System.Threading
174 | open System.Threading.Tasks
175 |
176 | ///
177 | /// Seamless conversion extensions in standard .NET Task based infrastructure.
178 | ///
179 | []
180 | type AsyncExtensions =
181 |
182 | ///////////////////////////////////////////////////////////////////////////////////
183 | // .NET (C#) side Async --> Task conversion extensions.
184 |
185 | ///
186 | /// Seamless conversion from F# Async to .NET Task.
187 | ///
188 | /// F# Async (FSharpAsync<Unit>)
189 | /// .NET Task
190 | []
191 | static member AsTask (async: Async) =
192 | Infrastructures.asTaskT (async, None) :> Task
193 |
194 | ///
195 | /// Seamless conversion from F# Async to .NET Task.
196 | ///
197 | /// F# Async (FSharpAsync<Unit>)
198 | /// Cancellation token
199 | /// .NET Task
200 | []
201 | static member AsTask (async: Async, token: CancellationToken) =
202 | Infrastructures.asTaskT (async, Some token) :> Task
203 |
204 | ///
205 | /// Seamless conversion from F# Async to .NET Task.
206 | ///
207 | /// Computation result type
208 | /// F# Async<'T> (FSharpAsync<'T>)
209 | /// .NET Task<'T>
210 | []
211 | static member AsTask (async: Async<'T>) =
212 | Infrastructures.asTaskT (async, None)
213 |
214 | ///
215 | /// Seamless conversion from F# Async to .NET Task.
216 | ///
217 | /// Computation result type
218 | /// F# Async<'T> (FSharpAsync<'T>)
219 | /// Cancellation token
220 | /// .NET Task<'T>
221 | []
222 | static member AsTask (async: Async<'T>, token: CancellationToken) =
223 | Infrastructures.asTaskT (async, Some token)
224 |
225 | ///////////////////////////////////////////////////////////////////////////////////
226 | // .NET (C#) side Async --> ValueTask conversion extensions.
227 |
228 | ///
229 | /// Seamless conversion from F# Async to .NET ValueTask.
230 | ///
231 | /// F# Async<unit> (FSharpAsync<unit>)
232 | /// .NET ValueTask
233 | []
234 | static member AsValueTask (async: Async) =
235 | Infrastructures.asValueTask (async, None)
236 |
237 | ///
238 | /// Seamless conversion from F# Async to .NET ValueTask.
239 | ///
240 | /// F# Async<unit> (FSharpAsync<unit>)
241 | /// Cancellation token
242 | /// .NET ValueTask
243 | []
244 | static member AsValueTask (async: Async, token: CancellationToken) =
245 | Infrastructures.asValueTask (async, Some token)
246 |
247 | ///
248 | /// Seamless conversion from F# Async to .NET ValueTask.
249 | ///
250 | /// Computation result type
251 | /// F# Async<'T> (FSharpAsync<'T>)
252 | /// .NET ValueTask<'T>
253 | []
254 | static member AsValueTask (async: Async<'T>) =
255 | Infrastructures.asValueTaskT (async, None)
256 |
257 | ///
258 | /// Seamless conversion from F# Async to .NET ValueTask.
259 | ///
260 | /// Computation result type
261 | /// F# Async<'T> (FSharpAsync<'T>)
262 | /// Cancellation token
263 | /// .NET ValueTask<'T>
264 | []
265 | static member AsValueTask (async: Async<'T>, token: CancellationToken) =
266 | Infrastructures.asValueTaskT (async, Some token)
267 |
268 | ///////////////////////////////////////////////////////////////////////////////////
269 | // .NET (C#) side Async configurable extensions.
270 |
271 | ///
272 | /// Seamless configuring context support for F# Async.
273 | ///
274 | /// F# Async (FSharpAsync<unit>)
275 | /// True if continuation running on captured SynchronizationContext
276 | /// .NET TaskAwaiter
277 | []
278 | static member ConfigureAwait (async: Async, continueOnCapturedContext: bool) =
279 | let task = Infrastructures.asTaskT (async, None) :> Task
280 | ConfiguredTaskAsyncAwaitable(task.ConfigureAwait(continueOnCapturedContext))
281 |
282 | ///
283 | /// Seamless configuring context support for F# Async.
284 | ///
285 | /// Computation result type
286 | /// F# Async<'T> (FSharpAsync<'T>)
287 | /// True if continuation running on captured SynchronizationContext
288 | /// .NET TaskAwaiter<'T>
289 | []
290 | static member ConfigureAwait (async: Async<'T>, continueOnCapturedContext: bool) =
291 | let task = Infrastructures.asTaskT (async, None)
292 | ConfiguredTaskAsyncAwaitable<'T>(task.ConfigureAwait(continueOnCapturedContext))
293 |
294 | ///////////////////////////////////////////////////////////////////////////////////
295 | // .NET (C#) side Async awaitabler extensions.
296 |
297 | ///
298 | /// Seamless awaiter support for F# Async.
299 | ///
300 | /// F# Async (FSharpAsync<unit>)
301 | /// .NET TaskAwaiter
302 | []
303 | static member GetAwaiter (async: Async) =
304 | let task = Infrastructures.asTaskT (async, None) :> Task
305 | AsyncAwaiter(task.GetAwaiter())
306 |
307 | ///
308 | /// Seamless awaiter support for F# Async.
309 | ///
310 | /// Computation result type
311 | /// F# Async<'T> (FSharpAsync<'T>)
312 | /// .NET TaskAwaiter<'T>
313 | []
314 | static member GetAwaiter (async: Async<'T>) =
315 | let task = Infrastructures.asTaskT (async, None)
316 | AsyncAwaiter<'T>(task.GetAwaiter())
317 |
--------------------------------------------------------------------------------
/FSharp.Control.FusionTasks/Utilities.fs:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | // Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo, @kekyo2)
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | /////////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | namespace Microsoft.FSharp.Control
21 |
22 | open System
23 | open System.Threading
24 | open System.Threading.Tasks
25 |
26 | ///////////////////////////////////////////////////////////////////////////////////
27 | // Utilities.
28 |
29 | module internal Utilities =
30 |
31 | let createCanceledException(token: CancellationToken option) =
32 | // TODO: Constructed stack traces. require?
33 | try
34 | match token with
35 | | Some t -> new OperationCanceledException(t) |> raise
36 | | None -> new OperationCanceledException() |> raise
37 | with :? OperationCanceledException as e -> e
38 |
39 | let getScheduler() =
40 | match SynchronizationContext.Current with
41 | | null -> TaskScheduler.Current
42 | | _ -> TaskScheduler.FromCurrentSynchronizationContext()
43 |
44 | let unwrap = function
45 | | Some t -> t
46 | | None -> Unchecked.defaultof
47 |
--------------------------------------------------------------------------------
/Images/FSharp.Control.FusionTasks.100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kekyo/FSharp.Control.FusionTasks/3685f0a25d9ce02ff1d133a8a96b8cc6c85c4931/Images/FSharp.Control.FusionTasks.100.png
--------------------------------------------------------------------------------
/Images/FSharp.Control.FusionTasks.128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kekyo/FSharp.Control.FusionTasks/3685f0a25d9ce02ff1d133a8a96b8cc6c85c4931/Images/FSharp.Control.FusionTasks.128.png
--------------------------------------------------------------------------------
/Images/FSharp.Control.FusionTasks.design:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kekyo/FSharp.Control.FusionTasks/3685f0a25d9ce02ff1d133a8a96b8cc6c85c4931/Images/FSharp.Control.FusionTasks.design
--------------------------------------------------------------------------------
/Images/linqpad5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kekyo/FSharp.Control.FusionTasks/3685f0a25d9ce02ff1d133a8a96b8cc6c85c4931/Images/linqpad5.png
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
2 | Copyright (c) 2016-2018 Kouji Matsui (@kozy_kekyo)
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 |
--------------------------------------------------------------------------------
/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # F# FusionTasks
2 |
3 | 
4 |
5 | ## Status
6 |
7 | | | main | devel |
8 | |:---|:--:|:--:|
9 | | NuGet Package | [](https://www.nuget.org/packages/FSharp.Control.FusionTasks) | |
10 | | Continuous integration | [](https://github.com/kekyo/FSharp.Control.FusionTasks/actions) | [](https://github.com/kekyo/FSharp.Control.FusionTasks/actions) |
11 |
12 | -----
13 |
14 | ## What is this?
15 |
16 | * F# Async workflow <--> .NET Task/ValueTask easy seamless interoperability library.
17 | * Sample code (F# side):
18 |
19 | ``` fsharp
20 | let asyncTest = async {
21 | use ms = new MemoryStream()
22 |
23 | // FusionTasks directly interpreted System.Threading.Tasks.Task class in F# async-workflow block.
24 | do! ms.WriteAsync(data, 0, data.Length)
25 | do ms.Position <- 0L
26 |
27 | // FusionTasks directly interpreted System.Threading.Tasks.Task class in F# async-workflow block.
28 | let! length = ms.ReadAsync(data2, 0, data2.Length)
29 | do length |> should equal data2.Length
30 | }
31 | ```
32 |
33 | * Sample code (C# side):
34 |
35 | ``` csharp
36 | using System.Threading.Tasks;
37 | using Microsoft.FSharp.Control;
38 |
39 | public async Task AsyncTest(FSharpAsync asyncIntComp)
40 | {
41 | // FusionTasks simple usage F#'s Async direct awaitable.
42 | await FSharpAsync.Sleep(500);
43 | Console.WriteLine("Awaited F# async function (unit).");
44 |
45 | // FusionTasks simple usage F#'s Async direct awaitable.
46 | var result = await asyncIntComp;
47 | Console.WriteLine("Awaited F# async function: Result=" + result);
48 | }
49 | ```
50 |
51 | -----
52 |
53 | ## Features
54 |
55 | * Easy interoperable .NET Task/ValueTask <--> F#'s Async.
56 | * F# async workflow block now supports directly .NET Task/ValueTask handle with let!, do! and use!.
57 | * .NET (C# async-await) now supports directly F#'s Async.
58 | * SyncronizationContext capture operation support (F#: AsyncConfigure method / .NET (C#) AsAsyncConfigured method)
59 | * .NET now supports standard asynchronous sequence called `IAsyncEnumerable`, FusionTasks supports it with `for` expression.
60 |
61 | ## Benefits
62 |
63 | * Easy interoperability, combination and relation standard .NET OSS packages using Task/ValueTask and F#'s Async.
64 | * F# 6.0/4.5 with .NET 6.0/5.0, .NET Core 3.0/2.0 (or higher), .NET Standard 1.6/2.0 and .NET Framework 4.5/4.6.1/4.8.
65 | * Ready to LINQPad 5.
66 |
67 | -----
68 |
69 | ## Environments
70 |
71 | * F# 6.0 or higher/4.5
72 | * .NET 6.0
73 | * .NET 5.0
74 | * .NET Core 3.0/2.0 or higher
75 | * .NET Standard 1.6/2.0/2.1
76 | * .NET Framework 4.5/4.6.1/4.8
77 |
78 | Combination chart:
79 |
80 | | .NET BCL | F# | Details |
81 | |:----|:----|:----|
82 | | .NET 6.0 | F# 6.0 or higher | |
83 | | .NET 5.0 | F# 6.0 or higher | |
84 | | .NET Core 3.1, 3.0 | F# 6.0 or higher | (3.0 is deprecated) |
85 | | .NET Core 2.,2 2.1, 2.0 | F# 6.0 or higher | (2.0 is deprecated) |
86 | | .NET Standard 2.1, 2.0 | F# 6.0 or higher | |
87 | | .NET Standard 1.6 | F# 4.5 | |
88 | | .NET Framework 4.8, 4.6.1 | F# 6.0 or higher | |
89 | | .NET Framework 4.5 | F# 4.5 | |
90 |
91 | -----
92 |
93 | ## How to use
94 |
95 | * Search NuGet package and install "FSharp.Control.FusionTasks".
96 | * F# use, autoopen'd namespace "FSharp.Control". "System.Threading.Tasks" is optional.
97 | * C# use, using namespace "System.Threading.Tasks". "Microsoft.FSharp.Control" is optional.
98 |
99 | ## Samples
100 |
101 | ### Basic async workflow:
102 |
103 | ``` fsharp
104 | let asyncTest = async {
105 | use ms = new MemoryStream()
106 |
107 | // FusionTasks directly interpreted System.Threading.Tasks.Task class in F# async-workflow block.
108 | // Sure, non-generic Task mapping to Async.
109 | do! ms.WriteAsync(data, 0, data.Length)
110 | do ms.Position <- 0L
111 |
112 | // FusionTasks directly interpreted System.Threading.Tasks.Task class in F# async-workflow block.
113 | // Standard usage, same as manually used Async.AwaitTask.
114 | let! length = ms.ReadAsync(data2, 0, data2.Length)
115 | do length |> should equal data2.Length
116 | }
117 | ```
118 |
119 | ### Without async workflow:
120 |
121 | ``` fsharp
122 | use ms = new MemoryStream()
123 |
124 | // Manually conversion by an operator "Async.AsAsync" : Task --> Async<'T>
125 | let asy = ms.ReadAsync(data, 0, data.Length) |> Async.AsAsync
126 | let length = asy |> Async.RunSynchronosly
127 | ```
128 |
129 | ### Without async workflow (CancellationToken):
130 |
131 | ``` fsharp
132 | use ms = new MemoryStream()
133 | let cts = new CancellationTokenSource()
134 |
135 | // Produce with CancellationToken:
136 | // TIPS: FusionTasks cannot handle directly CancellationToken IN ASYNC WORKFLOW.
137 | // Because async workflow semantics implicitly handled CancellationToken with Async.DefaultCancellationToken, CancellationToken and CancelDefaultToken().
138 | // (CancellationToken derived from Async.StartWithContinuations() in async workflow.)
139 | let asy = ms.ReadAsync(data, 0, data.Length).AsAsync(cts.Token)
140 | let length = asy |> Async.RunSynchronosly
141 | ```
142 |
143 | ### Handle Task.ConfigureAwait(...) (Capture/release SynchContext):
144 |
145 | ``` fsharp
146 | let asyncTest = async {
147 | use ms = new MemoryStream(...)
148 |
149 | // We can use ConfigureAwait() on let!/do!.
150 | let! length = ms.ReadAsync(data, 0, data.Length).ConfigureAwait(false)
151 | }
152 | ```
153 |
154 | NOTE: Older released contains `AsyncConfigure(bool)` method, but it was obsoleted.
155 | Because it existed for avoiding PCL strange linking errors.
156 |
157 | ### Delegate async continuation - works like TaskCompletionSource<T>:
158 |
159 | ``` fsharp
160 | open System.Threading
161 |
162 | let asyncCalculate() =
163 | // Create AsyncCompletionSource<'T>.
164 | let acs = new AsyncCompletionSource()
165 |
166 | // Execution with completely independent another thread...
167 | let thread = new Thread(new ThreadStart(fun _ ->
168 | Thread.Sleep(5000)
169 | // If you captured thread context (normally continuation or callbacks),
170 | // can delegation async continuation using AsyncCompletionSource<'T>.
171 | acs.SetResult(123 * 456)))
172 | thread.Start()
173 |
174 | // Async<'T> instance
175 | acs.Async
176 | ```
177 |
178 | ### Standard asynchronous sequence IAsyncEnumerable<T>:
179 |
180 | ``` fsharp
181 | let asyncTest = async {
182 | // FusionTasks directly interpreted System.Collection.Generic.IAsyncEnumerable in
183 | // F# async-workflow for expression.
184 | for value in FooBarAccessor.EnumerableAsync() do
185 | // Totally asynchronous operation in each asynchronous iteration:
186 | let! result = value |> FooBarCollector.calculate
187 | do! output.WriteAsync(result)
188 |
189 | // ... (Continuation is asynchronously behind `for` loop)
190 | }
191 | ```
192 |
193 | And, we can use `IAsyncEnumerable.ConfigureAwait(bool)` on it.
194 |
195 | NOTE: `IAsyncEnumerable` is supported only these environments:
196 |
197 | * net461 or higher.
198 | * netstandard2.0 or higher.
199 | * netcoreapp2.1 or higher.
200 |
201 | It limitation comes from [NuGet Microsoft.Bcl.AsyncInterfaces 5.0.0.](https://www.nuget.org/packages/Microsoft.Bcl.AsyncInterfaces/)
202 |
203 | ### Standard asynchronous disposer IAsyncDisposable:
204 |
205 | ``` fsharp
206 | let asyncTest = async {
207 | // FusionTasks directly interpreted System.IAsyncDisposable in
208 | // F# async-workflow use expression.
209 | // TIP: We can use `use` expression instead of `use!`,
210 | // Because the `use!` will be bound asynchronously BEFORE calling `DisposeAsync()`.
211 | use accessor = DatabaseAccessor.getAsyncDisposableAccessor()
212 |
213 | // (Use accessor...)
214 |
215 | // (Will be disposed asynchronously, calls `DisposeAsync()` at end of scope...)
216 | }
217 | ```
218 |
219 | ### TIPS: We have to add annotation for arguments if using it in async workflow:
220 |
221 | ``` fsharp
222 | let asyncInner arg0 = async {
223 | // Cause FS0041:
224 | // A unique overload for method 'Source' could not be determined based on type information prior to this program point.
225 | // A type annotation may be needed.
226 | // --> Because F# compiler conflict arg0 type inferences: Async or Task.
227 | let! result = arg0
228 | let calculated = result + 1
229 | printfn "%d" calculated
230 | }
231 |
232 | // Fixed with type annotation Async<'T> or Task<'T>:
233 | let asyncInner (arg0:Async<_>) = async {
234 | let! result = arg0
235 | let calculated = result + 1
236 | printfn "%d" calculated
237 | }
238 | ```
239 |
240 | ### In C# side:
241 |
242 | * Really need sample codes? huh? :)
243 |
244 | -----
245 |
246 | ### Easy LINQPad 5 driven:
247 |
248 | * Before setup NuGet package (FSharp.Control.FusionTasks) the LINQPad NuGet Manager.
249 |
250 | ``` fsharp
251 | open System.IO
252 |
253 | // Result is Async
254 | let asyncSequenceData =
255 | let r = new Random()
256 | let data = [| for i = 1 to 100 do yield byte (r.Next()) |]
257 | async {
258 | use fs = new MemoryStream()
259 | do! fs.WriteAsync(data, 0, data.Length)
260 | do! fs.FlushAsync()
261 | return fs.ToArray()
262 | }
263 |
264 | // Convert to Task and dump:
265 | asyncSequenceData.AsTask().Dump()
266 | ```
267 |
268 | 
269 |
270 | -----
271 |
272 | ## "task-like" and ValueTask appendix
273 |
274 | * .NET add new "task-like" type. "task-like" means applied a attribute "System.Runtime.CompilerServices.AsyncMethodBuilderAttribute" and declared the async method builder.
275 | * ValueTask overview:
276 | * New standard "task-like" type named for "ValueTask<T>" for C#. FusionTasks supported ValueTask<T> on 1.0.20.
277 | * ValueTask<T> declared by struct (Value type) for goal is improvement performance. But this type has the Task<T> instance inside and finally continuation handle by Task<T>.
278 | * ValueTask<T> performance effective situation maybe chatty-call fragments using both caller C# and awaiter C# code...
279 | * ValueTask<T> a little bit or no effect improvement performance, because usage of senario for FusionTasks.
280 | * "task-like" augumenting is difficult:
281 | * We have to apply to task-like type with the attribute "AsyncMethodBuilderAttribute".
282 | * Means if already declared type (Sure, we have FSharpAsync<'T>) cannot augument and cannot turn to task-like type.
283 | * Therefore cannot directly return for FSharpAsync<'T> from C#'s async-await method.
284 | * And cannot auto handle task-like type by FusionTasks, because no type safe declaration for task-like type...
285 | * For example, if force support task-like type, FusionTasks require augument "Source: taskLike: obj -> FSharpAsync<'T>" overload on FSharpAsync<'T>. This cannot type safe.
286 | * Conclusion:
287 | * So FusionTasks support only "ValueTask<T>" type and cannot support any other "task-like" types.
288 |
289 | ## Additional resources
290 |
291 | * Source codes available only "FSharp.Control.FusionTasks" folder.
292 | * The slides: "How to meets Async and Task" in Seattle F# Users group "MVP Summit Special: A Night of Lightning Talks" 2016.11.09 http://www.slideshare.net/kekyo/asyncs-vs-tasks
293 |
294 |
295 |
296 | ## TODO
297 |
298 | Improvements more easier/effective interfaces.
299 |
300 | -----
301 |
302 | ## License
303 |
304 | * Copyright (c) 2016-2022 Kouji Matsui (@kozy_kekyo)
305 | * Under Apache v2 http://www.apache.org/licenses/LICENSE-2.0
306 |
307 | ## History
308 |
309 | * 2.6.0:
310 | * Final version. Thank you using FusionTasks!
311 | * You are ready to use both FusionTasks and F# 7.0! See [issue #14](https://github.com/kekyo/FSharp.Control.FusionTasks/issues/14).
312 | * Added supporting .NET Core 2.2 environments.
313 | * Minimized package dependency.
314 | * 2.5.0:
315 | * Supported .NET 6.0 and F# 6.0 environment.
316 | * 2.4.0:
317 | * Supported varies for operator `Async.AsAsync`.
318 | * Completed supporting configured capturing context method `ConfigureAwait(bool)` on all Task based instances.
319 | * 2.3.3:
320 | * Supported .NET asynchronous disposer (`IAsyncDisposable`).
321 | * Supported releasing synchronization context by `IAsyncEnumerable.ConfigureAwait(bool)`.
322 | * Fixed minor exception leaking at the continuation for asynchronous sequence.
323 | * 2.3.0:
324 | * Supported .NET asynchronous sequence (`IAsyncEnumerable` and essential types).
325 | * 2.2.0:
326 | * Suppressed Task/ValueTask allocation when they were already completed (#12, @danielmarbach)
327 | * 2.1.1:
328 | * Downgraded FSharp.Core requirements from 5.0.1 to 5.0.0.
329 | * 2.1.0:
330 | * Added .NET 5, .NET Core 3 and .NET Framework 4.8 assemblies.
331 | * Fixed capturing synchronization context at the asynchronous continuations.
332 | * 2.0.2:
333 | * Fixed add xml comments into package.
334 | * 2.0.1:
335 | * Add support ValueTask for non-generic version.
336 | * Fixed XML comments.
337 | * 2.0.0:
338 | * Supported F# 4.5, .NET Standard 2.0 and .NET Core 2.0.
339 | * Sorry, archived all PCL's libraries. Now FusionTasks supports only .NET Framework 4.5, .NET Core 2.0 and .NET Standard 1.6/2.0.
340 | * Solution structure refablished. Changed to .NET Core modern-style.
341 | * 1.1.1:
342 | * Add ValueTask<'T> bind source overload.
343 | * 1.1.0:
344 | * Supported F# 4.1 and .NET Standard 1.6. (Unfortunately deprecated FS40.netcore (netstandard1.4) package, try to migrate to F# 4.1 :)
345 | * 1.0.20:
346 | * Support ValueTask<T> (Exclude net40 and Profile 47 platform, added dependency for System.Threading.Tasks.Extensions).
347 | * Update version for .NET Core F# (1.0.0-alpha-161205).
348 | * 1.0.13:
349 | * Reduce to only contains .NET Core's assembly in FS40.netcore package.
350 | * Refactor folder structures.
351 | * 1.0.12:
352 | * Add .NET Core support (Separated package: FSharp.Control.FusionTasks.FS40.netcore with -Pre option required)
353 | * 1.0.2:
354 | * Support 'for .. in' expressions. (Thx Armin!)
355 | * 1.0.1:
356 | * Fixed cause undefined Async<'T> using combination Async<'T> and Task/Task<T> in async workflow. (Thx Honza!)
357 | * 1.0.0:
358 | * RTM release :clap:
359 | * Add FSharp.Core NuGet references.
360 | * Temporary disable support .NET Core. If reached F# RTM, continue development... (PR welcome!!)
361 | * Add sample codes.
362 | * 0.9.6:
363 | * WIP release.
364 | * 0.9.5:
365 | * WIP release.
366 | * 0.9.4:
367 | * Fixed nuspec reference System, System.Core
368 | * 0.9.3:
369 | * Fixed nuspec frameworkAssemblies.
370 | * 0.9.2:
371 | * Add package targetFramework.
372 | * Updated RelaxVersioner.
373 | * 0.9.1:
374 | * Remove strongly-signed (Unit test doesn't work...)
375 | * Omit synchronizers (AsyncLock, AsyncLazy). Thats moving to FSharp.Control.AsyncPrimitives project (https://github.com/kekyo/FSharp.Control.AsyncPrimitives).
376 | * Add target dnxcore50 into F# 4.0 (for .NET Core 1.0)
377 | * Source codes and documents bit changed.
378 | * 0.5.8:
379 | * Add strongly-signed.
380 | * 0.5.7:
381 | * Add PCL Profile 7.
382 | * 0.5.6:
383 | * Add PCL Profile 78.
384 | * Fixed minor PCL moniker fragments.
385 | * 0.5.5:
386 | * Fixed version number.
387 | * Fixed icon image url.
388 | * 0.5.4:
389 | * Auto open FSharp.Control.
390 | * Manage AppVeyor CI.
391 | * 0.5.3: Implement awaiter classes.
392 | * 0.5.2: Add dependency assemblies.
393 | * 0.5.1: NuGet package support.
394 |
--------------------------------------------------------------------------------
/build-nupkg.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | rem FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | rem Copyright (c) 2016-2018 Kouji Matsui (@kozy_kekyo)
5 | rem
6 | rem Licensed under the Apache License, Version 2.0 (the "License");
7 | rem you may not use this file except in compliance with the License.
8 | rem You may obtain a copy of the License at
9 | rem
10 | rem http://www.apache.org/licenses/LICENSE-2.0
11 | rem
12 | rem Unless required by applicable law or agreed to in writing, software
13 | rem distributed under the License is distributed on an "AS IS" BASIS,
14 | rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | rem See the License for the specific language governing permissions and
16 | rem limitations under the License.
17 |
18 | dotnet pack --configuration Release --output artifacts FSharp.Control.FusionTasks\FSharp.Control.FusionTasks.fsproj
19 |
--------------------------------------------------------------------------------
/build-nupkg.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # FSharp.Control.FusionTasks - F# Async workflow <--> .NET Task easy seamless interoperability library.
4 | # Copyright (c) 2016-2021 Kouji Matsui (@kozy_kekyo)
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | dotnet pack --configuration Release --output artifacts FSharp.Control.FusionTasks/FSharp.Control.FusionTasks.fsproj
19 |
--------------------------------------------------------------------------------