mirror of
https://github.com/Cysharp/UniTask.git
synced 2026-05-15 03:20:16 +00:00
Compare commits
217 Commits
2.0.11-rc8
...
2.0.33
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9313969314 | ||
|
|
a40f89a922 | ||
|
|
e0d8410b62 | ||
|
|
bef1bd8ad1 | ||
|
|
81b4fcfac1 | ||
|
|
f1e4a3c65d | ||
|
|
65622b01f6 | ||
|
|
87dd5f13fd | ||
|
|
79cd2c17ba | ||
|
|
85fb08552e | ||
|
|
7bd4b6faf7 | ||
|
|
0b1ae7e295 | ||
|
|
35a893ad9e | ||
|
|
4955ed18f1 | ||
|
|
fe462328ab | ||
|
|
5136d92efa | ||
|
|
227f7872cb | ||
|
|
725b2fdc35 | ||
|
|
75abc8059f | ||
|
|
f1193743c8 | ||
|
|
109730eacd | ||
|
|
1f736afe86 | ||
|
|
4d554a6718 | ||
|
|
69c0c362e9 | ||
|
|
3bb446556a | ||
|
|
ea950d8cec | ||
|
|
a65f4da7a2 | ||
|
|
0bdc933c20 | ||
|
|
0c0f79c6db | ||
|
|
32f9b9d4ac | ||
|
|
53907a3719 | ||
|
|
4937aeee3f | ||
|
|
5e5b8aff89 | ||
|
|
a2cbbd82d0 | ||
|
|
7eac5d8ba8 | ||
|
|
e2b1ed55ae | ||
|
|
727c7102d3 | ||
|
|
1494ea6717 | ||
|
|
f1ce64dbd3 | ||
|
|
3bad5cd2bf | ||
|
|
7432c0073a | ||
|
|
f1813a7c94 | ||
|
|
9e45c0a4d1 | ||
|
|
2c652cdde7 | ||
|
|
7718d345c8 | ||
|
|
9f39708325 | ||
|
|
bb6dbfa920 | ||
|
|
ba265005bb | ||
|
|
4d7cc7ed61 | ||
|
|
b64f31eb0b | ||
|
|
38d159b69e | ||
|
|
d5455f3716 | ||
|
|
a72ceeba11 | ||
|
|
c6b7d332b2 | ||
|
|
f37278f2a6 | ||
|
|
3f3e03b83d | ||
|
|
c99d3eb3c3 | ||
|
|
08d5183e7e | ||
|
|
51769b2224 | ||
|
|
6ec0ed8d61 | ||
|
|
2e35324403 | ||
|
|
e9474649c4 | ||
|
|
a8e0ce50c8 | ||
|
|
db7ddba735 | ||
|
|
1999d94b33 | ||
|
|
44af123b6c | ||
|
|
547b700ba7 | ||
|
|
6b87d5d2b0 | ||
|
|
023894d45e | ||
|
|
009715c0da | ||
|
|
c2824027d4 | ||
|
|
65b6553a1a | ||
|
|
9d3b7adc8e | ||
|
|
3724fc204c | ||
|
|
b97451a915 | ||
|
|
9ddcac4c6c | ||
|
|
b61e3c347f | ||
|
|
0bb44066c0 | ||
|
|
305c4aaa07 | ||
|
|
42d627f3ba | ||
|
|
6dd2b464a3 | ||
|
|
32f24cf8f8 | ||
|
|
4a89e3ea86 | ||
|
|
887db5b281 | ||
|
|
fee5518a82 | ||
|
|
551128e64c | ||
|
|
c65ae8d18e | ||
|
|
c1f75d9ebd | ||
|
|
73a5ff6648 | ||
|
|
1c264f380e | ||
|
|
f02bfa0a1e | ||
|
|
9d684006fc | ||
|
|
c49f1ed028 | ||
|
|
d935b226c0 | ||
|
|
d9e20de8a5 | ||
|
|
23997f0f93 | ||
|
|
529272d11b | ||
|
|
1194c38568 | ||
|
|
f0d2ee2beb | ||
|
|
68cdda086a | ||
|
|
54ceca6ceb | ||
|
|
50bdf7460c | ||
|
|
c06e45d0bb | ||
|
|
3ed6e28a00 | ||
|
|
ab76098895 | ||
|
|
0a447e43b0 | ||
|
|
8df44f2768 | ||
|
|
a7ec64d644 | ||
|
|
868e104d85 | ||
|
|
7ced7f5764 | ||
|
|
12b39c6ba1 | ||
|
|
93df9d7693 | ||
|
|
3980f314fa | ||
|
|
c2538da1cd | ||
|
|
5ed943bca2 | ||
|
|
d6a0563319 | ||
|
|
af82a94b87 | ||
|
|
82219e6111 | ||
|
|
81f9c55c7f | ||
|
|
0640f278cc | ||
|
|
769b5c6bab | ||
|
|
bdd569e213 | ||
|
|
5bfff5bc24 | ||
|
|
edf32496e4 | ||
|
|
785f5837d1 | ||
|
|
8c9272bc9f | ||
|
|
3e00735b3d | ||
|
|
00a1be8666 | ||
|
|
a2783d3c8a | ||
|
|
85d1a8a4a4 | ||
|
|
bbfb8354bb | ||
|
|
2f68e47443 | ||
|
|
89339ffb29 | ||
|
|
0535862fe6 | ||
|
|
a9e5fd4589 | ||
|
|
a3f3a28ea1 | ||
|
|
59020df965 | ||
|
|
ac01be79bf | ||
|
|
de5951f208 | ||
|
|
ded9a561db | ||
|
|
a2c18eb343 | ||
|
|
0b27c3a342 | ||
|
|
9c86cfb508 | ||
|
|
7e5e6ed6c2 | ||
|
|
d081e5f40b | ||
|
|
344ae0738c | ||
|
|
1b553f67b0 | ||
|
|
bf0adad427 | ||
|
|
11ca42a527 | ||
|
|
4898e4c7bf | ||
|
|
d494e0b9e3 | ||
|
|
be26ab249b | ||
|
|
611d8d5513 | ||
|
|
95c93b7c3d | ||
|
|
1dd0c49eec | ||
|
|
5d8e0e61ad | ||
|
|
478126e256 | ||
|
|
80704e489d | ||
|
|
3c0aa03643 | ||
|
|
37cd00d347 | ||
|
|
859c4d706f | ||
|
|
7289fe6e25 | ||
|
|
0c33977f5a | ||
|
|
4d4466e801 | ||
|
|
79330d7cdb | ||
|
|
680ce1098b | ||
|
|
2337d705ec | ||
|
|
d2880a818f | ||
|
|
86ea128bf4 | ||
|
|
a66f378622 | ||
|
|
265f88584b | ||
|
|
686394c861 | ||
|
|
8bad158ab4 | ||
|
|
5e59e7ec86 | ||
|
|
8bb0a48720 | ||
|
|
b4468b4eba | ||
|
|
0725bd1b30 | ||
|
|
ebd80243e0 | ||
|
|
f1ac469058 | ||
|
|
2e0b603d25 | ||
|
|
1d90a40f66 | ||
|
|
1bec3f507e | ||
|
|
1d5ecbb3ab | ||
|
|
e1a4aeb9da | ||
|
|
43f1bb4d85 | ||
|
|
8b7a0e9b15 | ||
|
|
da329e19d1 | ||
|
|
b8260d4e91 | ||
|
|
345e32aaf0 | ||
|
|
d7bef8c5b5 | ||
|
|
b2f82df4d3 | ||
|
|
83596b3d1f | ||
|
|
0022598a1c | ||
|
|
f4294d3752 | ||
|
|
a1444c0b39 | ||
|
|
e1d5359d73 | ||
|
|
239bf749b6 | ||
|
|
2bf3b1e172 | ||
|
|
d225de201f | ||
|
|
c3b8a3852d | ||
|
|
c31dab888e | ||
|
|
d4cf59bd2f | ||
|
|
d5edc3acd3 | ||
|
|
130286e8c2 | ||
|
|
a9baa52309 | ||
|
|
3001996298 | ||
|
|
bfcd18aabb | ||
|
|
96aa299e7e | ||
|
|
24faa34418 | ||
|
|
21bf08a6b3 | ||
|
|
d5db96b913 | ||
|
|
a8455af16d | ||
|
|
2290b14532 | ||
|
|
90c5a6311b | ||
|
|
6e0ad3623b | ||
|
|
005e02a1fa | ||
|
|
10fb8060fa |
14
.github/workflows/build-debug.yml
vendored
14
.github/workflows/build-debug.yml
vendored
@@ -26,9 +26,10 @@ jobs:
|
||||
- run: dotnet test -c Debug ./src/UniTask.NetCoreTests/UniTask.NetCoreTests.csproj
|
||||
|
||||
build-unity:
|
||||
if: "(github.event == 'push' && github.repository_owner == 'Cysharp') || startsWith(github.event.pull_request.head.label, 'Cysharp:')"
|
||||
strategy:
|
||||
matrix:
|
||||
unity: ['2019.3.9f1', '2020.1.0b5']
|
||||
unity: ["2019.3.9f1", "2020.1.0b5"]
|
||||
include:
|
||||
- unity: 2019.3.9f1
|
||||
license: UNITY_2019_3
|
||||
@@ -41,13 +42,6 @@ jobs:
|
||||
steps:
|
||||
- run: apt update && apt install git -y
|
||||
- uses: actions/checkout@v2
|
||||
# create unity activation file and store to artifacts.
|
||||
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -logFile -createManualActivationFile || exit 0
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: Unity_v${{ matrix.unity }}.alf
|
||||
path: ./Unity_v${{ matrix.unity }}.alf
|
||||
# activate Unity from manual license file(ulf)
|
||||
- run: echo -n "$UNITY_LICENSE" >> .Unity.ulf
|
||||
env:
|
||||
UNITY_LICENSE: ${{ secrets[matrix.license] }}
|
||||
@@ -66,8 +60,8 @@ jobs:
|
||||
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||
working-directory: src/UniTask
|
||||
|
||||
# Store artifacts.
|
||||
# Store artifacts.
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: UniTask.unitypackage.zip
|
||||
path: ./src/UniTask/*.unitypackage
|
||||
path: ./src/UniTask/*.unitypackage
|
||||
|
||||
31
.github/workflows/build-docs.yml
vendored
Normal file
31
.github/workflows/build-docs.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: build-docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- feature/docs
|
||||
|
||||
jobs:
|
||||
run-docfx:
|
||||
if: "!(contains(github.event.head_commit.message, '[skip ci]') || contains(github.event.head_commit.message, '[ci skip]'))"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
repository: Cysharp/DocfxTemplate
|
||||
path: docs/_DocfxTemplate
|
||||
- uses: Kirbyrawr/docfx-action@master
|
||||
name: Docfx metadata
|
||||
with:
|
||||
args: metadata docs/docfx.json
|
||||
- uses: Kirbyrawr/docfx-action@master
|
||||
name: Docfx build
|
||||
with:
|
||||
args: build docs/docfx.json
|
||||
- name: Publish to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: docs/_site
|
||||
27
.github/workflows/build-release.yml
vendored
27
.github/workflows/build-release.yml
vendored
@@ -20,18 +20,17 @@ jobs:
|
||||
# set release tag(*.*.*) to env.GIT_TAG
|
||||
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
|
||||
|
||||
# build CommandTools first (use dotnet run command in ZLogger.csproj)
|
||||
- run: dotnet build -c Release ./tools/CommandTools/CommandTools.csproj
|
||||
# build and pack
|
||||
- run: dotnet build -c Release -p:Version=${{ env.GIT_TAG }}
|
||||
- run: dotnet test -c Release --no-build
|
||||
- run: dotnet pack ./src/ZLogger/ZLogger.csproj -c Release --no-build -p:Version=${{ env.GIT_TAG }}
|
||||
- run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ env.GIT_TAG }}
|
||||
|
||||
# Store artifacts.
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: nuget
|
||||
path: ./src/ZLogger/bin/Release/ZLogger.${{ env.GIT_TAG }}.nupkg
|
||||
|
||||
path: ./src/UniTask.NetCore/bin/Release/UniTask.${{ env.GIT_TAG }}.nupkg
|
||||
|
||||
build-unity:
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -57,15 +56,13 @@ jobs:
|
||||
# Execute scripts: Export Package
|
||||
- name: Export unitypackage
|
||||
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||
working-directory: src/ZLogger.Unity
|
||||
env:
|
||||
UNITY_PACKAGE_VERSION: ${{ env.GIT_TAG }}
|
||||
working-directory: src/UniTask
|
||||
|
||||
# Store artifacts.
|
||||
- uses: actions/upload-artifact@v1
|
||||
# Store artifacts.
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ZLogger.Unity.${{ env.GIT_TAG }}.unitypackage
|
||||
path: ./src/ZLogger.Unity/ZLogger.Unity.${{ env.GIT_TAG }}.unitypackage
|
||||
name: UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
path: ./src/UniTask/UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
|
||||
create-release:
|
||||
needs: [build-dotnet, build-unity]
|
||||
@@ -91,7 +88,7 @@ jobs:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Ver.${{ github.ref }}
|
||||
|
||||
# Download (All) Artifacts to current directory
|
||||
# Download(All) Artifacts to current directory
|
||||
- uses: actions/download-artifact@v2-preview
|
||||
|
||||
# Upload to NuGet
|
||||
@@ -103,6 +100,6 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./ZLogger.Unity.${{ env.GIT_TAG }}.unitypackage/ZLogger.Unity.${{ env.GIT_TAG }}.unitypackage
|
||||
asset_name: ZLogger.Unity.${{ env.GIT_TAG }}.unitypackage
|
||||
asset_path: ./UniTask.${{ env.GIT_TAG }}.unitypackage/UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
asset_name: UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
asset_content_type: application/octet-stream
|
||||
98
.gitignore
vendored
98
.gitignore
vendored
@@ -157,3 +157,101 @@ src/UniTask/UniTask.Tests.csproj
|
||||
src/UniTask/UniTask.Tests.Editor.csproj
|
||||
|
||||
src/UniTask/UniTask.*.unitypackage
|
||||
|
||||
src/UniTask/UniTask.Linq.csproj
|
||||
|
||||
src/UniTask/DOTween.Modules.csproj
|
||||
|
||||
src/UniTask/Unity.Addressables.csproj
|
||||
|
||||
src/UniTask/Unity.Addressables.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.Analytics.DataPrivacy.csproj
|
||||
|
||||
src/UniTask/Unity.Recorder.csproj
|
||||
|
||||
src/UniTask/Unity.Recorder.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.ResourceManager.csproj
|
||||
|
||||
src/UniTask/Unity.Rider.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.ScriptableBuildPipeline.csproj
|
||||
|
||||
src/UniTask/Unity.ScriptableBuildPipeline.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.TextMeshPro.csproj
|
||||
|
||||
src/UniTask/Unity.TextMeshPro.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.Timeline.csproj
|
||||
|
||||
src/UniTask/Unity.Timeline.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.VisualStudio.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.VSCode.Editor.csproj
|
||||
|
||||
src/UniTask/UnityEditor.CacheServer.csproj
|
||||
|
||||
src/UniTask/UnityEditor.TestRunner.csproj
|
||||
|
||||
src/UniTask/UnityEditor.UI.csproj
|
||||
|
||||
src/UniTask/UnityEngine.Advertisements.csproj
|
||||
|
||||
src/UniTask/UnityEngine.Monetization.csproj
|
||||
|
||||
src/UniTask/UnityEngine.TestRunner.csproj
|
||||
|
||||
src/UniTask/UnityEngine.UI.csproj
|
||||
|
||||
src/UniTask/TempAsm.csproj
|
||||
|
||||
src/UniTask/UniTask.Addressables.csproj
|
||||
|
||||
src/UniTask/UniTask.DOTween.csproj
|
||||
|
||||
src/UniTask/UniTask.TextMeshPro.csproj
|
||||
|
||||
src/UniTask/RuntimeUnitTestToolkit.Player.csproj
|
||||
|
||||
src/UniTask/TempAsm.Player.csproj
|
||||
|
||||
src/UniTask/UniTask.Addressables.Player.csproj
|
||||
|
||||
src/UniTask/UniTask.DOTween.Player.csproj
|
||||
|
||||
src/UniTask/UniTask.Linq.Player.csproj
|
||||
|
||||
src/UniTask/UniTask.Player.csproj
|
||||
|
||||
src/UniTask/UniTask.Tests.Player.csproj
|
||||
|
||||
src/UniTask/UniTask.TextMeshPro.Player.csproj
|
||||
|
||||
src/UniTask/Unity.Addressables.Player.csproj
|
||||
|
||||
src/UniTask/Unity.Analytics.DataPrivacy.Player.csproj
|
||||
|
||||
src/UniTask/Unity.ResourceManager.Player.csproj
|
||||
|
||||
src/UniTask/Unity.ScriptableBuildPipeline.Player.csproj
|
||||
|
||||
src/UniTask/Unity.TextMeshPro.Player.csproj
|
||||
|
||||
src/UniTask/Unity.Timeline.Player.csproj
|
||||
|
||||
src/UniTask/UnityEngine.Advertisements.Player.csproj
|
||||
|
||||
src/UniTask/UnityEngine.Monetization.Player.csproj
|
||||
|
||||
src/UniTask/UnityEngine.TestRunner.Player.csproj
|
||||
|
||||
src/UniTask/UnityEngine.UI.Player.csproj
|
||||
|
||||
src/UniTask/DOTween.Modules.Player.csproj
|
||||
|
||||
src/UniTask/Assembly-CSharp.Player.csproj
|
||||
|
||||
src/UniTask/Unity.EditorCoroutines.Editor.csproj
|
||||
|
||||
795
README.md
795
README.md
@@ -2,68 +2,115 @@ UniTask
|
||||
===
|
||||
[](https://github.com/Cysharp/UniTask/actions) [](https://github.com/Cysharp/UniTask/releases)
|
||||
|
||||
Provides an efficient async/await integration to Unity.
|
||||
Provides an efficient allocation free async/await integration to Unity.
|
||||
|
||||
* Struct based `UniTask<T>` and custom AsyncMethodBuilder to achive zero allocation
|
||||
* All Unity AsyncOperations and Coroutine to awaitable
|
||||
* PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc..) that enable to replace all coroutine operation
|
||||
* MonoBehaviour Message Events and uGUI Events as awaitable/async-enumerable
|
||||
* Completely run on Unity's PlayerLoop so don't use thread and run on WebGL, wasm, etc.
|
||||
* Asynchronous LINQ, with Channel and AsyncReactiveProperty
|
||||
* TaskTracker window to prevent memory leak
|
||||
* Highly compatible behaviour with Task/ValueTask/IValueTaskSource
|
||||
|
||||
Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await for Unity, with Asynchronous LINQ
|
||||
](https://medium.com/@neuecc/unitask-v2-zero-allocation-async-await-for-unity-with-asynchronous-linq-1aa9c96aa7dd)
|
||||
Advanced tips, see blog post: [Extends UnityWebRequest via async decorator pattern — Advanced Techniques of UniTask](https://medium.com/@neuecc/extends-unitywebrequest-via-async-decorator-pattern-advanced-techniques-of-unitask-ceff9c5ee846)
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
## Table of Contents
|
||||
|
||||
- [Getting started](#getting-started)
|
||||
- [`UniTask<T>`](#unitaskt)
|
||||
- [Basics of UniTask and AsyncOperation](#basics-of-unitask-and-asyncoperation)
|
||||
- [Cancellation and Exception handling](#cancellation-and-exception-handling)
|
||||
- [Progress](#progress)
|
||||
- [PlayerLoop](#playerloop)
|
||||
- [async void vs async UniTaskVoid](#async-void-vs-async-unitaskvoid)
|
||||
- [UniTaskTracker](#unitasktracker)
|
||||
- [Reusable Promises](#reusable-promises)
|
||||
- [awaitable Events](#awaitable-events)
|
||||
- [async void vs async UniTask/UniTaskVoid](#async-void-vs-async-unitaskunitaskvoid)
|
||||
- [External Assets](#external-assets)
|
||||
- [AsyncEnumerable and Async LINQ](#asyncenumerable-and-async-linq)
|
||||
- [Awaitable Events](#awaitable-events)
|
||||
- [Channel](#channel)
|
||||
- [For Unit Testing](#for-unit-testing)
|
||||
- [Method List](#method-list)
|
||||
- [ThreadPool limitation](#threadpool-limitation)
|
||||
- [IEnumerator.ToUniTask limitation](#ienumeratortounitask-limitation)
|
||||
- [For UnityEditor](#for-unityeditor)
|
||||
- [Compare with Standard Task API](#compare-with-standard-task-api)
|
||||
- [Pooling Configuration](#pooling-configuration)
|
||||
- [Allocation on Profiler](#allocation-on-profiler)
|
||||
- [UniTaskSynchronizationContext](#unitasksynchronizationcontext)
|
||||
- [API References](#api-references)
|
||||
- [UPM Package](#upm-package)
|
||||
- [ECS, PlayerLoop](#ecs-playerloop)
|
||||
- [Install via git URL](#install-via-git-url)
|
||||
- [Install via OpenUPM](#install-via-openupm)
|
||||
- [.NET Core](#net-core)
|
||||
- [License](#license)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
|
||||
Getting started
|
||||
---
|
||||
Install package(`UniRx.Async.unitypackage`) is available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases) page.
|
||||
Install via [UPM package](#upm-package) or asset package(`UniTask.*.*.*.unitypackage`) available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases) page.
|
||||
|
||||
```csharp
|
||||
// extension awaiter/methods can be used by this namespace
|
||||
using UniRx.Async;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
// You can return type as struct UniTask<T>(or UniTask), it is unity specialized lightweight alternative of Task<T>
|
||||
// no(or less) allocation and fast excution for zero overhead async/await integrate with Unity
|
||||
// zero allocation and fast excution for zero overhead async/await integrate with Unity
|
||||
async UniTask<string> DemoAsync()
|
||||
{
|
||||
// You can await Unity's AsyncObject
|
||||
var asset = await Resources.LoadAsync<TextAsset>("foo");
|
||||
|
||||
// .ConfigureAwait accepts progress callback
|
||||
await SceneManager.LoadSceneAsync("scene2").ConfigureAwait(Progress.Create<float>(x => Debug.Log(x)));
|
||||
|
||||
var txt = (await UnityWebRequest.Get("https://...").SendWebRequest()).downloadHandler.text;
|
||||
await SceneManager.LoadSceneAsync("scene2");
|
||||
|
||||
// .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
|
||||
var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy());
|
||||
|
||||
// .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
|
||||
var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x)));
|
||||
|
||||
// await frame-based operation like coroutine
|
||||
await UniTask.DelayFrame(100);
|
||||
|
||||
// replacement of WaitForSeconds/WaitForSecondsRealtime
|
||||
// replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false);
|
||||
|
||||
// replacement of WaitForEndOfFrame(or other timing like yield return null, yield return WaitForFixedUpdate)
|
||||
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||
// yield any playerloop timing(PreUpdate, Update, LateUpdate, etc...)
|
||||
await UniTask.Yield(PlayerLoopTiming.PreLateUpdate);
|
||||
|
||||
// replacement of yield return null
|
||||
await UniTask.Yield();
|
||||
await UniTask.NextFrame();
|
||||
|
||||
// replacement of WaitForEndOfFrame(same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate))
|
||||
await UniTask.WaitForEndOfFrame();
|
||||
|
||||
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
|
||||
await UniTask.WaitForFixedUpdate();
|
||||
|
||||
// replacement of yield return WaitUntil
|
||||
await UniTask.WaitUntil(() => isActive == false);
|
||||
|
||||
// special helper of WaitUntil
|
||||
await UniTask.WaitUntilValueChanged(this, x => x.isActive);
|
||||
|
||||
// You can await IEnumerator coroutine
|
||||
await FooCoroutineEnumerator();
|
||||
|
||||
// You can await standard task
|
||||
await Task.Run(() => 100);
|
||||
|
||||
// Multithreading, run on ThreadPool under this code(use SwitchToMainThread, same as `ObserveOnMainThreadDispatcher`)
|
||||
// Multithreading, run on ThreadPool under this code
|
||||
await UniTask.SwitchToThreadPool();
|
||||
|
||||
/* work on ThreadPool */
|
||||
|
||||
// return to MainThread(same as `ObserveOnMainThread` in UniRx)
|
||||
await UniTask.SwitchToMainThread();
|
||||
|
||||
// get async webrequest
|
||||
async UniTask<string> GetTextAsync(UnityWebRequest req)
|
||||
{
|
||||
@@ -77,6 +124,9 @@ async UniTask<string> DemoAsync()
|
||||
|
||||
// concurrent async-wait and get result easily by tuple syntax
|
||||
var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3);
|
||||
|
||||
// shorthand of WhenAll, tuple can await directly
|
||||
var (google2, bing2, yahoo2) = await (task1, task2, task3);
|
||||
|
||||
// You can handle timeout easily
|
||||
await GetTextAsync(UnityWebRequest.Get("http://unity.com")).Timeout(TimeSpan.FromMilliseconds(300));
|
||||
@@ -86,54 +136,44 @@ async UniTask<string> DemoAsync()
|
||||
}
|
||||
```
|
||||
|
||||
`UniTask<T>`
|
||||
Basics of UniTask and AsyncOperation
|
||||
---
|
||||
UniTask feature rely on C# 7.0([task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)) so required Unity version is after `Unity 2018.3`.
|
||||
UniTask feature rely on C# 7.0([task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)) so required Unity version is after `Unity 2018.3`, officialy lower support version is `Unity 2018.4.13f1`.
|
||||
|
||||
Why UniTask(custom task-like object) is required? Because Task is too heavy, not matched to Unity threading(single-thread). UniTask does not use thread and SynchronizationContext because almost Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It acquires more fast and more less allocation, completely integrated with Unity.
|
||||
Why UniTask(custom task-like object) is required? Because Task is too heavy, not matched to Unity threading(single-thread). UniTask does not use thread and SynchronizationContext/ExecutionContext because almost Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It acquires more fast and more less allocation, completely integrated with Unity.
|
||||
|
||||
> More details, please see this slide: [Deep Dive async/await in Unity with UniTask(EN)
|
||||
](https://www.slideshare.net/neuecc/deep-dive-asyncawait-in-unity-with-unitasken)
|
||||
You can await `AsyncOperation`, `ResourceRequest`, `AssetBundleRequest`, `AssetBundleCreateRequest`, `UnityWebRequestAsyncOperation`, `AsyncGPUReadbackRequest`, `IEnumerator` and others when `using Cysharp.Threading.Tasks;`.
|
||||
|
||||
You can await `AsyncOperation`, `ResourceRequest`, `UnityWebRequestAsyncOperation`, `IEnumerator` and others when using `UniRx.Async`.
|
||||
|
||||
`UniTask.Delay`, `UniTask.Yield`, `UniTask.Timeout` that is frame-based timer operators(no uses thread so works on WebGL publish) driven by custom PlayerLoop(Unity 2018 experimental feature). In default, UniTask initialize automatically when application begin, but it is override all. If you want to append PlayerLoop, please call `PlayerLoopHelper.Initialize(ref yourCustomizedPlayerLoop)` manually.
|
||||
|
||||
> Before Unity 2019.3, Unity does not have `PlayerLooop.GetCurrentPlayerLoop` so you can't use with Unity ECS package in default. If you want to use with ECS and before Unity 2019.3, you can use this hack below.
|
||||
UniTask provides three pattern of extension methods.
|
||||
|
||||
```csharp
|
||||
// Get ECS Loop.
|
||||
var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
|
||||
|
||||
// Setup UniTask's PlayerLoop.
|
||||
PlayerLoopHelper.Initialize(ref playerLoop);
|
||||
* await asyncOperation;
|
||||
* .WithCancellation(CancellationToken);
|
||||
* .ToUniTask(IProgress, PlayerLoopTiming, CancellationToken);
|
||||
```
|
||||
|
||||
`UniTask.WhenAll`, `UniTask.WhenAny` is like Task.WhenAll/WhenAny but return type is more useful.
|
||||
`WithCancellation` is a simple version of `ToUniTask`, both returns `UniTask`. Details of cancellation, see: [Cancellation and Exception handling](#cancellation-and-exception-handling) section.
|
||||
|
||||
`UniTask.ctor(Func<UniTask>)` is like the embeded [`AsyncLazy<T>`](https://blogs.msdn.microsoft.com/pfxteam/2011/01/15/asynclazyt/)
|
||||
> Note: WithCancellation is returned from native timing of PlayerLoop but ToUniTask is returned from specified PlayerLoopTiming. Details of timing, see: [PlayerLoop](#playerloop) section.
|
||||
|
||||
> Note: AssetBundleRequest has `asset` and `allAssets`, in default await returns `asset`. If you want to get `allAssets`, you can use `AwaitForAllAssets()` method.
|
||||
|
||||
The type of `UniTask` can use utility like `UniTask.WhenAll`, `UniTask.WhenAny`. It is like Task.WhenAll/WhenAny but return type is more useful, returns value tuple so can deconsrtuct each result and pass multiple type.
|
||||
|
||||
```csharp
|
||||
public class SceneAssets
|
||||
public async UniTaskVoid LoadManyAsync()
|
||||
{
|
||||
public readonly UniTask<Sprite> Front;
|
||||
public readonly UniTask<Sprite> Background;
|
||||
public readonly UniTask<Sprite> Effect;
|
||||
// parallel load.
|
||||
var (a, b, c) = await UniTask.WhenAll(
|
||||
LoadAsSprite("foo"),
|
||||
LoadAsSprite("bar"),
|
||||
LoadAsSprite("baz"));
|
||||
}
|
||||
|
||||
public SceneAssets()
|
||||
{
|
||||
// ctor(Func) overload is AsyncLazy, initialized once when await.
|
||||
// and after it, await returns zero-allocation value immediately.
|
||||
Front = new UniTask<Sprite>(() => LoadAsSprite("foo"));
|
||||
Background = new UniTask<Sprite>(() => LoadAsSprite("bar"));
|
||||
Effect = new UniTask<Sprite>(() => LoadAsSprite("baz"));
|
||||
}
|
||||
|
||||
async UniTask<Sprite> LoadAsSprite(string path)
|
||||
{
|
||||
var resource = await Resources.LoadAsync<Sprite>(path);
|
||||
return (resource as Sprite);
|
||||
}
|
||||
async UniTask<Sprite> LoadAsSprite(string path)
|
||||
{
|
||||
var resource = await Resources.LoadAsync<Sprite>(path);
|
||||
return (resource as Sprite);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -152,13 +192,32 @@ public UniTask<int> WrapByUniTaskCompletionSource()
|
||||
}
|
||||
```
|
||||
|
||||
You can convert Task -> UniTask: `AsUniTask`, `UniTask` -> `UniTask<AsyncUnit>`: `AsAsyncUnitUniTask`(this is useful to use WhenAll/WhenAny), `UniTask<T>` -> `UniTask`: `AsUniTask`.
|
||||
You can convert Task -> UniTask: `AsUniTask`, `UniTask` -> `UniTask<AsyncUnit>`: `AsAsyncUnitUniTask`, `UniTask<T>` -> `UniTask`: `AsUniTask`. `UniTask<T>` -> `UniTask`'s conversion cost is free.
|
||||
|
||||
If you want to convert async to coroutine, you can use `UniTask.ToCoroutine`, this is useful to use only allow coroutine system.
|
||||
If you want to convert async to coroutine, you can use `.ToCoroutine()`, this is useful to use only allow coroutine system.
|
||||
|
||||
UniTask can not await twice. This is a similar constraint to the [ValueTask/IValueTaskSource](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1?view=netcore-3.1) introduced in .NET Standard 2.1.
|
||||
|
||||
> The following operations should never be performed on a ValueTask<TResult> instance:
|
||||
>
|
||||
> * Awaiting the instance multiple times.
|
||||
> * Calling AsTask multiple times.
|
||||
> * Using .Result or .GetAwaiter().GetResult() when the operation hasn't yet completed, or using them multiple times.
|
||||
> * Using more than one of these techniques to consume the instance.
|
||||
>
|
||||
> If you do any of the above, the results are undefined.
|
||||
|
||||
```csharp
|
||||
var task = UniTask.DelayFrame(10);
|
||||
await task;
|
||||
await task; // NG, throws Exception
|
||||
```
|
||||
|
||||
Store to the class field, you can use `UniTask.Lazy` that gurantee call multipletimes. `.Preserve()` allows for multiple calls (internally cached results). This is useful when multiple calls in a function scope.
|
||||
|
||||
Cancellation and Exception handling
|
||||
---
|
||||
Some UniTask factory methods have `CancellationToken cancellation = default(CancellationToken)` parameter. Andalso some async operation for unity have `ConfigureAwait(..., CancellationToken cancellation = default(CancellationToken))` extension methods.
|
||||
Some UniTask factory methods have `CancellationToken cancellationToken = default` parameter. Andalso some async operation for unity have `WithCancellation(CancellationToken)` and `ToUniTask(..., CancellationToken cancellation = default)` extension methods.
|
||||
|
||||
You can pass `CancellationToken` to parameter by standard [`CancellationTokenSource`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource).
|
||||
|
||||
@@ -170,7 +229,7 @@ cancelButton.onClick.AddListener(() =>
|
||||
cts.Cancel();
|
||||
});
|
||||
|
||||
await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().ConfigureAwait(cancellation: cts.Token);
|
||||
await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().WithCancellation(cts.Token);
|
||||
|
||||
await UniTask.DelayFrame(1000, cancellationToken: cts.Token);
|
||||
```
|
||||
@@ -184,7 +243,7 @@ await UniTask.DelayFrame(1000, cancellationToken: this.GetCancellationTokenOnDes
|
||||
|
||||
When detect cancellation, all methods throws `OperationCanceledException` and propagate to upstream. `OperationCanceledException` is special exception, if not handled this exception, finally it is propagated to `UniTaskScheduler.UnobservedTaskException`.
|
||||
|
||||
Default behaviour of received unhandled exception is write log as warning. Log level can change by `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to change custom beavhiour, set action to `UniTaskScheduler.UnobservedTaskException.`
|
||||
Default behaviour of received unhandled exception is write log as exception. Log level can change by `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to change custom beavhiour, set action to `UniTaskScheduler.UnobservedTaskException.`
|
||||
|
||||
If you want to cancel behaviour in async UniTask method, throws `OperationCanceledException` manually.
|
||||
|
||||
@@ -223,23 +282,23 @@ if (isCanceled)
|
||||
}
|
||||
```
|
||||
|
||||
Note: Only suppress throws if you call it directly into the most source method.
|
||||
Note: Only suppress throws if you call it directly into the most source method. Otherwise, the return value will be converted, but the entire pipeline will not be suppressed throws.
|
||||
|
||||
Progress
|
||||
---
|
||||
Some async operation for unity have `ConfigureAwait(IProgress<float> progress = null, ...)` extension methods.
|
||||
Some async operation for unity have `ToUniTask(IProgress<float> progress = null, ...)` extension methods.
|
||||
|
||||
```csharp
|
||||
var progress = Progress.Create<float>(x => Debug.Log(x));
|
||||
|
||||
var request = await UnityWebRequest.Get("http://google.co.jp")
|
||||
.SendWebRequest()
|
||||
.ConfigureAwait(progress: progress);
|
||||
.ToUniTask(progress: progress);
|
||||
```
|
||||
|
||||
Should not use `new System.Progress<T>`, because it allocate every times. Use `UniRx.Async.Progress` instead. Progress factory has two methods, `Create` and `CreateOnlyValueChanged`. `CreateOnlyValueChanged` calls only when progress value changed. Should not use `new System.Progress<T>`, it allocate every times.
|
||||
You should not use standard `new System.Progress<T>`, because it causes allocation every times. Use `Cysharp.Threading.Tasks.Progress` instead. This progress factory has two methods, `Create` and `CreateOnlyValueChanged`. `CreateOnlyValueChanged` calls only when progress value changed.
|
||||
|
||||
Implements interface is more better.
|
||||
Implements IProgress interface to caller is more better, there is no allocation of lambda.
|
||||
|
||||
```csharp
|
||||
public class Foo : MonoBehaviour, IProgress<float>
|
||||
@@ -253,7 +312,148 @@ public class Foo : MonoBehaviour, IProgress<float>
|
||||
{
|
||||
var request = await UnityWebRequest.Get("http://google.co.jp")
|
||||
.SendWebRequest()
|
||||
.ConfigureAwait(progress: this); // pass this
|
||||
.ToUniTask(progress: this); // pass this
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
PlayerLoop
|
||||
---
|
||||
UniTask is run on custom [PlayerLoop](https://docs.unity3d.com/ScriptReference/LowLevel.PlayerLoop.html). UniTask's playerloop based method(such as `Delay`, `DelayFrame`, `asyncOperation.ToUniTask`, etc...) accepts this `PlayerLoopTiming`.
|
||||
|
||||
```csharp
|
||||
public enum PlayerLoopTiming
|
||||
{
|
||||
Initialization = 0,
|
||||
LastInitialization = 1,
|
||||
|
||||
EarlyUpdate = 2,
|
||||
LastEarlyUpdate = 3,
|
||||
|
||||
FixedUpdate = 4,
|
||||
LastFixedUpdate = 5,
|
||||
|
||||
PreUpdate = 6,
|
||||
LastPreUpdate = 7,
|
||||
|
||||
Update = 8,
|
||||
LastUpdate = 9,
|
||||
|
||||
PreLateUpdate = 10,
|
||||
LastPreLateUpdate = 11,
|
||||
|
||||
PostLateUpdate = 12,
|
||||
LastPostLateUpdate = 13
|
||||
}
|
||||
```
|
||||
|
||||
It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop.
|
||||
|
||||
`PlayerLoopTiming.Update` is similar as `yield return null` in coroutine, but it is called before Update(Update and uGUI events(button.onClick, etc...) are called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). `PlayerLoopTiming.FixedUpdate` is similar as `WaitForFixedUpdate`, `PlayerLoopTiming.LastPostLateUpdate` is similar as `WaitForEndOfFrame` in coroutine.
|
||||
|
||||
`yield return null` and `UniTask.Yield` is similar but different. `yield return null` always return next frame but `UniTask.Yield` return next called, that is, call `UniTask.Yield(PlayerLoopTiming.Update)` on `PreUpdate`, it returns same frame. `UniTask.NextFrame()` gurantees return next frame, this would be expected to behave exactly the same as `yield return null`.
|
||||
|
||||
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and run on YieldRunner. It is most lightweight and faster.
|
||||
|
||||
AsyncOperation is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after called, loaded scene's `Start` called from `EarlyUpdate.ScriptRunDelayedStartupFrame`. Also `await UnityWebRequest` is returned from `EarlyUpdate.ExecuteMainThreadJobs`.
|
||||
|
||||
In UniTask, await directly and `WithCancellation` use native timing, `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, causes different order of Start and continuation after await. so recommend not to use `LoadSceneAsync.ToUniTask`.
|
||||
|
||||
In stacktrace, you can check where is running in playerloop.
|
||||
|
||||

|
||||
|
||||
In default, UniTask's PlayerLoop is initialized at `[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]`.
|
||||
|
||||
The order in which methods are called in BeforeSceneLoad is indeterminate, so if you want to use UniTask in other BeforeSceneLoad methods, you should try to initialize it before this.
|
||||
|
||||
```csharp
|
||||
// AfterAssembliesLoaded is called before BeforeSceneLoad
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
|
||||
public static void InitUniTaskLoop()
|
||||
{
|
||||
var loop = PlayerLoop.GetCurrentPlayerLoop();
|
||||
Cysharp.Threading.Tasks.PlayerLoopHelper.Initialize(ref loop);
|
||||
}
|
||||
```
|
||||
|
||||
If you import Unity's `Entities` package, that reset custom player loop to default at `BeforeSceneLoad` and inject ECS's loop. When Unity call ECS's inject method after UniTask's initialize method, UniTask will no longer work.
|
||||
|
||||
To solve this issue, you can re-initialize UniTask PlayerLoop after ECS initialized.
|
||||
|
||||
```csharp
|
||||
// Get ECS Loop.
|
||||
var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
|
||||
|
||||
// Setup UniTask's PlayerLoop.
|
||||
PlayerLoopHelper.Initialize(ref playerLoop);
|
||||
```
|
||||
|
||||
You can diagnostic UniTask's player loop is ready by `PlayerLoopHelper.IsInjectedUniTaskPlayerLoop()`. And also `PlayerLoopHelper.DumpCurrentPlayerLoop` shows current all playerloop to console.
|
||||
|
||||
```csharp
|
||||
void Start()
|
||||
{
|
||||
UnityEngine.Debug.Log("UniTaskPlayerLoop ready? " + PlayerLoopHelper.IsInjectedUniTaskPlayerLoop());
|
||||
PlayerLoopHelper.DumpCurrentPlayerLoop();
|
||||
}
|
||||
```
|
||||
|
||||
async void vs async UniTaskVoid
|
||||
---
|
||||
`async void` is a standard C# task system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is a lightweight version of `async UniTask` because it does not have awaitable completion and report error immediately to `UniTaskScheduler.UnobservedTaskException`. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
|
||||
|
||||
```csharp
|
||||
public async UniTaskVoid FireAndForgetMethod()
|
||||
{
|
||||
// do anything...
|
||||
await UniTask.Yield();
|
||||
}
|
||||
|
||||
public void Caller()
|
||||
{
|
||||
FireAndForgetMethod().Forget();
|
||||
}
|
||||
```
|
||||
|
||||
Also UniTask have `Forget` method, it is similar with UniTaskVoid and same effects with it. However still UniTaskVoid is more efficient if completely do not use await。
|
||||
|
||||
```csharp
|
||||
public async UniTask DoAsync()
|
||||
{
|
||||
// do anything...
|
||||
await UniTask.Yield();
|
||||
}
|
||||
|
||||
public void Caller()
|
||||
{
|
||||
DoAsync().Forget();
|
||||
}
|
||||
```
|
||||
|
||||
Using async lambda in register event, it is used `async void`. To avoid it, you can use `UniTask.Action` or `UniTask.UnityAction` that creates delegate via `async UniTaskVoid` lambda.
|
||||
|
||||
```csharp
|
||||
Action actEvent;
|
||||
UnityAction unityEvent; // especially used in uGUI
|
||||
|
||||
// Bad: async void
|
||||
actEvent += async () => { };
|
||||
unityEvent += async () => { };
|
||||
|
||||
// Ok: create Action delegate by lambda
|
||||
actEvent += UniTask.Action(async () => { await UniTask.Yield(); });
|
||||
unityEvent += UniTask.UnityAction(async () => { await UniTask.Yield(); });
|
||||
```
|
||||
|
||||
`UniTaskVoid` can also use in MonoBehaviour's `Start` method.
|
||||
|
||||
```csharp
|
||||
class Sample : MonoBehaviour
|
||||
{
|
||||
async UniTaskVoid Start()
|
||||
{
|
||||
// async init code.
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -262,7 +462,7 @@ UniTaskTracker
|
||||
---
|
||||
useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTask Tracker`.
|
||||
|
||||

|
||||

|
||||
|
||||
* Enable AutoReload(Toggle) - Reload automatically.
|
||||
* Reload - Reload view.
|
||||
@@ -272,38 +472,133 @@ useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTa
|
||||
|
||||
For debug use, enable tracking and capture stacktrace is useful but it it decline performance. Recommended usage is enable both to find task leak, and when done, finally disable both.
|
||||
|
||||
Reusable Promises
|
||||
External Assets
|
||||
---
|
||||
Some UniTask factory can reuse to reduce allocation. The list is `Yield`, `Delay`, `DelayFrame`, `WaitUntil`, `WaitWhile`, `WaitUntilValueChanged`.
|
||||
In default, UniTask supports TextMeshPro(`BindTo(TMP_Text)` and `TMP_InputField` event extensions like standard uGUI `InputField`), DOTween(`Tween` as awaitable) and Addressables(`AsyncOperationHandle` and `AsyncOpereationHandle<T>` as awaitable).
|
||||
|
||||
There are defined in separated asmdef like `UniTask.TextMeshPro`, `UniTask.DOTween`, `UniTask.Addressables`.
|
||||
|
||||
TextMeshPro and Addressables support are automatically enabled when import there package from package manager. However DOTween support, require to `com.demigiant.dotween` import from [OpenUPM](https://openupm.com/packages/com.demigiant.dotween/) or define `UNITASK_DOTWEEN_SUPPORT` to enable it.
|
||||
|
||||
```csharp
|
||||
var reusePromise = UniTask.DelayFrame(10);
|
||||
// sequential
|
||||
await transform.DOMoveX(2, 10);
|
||||
await transform.DOMoveZ(5, 20);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
// parallel with cancellation
|
||||
var ct = this.GetCancellationTokenOnDestroy();
|
||||
|
||||
await UniTask.WhenAll(
|
||||
transform.DOMoveX(10, 3).WithCancellation(ct),
|
||||
transform.DOScale(10, 3).WithCancellation(ct));
|
||||
```
|
||||
|
||||
DOTween support's default behaviour(`await`, `WithCancellation`, `ToUniTask`) awaits tween is killed. It works both Complete(true/false) and Kill(true/false). But if you want to tween reuse(`SetAutoKill(false)`), it does not work you expected. Or, if you want to await for another timing, the following extension methods exist in Tween, `AwaitForComplete`, `AwaitForPause`, `AwaitForPlay`, `AwaitForRewind`, `AwaitForStepComplete`.
|
||||
|
||||
AsyncEnumerable and Async LINQ
|
||||
---
|
||||
Unity 2020.2.0a12 supports C# 8.0 so you can use `await foreach`. This is the new Update notation in async era.
|
||||
|
||||
```csharp
|
||||
// Unity 2020.2.0a12, C# 8.0
|
||||
await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate(token))
|
||||
{
|
||||
await reusePromise;
|
||||
Debug.Log("Update() " + Time.frameCount);
|
||||
}
|
||||
```
|
||||
|
||||
awaitable Events
|
||||
---
|
||||
Unity events can await like `OnClickAsync`, `OnCollisionEnterAsync`. It can use by `UniRx.Async.Triggers`.
|
||||
In a C# 7.3 environment, you can use the `ForEachAsync` method to work in almost the same way.
|
||||
|
||||
```csharp
|
||||
using UniRx.Async.Triggers;
|
||||
|
||||
async UniTask TripleClick(CancellationToken token)
|
||||
// C# 7.3(Unity 2018.3~)
|
||||
await UniTaskAsyncEnumerable.EveryUpdate(token).ForEachAsync(_ =>
|
||||
{
|
||||
await button.OnClickAsync(token);
|
||||
await button.OnClickAsync(token);
|
||||
await button.OnClickAsync(token);
|
||||
Debug.Log("Update() " + Time.frameCount);
|
||||
});
|
||||
```
|
||||
|
||||
UniTaskAsyncEnumerable implements asynchronous LINQ, similar to LINQ in `IEnumerable<T>` or Rx in `IObservable<T>`. All standard LINQ query operators can be applied to asynchronous streams. For example, the following code shows how to apply a Where filter to a button-click asynchronous stream that runs once every two clicks.
|
||||
|
||||
```csharp
|
||||
await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsync(_ =>
|
||||
{
|
||||
});
|
||||
```
|
||||
|
||||
Fire and Forget style(for example, event handling), also you can use `Subscribe`.
|
||||
|
||||
```csharp
|
||||
okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).Subscribe(_ =>
|
||||
{
|
||||
});
|
||||
```
|
||||
|
||||
Async LINQ is enabled when `using Cysharp.Threading.Tasks.Linq;`, and `UniTaskAsyncEnumerable` is defined in `UniTask.Linq` asmdef.
|
||||
|
||||
It's closer to UniRx (Reactive Extensions), but UniTaskAsyncEnumerable is a pull-based asynchronous stream, whereas Rx was a push-based asynchronous stream. Note that although similar, the characteristics are different and the details behave differently along with them.
|
||||
|
||||
`UniTaskAsyncEnumerable` is the entry point like `Enumerbale`. In addition to the standard query operators, there are other generators for Unity such as `EveryUpdate`, `Timer`, `TimerFrame`, `Interval`, `IntervalFrame`, and `EveryValueChanged`. And also added additional UniTask original query operators like `Append`, `Prepend`, `DistinctUntilChanged`, `ToHashSet`, `Buffer`, `CombineLatest`, `Do`, `Never`, `ForEachAsync`, `Pairwise`, `Publish`, `Queue`, `Return`, `SkipUntil`, `TakeUntil`, `SkipUntilCanceled`, `TakeUntilCanceled`, `TakeLast`, `Subscribe`.
|
||||
|
||||
The method with Func as an argument has three additional overloads, `***Await`, `***AwaitWithCancellation`.
|
||||
|
||||
```csharp
|
||||
Select(Func<T, TR> selector)
|
||||
SelectAwait(Func<T, UniTask<TR>> selector)
|
||||
SelectAwaitWithCancellation(Func<T, CancellationToken, UniTask<TR>> selector)
|
||||
```
|
||||
|
||||
If you want to use the `async` method inside the func, use the `***Await` or `***AwaitWithCancellation`.
|
||||
|
||||
How to create async iterator, C# 8.0 supports async iterator(`async yield return`) but it only allows `IAsyncEnumerable<T>` and of course requires C# 8.0. UniTask supports `UniTaskAsyncEnumerable.Create` method to create custom async iterator.
|
||||
|
||||
```csharp
|
||||
// IAsyncEnumerable, C# 8.0 version of async iterator. ( do not use this style, IAsyncEnumerable is not controled in UniTask).
|
||||
public async IAsyncEnumerable<int> MyEveryUpdate([EnumeratorCancellation]CancellationToken cancelationToken = default)
|
||||
{
|
||||
var frameCount = 0;
|
||||
await UniTask.Yield();
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
yield return frameCount++;
|
||||
await UniTask.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
// UniTaskAsyncEnumerable.Create and use `await writer.YieldAsync` instead of `yield return`.
|
||||
public IUniTaskAsyncEnumerable<int> MyEveryUpdate()
|
||||
{
|
||||
// writer(IAsyncWriter<T>) has `YieldAsync(value)` method.
|
||||
return UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
||||
{
|
||||
var frameCount = 0;
|
||||
await UniTask.Yield();
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
await writer.YieldAsync(frameCount++); // instead of `yield return`
|
||||
await UniTask.Yield();
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Awaitable Events
|
||||
---
|
||||
All uGUI component implements `***AsAsyncEnumerable` to convert asynchronous streams of events.
|
||||
|
||||
```csharp
|
||||
async UniTask TripleClick()
|
||||
{
|
||||
// In default, used button.GetCancellationTokenOnDestroy to manage lieftime of async
|
||||
await button.OnClickAsync();
|
||||
await button.OnClickAsync();
|
||||
await button.OnClickAsync();
|
||||
Debug.Log("Three times clicked");
|
||||
}
|
||||
|
||||
// more efficient way
|
||||
async UniTask TripleClick(CancellationToken token)
|
||||
async UniTask TripleClick()
|
||||
{
|
||||
using (var handler = button.GetAsyncClickEventHandler(token))
|
||||
using (var handler = button.GetAsyncClickEventHandler())
|
||||
{
|
||||
await handler.OnClickAsync();
|
||||
await handler.OnClickAsync();
|
||||
@@ -311,11 +606,156 @@ async UniTask TripleClick(CancellationToken token)
|
||||
Debug.Log("Three times clicked");
|
||||
}
|
||||
}
|
||||
|
||||
// use async LINQ
|
||||
async UniTask TripleClick(CancellationToken token)
|
||||
{
|
||||
await button.OnClickAsAsyncEnumerable().Take(3).Last();
|
||||
Debug.Log("Three times clicked");
|
||||
}
|
||||
|
||||
// use async LINQ2
|
||||
async UniTask TripleClick(CancellationToken token)
|
||||
{
|
||||
await button.OnClickAsAsyncEnumerable().Take(3).ForEachAsync(_ =>
|
||||
{
|
||||
Debug.Log("Every clicked");
|
||||
});
|
||||
Debug.Log("Three times clicked, complete.");
|
||||
}
|
||||
```
|
||||
|
||||
async void vs async UniTask/UniTaskVoid
|
||||
All MonoBehaviour message events can convert async-streams by `AsyncTriggers` that can enable by `using Cysharp.Threading.Tasks.Triggers;`.
|
||||
|
||||
```csharp
|
||||
using Cysharp.Threading.Tasks.Triggers;
|
||||
|
||||
async UniTaskVoid MonitorCollision()
|
||||
{
|
||||
await gameObject.OnCollisionEnterAsync();
|
||||
Debug.Log("Collision Enter");
|
||||
/* do anything */
|
||||
|
||||
await gameObject.OnCollisionExitAsync();
|
||||
Debug.Log("Collision Exit");
|
||||
}
|
||||
```
|
||||
|
||||
Similar as uGUI event, AsyncTrigger can get by `GetAsync***Trigger` and trigger it self is UniTaskAsyncEnumerable.
|
||||
|
||||
```csharp
|
||||
// use await multiple times, get AsyncTriggerHandler is more efficient.
|
||||
using(var trigger = this.GetOnCollisionEnterAsyncHandler())
|
||||
{
|
||||
await OnCollisionEnterAsync();
|
||||
await OnCollisionEnterAsync();
|
||||
await OnCollisionEnterAsync();
|
||||
}
|
||||
|
||||
// every moves.
|
||||
await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
|
||||
{
|
||||
});
|
||||
```
|
||||
|
||||
`AsyncReactiveProperty`, `AsyncReadOnlyReactiveProperty` is UniTask version of UniTask's ReactiveProperty. `BindTo` extension method of `IUniTaskAsyncEnumerable<T>` for binding asynchronous stream values to Unity components(Text/Selectable/TMP/Text).
|
||||
|
||||
```csharp
|
||||
var rp = new AsyncReactiveProperty<int>(99);
|
||||
|
||||
// AsyncReactiveProperty itself is IUniTaskAsyncEnumerable, you can query by LINQ
|
||||
rp.ForEachAsync(x =>
|
||||
{
|
||||
Debug.Log(x);
|
||||
}, this.GetCancellationTokenOnDestroy()).Forget();
|
||||
|
||||
rp.Value = 10; // push 10 to all subscriber
|
||||
rp.Value = 11; // push 11 to all subscriber
|
||||
|
||||
// WithoutCurrent ignore initial value
|
||||
// BindTo bind stream value to unity components.
|
||||
rp.WithoutCurrent().BindTo(this.textComponent);
|
||||
|
||||
await rp.WaitAsync(); // wait until next value set
|
||||
|
||||
// also exists ToReadOnlyReactiveProperty
|
||||
var rp2 = new AsyncReactiveProperty<int>(99);
|
||||
var rorp = rp.CombineLatest(rp2, (x, y) => (x, y)).ToReadOnlyReactiveProperty();
|
||||
```
|
||||
|
||||
A pull-type asynchronous stream does not get the next values until the asynchronous processing in the sequence is complete. This could spill data from push-type events such as buttons.
|
||||
|
||||
```csharp
|
||||
// can not get click event during 3 seconds complete.
|
||||
await button.OnClickAsAsyncEnumerable().ForEachAwaitAsync(async x =>
|
||||
{
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(3));
|
||||
});
|
||||
```
|
||||
|
||||
It is useful(prevent double-click) but not useful in sometimes.
|
||||
|
||||
Using `Queue()` method, which will also queue events during asynchronous processing.
|
||||
|
||||
```csharp
|
||||
// queued message in asynchronous processing
|
||||
await button.OnClickAsAsyncEnumerable().Queue().ForEachAwaitAsync(async x =>
|
||||
{
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(3));
|
||||
});
|
||||
```
|
||||
|
||||
Or use `Subscribe`, fire and forget style.
|
||||
|
||||
```csharp
|
||||
button.OnClickAsAsyncEnumerable().Subscribe(async x =>
|
||||
{
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(3));
|
||||
});
|
||||
```
|
||||
|
||||
Channel
|
||||
---
|
||||
`async void` is standard C# system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is lightweight version of `async UniTask` because it does not have awaitable completion. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
|
||||
`Channel` is same as [System.Threading.Tasks.Channels](https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.channels?view=netcore-3.1) that is similar as GoLang Channel.
|
||||
|
||||
Currently only supports multiple-producer, single-consumer unbounded channel. It can create by `Channel.CreateSingleConsumerUnbounded<T>()`.
|
||||
|
||||
For producer(`.Writer`), `TryWrite` to push value and `TryComplete` to complete channel. For consumer(`.Reader`), `TryRead`, `WaitToReadAsync`, `ReadAsync`, `Completion` and `ReadAllAsync` to read queued messages.
|
||||
|
||||
`ReadAllAsync` returns `IUniTaskAsyncEnumerable<T>` so query LINQ operators. Reader only allows single-consumer but use `.Publish()` query operator to enable multicast message. For example, make pub/sub utility.
|
||||
|
||||
```csharp
|
||||
public class AsyncMessageBroker<T> : IDisposable
|
||||
{
|
||||
Channel<T> channel;
|
||||
|
||||
IConnectableUniTaskAsyncEnumerable<T> multicastSource;
|
||||
IDisposable connection;
|
||||
|
||||
public AsyncMessageBroker()
|
||||
{
|
||||
channel = Channel.CreateSingleConsumerUnbounded<T>();
|
||||
multicastSource = channel.Reader.ReadAllAsync().Publish();
|
||||
connection = multicastSource.Connect(); // Publish returns IConnectableUniTaskAsyncEnumerable.
|
||||
}
|
||||
|
||||
public void Publish(T value)
|
||||
{
|
||||
channel.Writer.TryWrite(value);
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerable<T> Subscribe()
|
||||
{
|
||||
return multicastSource;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
channel.Writer.TryComplete();
|
||||
connection.Dispose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For Unit Testing
|
||||
---
|
||||
@@ -342,46 +782,185 @@ public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
|
||||
});
|
||||
```
|
||||
|
||||
Method List
|
||||
UniTask itself's unit test is written by Unity Test Runner and [Cysharp/RuntimeUnitTestToolkit](https://github.com/Cysharp/RuntimeUnitTestToolkit) to check on CI and IL2CPP working.
|
||||
|
||||
ThreadPool limitation
|
||||
---
|
||||
Most UniTask methods run in a single thread (PlayerLoop), but only `UniTask.Run` and `UniTask.SwitchToThreadPool` run on a thread pool. If you use a thread pool, it won't work with WebGL and so on.
|
||||
|
||||
`UniTask.Run` will be deprecated in the future (marked with an Obsolete) and only `RunOnThreadPool` will be used. Also, if you use `UniTask.Run`, consider whether you can use `UniTask.Create` or `UniTask.Void`.
|
||||
|
||||
IEnumerator.ToUniTask limitation
|
||||
---
|
||||
You can convert coroutine(IEnumerator) to UniTask(or await directly) but has some limitations.
|
||||
|
||||
* `WaitForEndOfFrame`/`WaitForFixedUpdate` is not supported, used `yield return null` instead.
|
||||
* Consuming loop timing is not same as StartCoroutine, it is used specified PlayerLoopTiming, and default's `PlayerLoopTiming.Update` is run before MonoBehaviour's Update and StartCoroutine's loop.
|
||||
|
||||
For UnityEditor
|
||||
---
|
||||
UniTask can run on Unity Edtitor like Editor Coroutine. However, there are some limitations.
|
||||
|
||||
* Delay, DelayFrame is not work correctly because can not get deltaTime in editor. Return the result of the await immediately; you can use `DelayType.Realtime` to wait for the right time.
|
||||
* All PlayerLoopTiming run on timing, `EditorApplication.update`.
|
||||
|
||||
Compare with Standard Task API
|
||||
---
|
||||
UniTask has many standard Task-like APIs. This table shows what is the alternative apis.
|
||||
|
||||
Use standard type.
|
||||
|
||||
| .NET Type | UniTask Type |
|
||||
| --- | --- |
|
||||
| `IProgress<T>` | --- |
|
||||
| `CancellationToken` | --- |
|
||||
| `CancellationTokenSource` | --- |
|
||||
|
||||
Use UniTask type.
|
||||
|
||||
| .NET Type | UniTask Type |
|
||||
| --- | --- |
|
||||
| `Task`/`ValueTask` | `UniTask` |
|
||||
| `Task<T>`/`ValueTask<T>` | `UniTask<T>` |
|
||||
| `async void` | `async UniTaskVoid` |
|
||||
| `+= async () => { }` | `UniTask.Void`, `UniTask.Action`, `UniTask.UnityAction` |
|
||||
| --- | `UniTaskCompletionSource` |
|
||||
| `TaskCompletionSource<T>` | `UniTaskCompletionSource<T>`/`AutoResetUniTaskCompletionSource<T>` |
|
||||
| `ManualResetValueTaskSourceCore<T>` | `UniTaskCompletionSourceCore<T>` |
|
||||
| `IValueTaskSource` | `IUniTaskSource` |
|
||||
| `IValueTaskSource<T>` | `IUniTaskSource<T>` |
|
||||
| `ValueTask.IsCompleted` | `UniTask.Status.IsCompleted()` |
|
||||
| `ValueTask<T>.IsCompleted` | `UniTask<T>.Status.IsCompleted()` |
|
||||
| `new Progress<T>` | `Progress.Create<T>` |
|
||||
| `CancellationToken.Register(UnsafeRegister)` | `CancellationToken.RegisterWithoutCaptureExecutionContext` |
|
||||
| `CancellationTokenSource.CancelAfter` | `CancellationTokenSource.CancelAfterSlim` |
|
||||
| `Channel.CreateUnbounded<T>(false){ SingleReader = true }` | `Channel.CreateSingleConsumerUnbounded<T>` |
|
||||
| `IAsyncEnumerable<T>` | `IUniTaskAsyncEnumerable<T>` |
|
||||
| `IAsyncEnumerator<T>` | `IUniTaskAsyncEnumerator<T>` |
|
||||
| `IAsyncDisposable` | `IUniTaskAsyncDisposable` |
|
||||
| `Task.Delay` | `UniTask.Delay` |
|
||||
| `Task.Yield` | `UniTask.Yield` |
|
||||
| `Task.Run` | `UniTask.Run` |
|
||||
| `Task.WhenAll` | `UniTask.WhenAll` |
|
||||
| `Task.WhenAny` | `UniTask.WhenAny` |
|
||||
| `Task.CompletedTask` | `UniTask.CompletedTask` |
|
||||
| `Task.FromException` | `UniTask.FromException` |
|
||||
| `Task.FromResult` | `UniTask.FromResult` |
|
||||
| `Task.FromCanceled` | `UniTask.FromCanceled` |
|
||||
| `Task.ContinueWith` | `UniTask.ContinueWith` |
|
||||
| `TaskScheduler.UnobservedTaskException` | `UniTaskScheduler.UnobservedTaskException` |
|
||||
|
||||
Pooling Configuration
|
||||
---
|
||||
UniTask is aggressively caching async promise object to achive zero allocation. In default, cache all promises but you can configure `TaskPool.SetMaxPoolSize` to your value, the value indicates cache size per type. `TaskPool.GetCacheSizeInfo` returns current cached object in pool.
|
||||
|
||||
```csharp
|
||||
UniTask.WaitUntil
|
||||
UniTask.WaitWhile
|
||||
UniTask.WaitUntilValueChanged
|
||||
UniTask.SwitchToThreadPool
|
||||
UniTask.SwitchToTaskPool
|
||||
UniTask.SwitchToMainThread
|
||||
UniTask.SwitchToSynchronizationContext
|
||||
UniTask.Yield
|
||||
UniTask.Run
|
||||
UniTask.Lazy
|
||||
UniTask.Void
|
||||
UniTask.ConfigureAwait
|
||||
UniTask.DelayFrame
|
||||
UniTask.Delay(..., bool ignoreTimeScale = false, ...) parameter
|
||||
foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
|
||||
{
|
||||
Debug.Log(type + ":" + size);
|
||||
}
|
||||
```
|
||||
|
||||
Allocation on Profiler
|
||||
---
|
||||
In UnityEditor profiler shows allocation of compiler generated AsyncStateMachine but it only occurs in debug(development) build. C# Compiler generate AsyncStateMachine as class on Debug build and as struct on Release build.
|
||||
|
||||
After Unity 2020.1 supports Code Optimization option on UnityEditor(right, footer).
|
||||
|
||||

|
||||
|
||||
You can change C# compiler optimization to release, it removes AsyncStateMachine allocation. Andalso optimization option can set via `Compilation.CompilationPipeline-codeOptimization`, and `Compilation.CodeOptimization`.
|
||||
|
||||
UniTaskSynchronizationContext
|
||||
---
|
||||
Unity's default SynchronizationContext(`UnitySynchronizationContext`) is poor implementation for performance. UniTask itself is bypass `SynchronizationContext`(and `ExecutionContext`) so does not use it but if exists in `async Task`, still used it. `UniTaskSynchronizationContext` is replacement of `UnitySynchronizationContext`, it is better for performance.
|
||||
|
||||
```csharp
|
||||
public class SyncContextInjecter
|
||||
{
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
public static void Inject()
|
||||
{
|
||||
SynchronizationContext.SetSynchronizationContext(new UniTaskSynchronizationContext());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is an optional choice and is not always recommended; `UniTaskSynchronizationContext` is less performance than `async UniTask` and is not a complete UniTask replacement. It also does not guarantee full behavioral compatibility with the `UnitySynchronizationContext`.
|
||||
|
||||
API References
|
||||
---
|
||||
UniTask's API References is hosted at [cysharp.github.io/UniTask](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.html) by [DocFX](https://dotnet.github.io/docfx/) and [Cysharp/DocfXTemplate](https://github.com/Cysharp/DocfxTemplate).
|
||||
|
||||
For example, UniTask's factory methods can see at [UniTask#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.UniTask.html#methods-1). UniTaskAsyncEnumerable's factory/extension methods can see at [UniTaskAsyncEnumerable#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.Linq.UniTaskAsyncEnumerable.html#methods-1).
|
||||
|
||||
UPM Package
|
||||
---
|
||||
After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of git package. You can add `https://github.com/Cysharp/UniTask.git?path=Assets/UniRx.Async` to Package Manager
|
||||
### Install via git URL
|
||||
|
||||
After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of git package. You can add `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask` to Package Manager
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=Assets/UniRx.Async"` to `Packages/manifest.json`.
|
||||
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"` to `Packages/manifest.json`.
|
||||
|
||||
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#1.3.0`. For example `https://github.com/Cysharp/UniTask.git?path=Assets/UniRx.Async#1.3.1`.
|
||||
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#2.0.31`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.0.31`.
|
||||
|
||||
ECS, PlayerLoop
|
||||
### Install via OpenUPM
|
||||
|
||||
The package is available on the [openupm registry](https://openupm.com). It's recommended to install it via [openupm-cli](https://github.com/openupm/openupm-cli).
|
||||
|
||||
```
|
||||
openupm add com.cysharp.unitask
|
||||
```
|
||||
|
||||
.NET Core
|
||||
---
|
||||
TODO:
|
||||
For .NET Core, use NuGet.
|
||||
|
||||
> PM> Install-Package [UniTask](https://www.nuget.org/packages/UniTask)
|
||||
|
||||
UniTask of .NET Core version is a subset of Unity UniTask, removed PlayerLoop dependent methods.
|
||||
|
||||
It runs at higher performance than the standard Task/ValueTask, but you should be careful to ignore the ExecutionContext/SynchronizationContext when using it. `AysncLocal` also does not work because it ignores ExecutionContext.
|
||||
|
||||
If you use UniTask internally, but provide ValueTask as an external API, you can write like the following(Inspired by [PooledAwait](https://github.com/mgravell/PooledAwait)).
|
||||
|
||||
```csharp
|
||||
var loop = PlayerLoop.GetCurrentPlayerLoop();
|
||||
PlayerLoopHelper.Initialize(ref loop);
|
||||
public class ZeroAllocAsyncAwaitInDotNetCore
|
||||
{
|
||||
public ValueTask<int> DoAsync(int x, int y)
|
||||
{
|
||||
return Core(this, x, y);
|
||||
|
||||
static async UniTask<int> Core(ZeroAllocAsyncAwaitInDotNetCore self, int x, int y)
|
||||
{
|
||||
// do anything...
|
||||
await Task.Delay(TimeSpan.FromSeconds(x + y));
|
||||
await UniTask.Yield();
|
||||
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UniTask does not return to original SynchronizationContext but you can use helper `ReturnToCurrentSynchronizationContext`.
|
||||
public ValueTask TestAsync()
|
||||
{
|
||||
await using (UniTask.ReturnToCurrentSynchronizationContext())
|
||||
{
|
||||
await UniTask.SwitchToThreadPool();
|
||||
// do anything..
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
.NET Core version is intended to allow users to use UniTask as an interface when sharing code with Unity (such as [Cysharp/MagicOnion](https://github.com/Cysharp/MagicOnion/)). .NET Core version of UniTask enables smooth code sharing.
|
||||
|
||||
Utility methods such as WhenAll which is equivalent to UniTask are provided as [Cysharp/ValueTaskSupplement](https://github.com/Cysharp/ValueTaskSupplement).
|
||||
|
||||
License
|
||||
---
|
||||
This library is under the MIT License.
|
||||
This library is under the MIT License.
|
||||
|
||||
10
docs/.gitignore
vendored
Normal file
10
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
###############
|
||||
# folder #
|
||||
###############
|
||||
/**/DROP/
|
||||
/**/TEMP/
|
||||
/**/packages/
|
||||
/**/bin/
|
||||
/**/obj/
|
||||
_site
|
||||
_DocfxTemplate
|
||||
5
docs/api/.gitignore
vendored
Normal file
5
docs/api/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
###############
|
||||
# temp file #
|
||||
###############
|
||||
*.yml
|
||||
.manifest
|
||||
70
docs/docfx.json
Normal file
70
docs/docfx.json
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"metadata": [
|
||||
{
|
||||
"src": [
|
||||
{
|
||||
"files": [
|
||||
"UniTask/Assets/Plugins/UniTask/Runtime/**/*.cs"
|
||||
],
|
||||
"src": "../src"
|
||||
}
|
||||
],
|
||||
"dest": "api",
|
||||
"disableGitFeatures": false,
|
||||
"disableDefaultFilter": false
|
||||
}
|
||||
],
|
||||
"build": {
|
||||
"globalMetadata": {
|
||||
"_disableContribution": true,
|
||||
"_appTitle": "UniTask"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"files": [
|
||||
"api/**.yml",
|
||||
"api/index.md"
|
||||
]
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"articles/**.md",
|
||||
"articles/**/toc.yml",
|
||||
"toc.yml",
|
||||
"*.md"
|
||||
]
|
||||
}
|
||||
],
|
||||
"resource": [
|
||||
{
|
||||
"files": [
|
||||
"images/**"
|
||||
]
|
||||
}
|
||||
],
|
||||
"overwrite": [
|
||||
{
|
||||
"files": [
|
||||
"apidoc/**.md"
|
||||
],
|
||||
"exclude": [
|
||||
"obj/**",
|
||||
"_site/**"
|
||||
]
|
||||
}
|
||||
],
|
||||
"dest": "_site",
|
||||
|
||||
"globalMetadataFiles": [],
|
||||
"fileMetadataFiles": [],
|
||||
"template": [
|
||||
"_DocfxTemplate/templates/default-v2.5.2",
|
||||
"_DocfxTemplate/templates/cysharp"
|
||||
],
|
||||
"postProcessors": [],
|
||||
"markdownEngineName": "markdig",
|
||||
"noLangKeyword": false,
|
||||
"keepFileLink": false,
|
||||
"cleanupCacheHistory": false
|
||||
}
|
||||
}
|
||||
8
docs/index.md
Normal file
8
docs/index.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: Home
|
||||
---
|
||||
# UniTask
|
||||
|
||||
Provides an efficient async/await integration to Unity.
|
||||
|
||||
https://github.com/Cysharp/UniTask
|
||||
11
docs/toc.yml
Normal file
11
docs/toc.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
- name: API Documentation
|
||||
href: api/
|
||||
homepage: api/Cysharp.Threading.Tasks.html
|
||||
|
||||
- name: Repository
|
||||
href: https://github.com/Cysharp/UniTask
|
||||
homepage: https://github.com/Cysharp/UniTask
|
||||
|
||||
- name: Releases
|
||||
href: https://github.com/Cysharp/UniTask/releases
|
||||
homepage: https://github.com/Cysharp/UniTask/releases
|
||||
BIN
src/UniTask.NetCore/Icon.png
Normal file
BIN
src/UniTask.NetCore/Icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
101
src/UniTask.NetCore/NetCore/AsyncEnumerableExtensions.cs
Normal file
101
src/UniTask.NetCore/NetCore/AsyncEnumerableExtensions.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
#if !NETSTANDARD2_0
|
||||
|
||||
#pragma warning disable 0649
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks.Sources;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static class AsyncEnumerableExtensions
|
||||
{
|
||||
public static IUniTaskAsyncEnumerable<T> AsUniTaskAsyncEnumerable<T>(this IAsyncEnumerable<T> source)
|
||||
{
|
||||
return new AsyncEnumerableToUniTaskAsyncEnumerable<T>(source);
|
||||
}
|
||||
|
||||
public static IAsyncEnumerable<T> AsAsyncEnumerable<T>(this IUniTaskAsyncEnumerable<T> source)
|
||||
{
|
||||
return new UniTaskAsyncEnumerableToAsyncEnumerable<T>(source);
|
||||
}
|
||||
|
||||
sealed class AsyncEnumerableToUniTaskAsyncEnumerable<T> : IUniTaskAsyncEnumerable<T>
|
||||
{
|
||||
readonly IAsyncEnumerable<T> source;
|
||||
|
||||
public AsyncEnumerableToUniTaskAsyncEnumerable(IAsyncEnumerable<T> source)
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new Enumerator(source.GetAsyncEnumerator(cancellationToken));
|
||||
}
|
||||
|
||||
sealed class Enumerator : IUniTaskAsyncEnumerator<T>
|
||||
{
|
||||
readonly IAsyncEnumerator<T> enumerator;
|
||||
|
||||
public Enumerator(IAsyncEnumerator<T> enumerator)
|
||||
{
|
||||
this.enumerator = enumerator;
|
||||
}
|
||||
|
||||
public T Current => enumerator.Current;
|
||||
|
||||
public async UniTask DisposeAsync()
|
||||
{
|
||||
await enumerator.DisposeAsync();
|
||||
}
|
||||
|
||||
public async UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return await enumerator.MoveNextAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class UniTaskAsyncEnumerableToAsyncEnumerable<T> : IAsyncEnumerable<T>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<T> source;
|
||||
|
||||
public UniTaskAsyncEnumerableToAsyncEnumerable(IUniTaskAsyncEnumerable<T> source)
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new Enumerator(source.GetAsyncEnumerator(cancellationToken));
|
||||
}
|
||||
|
||||
sealed class Enumerator : IAsyncEnumerator<T>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerator<T> enumerator;
|
||||
|
||||
public Enumerator(IUniTaskAsyncEnumerator<T> enumerator)
|
||||
{
|
||||
this.enumerator = enumerator;
|
||||
}
|
||||
|
||||
public T Current => enumerator.Current;
|
||||
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
|
||||
public ValueTask<bool> MoveNextAsync()
|
||||
{
|
||||
return enumerator.MoveNextAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma warning disable 0649
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks.Sources;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
@@ -8,23 +10,88 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static ValueTask AsValueTask(this in UniTask task)
|
||||
{
|
||||
#if NETSTANDARD2_0
|
||||
return new ValueTask(new UniTaskValueTaskSource(task), 0);
|
||||
#else
|
||||
return task;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static ValueTask<T> AsValueTask<T>(this in UniTask<T> task)
|
||||
{
|
||||
#if NETSTANDARD2_0
|
||||
return new ValueTask<T>(new UniTaskValueTaskSource<T>(task), 0);
|
||||
#else
|
||||
return task;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static UniTask<T> AsUniTask<T>(this ValueTask<T> task, bool useCurrentSynchronizationContext = true)
|
||||
public static async UniTask<T> AsUniTask<T>(this ValueTask<T> task)
|
||||
{
|
||||
// NOTE: get _obj and _token directly for low overhead conversion but not yet implemented.
|
||||
return task.AsTask().AsUniTask(useCurrentSynchronizationContext);
|
||||
return await task;
|
||||
}
|
||||
|
||||
public static UniTask AsUniTask(this ValueTask task, bool useCurrentSynchronizationContext = true)
|
||||
public static async UniTask AsUniTask(this ValueTask task)
|
||||
{
|
||||
return task.AsTask().AsUniTask(useCurrentSynchronizationContext);
|
||||
await task;
|
||||
}
|
||||
|
||||
#if NETSTANDARD2_0
|
||||
|
||||
class UniTaskValueTaskSource : IValueTaskSource
|
||||
{
|
||||
readonly UniTask task;
|
||||
readonly UniTask.Awaiter awaiter;
|
||||
|
||||
public UniTaskValueTaskSource(UniTask task)
|
||||
{
|
||||
this.task = task;
|
||||
this.awaiter = task.GetAwaiter();
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
awaiter.GetResult();
|
||||
}
|
||||
|
||||
public ValueTaskSourceStatus GetStatus(short token)
|
||||
{
|
||||
return (ValueTaskSourceStatus)task.Status;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
|
||||
{
|
||||
awaiter.SourceOnCompleted(continuation, state);
|
||||
}
|
||||
}
|
||||
|
||||
class UniTaskValueTaskSource<T> : IValueTaskSource<T>
|
||||
{
|
||||
readonly UniTask<T> task;
|
||||
readonly UniTask<T>.Awaiter awaiter;
|
||||
|
||||
public UniTaskValueTaskSource(UniTask<T> task)
|
||||
{
|
||||
this.task = task;
|
||||
this.awaiter = task.GetAwaiter();
|
||||
}
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
return awaiter.GetResult();
|
||||
}
|
||||
|
||||
public ValueTaskSourceStatus GetStatus(short token)
|
||||
{
|
||||
return (ValueTaskSourceStatus)task.Status;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
|
||||
{
|
||||
awaiter.SourceOnCompleted(continuation, state);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
39
src/UniTask.NetCore/NetCore/UniTask.Delay.cs
Normal file
39
src/UniTask.NetCore/NetCore/UniTask.Delay.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
//using Cysharp.Threading.Tasks.Internal;
|
||||
//using System;
|
||||
//using System.Collections.Concurrent;
|
||||
//using System.Runtime.CompilerServices;
|
||||
//using System.Threading;
|
||||
|
||||
//namespace Cysharp.Threading.Tasks
|
||||
//{
|
||||
// public partial struct UniTask
|
||||
// {
|
||||
// public static UniTask Delay()
|
||||
// {
|
||||
// return default;
|
||||
// }
|
||||
|
||||
// sealed class DelayPromise : IUniTaskSource
|
||||
// {
|
||||
// public void GetResult(short token)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public UniTaskStatus GetStatus(short token)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public UniTaskStatus UnsafeGetStatus()
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -59,16 +59,23 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
#if NETCOREAPP3_1
|
||||
|
||||
public sealed class ThreadPoolWorkItem : IThreadPoolWorkItem
|
||||
sealed class ThreadPoolWorkItem : IThreadPoolWorkItem, ITaskPoolNode<ThreadPoolWorkItem>
|
||||
{
|
||||
static readonly ConcurrentQueue<ThreadPoolWorkItem> pool = new ConcurrentQueue<ThreadPoolWorkItem>();
|
||||
static TaskPool<ThreadPoolWorkItem> pool;
|
||||
ThreadPoolWorkItem nextNode;
|
||||
public ref ThreadPoolWorkItem NextNode => ref nextNode;
|
||||
|
||||
static ThreadPoolWorkItem()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(ThreadPoolWorkItem), () => pool.Size);
|
||||
}
|
||||
|
||||
Action continuation;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ThreadPoolWorkItem Create(Action continuation)
|
||||
{
|
||||
if (!pool.TryDequeue(out var item))
|
||||
if (!pool.TryPop(out var item))
|
||||
{
|
||||
item = new ThreadPoolWorkItem();
|
||||
}
|
||||
@@ -82,9 +89,11 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
var call = continuation;
|
||||
continuation = null;
|
||||
pool.Enqueue(this);
|
||||
|
||||
call.Invoke();
|
||||
if (call != null)
|
||||
{
|
||||
pool.TryPush(this);
|
||||
call.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp3.1;netstandard2.1</TargetFrameworks>
|
||||
<TargetFrameworks>netcoreapp3.1;netstandard2.1;netstandard2.0</TargetFrameworks>
|
||||
<AssemblyName>UniTask</AssemblyName>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
|
||||
|
||||
<!-- NuGet Packaging -->
|
||||
<Id>UniTask</Id>
|
||||
<PackageVersion>$(Version)</PackageVersion>
|
||||
<Company>Cysharp</Company>
|
||||
<Authors>Cysharp</Authors>
|
||||
<Copyright>© Cysharp, Inc.</Copyright>
|
||||
<PackageTags>task;async</PackageTags>
|
||||
<Description>Provides an efficient async/await integration to Unity and .NET Core.</Description>
|
||||
<PackageProjectUrl>https://github.com/Cysharp/UniTask</PackageProjectUrl>
|
||||
<RepositoryUrl>$(PackageProjectUrl)</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageIcon>Icon.png</PackageIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Icon.png" Pack="true" PackagePath="/" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\UniTask\Assets\Plugins\UniTask\Runtime\**\*.cs"
|
||||
Exclude="
|
||||
@@ -18,7 +36,9 @@
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\DiagnosticsExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\PlayerLoopRunner.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\ContinuationQueue.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityWebRequestExtensions.cs;
|
||||
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTaskSynchronizationContext.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\CancellationTokenSourceExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\EnumeratorAsyncExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopHelper.cs;
|
||||
@@ -26,9 +46,7 @@
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.uGUI.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.MonoBehaviour.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.*;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;
|
||||
" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -29,7 +29,12 @@ public class AllocationCheck
|
||||
{
|
||||
for (int i = 0; i < InnerOps; i++)
|
||||
{
|
||||
await Core();
|
||||
var a = Core();
|
||||
var b = Core();
|
||||
var c = Core();
|
||||
await a;
|
||||
await b;
|
||||
await c;
|
||||
}
|
||||
|
||||
static async UniTask Core()
|
||||
@@ -46,7 +51,12 @@ public class AllocationCheck
|
||||
var sum = 0;
|
||||
for (int i = 0; i < InnerOps; i++)
|
||||
{
|
||||
sum += await Core();
|
||||
var a = Core();
|
||||
var b = Core();
|
||||
var c = Core();
|
||||
sum += await a;
|
||||
sum += await b;
|
||||
sum += await c;
|
||||
}
|
||||
return sum;
|
||||
|
||||
@@ -59,14 +69,16 @@ public class AllocationCheck
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = InnerOps)]
|
||||
public Task ViaUniTaskVoid()
|
||||
//[Benchmark(OperationsPerInvoke = InnerOps)]
|
||||
//[Benchmark]
|
||||
public void ViaUniTaskVoid()
|
||||
{
|
||||
for (int i = 0; i < InnerOps; i++)
|
||||
{
|
||||
Core().Forget();
|
||||
Core().Forget();
|
||||
Core().Forget();
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
|
||||
static async UniTaskVoid Core()
|
||||
{
|
||||
@@ -75,6 +87,46 @@ public class AllocationCheck
|
||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||
}
|
||||
}
|
||||
|
||||
struct Foo : IAsyncStateMachine
|
||||
{
|
||||
public AsyncUniTaskVoidMethodBuilder builder;
|
||||
public TestAwaiter awaiter;
|
||||
public TestAwaiter awaiterawaiter;
|
||||
|
||||
public int state;
|
||||
|
||||
public void MoveNext()
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case -1:
|
||||
awaiterawaiter = awaiter.GetAwaiter();
|
||||
if (awaiterawaiter.IsCompleted)
|
||||
{
|
||||
goto case 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
builder.AwaitUnsafeOnCompleted(ref awaiterawaiter, ref this);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0:
|
||||
default:
|
||||
goto END;
|
||||
}
|
||||
|
||||
END:
|
||||
builder.SetResult();
|
||||
}
|
||||
|
||||
public void SetStateMachine(IAsyncStateMachine stateMachine)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TaskTestException : Exception
|
||||
@@ -170,7 +222,15 @@ public struct TestAwaiter<T> : ICriticalNotifyCompletion
|
||||
|
||||
public sealed class ThreadPoolWorkItem : IThreadPoolWorkItem
|
||||
{
|
||||
static readonly ConcurrentQueue<ThreadPoolWorkItem> pool = new ConcurrentQueue<ThreadPoolWorkItem>();
|
||||
public static readonly ConcurrentQueue<ThreadPoolWorkItem> pool = new ConcurrentQueue<ThreadPoolWorkItem>();
|
||||
|
||||
public static void CreatePoolItems(int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
pool.Enqueue(new ThreadPoolWorkItem());
|
||||
}
|
||||
}
|
||||
|
||||
Action continuation;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public class BenchmarkConfig : ManualConfig
|
||||
public BenchmarkConfig()
|
||||
{
|
||||
AddDiagnoser(MemoryDiagnoser.Default);
|
||||
AddJob(Job.ShortRun.WithLaunchCount(1).WithIterationCount(1).WithWarmupCount(1));
|
||||
AddJob(Job.ShortRun.WithLaunchCount(1).WithIterationCount(1).WithWarmupCount(1)/*.RunOncePerIteration()*/);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
#pragma warning disable CS1998
|
||||
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
using System.Linq;
|
||||
using System;
|
||||
@@ -15,6 +17,19 @@ using System.Reactive.Concurrency;
|
||||
|
||||
namespace NetCoreSandbox
|
||||
{
|
||||
public class MySyncContext : SynchronizationContext
|
||||
{
|
||||
public MySyncContext()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Post(SendOrPostCallback d, object state)
|
||||
{
|
||||
Console.WriteLine("Called SyncContext Post!");
|
||||
base.Post(d, state);
|
||||
}
|
||||
}
|
||||
|
||||
public class Text
|
||||
{
|
||||
|
||||
@@ -43,6 +58,24 @@ namespace NetCoreSandbox
|
||||
|
||||
}
|
||||
|
||||
class Foo
|
||||
{
|
||||
public async UniTask MethodFooAsync()
|
||||
{
|
||||
await MethodBarAsync();
|
||||
}
|
||||
|
||||
private async UniTask MethodBarAsync()
|
||||
|
||||
{
|
||||
Throw();
|
||||
}
|
||||
|
||||
private void Throw()
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
public struct TestAwaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
@@ -193,43 +226,111 @@ namespace NetCoreSandbox
|
||||
await Task.Delay(10, cancellationToken);
|
||||
}
|
||||
|
||||
public class MyDisposable : IDisposable
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void Test()
|
||||
{
|
||||
var disp = new MyDisposable();
|
||||
|
||||
using var _ = new MyDisposable();
|
||||
|
||||
Console.WriteLine("tako");
|
||||
}
|
||||
|
||||
|
||||
static async UniTask FooBarAsync()
|
||||
{
|
||||
await using (UniTask.ReturnToCurrentSynchronizationContext())
|
||||
{
|
||||
await UniTask.SwitchToThreadPool();
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static async UniTask Aaa()
|
||||
{
|
||||
await FooBarAsync();
|
||||
|
||||
Console.WriteLine("FooBarAsync End");
|
||||
}
|
||||
|
||||
static async UniTask WhereSelect()
|
||||
{
|
||||
await foreach (var item in UniTaskAsyncEnumerable.Range(1, 10)
|
||||
.SelectAwait(async x =>
|
||||
{
|
||||
await UniTask.Yield();
|
||||
return x;
|
||||
})
|
||||
.Where(x => x % 2 == 0))
|
||||
{
|
||||
Console.WriteLine(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
#if !DEBUG
|
||||
|
||||
|
||||
|
||||
|
||||
//await new AllocationCheck().ViaUniTaskVoid();
|
||||
//Console.ReadLine();
|
||||
BenchmarkDotNet.Running.BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
|
||||
|
||||
//await new ComparisonBenchmarks().ViaUniTaskT();
|
||||
return;
|
||||
#endif
|
||||
|
||||
var e = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
Console.WriteLine($"Start {i}");
|
||||
await writer.YieldAsync(i);
|
||||
Console.WriteLine($"End {i}");
|
||||
}
|
||||
});
|
||||
|
||||
AsyncTest().Forget();
|
||||
var ee = e.GetAsyncEnumerator();
|
||||
while (await ee.MoveNextAsync())
|
||||
{
|
||||
Console.WriteLine("ForEach " + ee.Current);
|
||||
}
|
||||
|
||||
|
||||
//AsyncTest().Forget();
|
||||
|
||||
// AsyncTest().Forget();
|
||||
|
||||
}
|
||||
|
||||
static async UniTask YieldCore()
|
||||
{
|
||||
await UniTask.Yield();
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
#pragma warning disable CS1998
|
||||
|
||||
|
||||
static async UniTaskVoid AsyncTest()
|
||||
static async UniTask<int> AsyncTest()
|
||||
{
|
||||
// empty
|
||||
// empty
|
||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||
await new TestAwaiter(true, UniTaskStatus.Succeeded);
|
||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||
|
||||
|
||||
Console.WriteLine("foo");
|
||||
//return 10;
|
||||
return 10;
|
||||
}
|
||||
|
||||
|
||||
|
||||
473
src/UniTask.NetCoreSandbox/QueueCheck.cs
Normal file
473
src/UniTask.NetCoreSandbox/QueueCheck.cs
Normal file
@@ -0,0 +1,473 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
[Config(typeof(BenchmarkConfig))]
|
||||
public class QueueCheck
|
||||
{
|
||||
Node node1 = new Node();
|
||||
Node node2 = new Node();
|
||||
RefNode refNode1 = new RefNode();
|
||||
RefNode refNode2 = new RefNode();
|
||||
Queue<Node> q1 = new Queue<Node>();
|
||||
Stack<Node> s1 = new Stack<Node>();
|
||||
ConcurrentQueue<Node> cq = new ConcurrentQueue<Node>();
|
||||
ConcurrentStack<Node> cs = new ConcurrentStack<Node>();
|
||||
static TaskPool<Node> pool;
|
||||
static TaskPoolRefNode<RefNode> poolRefNode;
|
||||
static TaskPoolEqualNull<Node> poolEqualNull;
|
||||
static TaskPoolClass<Node> poolClass = new TaskPoolClass<Node>();
|
||||
static TaskPoolWithoutSize<Node> poolWithoutSize;
|
||||
static TaskPoolWithoutLock<Node> poolWithoutLock;
|
||||
|
||||
[Benchmark]
|
||||
public void Queue()
|
||||
{
|
||||
q1.Enqueue(node1);
|
||||
q1.Enqueue(node1);
|
||||
q1.TryDequeue(out _);
|
||||
q1.TryDequeue(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void QueueLock()
|
||||
{
|
||||
lock (q1) { q1.Enqueue(node1); }
|
||||
lock (q1) { q1.Enqueue(node1); }
|
||||
lock (q1) { q1.TryDequeue(out _); }
|
||||
lock (q1) { q1.TryDequeue(out _); }
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Stack()
|
||||
{
|
||||
s1.Push(node1);
|
||||
s1.Push(node2);
|
||||
s1.TryPop(out _);
|
||||
s1.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void StackLock()
|
||||
{
|
||||
lock (s1) { s1.Push(node1); }
|
||||
lock (s1) { s1.Push(node2); }
|
||||
lock (s1) { s1.TryPop(out _); }
|
||||
lock (s1) { s1.TryPop(out _); }
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ConcurrentQueue()
|
||||
{
|
||||
cq.Enqueue(node1);
|
||||
cq.Enqueue(node1);
|
||||
cq.TryDequeue(out _);
|
||||
cq.TryDequeue(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ConcurrentStack()
|
||||
{
|
||||
cs.Push(node1);
|
||||
cs.Push(node2);
|
||||
cs.TryPop(out _);
|
||||
cs.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPool()
|
||||
{
|
||||
pool.TryPush(node1);
|
||||
pool.TryPush(node2);
|
||||
pool.TryPop(out _);
|
||||
pool.TryPop(out _);
|
||||
}
|
||||
[Benchmark]
|
||||
public void TaskPoolRefNode()
|
||||
{
|
||||
poolRefNode.TryPush(refNode1);
|
||||
poolRefNode.TryPush(refNode2);
|
||||
poolRefNode.TryPop(out _);
|
||||
poolRefNode.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPoolEqualNull()
|
||||
{
|
||||
poolEqualNull.TryPush(node1);
|
||||
poolEqualNull.TryPush(node2);
|
||||
poolEqualNull.TryPop(out _);
|
||||
poolEqualNull.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPoolClass()
|
||||
{
|
||||
poolClass.TryPush(node1);
|
||||
poolClass.TryPush(node2);
|
||||
poolClass.TryPop(out _);
|
||||
poolClass.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPoolWithoutSize()
|
||||
{
|
||||
poolWithoutSize.TryPush(node1);
|
||||
poolWithoutSize.TryPush(node2);
|
||||
poolWithoutSize.TryPop(out _);
|
||||
poolWithoutSize.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPoolWithoutLock()
|
||||
{
|
||||
poolWithoutLock.TryPush(node1);
|
||||
poolWithoutLock.TryPush(node2);
|
||||
poolWithoutLock.TryPop(out _);
|
||||
poolWithoutLock.TryPop(out _);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Node : ITaskPoolNode<Node>
|
||||
{
|
||||
public Node NextNode { get; set; }
|
||||
}
|
||||
|
||||
public interface ITaskPoolNode<T>
|
||||
{
|
||||
T NextNode { get; set; }
|
||||
}
|
||||
|
||||
public sealed class RefNode :ITaskPoolRefNode<RefNode>
|
||||
{
|
||||
RefNode nextNode;
|
||||
public ref RefNode NextNode => ref nextNode;
|
||||
}
|
||||
|
||||
public interface ITaskPoolRefNode<T>
|
||||
{
|
||||
ref T NextNode { get; }
|
||||
}
|
||||
|
||||
|
||||
// mutable struct, don't mark readonly.
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TaskPoolWithoutLock<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int size;
|
||||
T root;
|
||||
|
||||
public int Size => size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
// Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
size++;
|
||||
// Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
//return false;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TaskPool<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int gate;
|
||||
int size;
|
||||
T root;
|
||||
|
||||
public int Size => size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
size++;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TaskPoolRefNode<T>
|
||||
where T : class, ITaskPoolRefNode<T>
|
||||
{
|
||||
int gate;
|
||||
int size;
|
||||
T root;
|
||||
|
||||
public int Size => size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
ref var nextNode = ref v.NextNode;
|
||||
root = nextNode;
|
||||
nextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
size++;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TaskPoolEqualNull<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int gate;
|
||||
int size;
|
||||
T root;
|
||||
|
||||
public int Size => size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (v != null)
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
size++;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class TaskPoolClass<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int gate;
|
||||
int size;
|
||||
T root;
|
||||
|
||||
public int Size => size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
size++;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TaskPoolWithoutSize<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int gate;
|
||||
T root;
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
167
src/UniTask.NetCoreTests/AsyncLazyTest.cs
Normal file
167
src/UniTask.NetCoreTests/AsyncLazyTest.cs
Normal file
@@ -0,0 +1,167 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests
|
||||
{
|
||||
public class AsyncLazyTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task LazyLazy()
|
||||
{
|
||||
{
|
||||
var l = UniTask.Lazy(() => After());
|
||||
var a = AwaitAwait(l.Task);
|
||||
var b = AwaitAwait(l.Task);
|
||||
var c = AwaitAwait(l.Task);
|
||||
|
||||
await a;
|
||||
await b;
|
||||
await c;
|
||||
}
|
||||
{
|
||||
var l = UniTask.Lazy(() => AfterException());
|
||||
var a = AwaitAwait(l.Task);
|
||||
var b = AwaitAwait(l.Task);
|
||||
var c = AwaitAwait(l.Task);
|
||||
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LazyImmediate()
|
||||
{
|
||||
{
|
||||
var l = UniTask.Lazy(() => UniTask.FromResult(1).AsUniTask());
|
||||
var a = AwaitAwait(l.Task);
|
||||
var b = AwaitAwait(l.Task);
|
||||
var c = AwaitAwait(l.Task);
|
||||
|
||||
await a;
|
||||
await b;
|
||||
await c;
|
||||
}
|
||||
{
|
||||
var l = UniTask.Lazy(() => UniTask.FromException(new TaskTestException()));
|
||||
var a = AwaitAwait(l.Task);
|
||||
var b = AwaitAwait(l.Task);
|
||||
var c = AwaitAwait(l.Task);
|
||||
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
|
||||
}
|
||||
}
|
||||
|
||||
static async UniTask AwaitAwait(UniTask t)
|
||||
{
|
||||
await t;
|
||||
}
|
||||
|
||||
|
||||
async UniTask After()
|
||||
{
|
||||
await UniTask.Yield();
|
||||
Thread.Sleep(TimeSpan.FromSeconds(1));
|
||||
await UniTask.Yield();
|
||||
await UniTask.Yield();
|
||||
}
|
||||
|
||||
async UniTask AfterException()
|
||||
{
|
||||
await UniTask.Yield();
|
||||
Thread.Sleep(TimeSpan.FromSeconds(1));
|
||||
await UniTask.Yield();
|
||||
throw new TaskTestException();
|
||||
}
|
||||
}
|
||||
|
||||
public class AsyncLazyTest2
|
||||
{
|
||||
[Fact]
|
||||
public async Task LazyLazy()
|
||||
{
|
||||
{
|
||||
var l = UniTask.Lazy(() => After());
|
||||
var a = AwaitAwait(l.Task);
|
||||
var b = AwaitAwait(l.Task);
|
||||
var c = AwaitAwait(l.Task);
|
||||
|
||||
var a2 = await a;
|
||||
var b2 = await b;
|
||||
var c2 = await c;
|
||||
(a2, b2, c2).Should().Be((10, 10, 10));
|
||||
}
|
||||
{
|
||||
var l = UniTask.Lazy(() => AfterException());
|
||||
var a = AwaitAwait(l.Task);
|
||||
var b = AwaitAwait(l.Task);
|
||||
var c = AwaitAwait(l.Task);
|
||||
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LazyImmediate()
|
||||
{
|
||||
{
|
||||
var l = UniTask.Lazy(() => UniTask.FromResult(1));
|
||||
var a = AwaitAwait(l.Task);
|
||||
var b = AwaitAwait(l.Task);
|
||||
var c = AwaitAwait(l.Task);
|
||||
|
||||
var a2 = await a;
|
||||
var b2 = await b;
|
||||
var c2 = await c;
|
||||
(a2, b2, c2).Should().Be((1, 1, 1));
|
||||
}
|
||||
{
|
||||
var l = UniTask.Lazy(() => UniTask.FromException<int>(new TaskTestException()));
|
||||
var a = AwaitAwait(l.Task);
|
||||
var b = AwaitAwait(l.Task);
|
||||
var c = AwaitAwait(l.Task);
|
||||
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
|
||||
}
|
||||
}
|
||||
|
||||
static async UniTask<int> AwaitAwait(UniTask<int> t)
|
||||
{
|
||||
return await t;
|
||||
}
|
||||
|
||||
|
||||
async UniTask<int> After()
|
||||
{
|
||||
await UniTask.Yield();
|
||||
Thread.Sleep(TimeSpan.FromSeconds(1));
|
||||
await UniTask.Yield();
|
||||
await UniTask.Yield();
|
||||
return 10;
|
||||
}
|
||||
|
||||
async UniTask<int> AfterException()
|
||||
{
|
||||
await UniTask.Yield();
|
||||
Thread.Sleep(TimeSpan.FromSeconds(1));
|
||||
await UniTask.Yield();
|
||||
throw new TaskTestException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,6 +112,85 @@ namespace NetCoreTests
|
||||
state.Value.Should().Be(20);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WaitAsyncTest()
|
||||
{
|
||||
var rp = new AsyncReactiveProperty<int>(128);
|
||||
|
||||
var f = await rp.FirstAsync();
|
||||
f.Should().Be(128);
|
||||
|
||||
{
|
||||
var t = rp.WaitAsync();
|
||||
rp.Value = 99;
|
||||
rp.Value = 100;
|
||||
var v = await t;
|
||||
|
||||
v.Should().Be(99);
|
||||
}
|
||||
|
||||
{
|
||||
var t = rp.WaitAsync();
|
||||
rp.Value = 99;
|
||||
rp.Value = 100;
|
||||
var v = await t;
|
||||
|
||||
v.Should().Be(99);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task WaitAsyncCancellationTest()
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
var rp = new AsyncReactiveProperty<int>(128);
|
||||
|
||||
var t = rp.WaitAsync(cts.Token);
|
||||
|
||||
cts.Cancel();
|
||||
|
||||
rp.Value = 99;
|
||||
rp.Value = 100;
|
||||
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(async () => { await t; });
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task ReadOnlyWaitAsyncTest()
|
||||
{
|
||||
var rp = new AsyncReactiveProperty<int>(128);
|
||||
var rrp = rp.ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
|
||||
|
||||
var t = rrp.WaitAsync();
|
||||
rp.Value = 99;
|
||||
rp.Value = 100;
|
||||
var v = await t;
|
||||
|
||||
v.Should().Be(99);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task ReadOnlyWaitAsyncCancellationTest()
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
var rp = new AsyncReactiveProperty<int>(128);
|
||||
var rrp = rp.ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
|
||||
|
||||
var t = rrp.WaitAsync(cts.Token);
|
||||
|
||||
cts.Cancel();
|
||||
|
||||
rp.Value = 99;
|
||||
rp.Value = 100;
|
||||
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(async () => { await t; });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
590
src/UniTask.NetCoreTests/CompletionSourceTest.cs
Normal file
590
src/UniTask.NetCoreTests/CompletionSourceTest.cs
Normal file
@@ -0,0 +1,590 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests
|
||||
{
|
||||
public class CompletionSourceTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task SetFirst()
|
||||
{
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
tcs.TrySetResult();
|
||||
await tcs.Task; // ok.
|
||||
await tcs.Task; // ok.
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||
}
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
tcs.TrySetException(new TestException());
|
||||
|
||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||
}
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
tcs.TrySetCanceled(cts.Token);
|
||||
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SingleOnFirst()
|
||||
{
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
async UniTask Await()
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
|
||||
tcs.TrySetResult();
|
||||
await a;
|
||||
await tcs.Task; // ok.
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||
}
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
async UniTask Await()
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
|
||||
tcs.TrySetException(new TestException());
|
||||
await Assert.ThrowsAsync<TestException>(async () => await a);
|
||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||
}
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
async UniTask Await()
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
|
||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
async UniTask Await()
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
|
||||
tcs.TrySetCanceled(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultiOne()
|
||||
{
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
async UniTask Await()
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
tcs.TrySetResult();
|
||||
await a;
|
||||
await b;
|
||||
await tcs.Task; // ok.
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||
}
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
async UniTask Await()
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
|
||||
tcs.TrySetException(new TestException());
|
||||
await Assert.ThrowsAsync<TestException>(async () => await a);
|
||||
await Assert.ThrowsAsync<TestException>(async () => await b);
|
||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||
}
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
async UniTask Await()
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
|
||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
async UniTask Await()
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
|
||||
tcs.TrySetCanceled(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultiTwo()
|
||||
{
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
async UniTask Await()
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
var c = Await();
|
||||
tcs.TrySetResult();
|
||||
await a;
|
||||
await b;
|
||||
await c;
|
||||
await tcs.Task; // ok.
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||
}
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
async UniTask Await()
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
var c = Await();
|
||||
|
||||
tcs.TrySetException(new TestException());
|
||||
await Assert.ThrowsAsync<TestException>(async () => await a);
|
||||
await Assert.ThrowsAsync<TestException>(async () => await b);
|
||||
await Assert.ThrowsAsync<TestException>(async () => await c);
|
||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||
}
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
async UniTask Await()
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
var c = Await();
|
||||
|
||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
|
||||
async UniTask Await()
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
var c = Await();
|
||||
|
||||
tcs.TrySetCanceled(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
}
|
||||
|
||||
class TestException : Exception
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class CompletionSourceTest2
|
||||
{
|
||||
[Fact]
|
||||
public async Task SetFirst()
|
||||
{
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
tcs.TrySetResult(10);
|
||||
var a = await tcs.Task; // ok.
|
||||
var b = await tcs.Task; // ok.
|
||||
a.Should().Be(10);
|
||||
b.Should().Be(10);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||
}
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
tcs.TrySetException(new TestException());
|
||||
|
||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||
}
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
tcs.TrySetCanceled(cts.Token);
|
||||
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SingleOnFirst()
|
||||
{
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
async UniTask<int> Await()
|
||||
{
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
|
||||
tcs.TrySetResult(10);
|
||||
var r1 = await a;
|
||||
var r2 = await tcs.Task; // ok.
|
||||
r1.Should().Be(10);
|
||||
r2.Should().Be(10);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||
}
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
async UniTask<int> Await()
|
||||
{
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
|
||||
tcs.TrySetException(new TestException());
|
||||
await Assert.ThrowsAsync<TestException>(async () => await a);
|
||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||
}
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
async UniTask<int> Await()
|
||||
{
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
|
||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
async UniTask<int> Await()
|
||||
{
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
|
||||
tcs.TrySetCanceled(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultiOne()
|
||||
{
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
async UniTask<int> Await()
|
||||
{
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
tcs.TrySetResult(10);
|
||||
var r1 = await a;
|
||||
var r2 = await b;
|
||||
var r3 = await tcs.Task; // ok.
|
||||
(r1, r2, r3).Should().Be((10, 10, 10));
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||
}
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
async UniTask<int> Await()
|
||||
{
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
|
||||
tcs.TrySetException(new TestException());
|
||||
await Assert.ThrowsAsync<TestException>(async () => await a);
|
||||
await Assert.ThrowsAsync<TestException>(async () => await b);
|
||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||
}
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
async UniTask<int> Await()
|
||||
{
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
|
||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
async UniTask<int> Await()
|
||||
{
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
|
||||
tcs.TrySetCanceled(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultiTwo()
|
||||
{
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
async UniTask<int> Await()
|
||||
{
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
var c = Await();
|
||||
tcs.TrySetResult(10);
|
||||
var r1 = await a;
|
||||
var r2 = await b;
|
||||
var r3 = await c;
|
||||
var r4 = await tcs.Task; // ok.
|
||||
(r1, r2, r3, r4).Should().Be((10, 10, 10, 10));
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||
}
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
async UniTask<int> Await()
|
||||
{
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
var c = Await();
|
||||
|
||||
tcs.TrySetException(new TestException());
|
||||
await Assert.ThrowsAsync<TestException>(async () => await a);
|
||||
await Assert.ThrowsAsync<TestException>(async () => await b);
|
||||
await Assert.ThrowsAsync<TestException>(async () => await c);
|
||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||
}
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
async UniTask<int> Await()
|
||||
{
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
var c = Await();
|
||||
|
||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
|
||||
async UniTask<int> Await()
|
||||
{
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
var a = Await();
|
||||
var b = Await();
|
||||
var c = Await();
|
||||
|
||||
tcs.TrySetCanceled(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||
}
|
||||
}
|
||||
|
||||
class TestException : Exception
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
170
src/UniTask.NetCoreTests/Linq/CreateTest.cs
Normal file
170
src/UniTask.NetCoreTests/Linq/CreateTest.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
#pragma warning disable CS1998
|
||||
#pragma warning disable CS0162
|
||||
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class CreateTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task SyncCreation()
|
||||
{
|
||||
var from = 10;
|
||||
var count = 100;
|
||||
|
||||
var xs = await UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
await writer.YieldAsync(from + i);
|
||||
}
|
||||
}).ToArrayAsync();
|
||||
|
||||
var ys = await Range(from, count).AsUniTaskAsyncEnumerable().ToArrayAsync();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SyncManually()
|
||||
{
|
||||
var list = new List<int>();
|
||||
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
||||
{
|
||||
list.Add(100);
|
||||
await writer.YieldAsync(10);
|
||||
|
||||
list.Add(200);
|
||||
await writer.YieldAsync(20);
|
||||
|
||||
list.Add(300);
|
||||
await writer.YieldAsync(30);
|
||||
|
||||
list.Add(400);
|
||||
});
|
||||
|
||||
list.Should().BeEmpty();
|
||||
var e = xs.GetAsyncEnumerator();
|
||||
|
||||
list.Should().BeEmpty();
|
||||
|
||||
await e.MoveNextAsync();
|
||||
list.Should().BeEquivalentTo(100);
|
||||
e.Current.Should().Be(10);
|
||||
|
||||
await e.MoveNextAsync();
|
||||
list.Should().BeEquivalentTo(100, 200);
|
||||
e.Current.Should().Be(20);
|
||||
|
||||
await e.MoveNextAsync();
|
||||
list.Should().BeEquivalentTo(100, 200, 300);
|
||||
e.Current.Should().Be(30);
|
||||
|
||||
(await e.MoveNextAsync()).Should().BeFalse();
|
||||
list.Should().BeEquivalentTo(100, 200, 300, 400);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SyncExceptionFirst()
|
||||
{
|
||||
var from = 10;
|
||||
var count = 100;
|
||||
|
||||
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
throw new UniTaskTestException();
|
||||
await writer.YieldAsync(from + i);
|
||||
}
|
||||
});
|
||||
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs.ToArrayAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SyncException()
|
||||
{
|
||||
var from = 10;
|
||||
var count = 100;
|
||||
|
||||
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
await writer.YieldAsync(from + i);
|
||||
|
||||
if (i == 15)
|
||||
{
|
||||
throw new UniTaskTestException();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs.ToArrayAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ASyncManually()
|
||||
{
|
||||
var list = new List<int>();
|
||||
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
||||
{
|
||||
await UniTask.Yield();
|
||||
|
||||
list.Add(100);
|
||||
await writer.YieldAsync(10);
|
||||
|
||||
await UniTask.Yield();
|
||||
|
||||
list.Add(200);
|
||||
await writer.YieldAsync(20);
|
||||
|
||||
await UniTask.Yield();
|
||||
list.Add(300);
|
||||
await UniTask.Yield();
|
||||
await writer.YieldAsync(30);
|
||||
|
||||
await UniTask.Yield();
|
||||
|
||||
list.Add(400);
|
||||
});
|
||||
|
||||
list.Should().BeEmpty();
|
||||
var e = xs.GetAsyncEnumerator();
|
||||
|
||||
list.Should().BeEmpty();
|
||||
|
||||
await e.MoveNextAsync();
|
||||
list.Should().BeEquivalentTo(100);
|
||||
e.Current.Should().Be(10);
|
||||
|
||||
await e.MoveNextAsync();
|
||||
list.Should().BeEquivalentTo(100, 200);
|
||||
e.Current.Should().Be(20);
|
||||
|
||||
await e.MoveNextAsync();
|
||||
list.Should().BeEquivalentTo(100, 200, 300);
|
||||
e.Current.Should().Be(30);
|
||||
|
||||
(await e.MoveNextAsync()).Should().BeFalse();
|
||||
list.Should().BeEquivalentTo(100, 200, 300, 400);
|
||||
}
|
||||
|
||||
async IAsyncEnumerable<int> Range(int from, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
yield return from + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ namespace NetCoreTests.Linq
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TakeUntil()
|
||||
public async Task TakeUntilCanceled()
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace NetCoreTests.Linq
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SkipUntil()
|
||||
public async Task SkipUntilCanceled()
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace NetCoreTests.Linq
|
||||
await c;
|
||||
var foo = await xs;
|
||||
|
||||
foo.Should().BeEquivalentTo(new[] { 30, 40 });
|
||||
foo.Should().BeEquivalentTo(new[] { 20, 30, 40 });
|
||||
|
||||
async Task CancelAsync()
|
||||
{
|
||||
@@ -102,5 +102,64 @@ namespace NetCoreTests.Linq
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TakeUntil()
|
||||
{
|
||||
var cts = new AsyncReactiveProperty<int>(0);
|
||||
|
||||
var rp = new AsyncReactiveProperty<int>(1);
|
||||
|
||||
var xs = rp.TakeUntil(cts.WaitAsync()).ToArrayAsync();
|
||||
|
||||
var c = CancelAsync();
|
||||
|
||||
await c;
|
||||
var foo = await xs;
|
||||
|
||||
foo.Should().BeEquivalentTo(new[] { 1, 10, 20 });
|
||||
|
||||
async Task CancelAsync()
|
||||
{
|
||||
rp.Value = 10;
|
||||
await Task.Yield();
|
||||
rp.Value = 20;
|
||||
await Task.Yield();
|
||||
cts.Value = 9999;
|
||||
rp.Value = 30;
|
||||
await Task.Yield();
|
||||
rp.Value = 40;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SkipUntil()
|
||||
{
|
||||
var cts = new AsyncReactiveProperty<int>(0);
|
||||
|
||||
var rp = new AsyncReactiveProperty<int>(1);
|
||||
|
||||
var xs = rp.SkipUntil(cts.WaitAsync()).ToArrayAsync();
|
||||
|
||||
var c = CancelAsync();
|
||||
|
||||
await c;
|
||||
var foo = await xs;
|
||||
|
||||
foo.Should().BeEquivalentTo(new[] { 20, 30, 40 });
|
||||
|
||||
async Task CancelAsync()
|
||||
{
|
||||
rp.Value = 10;
|
||||
await Task.Yield();
|
||||
rp.Value = 20;
|
||||
await Task.Yield();
|
||||
cts.Value = 9999;
|
||||
rp.Value = 30;
|
||||
await Task.Yield();
|
||||
rp.Value = 40;
|
||||
|
||||
rp.Dispose(); // complete.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
637
src/UniTask.NetCoreTests/TriggerEventTest.cs
Normal file
637
src/UniTask.NetCoreTests/TriggerEventTest.cs
Normal file
@@ -0,0 +1,637 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests
|
||||
{
|
||||
public class TriggerEventTest
|
||||
{
|
||||
[Fact]
|
||||
public void SimpleAdd()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
|
||||
ev.Add(one);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
// after removed, onemore
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
|
||||
ev.Add(one);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddFour()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
var four = new TestEvent(4);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(four);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
two.CompletedCalled.Count.Should().Be(1);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
|
||||
// after removed, onemore.
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
var four = new TestEvent(4);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(four);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
ev.Add(four);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
two.CompletedCalled.Count.Should().Be(1);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void OneRemove()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.Remove(one);
|
||||
|
||||
ev.SetResult(40);
|
||||
ev.SetResult(50);
|
||||
ev.SetResult(60);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public void TwoRemove()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.Remove(two);
|
||||
|
||||
ev.SetResult(40);
|
||||
ev.SetResult(50);
|
||||
ev.SetResult(60);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public void ThreeRemove()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.Remove(three);
|
||||
|
||||
ev.SetResult(40);
|
||||
ev.SetResult(50);
|
||||
ev.SetResult(60);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveSelf()
|
||||
{
|
||||
new RemoveMe().Run1();
|
||||
new RemoveMe().Run2();
|
||||
new RemoveMe().Run3();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveNextInIterating()
|
||||
{
|
||||
new RemoveNext().Run1();
|
||||
new RemoveNext().Run2();
|
||||
new RemoveNext().Run3();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveNextNextTest()
|
||||
{
|
||||
new RemoveNextNext().Run1();
|
||||
new RemoveNextNext().Run2();
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void AddTest()
|
||||
{
|
||||
new AddMe().Run1();
|
||||
new AddMe().Run2();
|
||||
}
|
||||
|
||||
public class RemoveMe
|
||||
{
|
||||
TriggerEvent<int> ev;
|
||||
|
||||
public void Run1()
|
||||
{
|
||||
TestEvent one = default;
|
||||
one = new TestEvent(1, () => ev.Remove(one));
|
||||
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
{
|
||||
TestEvent one = default;
|
||||
one = new TestEvent(1, () => ev.Remove(one));
|
||||
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(two);
|
||||
ev.Add(one); // add second.
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run3()
|
||||
{
|
||||
TestEvent one = default;
|
||||
one = new TestEvent(1, () => ev.Remove(one));
|
||||
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(one); // add thired.
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
}
|
||||
|
||||
public class RemoveNext
|
||||
{
|
||||
TriggerEvent<int> ev;
|
||||
|
||||
public void Run1()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
one = new TestEvent(1, () => ev.Remove(two));
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Count.Should().Be(0);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
one = new TestEvent(1, () => ev.Remove(two));
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
|
||||
ev.Add(two);
|
||||
ev.Add(one); // add second
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run3()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
one = new TestEvent(1, () => ev.Remove(two));
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(one); // add thired.
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
}
|
||||
|
||||
public class RemoveNextNext
|
||||
{
|
||||
TriggerEvent<int> ev;
|
||||
|
||||
public void Run1()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
TestEvent four = default;
|
||||
one = new TestEvent(1, () => { ev.Remove(two); ev.Remove(three); });
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
four = new TestEvent(4);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(four);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Count.Should().Be(0);
|
||||
three.NextCalled.Count.Should().Be(0);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
TestEvent four = default;
|
||||
one = new TestEvent(1, () => { ev.Remove(one); ev.Remove(two); ev.Remove(three); });
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
four = new TestEvent(4);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(four);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Count.Should().Be(0);
|
||||
three.NextCalled.Count.Should().Be(0);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class AddMe
|
||||
{
|
||||
TriggerEvent<int> ev;
|
||||
|
||||
public void Run1()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
TestEvent four = default;
|
||||
|
||||
one = new TestEvent(1, () =>
|
||||
{
|
||||
if (two == null)
|
||||
{
|
||||
ev.Add(two = new TestEvent(2));
|
||||
}
|
||||
else if (three == null)
|
||||
{
|
||||
ev.Add(three = new TestEvent(3));
|
||||
}
|
||||
else if (four == null)
|
||||
{
|
||||
ev.Add(four = new TestEvent(4));
|
||||
}
|
||||
});
|
||||
|
||||
ev.Add(one);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
ev.SetResult(40);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40);
|
||||
two.NextCalled.Should().BeEquivalentTo(20, 30, 40);
|
||||
three.NextCalled.Should().BeEquivalentTo(30, 40);
|
||||
four.NextCalled.Should().BeEquivalentTo(40);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
TestEvent four = default;
|
||||
|
||||
one = new TestEvent(1, () =>
|
||||
{
|
||||
if (two == null)
|
||||
{
|
||||
ev.Add(two = new TestEvent(2, () =>
|
||||
{
|
||||
if (three == null)
|
||||
{
|
||||
ev.Add(three = new TestEvent(3, () =>
|
||||
{
|
||||
if (four == null)
|
||||
{
|
||||
ev.Add(four = new TestEvent(4));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
ev.Add(one);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
ev.SetResult(40);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40);
|
||||
two.NextCalled.Should().BeEquivalentTo(20, 30, 40);
|
||||
three.NextCalled.Should().BeEquivalentTo(30, 40);
|
||||
four.NextCalled.Should().BeEquivalentTo(40);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TestEvent : ITriggerHandler<int>
|
||||
{
|
||||
public readonly int Id;
|
||||
readonly Action iteratingEvent;
|
||||
|
||||
public TestEvent(int id)
|
||||
{
|
||||
this.Id = id;
|
||||
}
|
||||
|
||||
public TestEvent(int id, Action iteratingEvent)
|
||||
{
|
||||
this.Id = id;
|
||||
this.iteratingEvent = iteratingEvent;
|
||||
}
|
||||
|
||||
public List<int> NextCalled = new List<int>();
|
||||
public List<Exception> ErrorCalled = new List<Exception>();
|
||||
public List<object> CompletedCalled = new List<object>();
|
||||
public List<CancellationToken> CancelCalled = new List<CancellationToken>();
|
||||
|
||||
public ITriggerHandler<int> Prev { get; set; }
|
||||
public ITriggerHandler<int> Next { get; set; }
|
||||
|
||||
public void OnCanceled(CancellationToken cancellationToken)
|
||||
{
|
||||
CancelCalled.Add(cancellationToken);
|
||||
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
CompletedCalled.Add(new object());
|
||||
}
|
||||
|
||||
public void OnError(Exception ex)
|
||||
{
|
||||
ErrorCalled.Add(ex);
|
||||
}
|
||||
|
||||
public void OnNext(int value)
|
||||
{
|
||||
NextCalled.Add(value);
|
||||
iteratingEvent?.Invoke();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Id.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
40
src/UniTask.NetCoreTests/WithCancellationTest.cs
Normal file
40
src/UniTask.NetCoreTests/WithCancellationTest.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests
|
||||
{
|
||||
public class WithCancellationTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task Standard()
|
||||
{
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
|
||||
var v = await UniTask.Run(() => 10).WithCancellation(cts.Token);
|
||||
|
||||
v.Should().Be(10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Cancel()
|
||||
{
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
|
||||
var t = UniTask.Create(async () =>
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||
return 10;
|
||||
}).WithCancellation(cts.Token);
|
||||
|
||||
cts.Cancel();
|
||||
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await t)).CancellationToken.Should().Be(cts.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
|
||||
@@ -7,113 +7,239 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public class AsyncLazy
|
||||
{
|
||||
Func<UniTask> valueFactory;
|
||||
UniTask target;
|
||||
static Action<object> continuation = SetCompletionSource;
|
||||
|
||||
Func<UniTask> taskFactory;
|
||||
UniTaskCompletionSource completionSource;
|
||||
UniTask.Awaiter awaiter;
|
||||
|
||||
object syncLock;
|
||||
bool initialized;
|
||||
|
||||
public AsyncLazy(Func<UniTask> valueFactory)
|
||||
public AsyncLazy(Func<UniTask> taskFactory)
|
||||
{
|
||||
this.valueFactory = valueFactory;
|
||||
this.target = default;
|
||||
this.taskFactory = taskFactory;
|
||||
this.completionSource = new UniTaskCompletionSource();
|
||||
this.syncLock = new object();
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
internal AsyncLazy(UniTask value)
|
||||
internal AsyncLazy(UniTask task)
|
||||
{
|
||||
this.valueFactory = null;
|
||||
this.target = value;
|
||||
this.taskFactory = null;
|
||||
this.completionSource = new UniTaskCompletionSource();
|
||||
this.syncLock = null;
|
||||
this.initialized = true;
|
||||
|
||||
var awaiter = task.GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
SetCompletionSource(awaiter);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.awaiter = awaiter;
|
||||
awaiter.SourceOnCompleted(continuation, this);
|
||||
}
|
||||
}
|
||||
|
||||
public UniTask Task => EnsureInitialized();
|
||||
public UniTask Task
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureInitialized();
|
||||
return completionSource.Task;
|
||||
}
|
||||
}
|
||||
|
||||
public UniTask.Awaiter GetAwaiter() => EnsureInitialized().GetAwaiter();
|
||||
|
||||
UniTask EnsureInitialized()
|
||||
public UniTask.Awaiter GetAwaiter() => Task.GetAwaiter();
|
||||
|
||||
void EnsureInitialized()
|
||||
{
|
||||
if (Volatile.Read(ref initialized))
|
||||
{
|
||||
return target;
|
||||
return;
|
||||
}
|
||||
|
||||
return EnsureInitializedCore();
|
||||
EnsureInitializedCore();
|
||||
}
|
||||
|
||||
UniTask EnsureInitializedCore()
|
||||
void EnsureInitializedCore()
|
||||
{
|
||||
lock (syncLock)
|
||||
{
|
||||
if (!Volatile.Read(ref initialized))
|
||||
{
|
||||
var f = Interlocked.Exchange(ref valueFactory, null);
|
||||
var f = Interlocked.Exchange(ref taskFactory, null);
|
||||
if (f != null)
|
||||
{
|
||||
target = f().Preserve(); // with preserve(allow multiple await).
|
||||
var task = f();
|
||||
var awaiter = task.GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
SetCompletionSource(awaiter);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.awaiter = awaiter;
|
||||
awaiter.SourceOnCompleted(continuation, this);
|
||||
}
|
||||
|
||||
Volatile.Write(ref initialized, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
void SetCompletionSource(in UniTask.Awaiter awaiter)
|
||||
{
|
||||
try
|
||||
{
|
||||
awaiter.GetResult();
|
||||
completionSource.TrySetResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
completionSource.TrySetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static void SetCompletionSource(object state)
|
||||
{
|
||||
var self = (AsyncLazy)state;
|
||||
try
|
||||
{
|
||||
self.awaiter.GetResult();
|
||||
self.completionSource.TrySetResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
self.completionSource.TrySetException(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
self.awaiter = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AsyncLazy<T>
|
||||
{
|
||||
Func<UniTask<T>> valueFactory;
|
||||
UniTask<T> target;
|
||||
static Action<object> continuation = SetCompletionSource;
|
||||
|
||||
Func<UniTask<T>> taskFactory;
|
||||
UniTaskCompletionSource<T> completionSource;
|
||||
UniTask<T>.Awaiter awaiter;
|
||||
|
||||
object syncLock;
|
||||
bool initialized;
|
||||
|
||||
public AsyncLazy(Func<UniTask<T>> valueFactory)
|
||||
public AsyncLazy(Func<UniTask<T>> taskFactory)
|
||||
{
|
||||
this.valueFactory = valueFactory;
|
||||
this.target = default;
|
||||
this.taskFactory = taskFactory;
|
||||
this.completionSource = new UniTaskCompletionSource<T>();
|
||||
this.syncLock = new object();
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
internal AsyncLazy(UniTask<T> value)
|
||||
internal AsyncLazy(UniTask<T> task)
|
||||
{
|
||||
this.valueFactory = null;
|
||||
this.target = value;
|
||||
this.taskFactory = null;
|
||||
this.completionSource = new UniTaskCompletionSource<T>();
|
||||
this.syncLock = null;
|
||||
this.initialized = true;
|
||||
|
||||
var awaiter = task.GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
SetCompletionSource(awaiter);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.awaiter = awaiter;
|
||||
awaiter.SourceOnCompleted(continuation, this);
|
||||
}
|
||||
}
|
||||
|
||||
public UniTask<T> Task => EnsureInitialized();
|
||||
public UniTask<T> Task
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureInitialized();
|
||||
return completionSource.Task;
|
||||
}
|
||||
}
|
||||
|
||||
public UniTask<T>.Awaiter GetAwaiter() => EnsureInitialized().GetAwaiter();
|
||||
|
||||
UniTask<T> EnsureInitialized()
|
||||
public UniTask<T>.Awaiter GetAwaiter() => Task.GetAwaiter();
|
||||
|
||||
void EnsureInitialized()
|
||||
{
|
||||
if (Volatile.Read(ref initialized))
|
||||
{
|
||||
return target;
|
||||
return;
|
||||
}
|
||||
|
||||
return EnsureInitializedCore();
|
||||
EnsureInitializedCore();
|
||||
}
|
||||
|
||||
UniTask<T> EnsureInitializedCore()
|
||||
void EnsureInitializedCore()
|
||||
{
|
||||
lock (syncLock)
|
||||
{
|
||||
if (!Volatile.Read(ref initialized))
|
||||
{
|
||||
var f = Interlocked.Exchange(ref valueFactory, null);
|
||||
var f = Interlocked.Exchange(ref taskFactory, null);
|
||||
if (f != null)
|
||||
{
|
||||
target = f().Preserve(); // with preserve(allow multiple await).
|
||||
var task = f();
|
||||
var awaiter = task.GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
SetCompletionSource(awaiter);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.awaiter = awaiter;
|
||||
awaiter.SourceOnCompleted(continuation, this);
|
||||
}
|
||||
|
||||
Volatile.Write(ref initialized, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
void SetCompletionSource(in UniTask<T>.Awaiter awaiter)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = awaiter.GetResult();
|
||||
completionSource.TrySetResult(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
completionSource.TrySetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static void SetCompletionSource(object state)
|
||||
{
|
||||
var self = (AsyncLazy<T>)state;
|
||||
try
|
||||
{
|
||||
var result = self.awaiter.GetResult();
|
||||
self.completionSource.TrySetResult(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
self.completionSource.TrySetException(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
self.awaiter = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
@@ -8,6 +7,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
T Value { get; }
|
||||
IUniTaskAsyncEnumerable<T> WithoutCurrent();
|
||||
UniTask<T> WaitAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public interface IAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>
|
||||
@@ -70,6 +70,11 @@ namespace Cysharp.Threading.Tasks
|
||||
return latestValue?.ToString();
|
||||
}
|
||||
|
||||
public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
static bool isValueType;
|
||||
|
||||
static AsyncReactiveProperty()
|
||||
@@ -77,7 +82,136 @@ namespace Cysharp.Threading.Tasks
|
||||
isValueType = typeof(T).IsValueType;
|
||||
}
|
||||
|
||||
class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
|
||||
sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
|
||||
{
|
||||
static Action<object> cancellationCallback = CancellationCallback;
|
||||
|
||||
static TaskPool<WaitAsyncSource> pool;
|
||||
WaitAsyncSource nextNode;
|
||||
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
|
||||
|
||||
static WaitAsyncSource()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size);
|
||||
}
|
||||
|
||||
AsyncReactiveProperty<T> parent;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
|
||||
WaitAsyncSource()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource<T> Create(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new WaitAsyncSource();
|
||||
}
|
||||
|
||||
result.parent = parent;
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result);
|
||||
}
|
||||
|
||||
result.parent.triggerEvent.Add(result);
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancellationTokenRegistration = default;
|
||||
parent.triggerEvent.Remove(this);
|
||||
parent = null;
|
||||
cancellationToken = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
static void CancellationCallback(object state)
|
||||
{
|
||||
var self = (WaitAsyncSource)state;
|
||||
self.OnCanceled(self.cancellationToken);
|
||||
}
|
||||
|
||||
// IUniTaskSource
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
return core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
GetResult(token);
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
// ITriggerHandler
|
||||
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||
|
||||
public void OnCanceled(CancellationToken cancellationToken)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
// Complete as Cancel.
|
||||
core.TrySetCanceled(CancellationToken.None);
|
||||
}
|
||||
|
||||
public void OnError(Exception ex)
|
||||
{
|
||||
core.TrySetException(ex);
|
||||
}
|
||||
|
||||
public void OnNext(T value)
|
||||
{
|
||||
core.TrySetResult(value);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
|
||||
{
|
||||
readonly AsyncReactiveProperty<T> parent;
|
||||
|
||||
@@ -120,6 +254,9 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public T Current => value;
|
||||
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
// raise latest value on first call.
|
||||
@@ -251,6 +388,11 @@ namespace Cysharp.Threading.Tasks
|
||||
return latestValue?.ToString();
|
||||
}
|
||||
|
||||
public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
static bool isValueType;
|
||||
|
||||
static ReadOnlyAsyncReactiveProperty()
|
||||
@@ -258,7 +400,136 @@ namespace Cysharp.Threading.Tasks
|
||||
isValueType = typeof(T).IsValueType;
|
||||
}
|
||||
|
||||
class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
|
||||
sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
|
||||
{
|
||||
static Action<object> cancellationCallback = CancellationCallback;
|
||||
|
||||
static TaskPool<WaitAsyncSource> pool;
|
||||
WaitAsyncSource nextNode;
|
||||
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
|
||||
|
||||
static WaitAsyncSource()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size);
|
||||
}
|
||||
|
||||
ReadOnlyAsyncReactiveProperty<T> parent;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
|
||||
WaitAsyncSource()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource<T> Create(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new WaitAsyncSource();
|
||||
}
|
||||
|
||||
result.parent = parent;
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result);
|
||||
}
|
||||
|
||||
result.parent.triggerEvent.Add(result);
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancellationTokenRegistration = default;
|
||||
parent.triggerEvent.Remove(this);
|
||||
parent = null;
|
||||
cancellationToken = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
static void CancellationCallback(object state)
|
||||
{
|
||||
var self = (WaitAsyncSource)state;
|
||||
self.OnCanceled(self.cancellationToken);
|
||||
}
|
||||
|
||||
// IUniTaskSource
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
return core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
GetResult(token);
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
// ITriggerHandler
|
||||
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||
|
||||
public void OnCanceled(CancellationToken cancellationToken)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
// Complete as Cancel.
|
||||
core.TrySetCanceled(CancellationToken.None);
|
||||
}
|
||||
|
||||
public void OnError(Exception ex)
|
||||
{
|
||||
core.TrySetException(ex);
|
||||
}
|
||||
|
||||
public void OnNext(T value)
|
||||
{
|
||||
core.TrySetResult(value);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
|
||||
{
|
||||
readonly ReadOnlyAsyncReactiveProperty<T> parent;
|
||||
|
||||
@@ -300,6 +571,8 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
public T Current => value;
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
|
||||
@@ -9,6 +9,56 @@ namespace Cysharp.Threading.Tasks
|
||||
public static class CancellationTokenExtensions
|
||||
{
|
||||
static readonly Action<object> cancellationTokenCallback = Callback;
|
||||
static readonly Action<object> disposeCallback = DisposeCallback;
|
||||
|
||||
public static CancellationToken ToCancellationToken(this UniTask task)
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
ToCancellationTokenCore(task, cts).Forget();
|
||||
return cts.Token;
|
||||
}
|
||||
|
||||
public static CancellationToken ToCancellationToken(this UniTask task, CancellationToken linkToken)
|
||||
{
|
||||
if (linkToken.IsCancellationRequested)
|
||||
{
|
||||
return linkToken;
|
||||
}
|
||||
|
||||
if (!linkToken.CanBeCanceled)
|
||||
{
|
||||
return ToCancellationToken(task);
|
||||
}
|
||||
|
||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(linkToken);
|
||||
ToCancellationTokenCore(task, cts).Forget();
|
||||
|
||||
return cts.Token;
|
||||
}
|
||||
|
||||
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task)
|
||||
{
|
||||
return ToCancellationToken(task.AsUniTask());
|
||||
}
|
||||
|
||||
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task, CancellationToken linkToken)
|
||||
{
|
||||
return ToCancellationToken(task.AsUniTask(), linkToken);
|
||||
}
|
||||
|
||||
static async UniTaskVoid ToCancellationTokenCore(UniTask task, CancellationTokenSource cts)
|
||||
{
|
||||
try
|
||||
{
|
||||
await task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
}
|
||||
cts.Cancel();
|
||||
cts.Dispose();
|
||||
}
|
||||
|
||||
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -75,6 +125,17 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static CancellationTokenRegistration AddTo(this IDisposable disposable, CancellationToken cancellationToken)
|
||||
{
|
||||
return cancellationToken.RegisterWithoutCaptureExecutionContext(disposeCallback, disposable);
|
||||
}
|
||||
|
||||
static void DisposeCallback(object state)
|
||||
{
|
||||
var d = (IDisposable)state;
|
||||
d.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public struct CancellationTokenAwaitable
|
||||
|
||||
@@ -9,15 +9,15 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static class CancellationTokenSourceExtensions
|
||||
{
|
||||
public static void CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||
public static void CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||
{
|
||||
var delay = UniTask.Delay(millisecondsDelay, ignoreTimeScale, delayTiming, cts.Token);
|
||||
var delay = UniTask.Delay(millisecondsDelay, delayType, delayTiming, cts.Token);
|
||||
CancelAfterCore(cts, delay).Forget();
|
||||
}
|
||||
|
||||
public static void CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||
public static void CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||
{
|
||||
var delay = UniTask.Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cts.Token);
|
||||
var delay = UniTask.Delay(delayTimeSpan, delayType, delayTiming, cts.Token);
|
||||
CancelAfterCore(cts, delay).Forget();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct AsyncUniTaskMethodBuilder
|
||||
{
|
||||
// cache items.
|
||||
AutoResetUniTaskCompletionSource promise;
|
||||
internal IMoveNextRunner runner;
|
||||
IStateMachineRunnerPromise runnerPromise;
|
||||
Exception ex;
|
||||
|
||||
// 1. Static Create method.
|
||||
[DebuggerHidden]
|
||||
@@ -31,102 +30,81 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if (promise != null)
|
||||
if (runnerPromise != null)
|
||||
{
|
||||
return promise.Task;
|
||||
return runnerPromise.Task;
|
||||
}
|
||||
|
||||
if (runner == null)
|
||||
else if (ex != null)
|
||||
{
|
||||
return UniTask.FromException(ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
promise = AutoResetUniTaskCompletionSource.Create();
|
||||
return promise.Task;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. SetException
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
var p = promise; // after return, promise will clear so require to store local.
|
||||
|
||||
// runner is finished, return first.
|
||||
if (runner != null)
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
runner.Return();
|
||||
runner = null;
|
||||
}
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
p.TrySetException(exception);
|
||||
ex = exception;
|
||||
}
|
||||
else
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource.CreateFromException(exception, out _);
|
||||
runnerPromise.SetException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. SetResult
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult()
|
||||
{
|
||||
var p = promise; // after return, promise will clear so require to store local.
|
||||
|
||||
// runner is finished, return first.
|
||||
if (runner != null)
|
||||
if (runnerPromise != null)
|
||||
{
|
||||
runner.Return();
|
||||
runner = null;
|
||||
}
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
p.TrySetResult();
|
||||
runnerPromise.SetResult();
|
||||
}
|
||||
}
|
||||
|
||||
// 5. AwaitOnCompleted
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (promise == null)
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource.Create();
|
||||
}
|
||||
if (runner == null)
|
||||
{
|
||||
MoveNextRunner<TStateMachine>.SetRunner(ref this, ref stateMachine);
|
||||
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
awaiter.OnCompleted(runnerPromise.MoveNext);
|
||||
}
|
||||
|
||||
// 6. AwaitUnsafeOnCompleted
|
||||
[DebuggerHidden]
|
||||
[SecuritySafeCritical]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (promise == null)
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource.Create();
|
||||
}
|
||||
if (runner == null)
|
||||
{
|
||||
MoveNextRunner<TStateMachine>.SetRunner(ref this, ref stateMachine);
|
||||
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||
}
|
||||
|
||||
awaiter.UnsafeOnCompleted(runner.CallMoveNext);
|
||||
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
|
||||
}
|
||||
|
||||
// 7. Start
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
@@ -160,9 +138,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct AsyncUniTaskMethodBuilder<T>
|
||||
{
|
||||
// cache items.
|
||||
AutoResetUniTaskCompletionSource<T> promise;
|
||||
internal IMoveNextRunner runner;
|
||||
IStateMachineRunnerPromise<T> runnerPromise;
|
||||
Exception ex;
|
||||
T result;
|
||||
|
||||
// 1. Static Create method.
|
||||
@@ -174,111 +151,91 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
}
|
||||
|
||||
// 2. TaskLike Task property.
|
||||
[DebuggerHidden]
|
||||
public UniTask<T> Task
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if (promise != null)
|
||||
if (runnerPromise != null)
|
||||
{
|
||||
return promise.Task;
|
||||
return runnerPromise.Task;
|
||||
}
|
||||
|
||||
if (runner == null)
|
||||
else if (ex != null)
|
||||
{
|
||||
return UniTask.FromException<T>(ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
return UniTask.FromResult(result);
|
||||
}
|
||||
|
||||
promise = AutoResetUniTaskCompletionSource<T>.Create();
|
||||
return promise.Task;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. SetException
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
var p = promise; // after return, promise will clear so require to store local.
|
||||
|
||||
// runner is finished, return first.
|
||||
if (runner != null)
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
runner.Return();
|
||||
runner = null;
|
||||
}
|
||||
|
||||
if (p == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource<T>.CreateFromException(exception, out _);
|
||||
ex = exception;
|
||||
}
|
||||
else
|
||||
{
|
||||
p.TrySetException(exception);
|
||||
runnerPromise.SetException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. SetResult
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult(T result)
|
||||
{
|
||||
var p = promise; // after return, promise will clear so require to store local.
|
||||
|
||||
// runner is finished, return first.
|
||||
if (runner != null)
|
||||
{
|
||||
runner.Return();
|
||||
runner = null;
|
||||
}
|
||||
|
||||
if (p == null)
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
this.result = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
p.TrySetResult(result);
|
||||
runnerPromise.SetResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. AwaitOnCompleted
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (promise == null)
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource<T>.Create();
|
||||
}
|
||||
if (runner == null)
|
||||
{
|
||||
MoveNextRunner<TStateMachine>.SetRunner(ref this, ref stateMachine);
|
||||
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
awaiter.OnCompleted(runnerPromise.MoveNext);
|
||||
}
|
||||
|
||||
// 6. AwaitUnsafeOnCompleted
|
||||
[DebuggerHidden]
|
||||
[SecuritySafeCritical]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (promise == null)
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource<T>.Create();
|
||||
}
|
||||
if (runner == null)
|
||||
{
|
||||
MoveNextRunner<TStateMachine>.SetRunner(ref this, ref stateMachine);
|
||||
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||
}
|
||||
|
||||
awaiter.UnsafeOnCompleted(runner.CallMoveNext);
|
||||
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
|
||||
}
|
||||
|
||||
// 7. Start
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
{
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct AsyncUniTaskVoidMethodBuilder
|
||||
{
|
||||
internal IMoveNextRunner runner;
|
||||
IStateMachineRunner runner;
|
||||
|
||||
// 1. Static Create method.
|
||||
[DebuggerHidden]
|
||||
@@ -33,12 +35,18 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 3. SetException
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
// runner is finished, return first.
|
||||
if (runner != null)
|
||||
{
|
||||
#if ENABLE_IL2CPP
|
||||
// workaround for IL2CPP bug.
|
||||
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, runner.ReturnAction);
|
||||
#else
|
||||
runner.Return();
|
||||
#endif
|
||||
runner = null;
|
||||
}
|
||||
|
||||
@@ -47,43 +55,51 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 4. SetResult
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult()
|
||||
{
|
||||
// runner is finished, return.
|
||||
if (runner != null)
|
||||
{
|
||||
#if ENABLE_IL2CPP
|
||||
// workaround for IL2CPP bug.
|
||||
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, runner.ReturnAction);
|
||||
#else
|
||||
runner.Return();
|
||||
#endif
|
||||
runner = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. AwaitOnCompleted
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (runner == null)
|
||||
{
|
||||
MoveNextRunner<TStateMachine>.SetRunner(ref this, ref stateMachine);
|
||||
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
awaiter.OnCompleted(runner.MoveNext);
|
||||
}
|
||||
|
||||
// 6. AwaitUnsafeOnCompleted
|
||||
[DebuggerHidden]
|
||||
[SecuritySafeCritical]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (runner == null)
|
||||
{
|
||||
MoveNextRunner<TStateMachine>.SetRunner(ref this, ref stateMachine);
|
||||
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner);
|
||||
}
|
||||
|
||||
awaiter.UnsafeOnCompleted(runner.CallMoveNext);
|
||||
awaiter.UnsafeOnCompleted(runner.MoveNext);
|
||||
}
|
||||
|
||||
// 7. Start
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
{
|
||||
internal interface IMoveNextRunner
|
||||
{
|
||||
Action CallMoveNext { get; }
|
||||
void Return();
|
||||
}
|
||||
|
||||
internal sealed class MoveNextRunner<TStateMachine> : IMoveNextRunner, IPromisePoolItem
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
static PromisePool<MoveNextRunner<TStateMachine>> pool = new PromisePool<MoveNextRunner<TStateMachine>>();
|
||||
|
||||
TStateMachine stateMachine;
|
||||
internal readonly Action callMoveNext;
|
||||
|
||||
public Action CallMoveNext => callMoveNext;
|
||||
|
||||
MoveNextRunner()
|
||||
{
|
||||
callMoveNext = Run;
|
||||
}
|
||||
|
||||
public static void SetRunner(ref AsyncUniTaskMethodBuilder builder, ref TStateMachine stateMachine)
|
||||
{
|
||||
var result = pool.TryRent();
|
||||
if (result == null)
|
||||
{
|
||||
result = new MoveNextRunner<TStateMachine>();
|
||||
}
|
||||
|
||||
builder.runner = result; // set runner before copied.
|
||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||
}
|
||||
|
||||
public static void SetRunner<T>(ref AsyncUniTaskMethodBuilder<T> builder, ref TStateMachine stateMachine)
|
||||
{
|
||||
var result = pool.TryRent();
|
||||
if (result == null)
|
||||
{
|
||||
result = new MoveNextRunner<TStateMachine>();
|
||||
}
|
||||
|
||||
builder.runner = result; // set runner before copied.
|
||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||
}
|
||||
|
||||
public static void SetRunner(ref AsyncUniTaskVoidMethodBuilder builder, ref TStateMachine stateMachine)
|
||||
{
|
||||
var result = pool.TryRent();
|
||||
if (result == null)
|
||||
{
|
||||
result = new MoveNextRunner<TStateMachine>();
|
||||
}
|
||||
|
||||
builder.runner = result; // set runner before copied.
|
||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void Run()
|
||||
{
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
public void Return()
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
}
|
||||
|
||||
void IPromisePoolItem.Reset()
|
||||
{
|
||||
stateMachine = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,380 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
{
|
||||
// #ENABLE_IL2CPP in this file is to avoid bug of IL2CPP VM.
|
||||
// Issue is tracked on https://issuetracker.unity3d.com/issues/il2cpp-incorrect-results-when-calling-a-method-from-outside-class-in-a-struct
|
||||
// but currently it is labeled `Won't Fix`.
|
||||
|
||||
internal interface IStateMachineRunner
|
||||
{
|
||||
Action MoveNext { get; }
|
||||
void Return();
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
Action ReturnAction { get; }
|
||||
#endif
|
||||
}
|
||||
|
||||
internal interface IStateMachineRunnerPromise : IUniTaskSource
|
||||
{
|
||||
Action MoveNext { get; }
|
||||
UniTask Task { get; }
|
||||
void SetResult();
|
||||
void SetException(Exception exception);
|
||||
}
|
||||
|
||||
internal interface IStateMachineRunnerPromise<T> : IUniTaskSource<T>
|
||||
{
|
||||
Action MoveNext { get; }
|
||||
UniTask<T> Task { get; }
|
||||
void SetResult(T result);
|
||||
void SetException(Exception exception);
|
||||
}
|
||||
|
||||
internal static class StateMachineUtility
|
||||
{
|
||||
// Get AsyncStateMachine internal state to check IL2CPP bug
|
||||
public static int GetState(IAsyncStateMachine stateMachine)
|
||||
{
|
||||
var info = stateMachine.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
|
||||
.First(x => x.Name.EndsWith("__state"));
|
||||
return (int)info.GetValue(stateMachine);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class AsyncUniTaskVoid<TStateMachine> : IStateMachineRunner, ITaskPoolNode<AsyncUniTaskVoid<TStateMachine>>, IUniTaskSource
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
static TaskPool<AsyncUniTaskVoid<TStateMachine>> pool;
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
public Action ReturnAction { get; }
|
||||
#endif
|
||||
|
||||
TStateMachine stateMachine;
|
||||
|
||||
public Action MoveNext { get; }
|
||||
|
||||
public AsyncUniTaskVoid()
|
||||
{
|
||||
MoveNext = Run;
|
||||
#if ENABLE_IL2CPP
|
||||
ReturnAction = Return;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunner runnerFieldRef)
|
||||
{
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new AsyncUniTaskVoid<TStateMachine>();
|
||||
}
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
runnerFieldRef = result; // set runner before copied.
|
||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||
}
|
||||
|
||||
static AsyncUniTaskVoid()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size);
|
||||
}
|
||||
|
||||
AsyncUniTaskVoid<TStateMachine> nextNode;
|
||||
public ref AsyncUniTaskVoid<TStateMachine> NextNode => ref nextNode;
|
||||
|
||||
public void Return()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
stateMachine = default;
|
||||
pool.TryPush(this);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void Run()
|
||||
{
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
// dummy interface implementation for TaskTracker.
|
||||
|
||||
UniTaskStatus IUniTaskSource.GetStatus(short token)
|
||||
{
|
||||
return UniTaskStatus.Pending;
|
||||
}
|
||||
|
||||
UniTaskStatus IUniTaskSource.UnsafeGetStatus()
|
||||
{
|
||||
return UniTaskStatus.Pending;
|
||||
}
|
||||
|
||||
void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class AsyncUniTask<TStateMachine> : IStateMachineRunnerPromise, IUniTaskSource, ITaskPoolNode<AsyncUniTask<TStateMachine>>
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
static TaskPool<AsyncUniTask<TStateMachine>> pool;
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
readonly Action returnDelegate;
|
||||
#endif
|
||||
public Action MoveNext { get; }
|
||||
|
||||
TStateMachine stateMachine;
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
AsyncUniTask()
|
||||
{
|
||||
MoveNext = Run;
|
||||
#if ENABLE_IL2CPP
|
||||
returnDelegate = Return;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise runnerPromiseFieldRef)
|
||||
{
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new AsyncUniTask<TStateMachine>();
|
||||
}
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
runnerPromiseFieldRef = result; // set runner before copied.
|
||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||
}
|
||||
|
||||
AsyncUniTask<TStateMachine> nextNode;
|
||||
public ref AsyncUniTask<TStateMachine> NextNode => ref nextNode;
|
||||
|
||||
static AsyncUniTask()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine>), () => pool.Size);
|
||||
}
|
||||
|
||||
void Return()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
stateMachine = default;
|
||||
pool.TryPush(this);
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
stateMachine = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void Run()
|
||||
{
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
public UniTask Task
|
||||
{
|
||||
[DebuggerHidden]
|
||||
get
|
||||
{
|
||||
return new UniTask(this, core.Version);
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public void SetResult()
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
core.TrySetException(exception);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
#if ENABLE_IL2CPP
|
||||
// workaround for IL2CPP bug.
|
||||
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate);
|
||||
#else
|
||||
TryReturn();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class AsyncUniTask<TStateMachine, T> : IStateMachineRunnerPromise<T>, IUniTaskSource<T>, ITaskPoolNode<AsyncUniTask<TStateMachine, T>>
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
static TaskPool<AsyncUniTask<TStateMachine, T>> pool;
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
readonly Action returnDelegate;
|
||||
#endif
|
||||
|
||||
public Action MoveNext { get; }
|
||||
|
||||
TStateMachine stateMachine;
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
|
||||
AsyncUniTask()
|
||||
{
|
||||
MoveNext = Run;
|
||||
#if ENABLE_IL2CPP
|
||||
returnDelegate = Return;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise<T> runnerPromiseFieldRef)
|
||||
{
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new AsyncUniTask<TStateMachine, T>();
|
||||
}
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
runnerPromiseFieldRef = result; // set runner before copied.
|
||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||
}
|
||||
|
||||
AsyncUniTask<TStateMachine, T> nextNode;
|
||||
public ref AsyncUniTask<TStateMachine, T> NextNode => ref nextNode;
|
||||
|
||||
static AsyncUniTask()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine, T>), () => pool.Size);
|
||||
}
|
||||
|
||||
void Return()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
stateMachine = default;
|
||||
pool.TryPush(this);
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
stateMachine = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void Run()
|
||||
{
|
||||
// UnityEngine.Debug.Log($"MoveNext State:" + StateMachineUtility.GetState(stateMachine));
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
public UniTask<T> Task
|
||||
{
|
||||
[DebuggerHidden]
|
||||
get
|
||||
{
|
||||
return new UniTask<T>(this, core.Version);
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public void SetResult(T result)
|
||||
{
|
||||
core.TrySetResult(result);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
core.TrySetException(exception);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public T GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
return core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
#if ENABLE_IL2CPP
|
||||
// workaround for IL2CPP bug.
|
||||
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate);
|
||||
#else
|
||||
TryReturn();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
GetResult(token);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,12 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static class EnumeratorAsyncExtensions
|
||||
{
|
||||
public static UniTask.Awaiter GetAwaiter(this IEnumerator enumerator)
|
||||
public static UniTask.Awaiter GetAwaiter<T>(this T enumerator)
|
||||
where T : IEnumerator
|
||||
{
|
||||
Error.ThrowArgumentNullException(enumerator, nameof(enumerator));
|
||||
return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, CancellationToken.None, out var token), token).GetAwaiter();
|
||||
var e = (IEnumerator)enumerator;
|
||||
Error.ThrowArgumentNullException(e, nameof(enumerator));
|
||||
return new UniTask(EnumeratorPromise.Create(e, PlayerLoopTiming.Update, CancellationToken.None, out var token), token).GetAwaiter();
|
||||
}
|
||||
|
||||
public static UniTask WithCancellation(this IEnumerator enumerator, CancellationToken cancellationToken)
|
||||
@@ -30,12 +32,22 @@ namespace Cysharp.Threading.Tasks
|
||||
return new UniTask(EnumeratorPromise.Create(enumerator, timing, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
|
||||
sealed class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<EnumeratorPromise>
|
||||
{
|
||||
static readonly PromisePool<EnumeratorPromise> pool = new PromisePool<EnumeratorPromise>();
|
||||
static TaskPool<EnumeratorPromise> pool;
|
||||
EnumeratorPromise nextNode;
|
||||
public ref EnumeratorPromise NextNode => ref nextNode;
|
||||
|
||||
static EnumeratorPromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(EnumeratorPromise), () => pool.Size);
|
||||
}
|
||||
|
||||
IEnumerator innerEnumerator;
|
||||
CancellationToken cancellationToken;
|
||||
int initialFrame;
|
||||
bool loopRunning;
|
||||
bool calledGetResult;
|
||||
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
|
||||
@@ -50,16 +62,23 @@ namespace Cysharp.Threading.Tasks
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
var result = pool.TryRent() ?? new EnumeratorPromise();
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new EnumeratorPromise();
|
||||
}
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
result.innerEnumerator = ConsumeEnumerator(innerEnumerator);
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
result.loopRunning = true;
|
||||
result.calledGetResult = false;
|
||||
result.initialFrame = -1;
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
token = result.core.Version;
|
||||
|
||||
result.MoveNext(); // run immediately.
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -67,12 +86,15 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
try
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
calledGetResult = true;
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
if (!loopRunning)
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,12 +115,38 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (calledGetResult)
|
||||
{
|
||||
loopRunning = false;
|
||||
TryReturn();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (innerEnumerator == null) // invalid status, returned but loop running?
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
loopRunning = false;
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (initialFrame == -1)
|
||||
{
|
||||
// Time can not touch in threadpool.
|
||||
if (PlayerLoopHelper.IsMainThread)
|
||||
{
|
||||
initialFrame = Time.frameCount;
|
||||
}
|
||||
}
|
||||
else if (initialFrame == Time.frameCount)
|
||||
{
|
||||
return true; // already executed in first frame, skip.
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (innerEnumerator.MoveNext())
|
||||
@@ -108,27 +156,23 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
loopRunning = false;
|
||||
core.TrySetException(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
loopRunning = false;
|
||||
core.TrySetResult(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
innerEnumerator = default;
|
||||
cancellationToken = default;
|
||||
}
|
||||
|
||||
~EnumeratorPromise()
|
||||
{
|
||||
if (pool.TryReturn(this))
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
// Unwrap YieldInstructions
|
||||
@@ -142,11 +186,10 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
else if (current is CustomYieldInstruction)
|
||||
else if (current is CustomYieldInstruction cyi)
|
||||
{
|
||||
// WWW, WaitForSecondsRealtime
|
||||
var e2 = UnwrapWaitCustomYieldInstruction((CustomYieldInstruction)current);
|
||||
while (e2.MoveNext())
|
||||
while (cyi.keepWaiting)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
@@ -191,26 +234,17 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
}
|
||||
|
||||
// WWW and others as CustomYieldInstruction.
|
||||
static IEnumerator UnwrapWaitCustomYieldInstruction(CustomYieldInstruction yieldInstruction)
|
||||
{
|
||||
while (yieldInstruction.keepWaiting)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly FieldInfo waitForSeconds_Seconds = typeof(WaitForSeconds).GetField("m_Seconds", BindingFlags.Instance | BindingFlags.GetField | BindingFlags.NonPublic);
|
||||
|
||||
static IEnumerator UnwrapWaitForSeconds(WaitForSeconds waitForSeconds)
|
||||
{
|
||||
var second = (float)waitForSeconds_Seconds.GetValue(waitForSeconds);
|
||||
var startTime = DateTimeOffset.UtcNow;
|
||||
var elapsed = 0.0f;
|
||||
while (true)
|
||||
{
|
||||
yield return null;
|
||||
|
||||
var elapsed = (DateTimeOffset.UtcNow - startTime).TotalSeconds;
|
||||
elapsed += Time.deltaTime;
|
||||
if (elapsed >= second)
|
||||
{
|
||||
break;
|
||||
|
||||
8
src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables.meta
vendored
Normal file
8
src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5b9231662e24c942b544bd85d4b39cb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -11,24 +11,38 @@ using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static class AddressableAsyncExtensions
|
||||
public static class AddressablesAsyncExtensions
|
||||
{
|
||||
#region AsyncOperationHandle
|
||||
#region AsyncOperationHandle
|
||||
|
||||
public static AsyncOperationHandleAwaiter GetAwaiter(this AsyncOperationHandle handle)
|
||||
public static UniTask.Awaiter GetAwaiter(this AsyncOperationHandle handle)
|
||||
{
|
||||
return new AsyncOperationHandleAwaiter(handle);
|
||||
return ToUniTask(handle).GetAwaiter();
|
||||
}
|
||||
|
||||
public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken)
|
||||
{
|
||||
if (handle.IsDone) return UniTask.CompletedTask;
|
||||
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
|
||||
return ToUniTask(handle, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (handle.IsDone) return UniTask.CompletedTask;
|
||||
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);
|
||||
|
||||
if (!handle.IsValid())
|
||||
{
|
||||
throw new Exception("Attempting to use an invalid operation handle");
|
||||
}
|
||||
|
||||
if (handle.IsDone)
|
||||
{
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
return UniTask.FromException(handle.OperationException);
|
||||
}
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
@@ -72,24 +86,33 @@ namespace Cysharp.Threading.Tasks
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
|
||||
continuationAction = continuation.AsFuncOfT<AsyncOperationHandle>(); // allocate delegate.
|
||||
continuationAction = PooledDelegate<AsyncOperationHandle>.Create(continuation);
|
||||
handle.Completed += continuationAction;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
|
||||
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
|
||||
{
|
||||
static readonly PromisePool<AsyncOperationHandleConfiguredSource> pool = new PromisePool<AsyncOperationHandleConfiguredSource>();
|
||||
static TaskPool<AsyncOperationHandleConfiguredSource> pool;
|
||||
AsyncOperationHandleConfiguredSource nextNode;
|
||||
public ref AsyncOperationHandleConfiguredSource NextNode => ref nextNode;
|
||||
|
||||
static AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
|
||||
}
|
||||
|
||||
readonly Action<AsyncOperationHandle> continuationAction;
|
||||
AsyncOperationHandle handle;
|
||||
IProgress<float> progress;
|
||||
CancellationToken cancellationToken;
|
||||
IProgress<float> progress;
|
||||
bool completed;
|
||||
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
|
||||
continuationAction = Continuation;
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
|
||||
@@ -99,31 +122,55 @@ namespace Cysharp.Threading.Tasks
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
var result = pool.TryRent() ?? new AsyncOperationHandleConfiguredSource();
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new AsyncOperationHandleConfiguredSource();
|
||||
}
|
||||
|
||||
result.handle = handle;
|
||||
result.progress = progress;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.completed = false;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
handle.Completed += result.continuationAction;
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Continuation(AsyncOperationHandle _)
|
||||
{
|
||||
handle.Completed -= continuationAction;
|
||||
|
||||
if (completed)
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
completed = true;
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
else if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
core.TrySetException(handle.OperationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
}
|
||||
core.GetResult(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
@@ -143,130 +190,95 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (completed)
|
||||
{
|
||||
TryReturn();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completed = true;
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (progress != null)
|
||||
if (progress != null && handle.IsValid())
|
||||
{
|
||||
progress.Report(handle.PercentComplete);
|
||||
}
|
||||
|
||||
if (handle.IsDone)
|
||||
{
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
core.TrySetException(handle.OperationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
handle = default;
|
||||
progress = default;
|
||||
cancellationToken = default;
|
||||
}
|
||||
|
||||
~AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
if (pool.TryReturn(this))
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region AsyncOperationHandle_T
|
||||
#region AsyncOperationHandle_T
|
||||
|
||||
public static AsyncOperationHandleAwaiter<T> GetAwaiter<T>(this AsyncOperationHandle<T> handle)
|
||||
public static UniTask<T>.Awaiter GetAwaiter<T>(this AsyncOperationHandle<T> handle)
|
||||
{
|
||||
return new AsyncOperationHandleAwaiter<T>(handle);
|
||||
return ToUniTask(handle).GetAwaiter();
|
||||
}
|
||||
|
||||
public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken)
|
||||
{
|
||||
if (handle.IsDone) return UniTask.FromResult(handle.Result);
|
||||
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
|
||||
return ToUniTask(handle, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (handle.IsDone) return UniTask.FromResult(handle.Result);
|
||||
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T>(cancellationToken);
|
||||
|
||||
if (!handle.IsValid())
|
||||
{
|
||||
throw new Exception("Attempting to use an invalid operation handle");
|
||||
}
|
||||
|
||||
if (handle.IsDone)
|
||||
{
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
return UniTask.FromException<T>(handle.OperationException);
|
||||
}
|
||||
return UniTask.FromResult(handle.Result);
|
||||
}
|
||||
|
||||
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
public struct AsyncOperationHandleAwaiter<T> : ICriticalNotifyCompletion
|
||||
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
|
||||
{
|
||||
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
|
||||
AsyncOperationHandleConfiguredSource<T> nextNode;
|
||||
public ref AsyncOperationHandleConfiguredSource<T> NextNode => ref nextNode;
|
||||
|
||||
static AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
|
||||
}
|
||||
|
||||
readonly Action<AsyncOperationHandle<T>> continuationAction;
|
||||
AsyncOperationHandle<T> handle;
|
||||
Action<AsyncOperationHandle> continuationAction;
|
||||
|
||||
public AsyncOperationHandleAwaiter(AsyncOperationHandle<T> handle)
|
||||
{
|
||||
this.handle = handle;
|
||||
this.continuationAction = null;
|
||||
}
|
||||
|
||||
public bool IsCompleted => handle.IsDone;
|
||||
|
||||
public T GetResult()
|
||||
{
|
||||
if (continuationAction != null)
|
||||
{
|
||||
handle.CompletedTypeless -= continuationAction;
|
||||
continuationAction = null;
|
||||
}
|
||||
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
var e = handle.OperationException;
|
||||
handle = default;
|
||||
ExceptionDispatchInfo.Capture(e).Throw();
|
||||
}
|
||||
|
||||
var result = handle.Result;
|
||||
handle = default;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
UnsafeOnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
|
||||
continuationAction = continuation.AsFuncOfT<AsyncOperationHandle>(); // allocate delegate.
|
||||
handle.CompletedTypeless += continuationAction;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, IPromisePoolItem
|
||||
{
|
||||
static readonly PromisePool<AsyncOperationHandleConfiguredSource<T>> pool = new PromisePool<AsyncOperationHandleConfiguredSource<T>>();
|
||||
|
||||
AsyncOperationHandle<T> handle;
|
||||
IProgress<float> progress;
|
||||
CancellationToken cancellationToken;
|
||||
IProgress<float> progress;
|
||||
bool completed;
|
||||
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
|
||||
AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
|
||||
continuationAction = Continuation;
|
||||
}
|
||||
|
||||
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
|
||||
@@ -276,32 +288,55 @@ namespace Cysharp.Threading.Tasks
|
||||
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
var result = pool.TryRent() ?? new AsyncOperationHandleConfiguredSource<T>();
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new AsyncOperationHandleConfiguredSource<T>();
|
||||
}
|
||||
|
||||
result.handle = handle;
|
||||
result.progress = progress;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.completed = false;
|
||||
result.progress = progress;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
handle.Completed += result.continuationAction;
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Continuation(AsyncOperationHandle<T> argHandle)
|
||||
{
|
||||
handle.Completed -= continuationAction;
|
||||
|
||||
if (completed)
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
completed = true;
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
else if (argHandle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
core.TrySetException(argHandle.OperationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(argHandle.Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
|
||||
return core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
}
|
||||
return core.GetResult(token);
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
@@ -326,51 +361,39 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (completed)
|
||||
{
|
||||
TryReturn();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completed = true;
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (progress != null)
|
||||
if (progress != null && handle.IsValid())
|
||||
{
|
||||
progress.Report(handle.PercentComplete);
|
||||
}
|
||||
|
||||
if (handle.IsDone)
|
||||
{
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
core.TrySetException(handle.OperationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(handle.Result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
handle = default;
|
||||
progress = default;
|
||||
cancellationToken = default;
|
||||
}
|
||||
|
||||
~AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
if (pool.TryReturn(this))
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
22
src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/UniTask.Addressables.asmdef
vendored
Normal file
22
src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/UniTask.Addressables.asmdef
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "UniTask.Addressables",
|
||||
"references": [
|
||||
"UniTask",
|
||||
"Unity.ResourceManager"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [
|
||||
{
|
||||
"name": "com.unity.addressables",
|
||||
"expression": "",
|
||||
"define": "UNITASK_ADDRESSABLE_SUPPORT"
|
||||
}
|
||||
],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 593a5b492d29ac6448b1ebf7f035ef33
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
src/UniTask/Assets/Plugins/UniTask/Runtime/External/DOTween.meta
vendored
Normal file
8
src/UniTask/Assets/Plugins/UniTask/Runtime/External/DOTween.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25cb2f742bfeb1d48a4e65d3140b955d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
447
src/UniTask/Assets/Plugins/UniTask/Runtime/External/DOTween/DOTweenAsyncExtensions.cs
vendored
Normal file
447
src/UniTask/Assets/Plugins/UniTask/Runtime/External/DOTween/DOTweenAsyncExtensions.cs
vendored
Normal file
@@ -0,0 +1,447 @@
|
||||
// asmdef Version Defines, enabled when com.demigiant.dotween is imported.
|
||||
|
||||
#if UNITASK_DOTWEEN_SUPPORT
|
||||
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using DG.Tweening;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public enum TweenCancelBehaviour
|
||||
{
|
||||
Kill,
|
||||
KillWithCompleteCallback,
|
||||
Complete,
|
||||
CompleteWithSeqeunceCallback,
|
||||
CancelAwait,
|
||||
|
||||
// AndCancelAwait
|
||||
KillAndCancelAwait,
|
||||
KillWithCompleteCallbackAndCancelAwait,
|
||||
CompleteAndCancelAwait,
|
||||
CompleteWithSeqeunceCallbackAndCancelAwait
|
||||
}
|
||||
|
||||
public static class DOTweenAsyncExtensions
|
||||
{
|
||||
enum CallbackType
|
||||
{
|
||||
Kill,
|
||||
Complete,
|
||||
Pause,
|
||||
Play,
|
||||
Rewind,
|
||||
StepComplete
|
||||
}
|
||||
|
||||
public static TweenAwaiter GetAwaiter(this Tween tween)
|
||||
{
|
||||
return new TweenAwaiter(tween);
|
||||
}
|
||||
|
||||
public static UniTask WithCancellation(this Tween tween, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
||||
|
||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
||||
return new UniTask(TweenConfiguredSource.Create(tween, TweenCancelBehaviour.Kill, cancellationToken, CallbackType.Kill, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask ToUniTask(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
||||
|
||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Kill, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask AwaitForComplete(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
||||
|
||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Complete, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask AwaitForPause(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
||||
|
||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Pause, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask AwaitForPlay(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
||||
|
||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Play, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask AwaitForRewind(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
||||
|
||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Rewind, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask AwaitForStepComplete(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
||||
|
||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.StepComplete, out var token), token);
|
||||
}
|
||||
|
||||
public struct TweenAwaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
readonly Tween tween;
|
||||
|
||||
// killed(non active) as completed.
|
||||
public bool IsCompleted => !tween.IsActive();
|
||||
|
||||
public TweenAwaiter(Tween tween)
|
||||
{
|
||||
this.tween = tween;
|
||||
}
|
||||
|
||||
public TweenAwaiter GetAwaiter()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public void GetResult()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnCompleted(System.Action continuation)
|
||||
{
|
||||
UnsafeOnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(System.Action continuation)
|
||||
{
|
||||
// onKill is called after OnCompleted, both Complete(false/true) and Kill(false/true).
|
||||
tween.onKill = PooledTweenCallback.Create(continuation);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class TweenConfiguredSource : IUniTaskSource, ITaskPoolNode<TweenConfiguredSource>
|
||||
{
|
||||
static TaskPool<TweenConfiguredSource> pool;
|
||||
TweenConfiguredSource nextNode;
|
||||
public ref TweenConfiguredSource NextNode => ref nextNode;
|
||||
|
||||
static TweenConfiguredSource()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
|
||||
}
|
||||
|
||||
static readonly TweenCallback EmptyTweenCallback = () => { };
|
||||
|
||||
readonly TweenCallback onCompleteCallbackDelegate;
|
||||
readonly TweenCallback onUpdateDelegate;
|
||||
|
||||
Tween tween;
|
||||
TweenCancelBehaviour cancelBehaviour;
|
||||
CancellationToken cancellationToken;
|
||||
CallbackType callbackType;
|
||||
bool canceled;
|
||||
|
||||
TweenCallback originalUpdateAction;
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
TweenConfiguredSource()
|
||||
{
|
||||
onCompleteCallbackDelegate = OnCompleteCallbackDelegate;
|
||||
onUpdateDelegate = OnUpdate;
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, CallbackType callbackType, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
DoCancelBeforeCreate(tween, cancelBehaviour);
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new TweenConfiguredSource();
|
||||
}
|
||||
|
||||
result.tween = tween;
|
||||
result.cancelBehaviour = cancelBehaviour;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.callbackType = callbackType;
|
||||
|
||||
result.originalUpdateAction = tween.onUpdate;
|
||||
result.canceled = false;
|
||||
|
||||
if (result.originalUpdateAction == result.onUpdateDelegate)
|
||||
{
|
||||
result.originalUpdateAction = null;
|
||||
}
|
||||
|
||||
tween.onUpdate = result.onUpdateDelegate;
|
||||
|
||||
switch (callbackType)
|
||||
{
|
||||
case CallbackType.Kill:
|
||||
tween.onKill = result.onCompleteCallbackDelegate;
|
||||
break;
|
||||
case CallbackType.Complete:
|
||||
tween.onComplete = result.onCompleteCallbackDelegate;
|
||||
break;
|
||||
case CallbackType.Pause:
|
||||
tween.onPause = result.onCompleteCallbackDelegate;
|
||||
break;
|
||||
case CallbackType.Play:
|
||||
tween.onPlay = result.onCompleteCallbackDelegate;
|
||||
break;
|
||||
case CallbackType.Rewind:
|
||||
tween.onRewind = result.onCompleteCallbackDelegate;
|
||||
break;
|
||||
case CallbackType.StepComplete:
|
||||
tween.onStepComplete = result.onCompleteCallbackDelegate;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
void OnCompleteCallbackDelegate()
|
||||
{
|
||||
if (canceled)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
}
|
||||
}
|
||||
|
||||
void OnUpdate()
|
||||
{
|
||||
originalUpdateAction?.Invoke();
|
||||
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this.cancelBehaviour)
|
||||
{
|
||||
case TweenCancelBehaviour.Kill:
|
||||
default:
|
||||
this.tween.Kill(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.KillAndCancelAwait:
|
||||
this.canceled = true;
|
||||
this.tween.Kill(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.KillWithCompleteCallback:
|
||||
this.tween.Kill(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
|
||||
this.canceled = true;
|
||||
this.tween.Kill(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.Complete:
|
||||
this.tween.Complete(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.CompleteAndCancelAwait:
|
||||
this.canceled = true;
|
||||
this.tween.Complete(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
|
||||
this.tween.Complete(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
|
||||
this.canceled = true;
|
||||
this.tween.Complete(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.CancelAwait:
|
||||
// replace to empty(avoid callback after Canceled(instance is returned to pool.)
|
||||
switch (callbackType)
|
||||
{
|
||||
case CallbackType.Kill:
|
||||
tween.onKill = EmptyTweenCallback;
|
||||
break;
|
||||
case CallbackType.Complete:
|
||||
tween.onComplete = EmptyTweenCallback;
|
||||
break;
|
||||
case CallbackType.Pause:
|
||||
tween.onPause = EmptyTweenCallback;
|
||||
break;
|
||||
case CallbackType.Play:
|
||||
tween.onPlay = EmptyTweenCallback;
|
||||
break;
|
||||
case CallbackType.Rewind:
|
||||
tween.onRewind = EmptyTweenCallback;
|
||||
break;
|
||||
case CallbackType.StepComplete:
|
||||
tween.onStepComplete = EmptyTweenCallback;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this.core.TrySetCanceled(this.cancellationToken);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour)
|
||||
{
|
||||
|
||||
switch (tweenCancelBehaviour)
|
||||
{
|
||||
case TweenCancelBehaviour.Kill:
|
||||
default:
|
||||
tween.Kill(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.KillAndCancelAwait:
|
||||
tween.Kill(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.KillWithCompleteCallback:
|
||||
tween.Kill(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
|
||||
tween.Kill(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.Complete:
|
||||
tween.Complete(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.CompleteAndCancelAwait:
|
||||
tween.Complete(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
|
||||
tween.Complete(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
|
||||
tween.Complete(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.CancelAwait:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
tween.onUpdate = originalUpdateAction;
|
||||
|
||||
switch (callbackType)
|
||||
{
|
||||
case CallbackType.Kill:
|
||||
tween.onKill = null;
|
||||
break;
|
||||
case CallbackType.Complete:
|
||||
tween.onComplete = null;
|
||||
break;
|
||||
case CallbackType.Pause:
|
||||
tween.onPause = null;
|
||||
break;
|
||||
case CallbackType.Play:
|
||||
tween.onPlay = null;
|
||||
break;
|
||||
case CallbackType.Rewind:
|
||||
tween.onRewind = null;
|
||||
break;
|
||||
case CallbackType.StepComplete:
|
||||
tween.onStepComplete = null;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
tween = default;
|
||||
cancellationToken = default;
|
||||
originalUpdateAction = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class PooledTweenCallback
|
||||
{
|
||||
static readonly ConcurrentQueue<PooledTweenCallback> pool = new ConcurrentQueue<PooledTweenCallback>();
|
||||
|
||||
readonly TweenCallback runDelegate;
|
||||
|
||||
Action continuation;
|
||||
|
||||
|
||||
PooledTweenCallback()
|
||||
{
|
||||
runDelegate = Run;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TweenCallback Create(Action continuation)
|
||||
{
|
||||
if (!pool.TryDequeue(out var item))
|
||||
{
|
||||
item = new PooledTweenCallback();
|
||||
}
|
||||
|
||||
item.continuation = continuation;
|
||||
return item.runDelegate;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void Run()
|
||||
{
|
||||
var call = continuation;
|
||||
continuation = null;
|
||||
if (call != null)
|
||||
{
|
||||
pool.Enqueue(this);
|
||||
call.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
22
src/UniTask/Assets/Plugins/UniTask/Runtime/External/DOTween/UniTask.DOTween.asmdef
vendored
Normal file
22
src/UniTask/Assets/Plugins/UniTask/Runtime/External/DOTween/UniTask.DOTween.asmdef
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "UniTask.DOTween",
|
||||
"references": [
|
||||
"UniTask",
|
||||
"DOTween.Modules"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [
|
||||
{
|
||||
"name": "com.demigiant.dotween",
|
||||
"expression": "",
|
||||
"define": "UNITASK_DOTWEEN_SUPPORT"
|
||||
}
|
||||
],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
7
src/UniTask/Assets/Plugins/UniTask/Runtime/External/DOTween/UniTask.DOTween.asmdef.meta
vendored
Normal file
7
src/UniTask/Assets/Plugins/UniTask/Runtime/External/DOTween/UniTask.DOTween.asmdef.meta
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 029c1c1b674aaae47a6841a0b89ad80e
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,239 +0,0 @@
|
||||
// asmdef Version Defines, enabled when com.demigiant.dotween is imported.
|
||||
|
||||
#if UNITASK_DOTWEEN_SUPPORT
|
||||
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using DG.Tweening;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
// The idea of TweenCancelBehaviour is borrowed from https://www.shibuya24.info/entry/dotween_async_await
|
||||
public enum TweenCancelBehaviour
|
||||
{
|
||||
Kill,
|
||||
KillWithCompleteCallback,
|
||||
Complete,
|
||||
CompleteWithSeqeunceCallback,
|
||||
CancelAwait,
|
||||
|
||||
// AndCancelAwait
|
||||
KillAndCancelAwait,
|
||||
KillWithCompleteCallbackAndCancelAwait,
|
||||
CompleteAndCancelAwait,
|
||||
CompleteWithSeqeunceCallbackAndCancelAwait
|
||||
}
|
||||
|
||||
public static class DOTweenAsyncExtensions
|
||||
{
|
||||
public static TweenAwaiter GetAwaiter(this Tween tween)
|
||||
{
|
||||
return new TweenAwaiter(tween);
|
||||
}
|
||||
|
||||
public static UniTask WithCancellation(this Tween tween, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
||||
|
||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
||||
return new UniTask(TweenConfiguredSource.Create(tween, TweenCancelBehaviour.Kill, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask ToUniTask(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
||||
|
||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
public struct TweenAwaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
readonly Tween tween;
|
||||
|
||||
// killed(non active) as completed.
|
||||
public bool IsCompleted => !tween.IsActive();
|
||||
|
||||
public TweenAwaiter(Tween tween)
|
||||
{
|
||||
this.tween = tween;
|
||||
}
|
||||
|
||||
public TweenAwaiter GetAwaiter()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public void GetResult()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnCompleted(System.Action continuation)
|
||||
{
|
||||
UnsafeOnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(System.Action continuation)
|
||||
{
|
||||
// convert Action -> TweenCallback allocation.
|
||||
// onKill is called after OnCompleted, both Complete(false/true) and Kill(false/true).
|
||||
tween.onKill = new TweenCallback(continuation);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class TweenConfiguredSource : IUniTaskSource, IPromisePoolItem
|
||||
{
|
||||
static readonly PromisePool<TweenConfiguredSource> pool = new PromisePool<TweenConfiguredSource>();
|
||||
static readonly Action<object> CancellationCallbackDelegate = CancellationCallback;
|
||||
static readonly TweenCallback EmptyTweenCallback = () => { };
|
||||
|
||||
Tween tween;
|
||||
TweenCancelBehaviour cancelBehaviour;
|
||||
CancellationToken cancellationToken;
|
||||
bool canceled;
|
||||
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
TweenConfiguredSource()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
var result = pool.TryRent() ?? new TweenConfiguredSource();
|
||||
|
||||
result.tween = tween;
|
||||
result.cancelBehaviour = cancelBehaviour;
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
result.RegisterEvent();
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
void RegisterEvent()
|
||||
{
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(CancellationCallbackDelegate, this);
|
||||
}
|
||||
|
||||
// allocate delegate.
|
||||
tween.OnKill(new TweenCallback(OnKill));
|
||||
}
|
||||
|
||||
void OnKill()
|
||||
{
|
||||
cancellationTokenRegistration.Dispose();
|
||||
if (canceled)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
}
|
||||
}
|
||||
|
||||
static void CancellationCallback(object state)
|
||||
{
|
||||
var self = (TweenConfiguredSource)state;
|
||||
|
||||
switch (self.cancelBehaviour)
|
||||
{
|
||||
case TweenCancelBehaviour.Kill:
|
||||
default:
|
||||
self.tween.Kill(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.KillAndCancelAwait:
|
||||
self.canceled = true;
|
||||
self.tween.Kill(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.KillWithCompleteCallback:
|
||||
self.tween.Kill(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
|
||||
self.canceled = true;
|
||||
self.tween.Kill(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.Complete:
|
||||
self.tween.Complete(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.CompleteAndCancelAwait:
|
||||
self.canceled = true;
|
||||
self.tween.Complete(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
|
||||
self.tween.Complete(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
|
||||
self.canceled = true;
|
||||
self.tween.Complete(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.CancelAwait:
|
||||
self.tween.onKill = EmptyTweenCallback; // replace to empty(avoid callback after Caceled(instance is returned to pool.)
|
||||
self.core.TrySetCanceled(self.cancellationToken);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
core.Reset();
|
||||
tween = default;
|
||||
cancellationToken = default;
|
||||
}
|
||||
|
||||
~TweenConfiguredSource()
|
||||
{
|
||||
if (pool.TryReturn(this))
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
8
src/UniTask/Assets/Plugins/UniTask/Runtime/External/TextMeshPro.meta
vendored
Normal file
8
src/UniTask/Assets/Plugins/UniTask/Runtime/External/TextMeshPro.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f89da606bde9a4e4e94ae1189a029887
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,224 @@
|
||||
#if UNITASK_TEXTMESHPRO_SUPPORT
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using TMPro;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static partial class TextMeshProAsyncExtensions
|
||||
{
|
||||
public static IAsyncValueChangedEventHandler<string> GetAsyncValueChangedEventHandler(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy(), false);
|
||||
}
|
||||
|
||||
public static IAsyncValueChangedEventHandler<string> GetAsyncValueChangedEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, cancellationToken, false);
|
||||
}
|
||||
|
||||
public static UniTask<string> OnValueChangedAsync(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static UniTask<string> OnValueChangedAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, cancellationToken, true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<string> OnValueChangedAsAsyncEnumerable(this TMP_InputField inputField)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy());
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<string> OnValueChangedAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onValueChanged, cancellationToken);
|
||||
}
|
||||
|
||||
public static IAsyncEndEditEventHandler<string> GetAsyncEndEditEventHandler(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy(), false);
|
||||
}
|
||||
|
||||
public static IAsyncEndEditEventHandler<string> GetAsyncEndEditEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, cancellationToken, false);
|
||||
}
|
||||
|
||||
public static UniTask<string> OnEndEditAsync(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static UniTask<string> OnEndEditAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, cancellationToken, true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<string> OnEndEditAsAsyncEnumerable(this TMP_InputField inputField)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy());
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<string> OnEndEditAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onEndEdit, cancellationToken);
|
||||
}
|
||||
|
||||
public static IAsyncEndTextSelectionEventHandler<(string, int, int)> GetAsyncEndTextSelectionEventHandler(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy(), false);
|
||||
}
|
||||
|
||||
public static IAsyncEndTextSelectionEventHandler<(string, int, int)> GetAsyncEndTextSelectionEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken, false);
|
||||
}
|
||||
|
||||
public static UniTask<(string, int, int)> OnEndTextSelectionAsync(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static UniTask<(string, int, int)> OnEndTextSelectionAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken, true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<(string, int, int)> OnEndTextSelectionAsAsyncEnumerable(this TMP_InputField inputField)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy());
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<(string, int, int)> OnEndTextSelectionAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken);
|
||||
}
|
||||
|
||||
public static IAsyncTextSelectionEventHandler<(string, int, int)> GetAsyncTextSelectionEventHandler(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy(), false);
|
||||
}
|
||||
|
||||
public static IAsyncTextSelectionEventHandler<(string, int, int)> GetAsyncTextSelectionEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken, false);
|
||||
}
|
||||
|
||||
public static UniTask<(string, int, int)> OnTextSelectionAsync(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static UniTask<(string, int, int)> OnTextSelectionAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken, true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<(string, int, int)> OnTextSelectionAsAsyncEnumerable(this TMP_InputField inputField)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy());
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<(string, int, int)> OnTextSelectionAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken);
|
||||
}
|
||||
|
||||
public static IAsyncDeselectEventHandler<string> GetAsyncDeselectEventHandler(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy(), false);
|
||||
}
|
||||
|
||||
public static IAsyncDeselectEventHandler<string> GetAsyncDeselectEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onDeselect, cancellationToken, false);
|
||||
}
|
||||
|
||||
public static UniTask<string> OnDeselectAsync(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static UniTask<string> OnDeselectAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onDeselect, cancellationToken, true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<string> OnDeselectAsAsyncEnumerable(this TMP_InputField inputField)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy());
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<string> OnDeselectAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onDeselect, cancellationToken);
|
||||
}
|
||||
|
||||
public static IAsyncSelectEventHandler<string> GetAsyncSelectEventHandler(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onSelect, inputField.GetCancellationTokenOnDestroy(), false);
|
||||
}
|
||||
|
||||
public static IAsyncSelectEventHandler<string> GetAsyncSelectEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onSelect, cancellationToken, false);
|
||||
}
|
||||
|
||||
public static UniTask<string> OnSelectAsync(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onSelect, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static UniTask<string> OnSelectAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onSelect, cancellationToken, true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<string> OnSelectAsAsyncEnumerable(this TMP_InputField inputField)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSelect, inputField.GetCancellationTokenOnDestroy());
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<string> OnSelectAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSelect, cancellationToken);
|
||||
}
|
||||
|
||||
public static IAsyncSubmitEventHandler<string> GetAsyncSubmitEventHandler(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy(), false);
|
||||
}
|
||||
|
||||
public static IAsyncSubmitEventHandler<string> GetAsyncSubmitEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onSubmit, cancellationToken, false);
|
||||
}
|
||||
|
||||
public static UniTask<string> OnSubmitAsync(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static UniTask<string> OnSubmitAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<string>(inputField.onSubmit, cancellationToken, true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<string> OnSubmitAsAsyncEnumerable(this TMP_InputField inputField)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy());
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<string> OnSubmitAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSubmit, cancellationToken);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d5a9a3e1f0f069478969f752fde29a9
|
||||
guid: 79f4f2475e0b2c44e97ed1dee760627b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,66 @@
|
||||
<#@ template debug="false" hostspecific="false" language="C#" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#
|
||||
var handlers = new (string name, string type)[] {
|
||||
("ValueChanged", "string"),
|
||||
("EndEdit", "string"),
|
||||
("EndTextSelection", "(string, int, int)"),
|
||||
("TextSelection", "(string, int, int)"),
|
||||
("Deselect", "string"),
|
||||
("Select", "string"),
|
||||
("Submit", "string"),
|
||||
};
|
||||
|
||||
Func<string, bool> shouldConvert = x => x.EndsWith("TextSelection");
|
||||
Func<string, string> eventName = x => shouldConvert(x) ? $"new TextSelectionEventConverter(inputField.on{x})" : $"inputField.on{x}";
|
||||
#>
|
||||
#if UNITASK_TEXTMESHPRO_SUPPORT
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using TMPro;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static partial class TextMeshProAsyncExtensions
|
||||
{
|
||||
<# foreach(var (name, type) in handlers) { #>
|
||||
public static IAsync<#= (name) #>EventHandler<<#= type #>> GetAsync<#= (name) #>EventHandler(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, inputField.GetCancellationTokenOnDestroy(), false);
|
||||
}
|
||||
|
||||
public static IAsync<#= (name) #>EventHandler<<#= type #>> GetAsync<#= (name) #>EventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, cancellationToken, false);
|
||||
}
|
||||
|
||||
public static UniTask<<#= type #>> On<#= (name) #>Async(this TMP_InputField inputField)
|
||||
{
|
||||
return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static UniTask<<#= type #>> On<#= (name) #>Async(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, cancellationToken, true).OnInvokeAsync();
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<<#= type #>> On<#= (name) #>AsAsyncEnumerable(this TMP_InputField inputField)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<<#= type #>>(<#= eventName(name) #>, inputField.GetCancellationTokenOnDestroy());
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<<#= type #>> On<#= (name) #>AsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
||||
{
|
||||
return new UnityEventHandlerAsyncEnumerable<<#= type #>>(<#= eventName(name) #>, cancellationToken);
|
||||
}
|
||||
|
||||
<# } #>
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9bb9fc551a975d44a7180e022a2debe
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
130
src/UniTask/Assets/Plugins/UniTask/Runtime/External/TextMeshPro/TextMeshProAsyncExtensions.cs
vendored
Normal file
130
src/UniTask/Assets/Plugins/UniTask/Runtime/External/TextMeshPro/TextMeshProAsyncExtensions.cs
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
#if UNITASK_TEXTMESHPRO_SUPPORT
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using TMPro;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static partial class TextMeshProAsyncExtensions
|
||||
{
|
||||
// <string> -> Text
|
||||
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, TMP_Text text, bool rebindOnError = true)
|
||||
{
|
||||
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
|
||||
}
|
||||
|
||||
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError = true)
|
||||
{
|
||||
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
|
||||
}
|
||||
|
||||
static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<string> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError)
|
||||
{
|
||||
var repeat = false;
|
||||
BIND_AGAIN:
|
||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
bool moveNext;
|
||||
try
|
||||
{
|
||||
moveNext = await e.MoveNextAsync();
|
||||
repeat = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is OperationCanceledException) return;
|
||||
|
||||
if (rebindOnError && !repeat)
|
||||
{
|
||||
repeat = true;
|
||||
goto BIND_AGAIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
if (!moveNext) return;
|
||||
|
||||
text.text = e.Current;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (e != null)
|
||||
{
|
||||
await e.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// <T> -> Text
|
||||
|
||||
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, TMP_Text text, bool rebindOnError = true)
|
||||
{
|
||||
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
|
||||
}
|
||||
|
||||
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError = true)
|
||||
{
|
||||
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
|
||||
}
|
||||
|
||||
public static void BindTo<T>(this AsyncReactiveProperty<T> source, TMP_Text text, bool rebindOnError = true)
|
||||
{
|
||||
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
|
||||
}
|
||||
|
||||
static async UniTaskVoid BindToCore<T>(IUniTaskAsyncEnumerable<T> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError)
|
||||
{
|
||||
var repeat = false;
|
||||
BIND_AGAIN:
|
||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
bool moveNext;
|
||||
try
|
||||
{
|
||||
moveNext = await e.MoveNextAsync();
|
||||
repeat = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is OperationCanceledException) return;
|
||||
|
||||
if (rebindOnError && !repeat)
|
||||
{
|
||||
repeat = true;
|
||||
goto BIND_AGAIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
if (!moveNext) return;
|
||||
|
||||
text.text = e.Current.ToString();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (e != null)
|
||||
{
|
||||
await e.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fcb1f7467a3e2b64c8a016c8aee2f9b4
|
||||
guid: b6ba480edafb67d4e91bb10feb64fae5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
22
src/UniTask/Assets/Plugins/UniTask/Runtime/External/TextMeshPro/UniTask.TextMeshPro.asmdef
vendored
Normal file
22
src/UniTask/Assets/Plugins/UniTask/Runtime/External/TextMeshPro/UniTask.TextMeshPro.asmdef
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "UniTask.TextMeshPro",
|
||||
"references": [
|
||||
"UniTask",
|
||||
"Unity.TextMeshPro"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [
|
||||
{
|
||||
"name": "com.unity.textmeshpro",
|
||||
"expression": "",
|
||||
"define": "UNITASK_TEXTMESHPRO_SUPPORT"
|
||||
}
|
||||
],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc47925d1a5fa2946bdd37746b2b5d48
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -19,7 +19,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
// similar as IValueTaskSource
|
||||
public interface IUniTaskSource
|
||||
#if !UNITY_2018_3_OR_NEWER
|
||||
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||
: System.Threading.Tasks.Sources.IValueTaskSource
|
||||
#pragma warning disable CS0108
|
||||
#endif
|
||||
@@ -30,7 +30,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
UniTaskStatus UnsafeGetStatus(); // only for debug use.
|
||||
|
||||
#if !UNITY_2018_3_OR_NEWER
|
||||
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||
#pragma warning restore CS0108
|
||||
|
||||
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(short token)
|
||||
@@ -53,13 +53,13 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
public interface IUniTaskSource<out T> : IUniTaskSource
|
||||
#if !UNITY_2018_3_OR_NEWER
|
||||
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||
, System.Threading.Tasks.Sources.IValueTaskSource<T>
|
||||
#endif
|
||||
{
|
||||
new T GetResult(short token);
|
||||
|
||||
#if !UNITY_2018_3_OR_NEWER
|
||||
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||
|
||||
new public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
{
|
||||
const int MaxArrayLength = 0X7FEFFFFF;
|
||||
const int InitialSize = 16;
|
||||
|
||||
|
||||
readonly PlayerLoopTiming timing;
|
||||
|
||||
SpinLock gate = new SpinLock();
|
||||
@@ -170,10 +170,17 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
|
||||
for (int i = 0; i < actionListCount; i++)
|
||||
{
|
||||
|
||||
var action = actionList[i];
|
||||
actionList[i] = null;
|
||||
|
||||
action();
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -239,7 +239,7 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
}
|
||||
else
|
||||
{
|
||||
var fname = fi.FullName.Replace(Path.DirectorySeparatorChar, '/').Replace(Application.dataPath, "");
|
||||
var fname = fi.FullName.Replace(Path.DirectorySeparatorChar, '/').Replace(PlayerLoopHelper.ApplicationDataPath, "");
|
||||
var withAssetsPath = "Assets/" + fname;
|
||||
return "<a href=\"" + withAssetsPath + "\" line=\"" + line + "\">" + withAssetsPath + ":" + line + "</a>";
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#if NET_4_6 || NET_STANDARD_2_0 || CSHARP_7_OR_LATER
|
||||
|
||||
using System;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Internal
|
||||
{
|
||||
internal static class FuncExtensions
|
||||
{
|
||||
// avoid lambda capture
|
||||
|
||||
internal static Action<T> AsFuncOfT<T>(this Action action)
|
||||
{
|
||||
return new Action<T>(action.Invoke);
|
||||
}
|
||||
|
||||
static void Invoke<T>(this Action action, T unused)
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -143,6 +143,7 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
{
|
||||
var j = tail - 1;
|
||||
|
||||
var loopItems = this.loopItems;
|
||||
// eliminate array-bound check for i
|
||||
for (int i = 0; i < loopItems.Length; i++)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Internal
|
||||
{
|
||||
internal sealed class PooledDelegate<T> : ITaskPoolNode<PooledDelegate<T>>
|
||||
{
|
||||
static TaskPool<PooledDelegate<T>> pool;
|
||||
|
||||
PooledDelegate<T> nextNode;
|
||||
public ref PooledDelegate<T> NextNode => ref nextNode;
|
||||
|
||||
static PooledDelegate()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(PooledDelegate<T>), () => pool.Size);
|
||||
}
|
||||
|
||||
readonly Action<T> runDelegate;
|
||||
Action continuation;
|
||||
|
||||
PooledDelegate()
|
||||
{
|
||||
runDelegate = Run;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Action<T> Create(Action continuation)
|
||||
{
|
||||
if (!pool.TryPop(out var item))
|
||||
{
|
||||
item = new PooledDelegate<T>();
|
||||
}
|
||||
|
||||
item.continuation = continuation;
|
||||
return item.runDelegate;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void Run(T _)
|
||||
{
|
||||
var call = continuation;
|
||||
continuation = null;
|
||||
if (call != null)
|
||||
{
|
||||
pool.TryPush(this);
|
||||
call.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8932579438742fa40b010edd412dbfba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,55 +0,0 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Internal
|
||||
{
|
||||
// public, allow to user create custom operator with pool.
|
||||
|
||||
public interface IPromisePoolItem
|
||||
{
|
||||
void Reset();
|
||||
}
|
||||
|
||||
public class PromisePool<T>
|
||||
where T : class, IPromisePoolItem
|
||||
{
|
||||
int count = 0;
|
||||
readonly ConcurrentQueue<T> queue = new ConcurrentQueue<T>();
|
||||
readonly int maxSize;
|
||||
|
||||
public PromisePool(int maxSize = 256)
|
||||
{
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T TryRent()
|
||||
{
|
||||
if (queue.TryDequeue(out var value))
|
||||
{
|
||||
Interlocked.Decrement(ref count);
|
||||
return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryReturn(T value)
|
||||
{
|
||||
value.Reset(); // reset when return.
|
||||
|
||||
if (count < maxSize)
|
||||
{
|
||||
queue.Enqueue(value);
|
||||
Interlocked.Increment(ref count);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
|
||||
@@ -59,9 +60,9 @@ namespace Cysharp.Threading.Tasks
|
||||
#endif
|
||||
|
||||
|
||||
static List<KeyValuePair<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>> listPool = new List<KeyValuePair<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>>();
|
||||
static List<KeyValuePair<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>> listPool = new List<KeyValuePair<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>>();
|
||||
|
||||
static readonly WeakDictionary<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)> tracking = new WeakDictionary<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>();
|
||||
static readonly WeakDictionary<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)> tracking = new WeakDictionary<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>();
|
||||
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
public static void TrackActiveTask(IUniTaskSource task, int skipFrame)
|
||||
@@ -70,7 +71,19 @@ namespace Cysharp.Threading.Tasks
|
||||
dirty = true;
|
||||
if (!EditorEnableState.EnableTracking) return;
|
||||
var stackTrace = EditorEnableState.EnableStackTrace ? new StackTrace(skipFrame, true).CleanupAsyncStackTrace() : "";
|
||||
tracking.TryAdd(task, (Interlocked.Increment(ref trackingId), DateTime.UtcNow, stackTrace));
|
||||
|
||||
string typeName;
|
||||
if (EditorEnableState.EnableStackTrace)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
TypeBeautify(task.GetType(), sb);
|
||||
typeName = sb.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
typeName = task.GetType().Name;
|
||||
}
|
||||
tracking.TryAdd(task, (typeName, Interlocked.Increment(ref trackingId), DateTime.UtcNow, stackTrace));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -103,19 +116,8 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
string typeName = null;
|
||||
var keyType = listPool[i].Key.GetType();
|
||||
if (keyType.IsNested)
|
||||
{
|
||||
typeName = keyType.DeclaringType.Name + "." + keyType.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
typeName = keyType.Name;
|
||||
}
|
||||
|
||||
action(listPool[i].Value.trackingId, typeName, listPool[i].Key.UnsafeGetStatus(), listPool[i].Value.addTime, listPool[i].Value.stackTrace);
|
||||
listPool[i] = new KeyValuePair<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>(null, (0, default(DateTime), null)); // clear
|
||||
action(listPool[i].Value.trackingId, listPool[i].Value.formattedType, listPool[i].Key.UnsafeGetStatus(), listPool[i].Value.addTime, listPool[i].Value.stackTrace);
|
||||
listPool[i] = default;
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -125,6 +127,52 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void TypeBeautify(Type type, StringBuilder sb)
|
||||
{
|
||||
if (type.IsNested)
|
||||
{
|
||||
// TypeBeautify(type.DeclaringType, sb);
|
||||
sb.Append(type.DeclaringType.Name.ToString());
|
||||
sb.Append(".");
|
||||
}
|
||||
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var genericsStart = type.Name.IndexOf("`");
|
||||
if (genericsStart != -1)
|
||||
{
|
||||
sb.Append(type.Name.Substring(0, genericsStart));
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(type.Name);
|
||||
}
|
||||
sb.Append("<");
|
||||
var first = true;
|
||||
foreach (var item in type.GetGenericArguments())
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
sb.Append(", ");
|
||||
}
|
||||
first = false;
|
||||
TypeBeautify(item, sb);
|
||||
}
|
||||
sb.Append(">");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(type.Name);
|
||||
}
|
||||
}
|
||||
|
||||
//static string RemoveUniTaskNamespace(string str)
|
||||
//{
|
||||
// return str.Replace("Cysharp.Threading.Tasks.CompilerServices", "")
|
||||
// .Replace("Cysharp.Threading.Tasks.Linq", "")
|
||||
// .Replace("Cysharp.Threading.Tasks", "");
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Internal
|
||||
{
|
||||
internal static class UnityWebRequestResultExtensions
|
||||
{
|
||||
public static bool IsError(this UnityWebRequest unityWebRequest)
|
||||
{
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
var result = unityWebRequest.result;
|
||||
return (result == UnityWebRequest.Result.ConnectionError)
|
||||
|| (result == UnityWebRequest.Result.DataProcessingError)
|
||||
|| (result == UnityWebRequest.Result.ProtocolError);
|
||||
#else
|
||||
return unityWebRequest.isHttpError || unityWebRequest.isNetworkError;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 111ba0e639de1d7428af6c823ead4918
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Internal
|
||||
{
|
||||
internal readonly struct ValueStopwatch
|
||||
{
|
||||
static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
|
||||
|
||||
readonly long startTimestamp;
|
||||
|
||||
public static ValueStopwatch StartNew() => new ValueStopwatch(Stopwatch.GetTimestamp());
|
||||
|
||||
ValueStopwatch(long startTimestamp)
|
||||
{
|
||||
this.startTimestamp = startTimestamp;
|
||||
}
|
||||
|
||||
public TimeSpan Elapsed => TimeSpan.FromTicks(this.ElapsedTicks);
|
||||
|
||||
public long ElapsedTicks
|
||||
{
|
||||
get
|
||||
{
|
||||
if (startTimestamp == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Detected invalid initialization(use 'default'), only to create from StartNew().");
|
||||
}
|
||||
|
||||
var delta = Stopwatch.GetTimestamp() - startTimestamp;
|
||||
return (long)(delta * TimestampToTicks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f16fb466974ad034c8732c79c7fd67ea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -3,66 +3,9 @@ using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
public abstract class MoveNextSource : IUniTaskSource<bool>
|
||||
{
|
||||
protected UniTaskCompletionSourceCore<bool> completionSource;
|
||||
|
||||
public bool GetResult(short token)
|
||||
{
|
||||
return completionSource.GetResult(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return completionSource.GetStatus(token);
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
completionSource.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return completionSource.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
completionSource.GetResult(token);
|
||||
}
|
||||
|
||||
protected bool TryGetResult<T>(UniTask<T>.Awaiter awaiter, out T result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = awaiter.GetResult();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
completionSource.TrySetException(ex);
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool TryGetResult(UniTask.Awaiter awaiter)
|
||||
{
|
||||
try
|
||||
{
|
||||
awaiter.GetResult();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
completionSource.TrySetException(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class AsyncEnumeratorBase<TSource, TResult> : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
// note: refactor all inherit class and should remove this.
|
||||
// see Select and Where.
|
||||
internal abstract class AsyncEnumeratorBase<TSource, TResult> : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
static readonly Action<object> moveNextCallbackDelegate = MoveNextCallBack;
|
||||
|
||||
@@ -188,7 +131,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class AsyncEnumeratorAwaitSelectorBase<TSource, TResult, TAwait> : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
internal abstract class AsyncEnumeratorAwaitSelectorBase<TSource, TResult, TAwait> : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
static readonly Action<object> moveNextCallbackDelegate = MoveNextCallBack;
|
||||
static readonly Action<object> setCurrentCallbackDelegate = SetCurrentCallBack;
|
||||
@@ -410,5 +353,4 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
174
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/Create.cs
Normal file
174
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/Create.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
public static partial class UniTaskAsyncEnumerable
|
||||
{
|
||||
public static IUniTaskAsyncEnumerable<T> Create<T>(Func<IAsyncWriter<T>, CancellationToken, UniTask> create)
|
||||
{
|
||||
Error.ThrowArgumentNullException(create, nameof(create));
|
||||
return new Create<T>(create);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IAsyncWriter<T>
|
||||
{
|
||||
UniTask YieldAsync(T value);
|
||||
}
|
||||
|
||||
internal sealed class Create<T> : IUniTaskAsyncEnumerable<T>
|
||||
{
|
||||
readonly Func<IAsyncWriter<T>, CancellationToken, UniTask> create;
|
||||
|
||||
public Create(Func<IAsyncWriter<T>, CancellationToken, UniTask> create)
|
||||
{
|
||||
this.create = create;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _Create(create, cancellationToken);
|
||||
}
|
||||
|
||||
sealed class _Create : MoveNextSource, IUniTaskAsyncEnumerator<T>
|
||||
{
|
||||
readonly Func<IAsyncWriter<T>, CancellationToken, UniTask> create;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
AsyncWriter writer;
|
||||
|
||||
public _Create(Func<IAsyncWriter<T>, CancellationToken, UniTask> create, CancellationToken cancellationToken)
|
||||
{
|
||||
this.create = create;
|
||||
this.cancellationToken = cancellationToken;
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
public T Current { get; private set; }
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return default;
|
||||
}
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void MoveNext()
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
{
|
||||
writer = new AsyncWriter(this);
|
||||
RunWriterTask(create(writer, cancellationToken)).Forget();
|
||||
if (Volatile.Read(ref state) == -2)
|
||||
{
|
||||
return; // complete synchronously
|
||||
}
|
||||
state = 0; // wait YieldAsync, it set TrySetResult(true)
|
||||
return;
|
||||
}
|
||||
case 0:
|
||||
writer.SignalWriter();
|
||||
return;
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
}
|
||||
|
||||
async UniTaskVoid RunWriterTask(UniTask task)
|
||||
{
|
||||
try
|
||||
{
|
||||
await task;
|
||||
goto DONE;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Volatile.Write(ref state, -2);
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
Volatile.Write(ref state, -2);
|
||||
completionSource.TrySetResult(false);
|
||||
}
|
||||
|
||||
public void SetResult(T value)
|
||||
{
|
||||
Current = value;
|
||||
completionSource.TrySetResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AsyncWriter : IUniTaskSource, IAsyncWriter<T>
|
||||
{
|
||||
readonly _Create enumerator;
|
||||
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
public AsyncWriter(_Create enumerator)
|
||||
{
|
||||
this.enumerator = enumerator;
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public UniTask YieldAsync(T value)
|
||||
{
|
||||
core.Reset();
|
||||
enumerator.SetResult(value);
|
||||
return new UniTask(this, core.Version);
|
||||
}
|
||||
|
||||
public void SignalWriter()
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0202f723469f93945afa063bfb440d15
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -79,41 +79,123 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _DistinctUntilChanged(source, comparer, cancellationToken);
|
||||
}
|
||||
|
||||
class _DistinctUntilChanged : AsyncEnumeratorBase<TSource, TSource>
|
||||
sealed class _DistinctUntilChanged : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly IEqualityComparer<TSource> comparer;
|
||||
TSource prev;
|
||||
bool first;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
Action moveNextAction;
|
||||
|
||||
public _DistinctUntilChanged(IUniTaskAsyncEnumerable<TSource> source, IEqualityComparer<TSource> comparer, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.comparer = comparer;
|
||||
this.first = true;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (sourceHasCurrent)
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void MoveNext()
|
||||
{
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
var v = SourceCurrent;
|
||||
if (first || !comparer.Equals(prev, v))
|
||||
switch (state)
|
||||
{
|
||||
first = false;
|
||||
Current = prev = v;
|
||||
result = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case -3;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = -3;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case -3: // first
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 0: // normal
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
var v = enumerator.Current;
|
||||
if (!comparer.Equals(Current, v))
|
||||
{
|
||||
Current = v;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case -2:
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
result = false;
|
||||
return true;
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,45 +218,128 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _DistinctUntilChanged(source, keySelector, comparer, cancellationToken);
|
||||
}
|
||||
|
||||
class _DistinctUntilChanged : AsyncEnumeratorBase<TSource, TSource>
|
||||
sealed class _DistinctUntilChanged : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IEqualityComparer<TKey> comparer;
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, TKey> keySelector;
|
||||
readonly IEqualityComparer<TKey> comparer;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
Action moveNextAction;
|
||||
TKey prev;
|
||||
bool first;
|
||||
|
||||
public _DistinctUntilChanged(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.comparer = comparer;
|
||||
this.source = source;
|
||||
this.keySelector = keySelector;
|
||||
this.first = true;
|
||||
this.comparer = comparer;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (sourceHasCurrent)
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void MoveNext()
|
||||
{
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
var v = SourceCurrent;
|
||||
var key = keySelector(v);
|
||||
if (first || !comparer.Equals(prev, key))
|
||||
switch (state)
|
||||
{
|
||||
first = false;
|
||||
prev = key;
|
||||
Current = v;
|
||||
result = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case -3;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = -3;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case -3: // first
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 0: // normal
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
var v = enumerator.Current;
|
||||
var key = keySelector(v);
|
||||
if (!comparer.Equals(prev, key))
|
||||
{
|
||||
prev = key;
|
||||
Current = v;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case -2:
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
result = false;
|
||||
return true;
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,42 +362,142 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _DistinctUntilChangedAwait(source, keySelector, comparer, cancellationToken);
|
||||
}
|
||||
|
||||
class _DistinctUntilChangedAwait : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, TKey>
|
||||
sealed class _DistinctUntilChangedAwait : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IEqualityComparer<TKey> comparer;
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, UniTask<TKey>> keySelector;
|
||||
readonly IEqualityComparer<TKey> comparer;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<TKey>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
TSource enumeratorCurrent;
|
||||
TKey prev;
|
||||
bool first;
|
||||
|
||||
public _DistinctUntilChangedAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.comparer = comparer;
|
||||
this.source = source;
|
||||
this.keySelector = keySelector;
|
||||
this.first = true;
|
||||
this.comparer = comparer;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override UniTask<TKey> TransformAsync(TSource sourceCurrent)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return keySelector(sourceCurrent);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(TKey key, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
if (first || !comparer.Equals(prev, key))
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
first = false;
|
||||
prev = key;
|
||||
Current = SourceCurrent;
|
||||
terminateIteration = false;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case -3;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = -3;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case -3: // first
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 0: // normal
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
enumeratorCurrent = enumerator.Current;
|
||||
awaiter2 = keySelector(enumeratorCurrent).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
var key = awaiter2.GetResult();
|
||||
if (!comparer.Equals(prev, key))
|
||||
{
|
||||
prev = key;
|
||||
Current = enumeratorCurrent;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
case -2:
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
terminateIteration = false;
|
||||
return false;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,42 +520,142 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _DistinctUntilChangedAwaitWithCancellation(source, keySelector, comparer, cancellationToken);
|
||||
}
|
||||
|
||||
class _DistinctUntilChangedAwaitWithCancellation : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, TKey>
|
||||
sealed class _DistinctUntilChangedAwaitWithCancellation : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IEqualityComparer<TKey> comparer;
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, CancellationToken, UniTask<TKey>> keySelector;
|
||||
readonly IEqualityComparer<TKey> comparer;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<TKey>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
TSource enumeratorCurrent;
|
||||
TKey prev;
|
||||
bool first;
|
||||
|
||||
public _DistinctUntilChangedAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.comparer = comparer;
|
||||
this.source = source;
|
||||
this.keySelector = keySelector;
|
||||
this.first = true;
|
||||
this.comparer = comparer;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override UniTask<TKey> TransformAsync(TSource sourceCurrent)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return keySelector(sourceCurrent, cancellationToken);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(TKey key, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
if (first || !comparer.Equals(prev, key))
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
first = false;
|
||||
prev = key;
|
||||
Current = SourceCurrent;
|
||||
terminateIteration = false;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case -3;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = -3;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case -3: // first
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 0: // normal
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
enumeratorCurrent = enumerator.Current;
|
||||
awaiter2 = keySelector(enumeratorCurrent, cancellationToken).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
var key = awaiter2.GetResult();
|
||||
if (!comparer.Equals(prev, key))
|
||||
{
|
||||
prev = key;
|
||||
Current = enumeratorCurrent;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
case -2:
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
terminateIteration = false;
|
||||
return false;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,8 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
}
|
||||
|
||||
public TSource Current { get; private set; }
|
||||
ITriggerHandler<TSource> ITriggerHandler<TSource>.Prev { get; set; }
|
||||
ITriggerHandler<TSource> ITriggerHandler<TSource>.Next { get; set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
|
||||
@@ -71,29 +71,94 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _Select(source, selector, cancellationToken);
|
||||
}
|
||||
|
||||
sealed class _Select : AsyncEnumeratorBase<TSource, TResult>
|
||||
sealed class _Select : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, TResult> selector;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
Action moveNextAction;
|
||||
|
||||
public _Select(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TResult> selector, CancellationToken cancellationToken)
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
|
||||
public TResult Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (sourceHasCurrent)
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void MoveNext()
|
||||
{
|
||||
try
|
||||
{
|
||||
Current = selector(SourceCurrent);
|
||||
result = true;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = selector(enumerator.Current);
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = false;
|
||||
return true;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,33 +176,98 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public IUniTaskAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _SelectInt(source, selector, cancellationToken);
|
||||
return new _Select(source, selector, cancellationToken);
|
||||
}
|
||||
|
||||
sealed class _SelectInt : AsyncEnumeratorBase<TSource, TResult>
|
||||
sealed class _Select : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, int, TResult> selector;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
Action moveNextAction;
|
||||
int index;
|
||||
|
||||
public _SelectInt(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, TResult> selector, CancellationToken cancellationToken)
|
||||
: base(source, cancellationToken)
|
||||
public _Select(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, TResult> selector, CancellationToken cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
|
||||
public TResult Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (sourceHasCurrent)
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void MoveNext()
|
||||
{
|
||||
try
|
||||
{
|
||||
Current = selector(SourceCurrent, checked(index++));
|
||||
result = true;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = selector(enumerator.Current, checked(index++));
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = false;
|
||||
return true;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,26 +288,107 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _SelectAwait(source, selector, cancellationToken);
|
||||
}
|
||||
|
||||
sealed class _SelectAwait : AsyncEnumeratorAwaitSelectorBase<TSource, TResult, TResult>
|
||||
sealed class _SelectAwait : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, UniTask<TResult>> selector;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<TResult>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
|
||||
public _SelectAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TResult>> selector, CancellationToken cancellationToken)
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
protected override UniTask<TResult> TransformAsync(TSource sourceCurrent)
|
||||
public TResult Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return selector(sourceCurrent);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(TResult awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
Current = awaitResult;
|
||||
terminateIteration = false;
|
||||
return true;
|
||||
try
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
awaiter2 = selector(enumerator.Current).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
Current = awaiter2.GetResult();
|
||||
goto CONTINUE;
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,30 +406,111 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public IUniTaskAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _SelectIntAwait(source, selector, cancellationToken);
|
||||
return new _SelectAwait(source, selector, cancellationToken);
|
||||
}
|
||||
|
||||
sealed class _SelectIntAwait : AsyncEnumeratorAwaitSelectorBase<TSource, TResult, TResult>
|
||||
sealed class _SelectAwait : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, int, UniTask<TResult>> selector;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<TResult>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
int index;
|
||||
|
||||
public _SelectIntAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, UniTask<TResult>> selector, CancellationToken cancellationToken)
|
||||
: base(source, cancellationToken)
|
||||
public _SelectAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, UniTask<TResult>> selector, CancellationToken cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
protected override UniTask<TResult> TransformAsync(TSource sourceCurrent)
|
||||
public TResult Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return selector(sourceCurrent, checked(index++));
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(TResult awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
Current = awaitResult;
|
||||
terminateIteration = false;
|
||||
return true;
|
||||
try
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
awaiter2 = selector(enumerator.Current, checked(index++)).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
Current = awaiter2.GetResult();
|
||||
goto CONTINUE;
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,26 +531,107 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _SelectAwaitWithCancellation(source, selector, cancellationToken);
|
||||
}
|
||||
|
||||
sealed class _SelectAwaitWithCancellation : AsyncEnumeratorAwaitSelectorBase<TSource, TResult, TResult>
|
||||
sealed class _SelectAwaitWithCancellation : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, CancellationToken, UniTask<TResult>> selector;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<TResult>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
|
||||
public _SelectAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TResult>> selector, CancellationToken cancellationToken)
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
protected override UniTask<TResult> TransformAsync(TSource sourceCurrent)
|
||||
public TResult Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return selector(sourceCurrent, cancellationToken);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(TResult awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
Current = awaitResult;
|
||||
terminateIteration = false;
|
||||
return true;
|
||||
try
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
awaiter2 = selector(enumerator.Current, cancellationToken).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
Current = awaiter2.GetResult();
|
||||
goto CONTINUE;
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,32 +649,112 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public IUniTaskAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _SelectIntAwaitWithCancellation(source, selector, cancellationToken);
|
||||
return new _SelectAwaitWithCancellation(source, selector, cancellationToken);
|
||||
}
|
||||
|
||||
sealed class _SelectIntAwaitWithCancellation : AsyncEnumeratorAwaitSelectorBase<TSource, TResult, TResult>
|
||||
sealed class _SelectAwaitWithCancellation : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, int, CancellationToken, UniTask<TResult>> selector;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<TResult>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
int index;
|
||||
|
||||
public _SelectIntAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, UniTask<TResult>> selector, CancellationToken cancellationToken)
|
||||
: base(source, cancellationToken)
|
||||
public _SelectAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, UniTask<TResult>> selector, CancellationToken cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
protected override UniTask<TResult> TransformAsync(TSource sourceCurrent)
|
||||
public TResult Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return selector(sourceCurrent, checked(index++), cancellationToken);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(TResult awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
Current = awaitResult;
|
||||
terminateIteration = false;
|
||||
return true;
|
||||
try
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
awaiter2 = selector(enumerator.Current, checked(index++), cancellationToken).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
Current = awaiter2.GetResult();
|
||||
goto CONTINUE;
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
187
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/SkipUntil.cs
Normal file
187
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/SkipUntil.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
public static partial class UniTaskAsyncEnumerable
|
||||
{
|
||||
public static IUniTaskAsyncEnumerable<TSource> SkipUntil<TSource>(this IUniTaskAsyncEnumerable<TSource> source, UniTask other)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
|
||||
return new SkipUntil<TSource>(source, other, null);
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<TSource> SkipUntil<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<CancellationToken, UniTask> other)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(source, nameof(other));
|
||||
|
||||
return new SkipUntil<TSource>(source, default, other);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SkipUntil<TSource> : IUniTaskAsyncEnumerable<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly UniTask other;
|
||||
readonly Func<CancellationToken, UniTask> other2;
|
||||
|
||||
public SkipUntil(IUniTaskAsyncEnumerable<TSource> source, UniTask other, Func<CancellationToken, UniTask> other2)
|
||||
{
|
||||
this.source = source;
|
||||
this.other = other;
|
||||
this.other2 = other2;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (other2 != null)
|
||||
{
|
||||
return new _SkipUntil(source, this.other2(cancellationToken), cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new _SkipUntil(source, this.other, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class _SkipUntil : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
static readonly Action<object> CancelDelegate1 = OnCanceled1;
|
||||
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
|
||||
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
CancellationToken cancellationToken1;
|
||||
|
||||
bool completed;
|
||||
CancellationTokenRegistration cancellationTokenRegistration1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
bool continueNext;
|
||||
Exception exception;
|
||||
|
||||
public _SkipUntil(IUniTaskAsyncEnumerable<TSource> source, UniTask other, CancellationToken cancellationToken1)
|
||||
{
|
||||
this.source = source;
|
||||
this.cancellationToken1 = cancellationToken1;
|
||||
if (cancellationToken1.CanBeCanceled)
|
||||
{
|
||||
this.cancellationTokenRegistration1 = cancellationToken1.RegisterWithoutCaptureExecutionContext(CancelDelegate1, this);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
RunOther(other).Forget();
|
||||
}
|
||||
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (exception != null)
|
||||
{
|
||||
return UniTask.FromException<bool>(exception);
|
||||
}
|
||||
|
||||
if (cancellationToken1.IsCancellationRequested)
|
||||
{
|
||||
return UniTask.FromCanceled<bool>(cancellationToken1);
|
||||
}
|
||||
|
||||
if (enumerator == null)
|
||||
{
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken1);
|
||||
}
|
||||
completionSource.Reset();
|
||||
|
||||
if (completed)
|
||||
{
|
||||
SourceMoveNext();
|
||||
}
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void SourceMoveNext()
|
||||
{
|
||||
try
|
||||
{
|
||||
LOOP:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
continueNext = true;
|
||||
MoveNextCore(this);
|
||||
if (continueNext)
|
||||
{
|
||||
continueNext = false;
|
||||
goto LOOP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
awaiter.SourceOnCompleted(MoveNextCoreDelegate, this);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
completionSource.TrySetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static void MoveNextCore(object state)
|
||||
{
|
||||
var self = (_SkipUntil)state;
|
||||
|
||||
if (self.TryGetResult(self.awaiter, out var result))
|
||||
{
|
||||
if (result)
|
||||
{
|
||||
self.Current = self.enumerator.Current;
|
||||
self.completionSource.TrySetResult(true);
|
||||
if (self.continueNext)
|
||||
{
|
||||
self.SourceMoveNext();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self.completionSource.TrySetResult(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async UniTaskVoid RunOther(UniTask other)
|
||||
{
|
||||
try
|
||||
{
|
||||
await other;
|
||||
completed = true;
|
||||
SourceMoveNext();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
completionSource.TrySetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnCanceled1(object state)
|
||||
{
|
||||
var self = (_SkipUntil)state;
|
||||
self.completionSource.TrySetCanceled(self.cancellationToken1);
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
cancellationTokenRegistration1.Dispose();
|
||||
if (enumerator != null)
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de932d79c8d9f3841a066d05ff29edc9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -32,13 +32,17 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
sealed class _SkipUntilCanceled : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
static readonly Action<object> CancelDelegate1 = OnCanceled1;
|
||||
static readonly Action<object> CancelDelegate2 = OnCanceled2;
|
||||
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
|
||||
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
CancellationToken cancellationToken1;
|
||||
CancellationToken cancellationToken2;
|
||||
CancellationTokenRegistration cancellationTokenRegistration1;
|
||||
CancellationTokenRegistration cancellationTokenRegistration2;
|
||||
|
||||
bool isCanceled;
|
||||
int isCanceled;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
bool continueNext;
|
||||
@@ -48,6 +52,14 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
this.source = source;
|
||||
this.cancellationToken1 = cancellationToken1;
|
||||
this.cancellationToken2 = cancellationToken2;
|
||||
if (cancellationToken1.CanBeCanceled)
|
||||
{
|
||||
this.cancellationTokenRegistration1 = cancellationToken1.RegisterWithoutCaptureExecutionContext(CancelDelegate1, this);
|
||||
}
|
||||
if (cancellationToken1 != cancellationToken2 && cancellationToken2.CanBeCanceled)
|
||||
{
|
||||
this.cancellationTokenRegistration2 = cancellationToken2.RegisterWithoutCaptureExecutionContext(CancelDelegate2, this);
|
||||
}
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
@@ -55,15 +67,18 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (cancellationToken1.IsCancellationRequested) isCanceled = true;
|
||||
if (cancellationToken2.IsCancellationRequested) isCanceled = true;
|
||||
|
||||
if (enumerator == null)
|
||||
{
|
||||
if (cancellationToken1.IsCancellationRequested) isCanceled = 1;
|
||||
if (cancellationToken2.IsCancellationRequested) isCanceled = 1;
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken2); // use only AsyncEnumerator provided token.
|
||||
}
|
||||
completionSource.Reset();
|
||||
SourceMoveNext();
|
||||
|
||||
if (isCanceled != 0)
|
||||
{
|
||||
SourceMoveNext();
|
||||
}
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
@@ -102,25 +117,11 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
if (result)
|
||||
{
|
||||
AGAIN:
|
||||
|
||||
if (self.isCanceled)
|
||||
self.Current = self.enumerator.Current;
|
||||
self.completionSource.TrySetResult(true);
|
||||
if (self.continueNext)
|
||||
{
|
||||
self.continueNext = false;
|
||||
self.Current = self.enumerator.Current;
|
||||
self.completionSource.TrySetResult(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self.cancellationToken1.IsCancellationRequested) self.isCanceled = true;
|
||||
if (self.cancellationToken2.IsCancellationRequested) self.isCanceled = true;
|
||||
|
||||
if (self.isCanceled) goto AGAIN;
|
||||
|
||||
if (!self.continueNext)
|
||||
{
|
||||
self.SourceMoveNext();
|
||||
}
|
||||
self.SourceMoveNext();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -130,9 +131,37 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
}
|
||||
}
|
||||
|
||||
static void OnCanceled1(object state)
|
||||
{
|
||||
var self = (_SkipUntilCanceled)state;
|
||||
if (self.isCanceled == 0)
|
||||
{
|
||||
if (Interlocked.Increment(ref self.isCanceled) == 1)
|
||||
{
|
||||
self.cancellationTokenRegistration2.Dispose();
|
||||
self.SourceMoveNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnCanceled2(object state)
|
||||
{
|
||||
var self = (_SkipUntilCanceled)state;
|
||||
if (self.isCanceled == 0)
|
||||
{
|
||||
if (Interlocked.Increment(ref self.isCanceled) == 1)
|
||||
{
|
||||
self.cancellationTokenRegistration2.Dispose();
|
||||
self.SourceMoveNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
cancellationTokenRegistration1.Dispose();
|
||||
cancellationTokenRegistration2.Dispose();
|
||||
if (enumerator != null)
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
|
||||
536
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/Subscribe.cs
Normal file
536
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/Subscribe.cs
Normal file
@@ -0,0 +1,536 @@
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Subscribes = Cysharp.Threading.Tasks.Linq.Subscribe;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
public static partial class UniTaskAsyncEnumerable
|
||||
{
|
||||
// OnNext
|
||||
|
||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> action)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(action, nameof(action));
|
||||
|
||||
var cts = new CancellationTokenDisposable();
|
||||
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
|
||||
return cts;
|
||||
}
|
||||
|
||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> action)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(action, nameof(action));
|
||||
|
||||
var cts = new CancellationTokenDisposable();
|
||||
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
|
||||
return cts;
|
||||
}
|
||||
|
||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTaskVoid> action)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(action, nameof(action));
|
||||
|
||||
var cts = new CancellationTokenDisposable();
|
||||
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
|
||||
return cts;
|
||||
}
|
||||
|
||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> action, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(action, nameof(action));
|
||||
|
||||
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> action, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(action, nameof(action));
|
||||
|
||||
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTaskVoid> action, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(action, nameof(action));
|
||||
|
||||
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
|
||||
var cts = new CancellationTokenDisposable();
|
||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
|
||||
return cts;
|
||||
}
|
||||
|
||||
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
|
||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
|
||||
var cts = new CancellationTokenDisposable();
|
||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
|
||||
return cts;
|
||||
}
|
||||
|
||||
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
|
||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
// OnNext, OnError
|
||||
|
||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> onError)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
||||
|
||||
var cts = new CancellationTokenDisposable();
|
||||
Subscribes.SubscribeCore(source, onNext, onError, Subscribes.NopCompleted, cts.Token).Forget();
|
||||
return cts;
|
||||
}
|
||||
|
||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action<Exception> onError)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
||||
|
||||
var cts = new CancellationTokenDisposable();
|
||||
Subscribes.SubscribeCore(source, onNext, onError, Subscribes.NopCompleted, cts.Token).Forget();
|
||||
return cts;
|
||||
}
|
||||
|
||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> onError, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
||||
|
||||
Subscribes.SubscribeCore(source, onNext, onError, Subscribes.NopCompleted, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action<Exception> onError, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
||||
|
||||
Subscribes.SubscribeCore(source, onNext, onError, Subscribes.NopCompleted, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action<Exception> onError)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
||||
|
||||
var cts = new CancellationTokenDisposable();
|
||||
Subscribes.SubscribeAwaitCore(source, onNext, onError, Subscribes.NopCompleted, cts.Token).Forget();
|
||||
return cts;
|
||||
}
|
||||
|
||||
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action<Exception> onError, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
||||
|
||||
Subscribes.SubscribeAwaitCore(source, onNext, onError, Subscribes.NopCompleted, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action<Exception> onError)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
||||
|
||||
var cts = new CancellationTokenDisposable();
|
||||
Subscribes.SubscribeAwaitCore(source, onNext, onError, Subscribes.NopCompleted, cts.Token).Forget();
|
||||
return cts;
|
||||
}
|
||||
|
||||
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action<Exception> onError, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
||||
|
||||
Subscribes.SubscribeAwaitCore(source, onNext, onError, Subscribes.NopCompleted, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
// OnNext, OnCompleted
|
||||
|
||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action onCompleted)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
||||
|
||||
var cts = new CancellationTokenDisposable();
|
||||
Subscribes.SubscribeCore(source, onNext, Subscribes.NopError, onCompleted, cts.Token).Forget();
|
||||
return cts;
|
||||
}
|
||||
|
||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action onCompleted)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
||||
|
||||
var cts = new CancellationTokenDisposable();
|
||||
Subscribes.SubscribeCore(source, onNext, Subscribes.NopError, onCompleted, cts.Token).Forget();
|
||||
return cts;
|
||||
}
|
||||
|
||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action onCompleted, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
||||
|
||||
Subscribes.SubscribeCore(source, onNext, Subscribes.NopError, onCompleted, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action onCompleted, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
||||
|
||||
Subscribes.SubscribeCore(source, onNext, Subscribes.NopError, onCompleted, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action onCompleted)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
||||
|
||||
var cts = new CancellationTokenDisposable();
|
||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, onCompleted, cts.Token).Forget();
|
||||
return cts;
|
||||
}
|
||||
|
||||
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action onCompleted, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
||||
|
||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, onCompleted, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action onCompleted)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
||||
|
||||
var cts = new CancellationTokenDisposable();
|
||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, onCompleted, cts.Token).Forget();
|
||||
return cts;
|
||||
}
|
||||
|
||||
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action onCompleted, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
||||
|
||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, onCompleted, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
// IObserver
|
||||
|
||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, IObserver<TSource> observer)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(observer, nameof(observer));
|
||||
|
||||
var cts = new CancellationTokenDisposable();
|
||||
Subscribes.SubscribeCore(source, observer, cts.Token).Forget();
|
||||
return cts;
|
||||
}
|
||||
|
||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, IObserver<TSource> observer, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(observer, nameof(observer));
|
||||
|
||||
Subscribes.SubscribeCore(source, observer, cancellationToken).Forget();
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class CancellationTokenDisposable : IDisposable
|
||||
{
|
||||
readonly CancellationTokenSource cts = new CancellationTokenSource();
|
||||
|
||||
public CancellationToken Token => cts.Token;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!cts.IsCancellationRequested)
|
||||
{
|
||||
cts.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class Subscribe
|
||||
{
|
||||
public static readonly Action<Exception> NopError = _ => { };
|
||||
public static readonly Action NopCompleted = () => { };
|
||||
|
||||
public static async UniTaskVoid SubscribeCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
|
||||
{
|
||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
||||
try
|
||||
{
|
||||
while (await e.MoveNextAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
onNext(e.Current);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
}
|
||||
}
|
||||
onCompleted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (onError == NopError)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ex is OperationCanceledException) return;
|
||||
|
||||
onError(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (e != null)
|
||||
{
|
||||
await e.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async UniTaskVoid SubscribeCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
|
||||
{
|
||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
||||
try
|
||||
{
|
||||
while (await e.MoveNextAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
onNext(e.Current).Forget();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
}
|
||||
}
|
||||
onCompleted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (onError == NopError)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ex is OperationCanceledException) return;
|
||||
|
||||
onError(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (e != null)
|
||||
{
|
||||
await e.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async UniTaskVoid SubscribeCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTaskVoid> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
|
||||
{
|
||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
||||
try
|
||||
{
|
||||
while (await e.MoveNextAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
onNext(e.Current, cancellationToken).Forget();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
}
|
||||
}
|
||||
onCompleted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (onError == NopError)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ex is OperationCanceledException) return;
|
||||
|
||||
onError(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (e != null)
|
||||
{
|
||||
await e.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async UniTaskVoid SubscribeCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, IObserver<TSource> observer, CancellationToken cancellationToken)
|
||||
{
|
||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
||||
try
|
||||
{
|
||||
while (await e.MoveNextAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
observer.OnNext(e.Current);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
}
|
||||
}
|
||||
observer.OnCompleted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is OperationCanceledException) return;
|
||||
|
||||
observer.OnError(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (e != null)
|
||||
{
|
||||
await e.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async UniTaskVoid SubscribeAwaitCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
|
||||
{
|
||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
||||
try
|
||||
{
|
||||
while (await e.MoveNextAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
await onNext(e.Current);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
}
|
||||
}
|
||||
onCompleted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (onError == NopError)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ex is OperationCanceledException) return;
|
||||
|
||||
onError(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (e != null)
|
||||
{
|
||||
await e.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async UniTaskVoid SubscribeAwaitCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
|
||||
{
|
||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
||||
try
|
||||
{
|
||||
while (await e.MoveNextAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
await onNext(e.Current, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
}
|
||||
}
|
||||
onCompleted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (onError == NopError)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ex is OperationCanceledException) return;
|
||||
|
||||
onError(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (e != null)
|
||||
{
|
||||
await e.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 263479eb04c189741931fc0e2f615c2d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
190
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/TakeUntil.cs
Normal file
190
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/TakeUntil.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
public static partial class UniTaskAsyncEnumerable
|
||||
{
|
||||
public static IUniTaskAsyncEnumerable<TSource> TakeUntil<TSource>(this IUniTaskAsyncEnumerable<TSource> source, UniTask other)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
|
||||
return new TakeUntil<TSource>(source, other, null);
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<TSource> TakeUntil<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<CancellationToken, UniTask> other)
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
Error.ThrowArgumentNullException(source, nameof(other));
|
||||
|
||||
return new TakeUntil<TSource>(source, default, other);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TakeUntil<TSource> : IUniTaskAsyncEnumerable<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly UniTask other;
|
||||
readonly Func<CancellationToken, UniTask> other2;
|
||||
|
||||
public TakeUntil(IUniTaskAsyncEnumerable<TSource> source, UniTask other, Func<CancellationToken, UniTask> other2)
|
||||
{
|
||||
this.source = source;
|
||||
this.other = other;
|
||||
this.other2 = other2;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (other2 != null)
|
||||
{
|
||||
return new _TakeUntil(source, this.other2(cancellationToken), cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new _TakeUntil(source, this.other, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class _TakeUntil : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
static readonly Action<object> CancelDelegate1 = OnCanceled1;
|
||||
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
|
||||
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
CancellationToken cancellationToken1;
|
||||
CancellationTokenRegistration cancellationTokenRegistration1;
|
||||
|
||||
bool completed;
|
||||
Exception exception;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
|
||||
public _TakeUntil(IUniTaskAsyncEnumerable<TSource> source, UniTask other, CancellationToken cancellationToken1)
|
||||
{
|
||||
this.source = source;
|
||||
this.cancellationToken1 = cancellationToken1;
|
||||
|
||||
if (cancellationToken1.CanBeCanceled)
|
||||
{
|
||||
this.cancellationTokenRegistration1 = cancellationToken1.RegisterWithoutCaptureExecutionContext(CancelDelegate1, this);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
|
||||
RunOther(other).Forget();
|
||||
}
|
||||
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (completed)
|
||||
{
|
||||
return CompletedTasks.False;
|
||||
}
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
return UniTask.FromException<bool>(exception);
|
||||
}
|
||||
|
||||
if (cancellationToken1.IsCancellationRequested)
|
||||
{
|
||||
return UniTask.FromCanceled<bool>(cancellationToken1);
|
||||
}
|
||||
|
||||
if (enumerator == null)
|
||||
{
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken1);
|
||||
}
|
||||
|
||||
completionSource.Reset();
|
||||
SourceMoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void SourceMoveNext()
|
||||
{
|
||||
try
|
||||
{
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
MoveNextCore(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
awaiter.SourceOnCompleted(MoveNextCoreDelegate, this);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
completionSource.TrySetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static void MoveNextCore(object state)
|
||||
{
|
||||
var self = (_TakeUntil)state;
|
||||
|
||||
if (self.TryGetResult(self.awaiter, out var result))
|
||||
{
|
||||
if (result)
|
||||
{
|
||||
if (self.exception != null)
|
||||
{
|
||||
self.completionSource.TrySetException(self.exception);
|
||||
}
|
||||
else if (self.cancellationToken1.IsCancellationRequested)
|
||||
{
|
||||
self.completionSource.TrySetCanceled(self.cancellationToken1);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.Current = self.enumerator.Current;
|
||||
self.completionSource.TrySetResult(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self.completionSource.TrySetResult(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async UniTaskVoid RunOther(UniTask other)
|
||||
{
|
||||
try
|
||||
{
|
||||
await other;
|
||||
completed = true;
|
||||
completionSource.TrySetResult(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
completionSource.TrySetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnCanceled1(object state)
|
||||
{
|
||||
var self = (_TakeUntil)state;
|
||||
self.completionSource.TrySetCanceled(self.cancellationToken1);
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
cancellationTokenRegistration1.Dispose();
|
||||
if (enumerator != null)
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12bda324162f15349afefc2c152ac07f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -248,15 +248,18 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return current;
|
||||
}
|
||||
|
||||
if (queuedResult.Count != 0)
|
||||
lock (queuedResult)
|
||||
{
|
||||
current = queuedResult.Dequeue();
|
||||
useCachedCurrent = true;
|
||||
return current;
|
||||
}
|
||||
else
|
||||
{
|
||||
return default; // undefined.
|
||||
if (queuedResult.Count != 0)
|
||||
{
|
||||
current = queuedResult.Dequeue();
|
||||
useCachedCurrent = true;
|
||||
return current;
|
||||
}
|
||||
else
|
||||
{
|
||||
return default; // undefined.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "UniTask.Linq",
|
||||
"references": [
|
||||
"UniTask"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c01796d064528144a599661eaab93a6
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -34,6 +34,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
public _EveryUpdate(PlayerLoopTiming updateTiming, CancellationToken cancellationToken)
|
||||
{
|
||||
this.updateTiming = updateTiming;
|
||||
this.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(this, 2);
|
||||
PlayerLoopHelper.AddAction(updateTiming, this);
|
||||
|
||||
@@ -208,7 +208,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
UnityEngine.Debug.Log("TRY_RESULT:" + target.TryGetTarget(out var _));
|
||||
if (disposed || cancellationToken.IsCancellationRequested || !target.TryGetTarget(out var t))
|
||||
{
|
||||
completionSource.TrySetResult(false);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
@@ -22,16 +23,34 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update)
|
||||
{
|
||||
if (dueTimeFrameCount < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. dueTimeFrameCount:" + dueTimeFrameCount);
|
||||
}
|
||||
|
||||
return new TimerFrame(dueTimeFrameCount, null, updateTiming);
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, int periodFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update)
|
||||
{
|
||||
if (dueTimeFrameCount < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. dueTimeFrameCount:" + dueTimeFrameCount);
|
||||
}
|
||||
if (periodFrameCount < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Delay does not allow minus periodFrameCount. periodFrameCount:" + dueTimeFrameCount);
|
||||
}
|
||||
|
||||
return new TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming);
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> IntervalFrame(int intervalFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update)
|
||||
{
|
||||
if (intervalFrameCount < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Delay does not allow minus intervalFrameCount. intervalFrameCount:" + intervalFrameCount);
|
||||
}
|
||||
return new TimerFrame(intervalFrameCount, intervalFrameCount, updateTiming);
|
||||
}
|
||||
}
|
||||
@@ -64,6 +83,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
readonly bool ignoreTimeScale;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
int initialFrame;
|
||||
float elapsed;
|
||||
bool dueTimePhase;
|
||||
bool completed;
|
||||
@@ -80,9 +100,11 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
if (this.period <= 0) this.period = 1;
|
||||
}
|
||||
|
||||
this.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
|
||||
this.dueTimePhase = true;
|
||||
this.updateTiming = updateTiming;
|
||||
this.ignoreTimeScale = ignoreTimeScale;
|
||||
this.cancellationToken = cancellationToken;
|
||||
TaskTracker.TrackActiveTask(this, 2);
|
||||
PlayerLoopHelper.AddAction(updateTiming, this);
|
||||
}
|
||||
@@ -119,9 +141,19 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return false;
|
||||
}
|
||||
|
||||
elapsed += (ignoreTimeScale) ? UnityEngine.Time.unscaledDeltaTime : UnityEngine.Time.deltaTime;
|
||||
if (dueTimePhase)
|
||||
{
|
||||
if (elapsed == 0)
|
||||
{
|
||||
// skip in initial frame.
|
||||
if (initialFrame == Time.frameCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
elapsed += (ignoreTimeScale) ? UnityEngine.Time.unscaledDeltaTime : UnityEngine.Time.deltaTime;
|
||||
|
||||
if (elapsed >= dueTime)
|
||||
{
|
||||
dueTimePhase = false;
|
||||
@@ -137,6 +169,8 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return false;
|
||||
}
|
||||
|
||||
elapsed += (ignoreTimeScale) ? UnityEngine.Time.unscaledDeltaTime : UnityEngine.Time.deltaTime;
|
||||
|
||||
if (elapsed >= period)
|
||||
{
|
||||
completionSource.TrySetResult(true);
|
||||
@@ -172,6 +206,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
readonly int? periodFrameCount;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
int initialFrame;
|
||||
int currentFrame;
|
||||
bool dueTimePhase;
|
||||
bool completed;
|
||||
@@ -185,9 +220,11 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
if (periodFrameCount <= 0) periodFrameCount = 1;
|
||||
}
|
||||
|
||||
this.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
|
||||
this.dueTimePhase = true;
|
||||
this.dueTimeFrameCount = dueTimeFrameCount;
|
||||
this.periodFrameCount = periodFrameCount;
|
||||
this.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(this, 2);
|
||||
PlayerLoopHelper.AddAction(updateTiming, this);
|
||||
@@ -228,11 +265,30 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
if (dueTimePhase)
|
||||
{
|
||||
if (currentFrame++ >= dueTimeFrameCount)
|
||||
if (currentFrame == 0)
|
||||
{
|
||||
if (dueTimeFrameCount == 0)
|
||||
{
|
||||
dueTimePhase = false;
|
||||
completionSource.TrySetResult(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// skip in initial frame.
|
||||
if (initialFrame == Time.frameCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (++currentFrame >= dueTimeFrameCount)
|
||||
{
|
||||
dueTimePhase = false;
|
||||
completionSource.TrySetResult(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -71,36 +71,103 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _Where(source, predicate, cancellationToken);
|
||||
}
|
||||
|
||||
class _Where : AsyncEnumeratorBase<TSource, TSource>
|
||||
sealed class _Where : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, bool> predicate;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
Action moveNextAction;
|
||||
|
||||
public _Where(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, bool> predicate, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.predicate = predicate;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (sourceHasCurrent)
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void MoveNext()
|
||||
{
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
if (predicate(SourceCurrent))
|
||||
switch (state)
|
||||
{
|
||||
Current = SourceCurrent;
|
||||
result = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
if (predicate(Current))
|
||||
{
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
result = false;
|
||||
return true;
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,40 +185,107 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _WhereInt(source, predicate, cancellationToken);
|
||||
return new _Where(source, predicate, cancellationToken);
|
||||
}
|
||||
|
||||
class _WhereInt : AsyncEnumeratorBase<TSource, TSource>
|
||||
sealed class _Where : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, int, bool> predicate;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
Action moveNextAction;
|
||||
int index;
|
||||
|
||||
public _WhereInt(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, bool> predicate, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
public _Where(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, bool> predicate, CancellationToken cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.predicate = predicate;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (sourceHasCurrent)
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void MoveNext()
|
||||
{
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
if (predicate(SourceCurrent, checked(index++)))
|
||||
switch (state)
|
||||
{
|
||||
Current = SourceCurrent;
|
||||
result = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
if (predicate(Current, checked(index++)))
|
||||
{
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
result = false;
|
||||
return true;
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,34 +306,117 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _WhereAwait(source, predicate, cancellationToken);
|
||||
}
|
||||
|
||||
class _WhereAwait : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, bool>
|
||||
sealed class _WhereAwait : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, UniTask<bool>> predicate;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<bool>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
|
||||
public _WhereAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<bool>> predicate, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.predicate = predicate;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
protected override UniTask<bool> TransformAsync(TSource sourceCurrent)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return predicate(sourceCurrent);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
terminateIteration = false;
|
||||
if (awaitResult)
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
Current = SourceCurrent;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
|
||||
awaiter2 = predicate(Current).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
if (awaiter2.GetResult())
|
||||
{
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
return false;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,44 +434,125 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _WhereIntAwait(source, predicate, cancellationToken);
|
||||
return new _WhereAwait(source, predicate, cancellationToken);
|
||||
}
|
||||
|
||||
class _WhereIntAwait : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, bool>
|
||||
sealed class _WhereAwait : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, int, UniTask<bool>> predicate;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<bool>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
int index;
|
||||
|
||||
public _WhereIntAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, UniTask<bool>> predicate, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
public _WhereAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, UniTask<bool>> predicate, CancellationToken cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.predicate = predicate;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
protected override UniTask<bool> TransformAsync(TSource sourceCurrent)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return predicate(sourceCurrent, checked(index++));
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
terminateIteration = false;
|
||||
if (awaitResult)
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
Current = SourceCurrent;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
|
||||
awaiter2 = predicate(Current, checked(index++)).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
if (awaiter2.GetResult())
|
||||
{
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
return false;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal sealed class WhereAwaitWithCancellation<TSource> : IUniTaskAsyncEnumerable<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
@@ -271,34 +569,117 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _WhereAwaitWithCancellation(source, predicate, cancellationToken);
|
||||
}
|
||||
|
||||
class _WhereAwaitWithCancellation : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, bool>
|
||||
sealed class _WhereAwaitWithCancellation : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, CancellationToken, UniTask<bool>> predicate;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<bool>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
|
||||
public _WhereAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<bool>> predicate, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.predicate = predicate;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
protected override UniTask<bool> TransformAsync(TSource sourceCurrent)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return predicate(sourceCurrent, cancellationToken);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
terminateIteration = false;
|
||||
if (awaitResult)
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
Current = SourceCurrent;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
|
||||
awaiter2 = predicate(Current, cancellationToken).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
if (awaiter2.GetResult())
|
||||
{
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
return false;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -316,40 +697,122 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _WhereIntAwaitWithCancellation(source, predicate, cancellationToken);
|
||||
return new _WhereAwaitWithCancellation(source, predicate, cancellationToken);
|
||||
}
|
||||
|
||||
class _WhereIntAwaitWithCancellation : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, bool>
|
||||
sealed class _WhereAwaitWithCancellation : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, int, CancellationToken, UniTask<bool>> predicate;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<bool>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
int index;
|
||||
|
||||
public _WhereIntAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, UniTask<bool>> predicate, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
public _WhereAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, UniTask<bool>> predicate, CancellationToken cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.predicate = predicate;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
protected override UniTask<bool> TransformAsync(TSource sourceCurrent)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return predicate(sourceCurrent, checked(index++), cancellationToken);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
terminateIteration = false;
|
||||
if (awaitResult)
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
Current = SourceCurrent;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
|
||||
awaiter2 = predicate(Current, checked(index++), cancellationToken).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
if (awaiter2.GetResult())
|
||||
{
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
return false;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
63
src/UniTask/Assets/Plugins/UniTask/Runtime/MoveNextSource.cs
Normal file
63
src/UniTask/Assets/Plugins/UniTask/Runtime/MoveNextSource.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public abstract class MoveNextSource : IUniTaskSource<bool>
|
||||
{
|
||||
protected UniTaskCompletionSourceCore<bool> completionSource;
|
||||
|
||||
public bool GetResult(short token)
|
||||
{
|
||||
return completionSource.GetResult(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return completionSource.GetStatus(token);
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
completionSource.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return completionSource.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
completionSource.GetResult(token);
|
||||
}
|
||||
|
||||
protected bool TryGetResult<T>(UniTask<T>.Awaiter awaiter, out T result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = awaiter.GetResult();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
completionSource.TrySetException(ex);
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool TryGetResult(UniTask.Awaiter awaiter)
|
||||
{
|
||||
try
|
||||
{
|
||||
awaiter.GetResult();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
completionSource.TrySetException(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc4c5dc2a5f246e4f8df44cab735826c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -92,8 +92,12 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static SynchronizationContext UnitySynchronizationContext => unitySynchronizationContetext;
|
||||
public static int MainThreadId => mainThreadId;
|
||||
internal static string ApplicationDataPath => applicationDataPath;
|
||||
|
||||
public static bool IsMainThread => Thread.CurrentThread.ManagedThreadId == mainThreadId;
|
||||
|
||||
static int mainThreadId;
|
||||
static string applicationDataPath;
|
||||
static SynchronizationContext unitySynchronizationContetext;
|
||||
static ContinuationQueue[] yielders;
|
||||
static PlayerLoopRunner[] runners;
|
||||
@@ -171,12 +175,43 @@ namespace Cysharp.Threading.Tasks
|
||||
return dest;
|
||||
}
|
||||
|
||||
static PlayerLoopSystem[] InsertUniTaskSynchronizationContext(PlayerLoopSystem loopSystem)
|
||||
{
|
||||
var loop = new PlayerLoopSystem
|
||||
{
|
||||
type = typeof(UniTaskSynchronizationContext),
|
||||
updateDelegate = UniTaskSynchronizationContext.Run
|
||||
};
|
||||
|
||||
// Remove items from previous initializations.
|
||||
var source = loopSystem.subSystemList
|
||||
.Where(ls => ls.type != typeof(UniTaskSynchronizationContext))
|
||||
.ToArray();
|
||||
|
||||
var dest = new System.Collections.Generic.List<PlayerLoopSystem>(source);
|
||||
|
||||
var index = dest.FindIndex(x => x.type.Name == "ScriptRunDelayedTasks");
|
||||
if (index == -1)
|
||||
{
|
||||
index = dest.FindIndex(x => x.type.Name == "UniTaskLoopRunnerUpdate");
|
||||
}
|
||||
|
||||
dest.Insert(index + 1, loop);
|
||||
|
||||
return dest.ToArray();
|
||||
}
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
static void Init()
|
||||
{
|
||||
// capture default(unity) sync-context.
|
||||
unitySynchronizationContetext = SynchronizationContext.Current;
|
||||
mainThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
try
|
||||
{
|
||||
applicationDataPath = Application.dataPath;
|
||||
}
|
||||
catch { }
|
||||
|
||||
#if UNITY_EDITOR && UNITY_2019_3_OR_NEWER
|
||||
// When domain reload is disabled, re-initialization is required when entering play mode;
|
||||
@@ -237,6 +272,8 @@ namespace Cysharp.Threading.Tasks
|
||||
if (item != null) item.Run();
|
||||
}
|
||||
}
|
||||
|
||||
UniTaskSynchronizationContext.Run();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -251,7 +288,7 @@ namespace Cysharp.Threading.Tasks
|
||||
// Initialization
|
||||
copyList[0].subSystemList = InsertRunner(copyList[0], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), yielders[0] = new ContinuationQueue(PlayerLoopTiming.Initialization),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldInitialization), yielders[1] = new ContinuationQueue(PlayerLoopTiming.LastInitialization),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), runners[1] = new PlayerLoopRunner(PlayerLoopTiming.Initialization),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), runners[0] = new PlayerLoopRunner(PlayerLoopTiming.Initialization),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastInitialization), runners[1] = new PlayerLoopRunner(PlayerLoopTiming.LastInitialization));
|
||||
// EarlyUpdate
|
||||
copyList[1].subSystemList = InsertRunner(copyList[1], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), yielders[2] = new ContinuationQueue(PlayerLoopTiming.EarlyUpdate),
|
||||
@@ -284,6 +321,9 @@ namespace Cysharp.Threading.Tasks
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), runners[12] = new PlayerLoopRunner(PlayerLoopTiming.PostLateUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPostLateUpdate), runners[13] = new PlayerLoopRunner(PlayerLoopTiming.LastPostLateUpdate));
|
||||
|
||||
// Insert UniTaskSynchronizationContext to Update loop
|
||||
copyList[4].subSystemList = InsertUniTaskSynchronizationContext(copyList[4]);
|
||||
|
||||
playerLoop.subSystemList = copyList;
|
||||
PlayerLoop.SetPlayerLoop(playerLoop);
|
||||
}
|
||||
@@ -297,6 +337,56 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
yielders[(int)timing].Enqueue(continuation);
|
||||
}
|
||||
|
||||
// Diagnostics helper
|
||||
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
|
||||
public static void DumpCurrentPlayerLoop()
|
||||
{
|
||||
var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop();
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine($"PlayerLoop List");
|
||||
foreach (var header in playerLoop.subSystemList)
|
||||
{
|
||||
sb.AppendFormat("------{0}------", header.type.Name);
|
||||
sb.AppendLine();
|
||||
foreach (var subSystem in header.subSystemList)
|
||||
{
|
||||
sb.AppendFormat("{0}", subSystem.type.Name);
|
||||
sb.AppendLine();
|
||||
|
||||
if (subSystem.subSystemList != null)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("More Subsystem:" + subSystem.subSystemList.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnityEngine.Debug.Log(sb.ToString());
|
||||
}
|
||||
|
||||
public static bool IsInjectedUniTaskPlayerLoop()
|
||||
{
|
||||
var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop();
|
||||
|
||||
foreach (var header in playerLoop.subSystemList)
|
||||
{
|
||||
foreach (var subSystem in header.subSystemList)
|
||||
{
|
||||
if (subSystem.type == typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
116
src/UniTask/Assets/Plugins/UniTask/Runtime/TaskPool.cs
Normal file
116
src/UniTask/Assets/Plugins/UniTask/Runtime/TaskPool.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
// internaly used but public, allow to user create custom operator with pooling.
|
||||
|
||||
public static class TaskPool
|
||||
{
|
||||
internal static int MaxPoolSize;
|
||||
static ConcurrentDictionary<Type, Func<int>> sizes = new ConcurrentDictionary<Type, Func<int>>();
|
||||
|
||||
static TaskPool()
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = Environment.GetEnvironmentVariable("UNITASK_MAX_POOLSIZE");
|
||||
if (value != null)
|
||||
{
|
||||
if (int.TryParse(value, out var size))
|
||||
{
|
||||
MaxPoolSize = size;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
MaxPoolSize = int.MaxValue;
|
||||
}
|
||||
|
||||
public static void SetMaxPoolSize(int maxPoolSize)
|
||||
{
|
||||
MaxPoolSize = maxPoolSize;
|
||||
}
|
||||
|
||||
public static IEnumerable<(Type, int)> GetCacheSizeInfo()
|
||||
{
|
||||
foreach (var item in sizes)
|
||||
{
|
||||
yield return (item.Key, item.Value());
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterSizeGetter(Type type, Func<int> getSize)
|
||||
{
|
||||
sizes[type] = getSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface ITaskPoolNode<T>
|
||||
{
|
||||
ref T NextNode { get; }
|
||||
}
|
||||
|
||||
// mutable struct, don't mark readonly.
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TaskPool<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int gate;
|
||||
int size;
|
||||
T root;
|
||||
|
||||
public int Size => size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
ref var nextNode = ref v.NextNode;
|
||||
root = nextNode;
|
||||
nextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
size++;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/UniTask/Assets/Plugins/UniTask/Runtime/TaskPool.cs.meta
Normal file
11
src/UniTask/Assets/Plugins/UniTask/Runtime/TaskPool.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19f4e6575150765449cc99f25f06f25f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,5 +1,4 @@
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
@@ -10,341 +9,302 @@ namespace Cysharp.Threading.Tasks
|
||||
void OnError(Exception ex);
|
||||
void OnCompleted();
|
||||
void OnCanceled(CancellationToken cancellationToken);
|
||||
|
||||
// set/get from TriggerEvent<T>
|
||||
ITriggerHandler<T> Prev { get; set; }
|
||||
ITriggerHandler<T> Next { get; set; }
|
||||
}
|
||||
|
||||
// be careful to use, itself is struct.
|
||||
public struct TriggerEvent<T>
|
||||
{
|
||||
// optimize: many cases, handler is single.
|
||||
ITriggerHandler<T> singleHandler;
|
||||
ITriggerHandler<T> head; // head.prev is last
|
||||
ITriggerHandler<T> iteratingHead;
|
||||
|
||||
ITriggerHandler<T>[] handlers;
|
||||
bool preserveRemoveSelf;
|
||||
ITriggerHandler<T> iteratingNode;
|
||||
|
||||
// when running(in TrySetResult), does not add immediately(trampoline).
|
||||
bool isRunning;
|
||||
ITriggerHandler<T> waitHandler;
|
||||
MinimumQueue<ITriggerHandler<T>> waitQueue;
|
||||
void LogError(Exception ex)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void SetResult(T value)
|
||||
{
|
||||
isRunning = true;
|
||||
|
||||
if (singleHandler != null)
|
||||
if (iteratingNode != null)
|
||||
{
|
||||
throw new InvalidOperationException("Can not trigger itself in iterating.");
|
||||
}
|
||||
|
||||
var h = head;
|
||||
while (h != null)
|
||||
{
|
||||
iteratingNode = h;
|
||||
|
||||
try
|
||||
{
|
||||
singleHandler.OnNext(value);
|
||||
h.OnNext(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
LogError(ex);
|
||||
Remove(h);
|
||||
}
|
||||
}
|
||||
|
||||
if (handlers != null)
|
||||
{
|
||||
for (int i = 0; i < handlers.Length; i++)
|
||||
if (preserveRemoveSelf)
|
||||
{
|
||||
if (handlers[i] != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
handlers[i].OnNext(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
handlers[i] = null;
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
preserveRemoveSelf = false;
|
||||
iteratingNode = null;
|
||||
var next = h.Next;
|
||||
Remove(h);
|
||||
h = next;
|
||||
}
|
||||
}
|
||||
|
||||
isRunning = false;
|
||||
|
||||
if (waitHandler != null)
|
||||
{
|
||||
var h = waitHandler;
|
||||
waitHandler = null;
|
||||
Add(h);
|
||||
}
|
||||
|
||||
if (waitQueue != null)
|
||||
{
|
||||
while (waitQueue.Count != 0)
|
||||
else
|
||||
{
|
||||
Add(waitQueue.Dequeue());
|
||||
h = h.Next;
|
||||
}
|
||||
}
|
||||
|
||||
iteratingNode = null;
|
||||
if (iteratingHead != null)
|
||||
{
|
||||
Add(iteratingHead);
|
||||
iteratingHead = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCanceled(CancellationToken cancellationToken)
|
||||
{
|
||||
isRunning = true;
|
||||
|
||||
if (singleHandler != null)
|
||||
if (iteratingNode != null)
|
||||
{
|
||||
throw new InvalidOperationException("Can not trigger itself in iterating.");
|
||||
}
|
||||
|
||||
var h = head;
|
||||
while (h != null)
|
||||
{
|
||||
iteratingNode = h;
|
||||
try
|
||||
{
|
||||
(singleHandler).OnCanceled(cancellationToken);
|
||||
h.OnCanceled(cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
LogError(ex);
|
||||
}
|
||||
|
||||
preserveRemoveSelf = false;
|
||||
iteratingNode = null;
|
||||
var next = h.Next;
|
||||
Remove(h);
|
||||
h = next;
|
||||
}
|
||||
|
||||
if (handlers != null)
|
||||
iteratingNode = null;
|
||||
if (iteratingHead != null)
|
||||
{
|
||||
for (int i = 0; i < handlers.Length; i++)
|
||||
{
|
||||
if (handlers[i] != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
(handlers[i]).OnCanceled(cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
handlers[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isRunning = false;
|
||||
|
||||
if (waitHandler != null)
|
||||
{
|
||||
var h = waitHandler;
|
||||
waitHandler = null;
|
||||
Add(h);
|
||||
}
|
||||
|
||||
if (waitQueue != null)
|
||||
{
|
||||
while (waitQueue.Count != 0)
|
||||
{
|
||||
Add(waitQueue.Dequeue());
|
||||
}
|
||||
Add(iteratingHead);
|
||||
iteratingHead = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCompleted()
|
||||
{
|
||||
isRunning = true;
|
||||
|
||||
if (singleHandler != null)
|
||||
if (iteratingNode != null)
|
||||
{
|
||||
throw new InvalidOperationException("Can not trigger itself in iterating.");
|
||||
}
|
||||
|
||||
var h = head;
|
||||
while (h != null)
|
||||
{
|
||||
iteratingNode = h;
|
||||
try
|
||||
{
|
||||
(singleHandler).OnCompleted();
|
||||
h.OnCompleted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
LogError(ex);
|
||||
}
|
||||
|
||||
preserveRemoveSelf = false;
|
||||
iteratingNode = null;
|
||||
var next = h.Next;
|
||||
Remove(h);
|
||||
h = next;
|
||||
}
|
||||
|
||||
if (handlers != null)
|
||||
iteratingNode = null;
|
||||
if (iteratingHead != null)
|
||||
{
|
||||
for (int i = 0; i < handlers.Length; i++)
|
||||
{
|
||||
if (handlers[i] != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
(handlers[i]).OnCompleted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
handlers[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isRunning = false;
|
||||
|
||||
if (waitHandler != null)
|
||||
{
|
||||
var h = waitHandler;
|
||||
waitHandler = null;
|
||||
Add(h);
|
||||
}
|
||||
|
||||
if (waitQueue != null)
|
||||
{
|
||||
while (waitQueue.Count != 0)
|
||||
{
|
||||
Add(waitQueue.Dequeue());
|
||||
}
|
||||
Add(iteratingHead);
|
||||
iteratingHead = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetError(Exception exception)
|
||||
{
|
||||
isRunning = true;
|
||||
|
||||
if (singleHandler != null)
|
||||
if (iteratingNode != null)
|
||||
{
|
||||
throw new InvalidOperationException("Can not trigger itself in iterating.");
|
||||
}
|
||||
|
||||
var h = head;
|
||||
while (h != null)
|
||||
{
|
||||
iteratingNode = h;
|
||||
try
|
||||
{
|
||||
singleHandler.OnError(exception);
|
||||
h.OnError(exception);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
LogError(ex);
|
||||
}
|
||||
|
||||
preserveRemoveSelf = false;
|
||||
iteratingNode = null;
|
||||
var next = h.Next;
|
||||
Remove(h);
|
||||
h = next;
|
||||
}
|
||||
|
||||
if (handlers != null)
|
||||
iteratingNode = null;
|
||||
if (iteratingHead != null)
|
||||
{
|
||||
for (int i = 0; i < handlers.Length; i++)
|
||||
{
|
||||
if (handlers[i] != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
handlers[i].OnError(exception);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
handlers[i] = null;
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isRunning = false;
|
||||
|
||||
if (waitHandler != null)
|
||||
{
|
||||
var h = waitHandler;
|
||||
waitHandler = null;
|
||||
Add(h);
|
||||
}
|
||||
|
||||
if (waitQueue != null)
|
||||
{
|
||||
while (waitQueue.Count != 0)
|
||||
{
|
||||
Add(waitQueue.Dequeue());
|
||||
}
|
||||
Add(iteratingHead);
|
||||
iteratingHead = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(ITriggerHandler<T> handler)
|
||||
{
|
||||
if (isRunning)
|
||||
{
|
||||
if (waitHandler == null)
|
||||
{
|
||||
waitHandler = handler;
|
||||
return;
|
||||
}
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
if (waitQueue == null)
|
||||
{
|
||||
waitQueue = new MinimumQueue<ITriggerHandler<T>>(4);
|
||||
}
|
||||
waitQueue.Enqueue(handler);
|
||||
// zero node.
|
||||
if (head == null)
|
||||
{
|
||||
head = handler;
|
||||
return;
|
||||
}
|
||||
|
||||
if (singleHandler == null)
|
||||
if (iteratingNode != null)
|
||||
{
|
||||
singleHandler = handler;
|
||||
if (iteratingHead == null)
|
||||
{
|
||||
iteratingHead = handler;
|
||||
return;
|
||||
}
|
||||
|
||||
var last = iteratingHead.Prev;
|
||||
if (last == null)
|
||||
{
|
||||
// single node.
|
||||
iteratingHead.Prev = handler;
|
||||
iteratingHead.Next = handler;
|
||||
handler.Prev = iteratingHead;
|
||||
}
|
||||
else
|
||||
{
|
||||
// multi node
|
||||
iteratingHead.Prev = handler;
|
||||
last.Next = handler;
|
||||
handler.Prev = last;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (handlers == null)
|
||||
var last = head.Prev;
|
||||
if (last == null)
|
||||
{
|
||||
handlers = new ITriggerHandler<T>[4];
|
||||
// single node.
|
||||
head.Prev = handler;
|
||||
head.Next = handler;
|
||||
handler.Prev = head;
|
||||
}
|
||||
|
||||
// check empty
|
||||
for (int i = 0; i < handlers.Length; i++)
|
||||
else
|
||||
{
|
||||
if (handlers[i] == null)
|
||||
{
|
||||
handlers[i] = handler;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// full, ensure capacity
|
||||
var last = handlers.Length;
|
||||
{
|
||||
EnsureCapacity(ref handlers);
|
||||
handlers[last] = handler;
|
||||
// multi node
|
||||
head.Prev = handler;
|
||||
last.Next = handler;
|
||||
handler.Prev = last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void EnsureCapacity(ref ITriggerHandler<T>[] array)
|
||||
{
|
||||
var newSize = array.Length * 2;
|
||||
var newArray = new ITriggerHandler<T>[newSize];
|
||||
Array.Copy(array, 0, newArray, 0, array.Length);
|
||||
array = newArray;
|
||||
}
|
||||
|
||||
public void Remove(ITriggerHandler<T> handler)
|
||||
{
|
||||
if (singleHandler == handler)
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
if (iteratingNode != null && iteratingNode == handler)
|
||||
{
|
||||
singleHandler = null;
|
||||
// if remove self, reserve remove self after invoke completed.
|
||||
preserveRemoveSelf = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (handlers != null)
|
||||
var prev = handler.Prev;
|
||||
var next = handler.Next;
|
||||
|
||||
if (next != null)
|
||||
{
|
||||
for (int i = 0; i < handlers.Length; i++)
|
||||
next.Prev = prev;
|
||||
}
|
||||
|
||||
if (handler == head)
|
||||
{
|
||||
head = next;
|
||||
}
|
||||
else if (handler == iteratingHead)
|
||||
{
|
||||
iteratingHead = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// when handler is head, prev indicate last so don't use it.
|
||||
if (prev != null)
|
||||
{
|
||||
if (handlers[i] == handler)
|
||||
prev.Next = next;
|
||||
}
|
||||
}
|
||||
|
||||
if (head != null)
|
||||
{
|
||||
if (head.Prev == handler)
|
||||
{
|
||||
if (prev != head)
|
||||
{
|
||||
// fill null.
|
||||
handlers[i] = null;
|
||||
return;
|
||||
head.Prev = prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
head.Prev = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iteratingHead != null)
|
||||
{
|
||||
if (iteratingHead.Prev == handler)
|
||||
{
|
||||
if (prev != iteratingHead.Prev)
|
||||
{
|
||||
iteratingHead.Prev = prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
iteratingHead.Prev = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handler.Prev = null;
|
||||
handler.Next = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
@@ -103,6 +102,8 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
}
|
||||
|
||||
public T Current { get; private set; }
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
@@ -189,6 +190,9 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
|
||||
internal CancellationToken CancellationToken => cancellationToken;
|
||||
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||
|
||||
internal AsyncTriggerHandler(AsyncTriggerBase<T> trigger, bool callOnce)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public enum DelayType
|
||||
{
|
||||
/// <summary>use Time.deltaTime.</summary>
|
||||
DeltaTime,
|
||||
/// <summary>Ignore timescale, use Time.unscaledDeltaTime.</summary>
|
||||
UnscaledDeltaTime,
|
||||
/// <summary>use Stopwatch.GetTimestamp().</summary>
|
||||
Realtime
|
||||
}
|
||||
|
||||
public partial struct UniTask
|
||||
{
|
||||
public static YieldAwaitable Yield(PlayerLoopTiming timing = PlayerLoopTiming.Update)
|
||||
@@ -21,6 +31,46 @@ namespace Cysharp.Threading.Tasks
|
||||
return new UniTask(YieldPromise.Create(timing, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Similar as UniTask.Yield but guaranteed run on next frame.
|
||||
/// </summary>
|
||||
public static UniTask NextFrame(PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new UniTask(NextFramePromise.Create(timing, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate).
|
||||
/// </summary>
|
||||
public static YieldAwaitable WaitForEndOfFrame()
|
||||
{
|
||||
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken).
|
||||
/// </summary>
|
||||
public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken)
|
||||
{
|
||||
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate).
|
||||
/// </summary>
|
||||
public static YieldAwaitable WaitForFixedUpdate()
|
||||
{
|
||||
return UniTask.Yield(PlayerLoopTiming.FixedUpdate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken).
|
||||
/// </summary>
|
||||
public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken)
|
||||
{
|
||||
return UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken);
|
||||
}
|
||||
|
||||
public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (delayFrameCount < 0)
|
||||
@@ -34,31 +84,56 @@ namespace Cysharp.Threading.Tasks
|
||||
public static UniTask Delay(int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
|
||||
if (delayTimeSpan < TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Delay does not allow minus millisecondsDelay. millisecondsDelay:" + millisecondsDelay);
|
||||
}
|
||||
|
||||
return (ignoreTimeScale)
|
||||
? new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token)
|
||||
: new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out token), token);
|
||||
return Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cancellationToken);
|
||||
}
|
||||
|
||||
public static UniTask Delay(TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var delayType = ignoreTimeScale ? DelayType.UnscaledDeltaTime : DelayType.DeltaTime;
|
||||
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken);
|
||||
}
|
||||
|
||||
public static UniTask Delay(int millisecondsDelay, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
|
||||
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken);
|
||||
}
|
||||
|
||||
public static UniTask Delay(TimeSpan delayTimeSpan, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (delayTimeSpan < TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Delay does not allow minus delayTimeSpan. delayTimeSpan:" + delayTimeSpan);
|
||||
}
|
||||
|
||||
return (ignoreTimeScale)
|
||||
? new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token)
|
||||
: new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out token), token);
|
||||
switch (delayType)
|
||||
{
|
||||
case DelayType.UnscaledDeltaTime:
|
||||
{
|
||||
return new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
|
||||
}
|
||||
case DelayType.Realtime:
|
||||
{
|
||||
return new UniTask(DelayRealtimePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
|
||||
}
|
||||
case DelayType.DeltaTime:
|
||||
default:
|
||||
{
|
||||
return new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class YieldPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
|
||||
sealed class YieldPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<YieldPromise>
|
||||
{
|
||||
static readonly PromisePool<YieldPromise> pool = new PromisePool<YieldPromise>();
|
||||
static TaskPool<YieldPromise> pool;
|
||||
YieldPromise nextNode;
|
||||
public ref YieldPromise NextNode => ref nextNode;
|
||||
|
||||
static YieldPromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(YieldPromise), () => pool.Size);
|
||||
}
|
||||
|
||||
CancellationToken cancellationToken;
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
@@ -74,7 +149,11 @@ namespace Cysharp.Threading.Tasks
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
var result = pool.TryRent() ?? new YieldPromise();
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new YieldPromise();
|
||||
}
|
||||
|
||||
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
@@ -90,12 +169,11 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
try
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,30 +204,127 @@ namespace Cysharp.Threading.Tasks
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
cancellationToken = default;
|
||||
}
|
||||
|
||||
~YieldPromise()
|
||||
{
|
||||
if (pool.TryReturn(this))
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
|
||||
sealed class NextFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<NextFramePromise>
|
||||
{
|
||||
static readonly PromisePool<DelayFramePromise> pool = new PromisePool<DelayFramePromise>();
|
||||
static TaskPool<NextFramePromise> pool;
|
||||
NextFramePromise nextNode;
|
||||
public ref NextFramePromise NextNode => ref nextNode;
|
||||
|
||||
static NextFramePromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(NextFramePromise), () => pool.Size);
|
||||
}
|
||||
|
||||
int frameCount;
|
||||
CancellationToken cancellationToken;
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
NextFramePromise()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new NextFramePromise();
|
||||
}
|
||||
|
||||
result.frameCount = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (frameCount == Time.frameCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
cancellationToken = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise>
|
||||
{
|
||||
static TaskPool<DelayFramePromise> pool;
|
||||
DelayFramePromise nextNode;
|
||||
public ref DelayFramePromise NextNode => ref nextNode;
|
||||
|
||||
static DelayFramePromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(DelayFramePromise), () => pool.Size);
|
||||
}
|
||||
|
||||
int initialFrame;
|
||||
int delayFrameCount;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
int currentFrameCount;
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
DelayFramePromise()
|
||||
{
|
||||
@@ -162,10 +337,14 @@ namespace Cysharp.Threading.Tasks
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
var result = pool.TryRent() ?? new DelayFramePromise();
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new DelayFramePromise();
|
||||
}
|
||||
|
||||
result.delayFrameCount = delayFrameCount;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -179,12 +358,11 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
try
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,38 +389,54 @@ namespace Cysharp.Threading.Tasks
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentFrameCount == delayFrameCount)
|
||||
if (currentFrameCount == 0)
|
||||
{
|
||||
core.TrySetResult(null);
|
||||
if (delayFrameCount == 0) // same as Yield
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
return false;
|
||||
}
|
||||
|
||||
// skip in initial frame.
|
||||
if (initialFrame == Time.frameCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (++currentFrameCount >= delayFrameCount)
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
return false;
|
||||
}
|
||||
|
||||
currentFrameCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
currentFrameCount = default;
|
||||
delayFrameCount = default;
|
||||
cancellationToken = default;
|
||||
}
|
||||
|
||||
~DelayFramePromise()
|
||||
{
|
||||
if (pool.TryReturn(this))
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DelayPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
|
||||
sealed class DelayPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayPromise>
|
||||
{
|
||||
static readonly PromisePool<DelayPromise> pool = new PromisePool<DelayPromise>();
|
||||
static TaskPool<DelayPromise> pool;
|
||||
DelayPromise nextNode;
|
||||
public ref DelayPromise NextNode => ref nextNode;
|
||||
|
||||
float delayFrameTimeSpan;
|
||||
static DelayPromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(DelayPromise), () => pool.Size);
|
||||
}
|
||||
|
||||
int initialFrame;
|
||||
float delayTimeSpan;
|
||||
float elapsed;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
@@ -252,18 +446,22 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(TimeSpan delayFrameTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
var result = pool.TryRent() ?? new DelayPromise();
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new DelayPromise();
|
||||
}
|
||||
|
||||
result.elapsed = 0.0f;
|
||||
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
|
||||
result.delayTimeSpan = (float)delayTimeSpan.TotalSeconds;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -277,12 +475,11 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
try
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,8 +506,16 @@ namespace Cysharp.Threading.Tasks
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elapsed == 0.0f)
|
||||
{
|
||||
if (initialFrame == Time.frameCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
elapsed += Time.deltaTime;
|
||||
if (elapsed >= delayFrameTimeSpan)
|
||||
if (elapsed >= delayTimeSpan)
|
||||
{
|
||||
core.TrySetResult(null);
|
||||
return false;
|
||||
@@ -319,29 +524,31 @@ namespace Cysharp.Threading.Tasks
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
delayFrameTimeSpan = default;
|
||||
delayTimeSpan = default;
|
||||
elapsed = default;
|
||||
cancellationToken = default;
|
||||
}
|
||||
|
||||
~DelayPromise()
|
||||
{
|
||||
if (pool.TryReturn(this))
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DelayIgnoreTimeScalePromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
|
||||
sealed class DelayIgnoreTimeScalePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayIgnoreTimeScalePromise>
|
||||
{
|
||||
static readonly PromisePool<DelayIgnoreTimeScalePromise> pool = new PromisePool<DelayIgnoreTimeScalePromise>();
|
||||
static TaskPool<DelayIgnoreTimeScalePromise> pool;
|
||||
DelayIgnoreTimeScalePromise nextNode;
|
||||
public ref DelayIgnoreTimeScalePromise NextNode => ref nextNode;
|
||||
|
||||
static DelayIgnoreTimeScalePromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(DelayIgnoreTimeScalePromise), () => pool.Size);
|
||||
}
|
||||
|
||||
float delayFrameTimeSpan;
|
||||
float elapsed;
|
||||
int initialFrame;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
@@ -357,10 +564,14 @@ namespace Cysharp.Threading.Tasks
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
var result = pool.TryRent() ?? new DelayIgnoreTimeScalePromise();
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new DelayIgnoreTimeScalePromise();
|
||||
}
|
||||
|
||||
result.elapsed = 0.0f;
|
||||
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
|
||||
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
@@ -375,12 +586,11 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
try
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,6 +617,14 @@ namespace Cysharp.Threading.Tasks
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elapsed == 0.0f)
|
||||
{
|
||||
if (initialFrame == Time.frameCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
if (elapsed >= delayFrameTimeSpan)
|
||||
{
|
||||
@@ -417,20 +635,113 @@ namespace Cysharp.Threading.Tasks
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
delayFrameTimeSpan = default;
|
||||
elapsed = default;
|
||||
cancellationToken = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DelayRealtimePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayRealtimePromise>
|
||||
{
|
||||
static TaskPool<DelayRealtimePromise> pool;
|
||||
DelayRealtimePromise nextNode;
|
||||
public ref DelayRealtimePromise NextNode => ref nextNode;
|
||||
|
||||
static DelayRealtimePromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(DelayRealtimePromise), () => pool.Size);
|
||||
}
|
||||
|
||||
~DelayIgnoreTimeScalePromise()
|
||||
long delayTimeSpanTicks;
|
||||
ValueStopwatch stopwatch;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
DelayRealtimePromise()
|
||||
{
|
||||
if (pool.TryReturn(this))
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new DelayRealtimePromise();
|
||||
}
|
||||
|
||||
result.stopwatch = ValueStopwatch.StartNew();
|
||||
result.delayTimeSpanTicks = delayTimeSpan.Ticks;
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stopwatch.ElapsedTicks >= delayTimeSpanTicks)
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
stopwatch = default;
|
||||
cancellationToken = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
@@ -9,10 +12,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
static readonly UniTask CanceledUniTask = new Func<UniTask>(() =>
|
||||
{
|
||||
var promise = new UniTaskCompletionSource();
|
||||
promise.TrySetCanceled(CancellationToken.None);
|
||||
promise.MarkHandled();
|
||||
return promise.Task;
|
||||
return new UniTask(new CanceledResultSource(CancellationToken.None), 0);
|
||||
})();
|
||||
|
||||
static class CanceledUniTaskCache<T>
|
||||
@@ -21,10 +21,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static CanceledUniTaskCache()
|
||||
{
|
||||
var promise = new UniTaskCompletionSource<T>();
|
||||
promise.TrySetCanceled(CancellationToken.None);
|
||||
promise.MarkHandled();
|
||||
Task = promise.Task;
|
||||
Task = new UniTask<T>(new CanceledResultSource<T>(CancellationToken.None), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,18 +29,22 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public static UniTask FromException(Exception ex)
|
||||
{
|
||||
var promise = new UniTaskCompletionSource();
|
||||
promise.TrySetException(ex);
|
||||
promise.MarkHandled();
|
||||
return promise.Task;
|
||||
if (ex is OperationCanceledException oce)
|
||||
{
|
||||
return FromCanceled(oce.CancellationToken);
|
||||
}
|
||||
|
||||
return new UniTask(new ExceptionResultSource(ex), 0);
|
||||
}
|
||||
|
||||
public static UniTask<T> FromException<T>(Exception ex)
|
||||
{
|
||||
var promise = new UniTaskCompletionSource<T>();
|
||||
promise.TrySetException(ex);
|
||||
promise.MarkHandled();
|
||||
return promise.Task;
|
||||
if (ex is OperationCanceledException oce)
|
||||
{
|
||||
return FromCanceled<T>(oce.CancellationToken);
|
||||
}
|
||||
|
||||
return new UniTask<T>(new ExceptionResultSource<T>(ex), 0);
|
||||
}
|
||||
|
||||
public static UniTask<T> FromResult<T>(T value)
|
||||
@@ -59,10 +60,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
else
|
||||
{
|
||||
var promise = new UniTaskCompletionSource();
|
||||
promise.TrySetCanceled(cancellationToken);
|
||||
promise.MarkHandled();
|
||||
return promise.Task;
|
||||
return new UniTask(new CanceledResultSource(cancellationToken), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,10 +72,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
else
|
||||
{
|
||||
var promise = new UniTaskCompletionSource<T>();
|
||||
promise.TrySetCanceled(cancellationToken);
|
||||
promise.MarkHandled();
|
||||
return promise.Task;
|
||||
return new UniTask<T>(new CanceledResultSource<T>(cancellationToken), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,8 +97,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// helper of create add UniTaskVoid to delegate.
|
||||
/// For example: FooEvent += () => UniTask.Void(async () => { /* */ })
|
||||
/// helper of fire and forget void action.
|
||||
/// </summary>
|
||||
public static void Void(Func<UniTaskVoid> asyncAction)
|
||||
{
|
||||
@@ -111,7 +105,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// helper of create add UniTaskVoid to delegate.
|
||||
/// helper of fire and forget void action.
|
||||
/// </summary>
|
||||
public static void Void(Func<CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -119,8 +113,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// helper of create add UniTaskVoid to delegate.
|
||||
/// For example: FooEvent += (sender, e) => UniTask.Void(async arg => { /* */ }, (sender, e))
|
||||
/// helper of fire and forget void action.
|
||||
/// </summary>
|
||||
public static void Void<T>(Func<T, UniTaskVoid> asyncAction, T state)
|
||||
{
|
||||
@@ -182,6 +175,153 @@ namespace Cysharp.Threading.Tasks
|
||||
return new UniTask<T>(new DeferPromise<T>(factory), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Never complete.
|
||||
/// </summary>
|
||||
public static UniTask Never(CancellationToken cancellationToken)
|
||||
{
|
||||
return new UniTask<AsyncUnit>(new NeverPromise<AsyncUnit>(cancellationToken), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Never complete.
|
||||
/// </summary>
|
||||
public static UniTask<T> Never<T>(CancellationToken cancellationToken)
|
||||
{
|
||||
return new UniTask<T>(new NeverPromise<T>(cancellationToken), 0);
|
||||
}
|
||||
|
||||
sealed class ExceptionResultSource : IUniTaskSource
|
||||
{
|
||||
readonly ExceptionDispatchInfo exception;
|
||||
|
||||
public ExceptionResultSource(Exception exception)
|
||||
{
|
||||
this.exception = ExceptionDispatchInfo.Capture(exception);
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
exception.Throw();
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return UniTaskStatus.Faulted;
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return UniTaskStatus.Faulted;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
continuation(state);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ExceptionResultSource<T> : IUniTaskSource<T>
|
||||
{
|
||||
readonly ExceptionDispatchInfo exception;
|
||||
|
||||
public ExceptionResultSource(Exception exception)
|
||||
{
|
||||
this.exception = ExceptionDispatchInfo.Capture(exception);
|
||||
}
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
exception.Throw();
|
||||
return default;
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
exception.Throw();
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return UniTaskStatus.Faulted;
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return UniTaskStatus.Faulted;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
continuation(state);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class CanceledResultSource : IUniTaskSource
|
||||
{
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
public CanceledResultSource(CancellationToken cancellationToken)
|
||||
{
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
throw new OperationCanceledException(cancellationToken);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return UniTaskStatus.Canceled;
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return UniTaskStatus.Canceled;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
continuation(state);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class CanceledResultSource<T> : IUniTaskSource<T>
|
||||
{
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
public CanceledResultSource(CancellationToken cancellationToken)
|
||||
{
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
throw new OperationCanceledException(cancellationToken);
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
throw new OperationCanceledException(cancellationToken);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return UniTaskStatus.Canceled;
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return UniTaskStatus.Canceled;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
continuation(state);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DeferPromise : IUniTaskSource
|
||||
{
|
||||
Func<UniTask> factory;
|
||||
@@ -204,7 +344,7 @@ namespace Cysharp.Threading.Tasks
|
||||
if (f == null) throw new InvalidOperationException("Can't call twice.");
|
||||
|
||||
task = f();
|
||||
awaiter = f().GetAwaiter();
|
||||
awaiter = task.GetAwaiter();
|
||||
return task.Status;
|
||||
}
|
||||
|
||||
@@ -246,7 +386,7 @@ namespace Cysharp.Threading.Tasks
|
||||
if (f == null) throw new InvalidOperationException("Can't call twice.");
|
||||
|
||||
task = f();
|
||||
awaiter = f().GetAwaiter();
|
||||
awaiter = task.GetAwaiter();
|
||||
return task.Status;
|
||||
}
|
||||
|
||||
@@ -260,6 +400,54 @@ namespace Cysharp.Threading.Tasks
|
||||
return task.Status;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class NeverPromise<T> : IUniTaskSource<T>
|
||||
{
|
||||
static readonly Action<object> cancellationCallback = CancellationCallback;
|
||||
|
||||
CancellationToken cancellationToken;
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
|
||||
public NeverPromise(CancellationToken cancellationToken)
|
||||
{
|
||||
this.cancellationToken = cancellationToken;
|
||||
if (this.cancellationToken.CanBeCanceled)
|
||||
{
|
||||
this.cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
|
||||
}
|
||||
}
|
||||
|
||||
static void CancellationCallback(object state)
|
||||
{
|
||||
var self = (NeverPromise<T>)state;
|
||||
self.core.TrySetCanceled(self.cancellationToken);
|
||||
}
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
return core.GetResult(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class CompletedTasks
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user