Compare commits

...

110 Commits

Author SHA1 Message Date
neuecc
7d21a75ea8 2.1.1 2021-01-20 17:37:32 +09:00
neuecc
88817b7093 fix Coroutine.ToUniTask breaks pooling when coroutine return immediately 2021-01-20 17:31:55 +09:00
neuecc
52cdadc035 u 2021-01-07 17:43:35 +09:00
neuecc
5401b9b227 ready for release 2.1.0 2021-01-07 17:28:09 +09:00
neuecc
38de930f81 s 2021-01-07 17:25:03 +09:00
neuecc
9be6ef7ba6 new PlayerLoopType detector supports before 2019_4 2021-01-07 17:18:21 +09:00
neuecc
739bc6e26c add stale 2021-01-07 16:59:38 +09:00
neuecc
71879266ac Fix DelayType.Realtime does not work when use BuildPlayer in UnityEditor 2021-01-07 16:40:41 +09:00
neuecc
5ced0a1d4b Add UnityWebRequestException.ResponseHeaders #198 2021-01-07 16:03:27 +09:00
neuecc
908e361985 Add PlayerLoopTiming.TimeUpdate/LastTimeUpdate in Unity 2020.2 2021-01-07 13:02:18 +09:00
neuecc
797affae4d throw 2021-01-07 12:09:55 +09:00
Yoshifumi Kawai
ae3b825e29 Merge pull request #210 from WallyCZ/2020.2_fix
Unity 2020.2 changed order of the PlayerLoop subsystem list (should fix #208)
2021-01-07 12:01:11 +09:00
neuecc
143d97a73b update version 2021-01-07 11:59:31 +09:00
Václav Lipert
90631c54b1 More robust finding of right player loop system 2021-01-06 23:32:23 +01:00
Yoshifumi Kawai
478e2998a8 Merge pull request #204 from piti6/fix-defer
Accept calling DeferPromise.GetStatus multiple times
2021-01-06 17:45:28 +09:00
Yoshifumi Kawai
9406305b2e Merge pull request #203 from RamType0/FixCompletedTaskAsUniTaskAllocating
Suppress allocations around UniTask<T>.AsUniTask and UniTask.AsAsyncUnitUniTask
2021-01-06 17:19:19 +09:00
Václav Lipert
1c26c81b20 2020.2 changed PlayerLoopSystem order fix 2021-01-03 00:29:00 +01:00
RamType0
a455de88b0 Suppress allocations around UniTask<T>.AsUniTask and UniTask.AsAsyncUnitUniTask
- Fix completedTask.AsUniTask or completedTask.AsAsyncUnitUniTask leaks IUniTaskSource
- UniTask.AsAsyncUnitUniTask no longer allocates when its inner IUniTaskSource is IUniTaskSource<AsyncUnit>
2020-12-17 14:28:10 +09:00
piti6
fba6942d5f Accept GetStatus calling multiple times 2020-12-17 12:31:43 +09:00
Yoshifumi Kawai
3115efb672 Merge pull request #196 from RamType0/ForceCompleteJobHandleOnApplicationQuit
Force complete awaiting JobHandles when quitted in UnityEditor
2020-11-25 09:00:21 +09:00
Ram.Type-0
fd70c031cb Force complete awaiting JobHandles when quitted in UnityEditor 2020-11-24 23:50:01 +09:00
neuecc
a2eb75df68 Merge remote-tracking branch 'origin/master' 2020-11-09 16:08:20 +09:00
neuecc
4a62d7eba6 2.0.37 2020-11-09 16:08:17 +09:00
Yoshifumi Kawai
40d2d2fe06 Merge pull request #189 from IllusionCui/master
[DoTween]:fix CancellationToken can't stop UniTask
2020-11-09 15:52:13 +09:00
neuecc
d5d2cb5937 use Dictionary instead of ConcurrentDictionary for safety of WebGL build, #179 2020-11-09 14:34:11 +09:00
neuecc
854100c075 fix invalid usage of SpinLock, #195 2020-11-09 14:20:03 +09:00
cuibeibei
5837b26208 [DoTween]:fix CancellationToken can't stop UniTask 2020-10-28 18:48:10 +08:00
Yoshifumi Kawai
da0e654e7d Merge pull request #183 from Cysharp/chore/github_Actions_security_fix
chore: replace set-env to ENV FILE $GITHUB_ENV
2020-10-14 12:39:48 +09:00
Ikiru Yoshizaki
a3e9932be7 chore: replace set-env to ENV FILE $GITHUB_ENV
fix https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/
2020-10-14 12:12:28 +09:00
neuecc
e82353b4d9 UNITY_WEBGL unittest 2020-10-07 10:38:27 +09:00
Yoshifumi Kawai
944b61f28c Update README.md 2020-09-25 17:32:17 +09:00
Yoshifumi Kawai
457c574865 Update README.md 2020-09-24 10:31:59 +09:00
Yoshifumi Kawai
089a509663 Update README.md 2020-09-23 17:16:09 +09:00
neuecc
3bebaef969 2.0.36 2020-09-22 10:49:04 +09:00
neuecc
37e8b4500e Add enumerator.ToUniTask(MonoBehaviour coroutineRunner), log WARN on await enumerator when yield not supported types(Coroutine, WaitForEndOfFrame, WaitForFixedUpdate). 2020-09-22 10:08:03 +09:00
neuecc
8537ddf8a6 fix Forget when source is already completed and source is manualy optimized task source, does not work correctly 2020-09-22 09:54:05 +09:00
neuecc
346b1e0a6b 2.0.35 2020-09-21 12:56:13 +09:00
neuecc
fc7b9660a5 remove OnMouse_event trigger to prevent warning on Andoird, iOS, #170 2020-09-21 12:50:25 +09:00
neuecc
21e5cc22c7 notice for WaitForEndOfFrame #169 2020-09-21 12:49:26 +09:00
neuecc
3f18b37e5f changed clear loop runner queue timing in UnityEditor and run rest action when quitted 2020-09-21 12:30:31 +09:00
neuecc
5d4a90e9bd fix broken loop-runner from 2.0.32, #172 2020-09-21 11:42:02 +09:00
neuecc
2bf9f4f062 fix 2020-09-16 09:58:30 +09:00
neuecc
d69490cb49 one more things 2020-09-16 09:55:14 +09:00
neuecc
4e460c11ca 2.0.34 2020-09-16 09:48:57 +09:00
neuecc
9313969314 2.0.33 2020-09-15 06:53:58 +09:00
neuecc
a40f89a922 Merge remote-tracking branch 'origin/master' 2020-09-15 06:35:28 +09:00
neuecc
e0d8410b62 fix IEnumerator.ToUniTask, does not work correctly when coroutine complete immediately 2020-09-15 06:35:22 +09:00
Yoshifumi Kawai
bef1bd8ad1 Merge pull request #164 from c0nd3v/master
Fixed AwaitForAllAssets()
2020-09-12 06:09:29 +09:00
c0nd3v
81b4fcfac1 Fixed AwaitForAllAssets() 2020-09-11 10:41:41 -04:00
Yoshifumi Kawai
f1e4a3c65d Merge pull request #162 from hikarin522/patch-3
Fix UniTaskExtensions.Unwrap()
2020-09-09 15:38:03 +09:00
hikari
65622b01f6 [Unwrap] add ConfigureAwait 2020-09-09 14:15:03 +09:00
hikari
87dd5f13fd [Unwrap()] fix: remove type parameter 2020-09-09 14:05:37 +09:00
Yoshifumi Kawai
79cd2c17ba Update README.md 2020-09-07 18:55:44 +09:00
neuecc
85fb08552e Merge remote-tracking branch 'origin/master' 2020-09-07 18:52:22 +09:00
neuecc
7bd4b6faf7 2.0.32 2020-09-07 18:52:11 +09:00
neuecc
0b1ae7e295 docs: update TOC 2020-09-07 08:56:35 +00:00
neuecc
35a893ad9e Add UniTask.RunOnThreadPool 2020-09-07 17:56:02 +09:00
Yoshifumi Kawai
4955ed18f1 Merge pull request #157 from RamType-0/OptimizeConsumeEnumerator
Optimize EnumeratorPromise.ConsumeEnumerator while consuming CustomYieldInstruction
2020-09-07 17:23:08 +09:00
Yoshifumi Kawai
fe462328ab Merge pull request #148 from RamType-0/TaskPoolNodeByRef
Use ref T for ITaskPoolNode<T>.NextNode
2020-09-07 15:33:22 +09:00
Yoshifumi Kawai
5136d92efa Merge pull request #147 from RamType-0/OptimizeInternalContinuationIteration
Optimize ContinuationQueue and PlayerLoopRunner iteration
2020-09-07 15:32:50 +09:00
RamType0
227f7872cb Remove comments 2020-09-02 13:19:11 +09:00
Ram.Type-0
725b2fdc35 Format comment 2020-09-01 21:25:43 +09:00
Ram.Type-0
75abc8059f Optimize EnumeratorPromise.ConsumeEnumerator while consuming CustomYieldInstruction 2020-09-01 21:18:13 +09:00
RamType0
f1193743c8 Remove deprecated comment 2020-09-01 11:53:10 +09:00
Ram.Type-0
109730eacd Add missing migration from previous one 2020-08-30 14:18:55 +09:00
Ram.Type-0
1f736afe86 Revert change for ContinuationQueue.RunCore 2020-08-30 13:53:29 +09:00
Yoshifumi Kawai
4d554a6718 Merge pull request #149 from RamType-0/PlayerLoopRunnerInitializationFix
Fix PlayerLoopRunner at PlayerLoopTiming.Initialization is not assigned to PlayerLoopHelper.runners[0]
2020-08-30 09:49:19 +09:00
Ram.Type-0
69c0c362e9 Fix PlayerLoopRunner at PlayerLoopTiming.Initialization is not assigned to PlayerLoopHelper.runners[0] 2020-08-28 21:15:45 +09:00
RamType0
3bb446556a Use ref T for ITaskPoolNode<T>.NextNode 2020-08-28 18:03:22 +09:00
RamType0
ea950d8cec Optimize ContinuationQueue and PlayerLoopRunner iteration
- Fix failing to eliminate array bounds check in PlayerLoopRunner.RunCore
- Reduce number of array bounds check of continuation iteration in ContinuationQueue.RunCore
2020-08-28 17:12:03 +09:00
Yoshifumi Kawai
a65f4da7a2 Merge pull request #145 from RamType-0/TaskPoolNodeBenchmark
Add TaskPoolRefNode benchmark
2020-08-28 11:15:57 +09:00
Ram.Type-0
0bdc933c20 Add TaskPoolRefNode benchmark 2020-08-28 10:54:33 +09:00
Yoshifumi Kawai
0c0f79c6db Merge pull request #142 from hikarin522/patch-2
Add UniTaskExtensions.Unwrap()
2020-08-28 04:51:16 +09:00
hikari
32f9b9d4ac add Unwrap 2020-08-26 15:59:40 +09:00
neuecc
53907a3719 Merge remote-tracking branch 'origin/master' 2020-08-25 07:16:16 +09:00
neuecc
4937aeee3f 2.0.31 2020-08-25 07:16:08 +09:00
Yoshifumi Kawai
5e5b8aff89 Merge pull request #140 from hikarin522/patch-1
Fix ToCancellationToken
2020-08-25 07:13:56 +09:00
Yoshifumi Kawai
a2cbbd82d0 Merge pull request #139 from Cysharp/feature/SubscribeAwait
Add UniTaskAsyncEnumerable.SubscribeAwait
2020-08-25 07:12:00 +09:00
Yoshifumi Kawai
7eac5d8ba8 Merge pull request #138 from Cysharp/hotfix/ContinueOnErrorCallingOnNext
Continue to subscribe if an exception is raised when calling onNext
2020-08-25 07:11:43 +09:00
Yoshifumi Kawai
e2b1ed55ae Merge pull request #134 from SeungHuLee/Add-TMP-Generic-BindTo
Add TMP Generic BindTo Support
2020-08-25 07:11:26 +09:00
neuecc
727c7102d3 Add more DoTween support - AwaitForComplete, AwaitForPause, AwaitForPlay, AwaitForRewind, AwaitForStepComplete. 2020-08-25 07:10:39 +09:00
neuecc
1494ea6717 removelog spam on UniTaskAsyncEnumerable.EveryValueChanged #141 2020-08-25 06:43:31 +09:00
neuecc
f1ce64dbd3 middleware sample 2020-08-25 06:43:03 +09:00
hikari
3bad5cd2bf fixup! Fix ToCancellationToken linkToken 2020-08-25 02:55:09 +09:00
hikari
7432c0073a Fix ToCancellationToken linkToken 2020-08-24 17:10:26 +09:00
Mayuki Sawatari
f1813a7c94 Continue to subscribe if an exception is raised when calling onNext 2020-08-24 14:44:48 +09:00
Mayuki Sawatari
9e45c0a4d1 Add UniTaskAsyncEnumerable.SubscribeAwait 2020-08-24 14:35:20 +09:00
LSH
2c652cdde7 Add TMP Generic BindTo Support 2020-08-20 03:30:41 +09:00
neuecc
7718d345c8 2.0.30 2020-08-14 17:36:50 +09:00
neuecc
9f39708325 Merge remote-tracking branch 'origin/master' 2020-08-14 17:29:39 +09:00
neuecc
bb6dbfa920 Fix await IEnumerator + WaitForSeconds does not follow timescale(to behave same as StartCoroutine) #133 2020-08-14 17:29:30 +09:00
Yoshifumi Kawai
ba265005bb Merge pull request #132 from RamType-0/Disable-AutoReference-EditorAssembly
Disable "Auto Referenced" of UniTask.Editor
2020-08-14 06:25:21 +09:00
Ram.Type-0
4d7cc7ed61 Disable auto reference of UniTask.Editor 2020-08-14 04:56:58 +09:00
neuecc
b64f31eb0b 2.0.28 2020-08-13 17:44:15 +09:00
neuecc
38d159b69e IEnumerator.ToUniTask() behave same as StartCoroutine #120 2020-08-13 17:44:06 +09:00
neuecc
d5455f3716 Merge remote-tracking branch 'origin/master' 2020-08-13 16:33:41 +09:00
neuecc
a72ceeba11 UNITASK_ASSETBUNDLE_SUPPORT #131 2020-08-13 16:33:32 +09:00
Yoshifumi Kawai
c6b7d332b2 Update README.md 2020-08-12 11:41:21 +09:00
neuecc
f37278f2a6 docs: update TOC 2020-08-12 02:23:09 +00:00
Yoshifumi Kawai
3f3e03b83d Update README.md 2020-08-12 11:22:52 +09:00
Yoshifumi Kawai
c99d3eb3c3 Update README.md 2020-08-05 15:55:09 +09:00
Yoshifumi Kawai
08d5183e7e Merge pull request #121 from Cysharp/chore/unity_activation
chore: remove generate unity alf (activation license file)
2020-08-04 14:54:50 +09:00
Ikiru Yoshizaki
51769b2224 chore: remove generate unity alf (activation license file) 2020-08-04 13:20:33 +09:00
neuecc
6ec0ed8d61 docs: update TOC 2020-07-29 23:18:59 +00:00
neuecc
2e35324403 ready for 2.0.27 2020-07-30 08:12:32 +09:00
neuecc
e9474649c4 Add AssetBundleRequest.AwaitForAllAssets 2020-07-30 08:12:24 +09:00
neuecc
a8e0ce50c8 Fix Addressables async extensions when autoReleaseHandle: true 2020-07-30 08:12:03 +09:00
neuecc
db7ddba735 Add UniTaskSynchronizationContext, PlayerLoopHelper.IsInjectedUniTaskPlayerLoop, DumpCurrentPlayerLoop 2020-07-30 08:11:07 +09:00
neuecc
1999d94b33 Add UniTask.WithCancellation 2020-07-30 08:10:16 +09:00
neuecc
44af123b6c Update project version to 2019.4.5f1 2020-07-30 07:01:29 +09:00
63 changed files with 3178 additions and 791 deletions

View File

@@ -3,7 +3,7 @@ name: Build-Debug
on:
push:
branches:
- "**"
- "master"
tags:
- "!*" # not a tag push
pull_request:
@@ -22,7 +22,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.101
dotnet-version: 3.1.x
- run: dotnet test -c Debug ./src/UniTask.NetCoreTests/UniTask.NetCoreTests.csproj
build-unity:
@@ -42,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] }}

View File

@@ -16,9 +16,9 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.101
dotnet-version: 3.1.x
# set release tag(*.*.*) to env.GIT_TAG
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
- run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
# build and pack
- run: dotnet build -c Release -p:Version=${{ env.GIT_TAG }}
@@ -34,7 +34,7 @@ jobs:
build-unity:
strategy:
matrix:
unity: ['2019.3.9f1']
unity: ["2019.3.9f1"]
include:
- unity: 2019.3.9f1
license: UNITY_2019_3
@@ -51,14 +51,14 @@ jobs:
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -manualLicenseFile .Unity.ulf || exit 0
# set release tag(*.*.*) to env.GIT_TAG
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
- run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
# 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/UniTask
# Store artifacts.
# Store artifacts.
- uses: actions/upload-artifact@v2
with:
name: UniTask.${{ env.GIT_TAG }}.unitypackage
@@ -72,34 +72,34 @@ jobs:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
NUGET_XMLDOC_MODE: skip
steps:
# setup dotnet for nuget push
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.101
# set release tag(*.*.*) to env.GIT_TAG
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
# setup dotnet for nuget push
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.x
# set release tag(*.*.*) to env.GIT_TAG
- run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
# Create Releases
- uses: actions/create-release@v1
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Ver.${{ github.ref }}
# Create Releases
- uses: actions/create-release@v1
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Ver.${{ github.ref }}
# Download(All) Artifacts to current directory
- uses: actions/download-artifact@v2-preview
# Download(All) Artifacts to current directory
- uses: actions/download-artifact@v2-preview
# Upload to NuGet
- run: dotnet nuget push "./nuget/*.nupkg" -s https://www.nuget.org/api/v2/package -k ${{ secrets.NUGET_KEY }}
# Upload to NuGet
- run: dotnet nuget push "./nuget/*.nupkg" -s https://www.nuget.org/api/v2/package -k ${{ secrets.NUGET_KEY }}
# Upload to Releases(unitypackage)
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
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
# Upload to Releases(unitypackage)
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
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

24
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: "Close stale issues"
on:
schedule:
- cron: "0 0 * * *"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# enable issue
stale-issue-message: "This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 7 days."
stale-issue-label: "stale"
# enable pr
stale-pr-message: "This PR is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 7 days."
stale-pr-label: "stale"
days-before-stale: 90
days-before-close: 7
exempt-issue-labels: "wip"
exempt-pr-labels: "wip"
remove-stale-when-updated: true

46
.gitignore vendored
View File

@@ -213,3 +213,49 @@ 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
src/UniTask/.vsconfig
src/UniTask/Logs/ApiUpdaterCheck.txt

113
README.md
View File

@@ -14,7 +14,8 @@ Provides an efficient allocation free async/await integration to Unity.
* 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)
](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 -->
@@ -32,8 +33,13 @@ Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await f
- [Awaitable Events](#awaitable-events)
- [Channel](#channel)
- [For Unit Testing](#for-unit-testing)
- [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)
- [Install via git URL](#install-via-git-url)
@@ -150,25 +156,24 @@ UniTask provides three pattern of extension methods.
> 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 SceneAssets()
{
// parallel load.
var (a, b, c) = await UniTask.WhenAll(
LoadAsSprite("foo"),
LoadAsSprite("bar"),
LoadAsSprite("baz"));
}
// parallel load.
var (a, b, c) = await UniTask.WhenAll(
LoadAsSprite("foo"),
LoadAsSprite("bar"),
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);
}
```
@@ -210,6 +215,8 @@ 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.
Also `UniTaskCompletionSource` can await multipletimes and await from many caller.
Cancellation and Exception handling
---
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.
@@ -339,6 +346,11 @@ public enum PlayerLoopTiming
PostLateUpdate = 12,
LastPostLateUpdate = 13
#if UNITY_2020_2_OR_NEWER
TimeUpdate = 14,
LastTimeUpdate = 15,
#endif
}
```
@@ -346,6 +358,8 @@ It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.
`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.
> `await UniTask.WaitForEndOfFrame()` is not equilavelnt to coroutine's `yield return new WaitForEndOfFrame()`. Coroutine's WaitForEndOfFrame seems to run after the PlayerLoop is done. Some methods that require coroutine's end of frame(`ScreenCapture.CaptureScreenshotAsTexture`, `CommandBuffer`, etc) does not work correctly when replace to async/await. In that case, use a 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.
@@ -384,6 +398,16 @@ var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
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()`.
@@ -478,12 +502,14 @@ await UniTask.WhenAll(
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.
Unity 2020.2 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
// Unity 2020.2, C# 8.0
await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate(token))
{
Debug.Log("Update() " + Time.frameCount);
@@ -767,6 +793,28 @@ public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
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`/`Coroutine` is not supported.
* 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.
If you want to convert fully compatible from coroutine to async, use `IEnumerator.ToUniTask(MonoBehaviour coroutineRunner)` overload. It executes StartCoroutine on an instance of the argument MonoBehaviour and waits for it to complete in UniTask.
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.
@@ -815,7 +863,7 @@ Use UniTask type.
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.
UniTask is aggressively caching async promise object to achive zero allocation(technical 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)). 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
foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
@@ -824,7 +872,32 @@ foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
}
```
> 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.
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).
![](https://user-images.githubusercontent.com/46207/89967342-2f944600-dc8c-11ea-99fc-0b74527a16f6.png)
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
---
@@ -844,7 +917,7 @@ After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of gi
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 `#2.0.24`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.0.24`.
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#2.1.0`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0`.
### Install via OpenUPM

View File

@@ -62,7 +62,8 @@ namespace Cysharp.Threading.Tasks
sealed class ThreadPoolWorkItem : IThreadPoolWorkItem, ITaskPoolNode<ThreadPoolWorkItem>
{
static TaskPool<ThreadPoolWorkItem> pool;
public ThreadPoolWorkItem NextNode { get; set; }
ThreadPoolWorkItem nextNode;
public ref ThreadPoolWorkItem NextNode => ref nextNode;
static ThreadPoolWorkItem()
{

View File

@@ -38,6 +38,7 @@
..\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;

View File

@@ -10,11 +10,14 @@ 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;
@@ -82,6 +85,14 @@ public class QueueCheck
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()
@@ -130,6 +141,18 @@ 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>
@@ -237,6 +260,60 @@ public struct TaskPool<T>
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>

View 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);
}
}
}

View File

@@ -10,7 +10,7 @@
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false

View File

@@ -87,7 +87,8 @@ namespace Cysharp.Threading.Tasks
static Action<object> cancellationCallback = CancellationCallback;
static TaskPool<WaitAsyncSource> pool;
WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode { get; set; }
WaitAsyncSource nextNode;
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
static WaitAsyncSource()
{
@@ -404,7 +405,8 @@ namespace Cysharp.Threading.Tasks
static Action<object> cancellationCallback = CancellationCallback;
static TaskPool<WaitAsyncSource> pool;
WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode { get; set; }
WaitAsyncSource nextNode;
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
static WaitAsyncSource()
{

View File

@@ -30,10 +30,10 @@ namespace Cysharp.Threading.Tasks
return ToCancellationToken(task);
}
var cts = new CancellationTokenSource();
var cts = CancellationTokenSource.CreateLinkedTokenSource(linkToken);
ToCancellationTokenCore(task, cts).Forget();
return CancellationTokenSource.CreateLinkedTokenSource(linkToken).Token;
return cts.Token;
}
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task)

View File

@@ -87,7 +87,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
TaskPool.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size);
}
public AsyncUniTaskVoid<TStateMachine> NextNode { get; set; }
AsyncUniTaskVoid<TStateMachine> nextNode;
public ref AsyncUniTaskVoid<TStateMachine> NextNode => ref nextNode;
public void Return()
{
@@ -157,7 +158,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
public AsyncUniTask<TStateMachine> NextNode { get; set; }
AsyncUniTask<TStateMachine> nextNode;
public ref AsyncUniTask<TStateMachine> NextNode => ref nextNode;
static AsyncUniTask()
{
@@ -279,7 +281,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
public AsyncUniTask<TStateMachine, T> NextNode { get; set; }
AsyncUniTask<TStateMachine, T> nextNode;
public ref AsyncUniTask<TStateMachine, T> NextNode => ref nextNode;
static AsyncUniTask()
{

View File

@@ -32,10 +32,24 @@ namespace Cysharp.Threading.Tasks
return new UniTask(EnumeratorPromise.Create(enumerator, timing, cancellationToken, out var token), token);
}
public static UniTask ToUniTask(this IEnumerator enumerator, MonoBehaviour coroutineRunner)
{
var source = AutoResetUniTaskCompletionSource.Create();
coroutineRunner.StartCoroutine(Core(enumerator, coroutineRunner, source));
return source.Task;
}
static IEnumerator Core(IEnumerator inner, MonoBehaviour coroutineRunner, AutoResetUniTaskCompletionSource source)
{
yield return coroutineRunner.StartCoroutine(inner);
source.TrySetResult();
}
sealed class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<EnumeratorPromise>
{
static TaskPool<EnumeratorPromise> pool;
public EnumeratorPromise NextNode { get; set; }
EnumeratorPromise nextNode;
public ref EnumeratorPromise NextNode => ref nextNode;
static EnumeratorPromise()
{
@@ -44,6 +58,9 @@ namespace Cysharp.Threading.Tasks
IEnumerator innerEnumerator;
CancellationToken cancellationToken;
int initialFrame;
bool loopRunning;
bool calledGetResult;
UniTaskCompletionSourceCore<object> core;
@@ -66,10 +83,18 @@ namespace Cysharp.Threading.Tasks
result.innerEnumerator = ConsumeEnumerator(innerEnumerator);
result.cancellationToken = cancellationToken;
PlayerLoopHelper.AddAction(timing, result);
result.loopRunning = true;
result.calledGetResult = false;
result.initialFrame = -1;
token = result.core.Version;
// run immediately.
if (result.MoveNext())
{
PlayerLoopHelper.AddAction(timing, result);
}
return result;
}
@@ -77,11 +102,15 @@ namespace Cysharp.Threading.Tasks
{
try
{
calledGetResult = true;
core.GetResult(token);
}
finally
{
TryReturn();
if (!loopRunning)
{
TryReturn();
}
}
}
@@ -102,12 +131,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())
@@ -117,10 +172,12 @@ namespace Cysharp.Threading.Tasks
}
catch (Exception ex)
{
loopRunning = false;
core.TrySetException(ex);
return false;
}
loopRunning = false;
core.TrySetResult(null);
return false;
}
@@ -131,6 +188,7 @@ namespace Cysharp.Threading.Tasks
core.Reset();
innerEnumerator = default;
cancellationToken = default;
return pool.TryPush(this);
}
@@ -145,11 +203,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;
}
@@ -175,7 +232,7 @@ namespace Cysharp.Threading.Tasks
}
else
{
yield return null;
goto WARN;
}
}
else if (current is IEnumerator e3)
@@ -188,17 +245,14 @@ namespace Cysharp.Threading.Tasks
}
else
{
// WaitForEndOfFrame, WaitForFixedUpdate, others.
yield return null;
goto WARN;
}
}
}
// WWW and others as CustomYieldInstruction.
static IEnumerator UnwrapWaitCustomYieldInstruction(CustomYieldInstruction yieldInstruction)
{
while (yieldInstruction.keepWaiting)
{
continue;
WARN:
// WaitForEndOfFrame, WaitForFixedUpdate, others.
UnityEngine.Debug.LogWarning($"yield {current.GetType().Name} is not supported on await IEnumerator or IEnumerator.ToUniTask(), please use ToUniTask(MonoBehaviour coroutineRunner) instead.");
yield return null;
}
}
@@ -208,12 +262,12 @@ namespace Cysharp.Threading.Tasks
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;
@@ -230,5 +284,4 @@ namespace Cysharp.Threading.Tasks
}
}
}
}
}

View File

@@ -13,31 +13,28 @@ namespace Cysharp.Threading.Tasks
{
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 (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);
if (handle.IsDone)
{
if (handle.Status == AsyncOperationStatus.Failed)
{
return UniTask.FromException(handle.OperationException);
}
return UniTask.CompletedTask;
}
return new UniTask(AsyncOperationHandleWithCancellationSource.Create(handle, 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 (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);
if (!handle.IsValid())
{
// autoReleaseHandle:true handle is invalid(immediately internal handle == null) so return completed.
return UniTask.CompletedTask;
}
if (handle.IsDone)
{
if (handle.Status == AsyncOperationStatus.Failed)
@@ -95,29 +92,31 @@ namespace Cysharp.Threading.Tasks
}
}
sealed class AsyncOperationHandleWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleWithCancellationSource>
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
{
static TaskPool<AsyncOperationHandleWithCancellationSource> pool;
public AsyncOperationHandleWithCancellationSource NextNode { get; set; }
static TaskPool<AsyncOperationHandleConfiguredSource> pool;
AsyncOperationHandleConfiguredSource nextNode;
public ref AsyncOperationHandleConfiguredSource NextNode => ref nextNode;
static AsyncOperationHandleWithCancellationSource()
static AsyncOperationHandleConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleWithCancellationSource), () => pool.Size);
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
}
readonly Action<AsyncOperationHandle> continuationAction;
AsyncOperationHandle handle;
CancellationToken cancellationToken;
IProgress<float> progress;
bool completed;
UniTaskCompletionSourceCore<AsyncUnit> core;
AsyncOperationHandleWithCancellationSource()
AsyncOperationHandleConfiguredSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource Create(AsyncOperationHandle handle, CancellationToken cancellationToken, out short token)
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
@@ -126,16 +125,17 @@ namespace Cysharp.Threading.Tasks
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleWithCancellationSource();
result = new AsyncOperationHandleConfiguredSource();
}
result.handle = handle;
result.progress = progress;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
PlayerLoopHelper.AddAction(timing, result);
handle.Completed += result.continuationAction;
@@ -204,125 +204,18 @@ namespace Cysharp.Threading.Tasks
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
handle = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
{
static TaskPool<AsyncOperationHandleConfiguredSource> pool;
public AsyncOperationHandleConfiguredSource NextNode { get; set; }
static AsyncOperationHandleConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
}
AsyncOperationHandle handle;
IProgress<float> progress;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<AsyncUnit> core;
AsyncOperationHandleConfiguredSource()
{
}
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleConfiguredSource();
}
result.handle = handle;
result.progress = progress;
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 (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;
}
bool TryReturn()
{
core.Reset();
TaskTracker.RemoveTracking(this);
core.Reset();
handle = default;
progress = default;
cancellationToken = default;
@@ -330,32 +223,29 @@ namespace Cysharp.Threading.Tasks
}
}
#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 (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T>(cancellationToken);
if (handle.IsDone)
{
if (handle.Status == AsyncOperationStatus.Failed)
{
return UniTask.FromException<T>(handle.OperationException);
}
return UniTask.FromResult(handle.Result);
}
return new UniTask<T>(AsyncOperationHandleWithCancellationSource<T>.Create(handle, 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 (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)
@@ -368,75 +258,31 @@ namespace Cysharp.Threading.Tasks
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>>
{
AsyncOperationHandle<T> handle;
Action<AsyncOperationHandle> continuationAction;
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
AsyncOperationHandleConfiguredSource<T> nextNode;
public ref AsyncOperationHandleConfiguredSource<T> NextNode => ref nextNode;
public AsyncOperationHandleAwaiter(AsyncOperationHandle<T> handle)
static AsyncOperationHandleConfiguredSource()
{
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 = PooledDelegate<AsyncOperationHandle>.Create(continuation);
handle.CompletedTypeless += continuationAction;
}
}
sealed class AsyncOperationHandleWithCancellationSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleWithCancellationSource<T>>
{
static TaskPool<AsyncOperationHandleWithCancellationSource<T>> pool;
public AsyncOperationHandleWithCancellationSource<T> NextNode { get; set; }
static AsyncOperationHandleWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleWithCancellationSource<T>), () => pool.Size);
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
}
readonly Action<AsyncOperationHandle<T>> continuationAction;
AsyncOperationHandle<T> handle;
CancellationToken cancellationToken;
IProgress<float> progress;
bool completed;
UniTaskCompletionSourceCore<T> core;
AsyncOperationHandleWithCancellationSource()
AsyncOperationHandleConfiguredSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, CancellationToken cancellationToken, out short token)
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
@@ -445,16 +291,17 @@ namespace Cysharp.Threading.Tasks
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleWithCancellationSource<T>();
result = new AsyncOperationHandleConfiguredSource<T>();
}
result.handle = handle;
result.cancellationToken = cancellationToken;
result.completed = false;
result.progress = progress;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
PlayerLoopHelper.AddAction(timing, result);
handle.Completed += result.continuationAction;
@@ -462,7 +309,7 @@ namespace Cysharp.Threading.Tasks
return result;
}
void Continuation(AsyncOperationHandle<T> _)
void Continuation(AsyncOperationHandle<T> argHandle)
{
handle.Completed -= continuationAction;
@@ -477,13 +324,13 @@ namespace Cysharp.Threading.Tasks
{
core.TrySetCanceled(cancellationToken);
}
else if (handle.Status == AsyncOperationStatus.Failed)
else if (argHandle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(handle.OperationException);
core.TrySetException(argHandle.OperationException);
}
else
{
core.TrySetResult(handle.Result);
core.TrySetResult(argHandle.Result);
}
}
}
@@ -528,122 +375,11 @@ namespace Cysharp.Threading.Tasks
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
handle = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
{
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
public AsyncOperationHandleConfiguredSource<T> NextNode { get; set; }
static AsyncOperationHandleConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
}
AsyncOperationHandle<T> handle;
IProgress<float> progress;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<T> core;
AsyncOperationHandleConfiguredSource()
{
}
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleConfiguredSource<T>();
}
result.handle = handle;
result.progress = progress;
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public T GetResult(short token)
{
try
{
return core.GetResult(token);
}
finally
{
TryReturn();
}
}
void IUniTaskSource.GetResult(short token)
{
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 bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
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;
}
@@ -658,7 +394,7 @@ namespace Cysharp.Threading.Tasks
}
}
#endregion
#endregion
}
}

View File

@@ -11,7 +11,6 @@ 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,
@@ -29,6 +28,16 @@ namespace Cysharp.Threading.Tasks
public static class DOTweenAsyncExtensions
{
enum CallbackType
{
Kill,
Complete,
Pause,
Play,
Rewind,
StepComplete
}
public static TweenAwaiter GetAwaiter(this Tween tween)
{
return new TweenAwaiter(tween);
@@ -39,7 +48,7 @@ namespace Cysharp.Threading.Tasks
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, TweenCancelBehaviour.Kill, cancellationToken, out var token), token);
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)
@@ -47,7 +56,47 @@ namespace Cysharp.Threading.Tasks
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, out var token), token);
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
@@ -86,7 +135,8 @@ namespace Cysharp.Threading.Tasks
sealed class TweenConfiguredSource : IUniTaskSource, ITaskPoolNode<TweenConfiguredSource>
{
static TaskPool<TweenConfiguredSource> pool;
public TweenConfiguredSource NextNode { get; set; }
TweenConfiguredSource nextNode;
public ref TweenConfiguredSource NextNode => ref nextNode;
static TweenConfiguredSource()
{
@@ -95,12 +145,13 @@ namespace Cysharp.Threading.Tasks
static readonly TweenCallback EmptyTweenCallback = () => { };
readonly TweenCallback onKillDelegate;
readonly TweenCallback onCompleteCallbackDelegate;
readonly TweenCallback onUpdateDelegate;
Tween tween;
TweenCancelBehaviour cancelBehaviour;
CancellationToken cancellationToken;
CallbackType callbackType;
bool canceled;
TweenCallback originalUpdateAction;
@@ -108,11 +159,11 @@ namespace Cysharp.Threading.Tasks
TweenConfiguredSource()
{
onKillDelegate = OnKill;
onCompleteCallbackDelegate = OnCompleteCallbackDelegate;
onUpdateDelegate = OnUpdate;
}
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, out short token)
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, CallbackType callbackType, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
@@ -128,6 +179,7 @@ namespace Cysharp.Threading.Tasks
result.tween = tween;
result.cancelBehaviour = cancelBehaviour;
result.cancellationToken = cancellationToken;
result.callbackType = callbackType;
result.originalUpdateAction = tween.onUpdate;
result.canceled = false;
@@ -138,7 +190,30 @@ namespace Cysharp.Threading.Tasks
}
tween.onUpdate = result.onUpdateDelegate;
tween.onKill = result.onKillDelegate;
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);
@@ -146,8 +221,19 @@ namespace Cysharp.Threading.Tasks
return result;
}
void OnKill()
void OnCompleteCallbackDelegate()
{
if (cancellationToken.IsCancellationRequested)
{
if (this.cancelBehaviour == TweenCancelBehaviour.KillAndCancelAwait
|| this.cancelBehaviour == TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteAndCancelAwait
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait
|| this.cancelBehaviour == TweenCancelBehaviour.CancelAwait)
{
canceled = true;
}
}
if (canceled)
{
core.TrySetCanceled(cancellationToken);
@@ -199,7 +285,31 @@ namespace Cysharp.Threading.Tasks
this.tween.Complete(true);
break;
case TweenCancelBehaviour.CancelAwait:
this.tween.onKill = EmptyTweenCallback; // replace to empty(avoid callback after Canceled(instance is returned to pool.)
// 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;
}
@@ -272,7 +382,31 @@ namespace Cysharp.Threading.Tasks
TaskTracker.RemoveTracking(this);
core.Reset();
tween.onUpdate = originalUpdateAction;
tween.onKill = null;
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;

View File

@@ -9,6 +9,7 @@ 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();
@@ -62,6 +63,67 @@ namespace Cysharp.Threading.Tasks
}
}
}
// <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();
}
}
}
}
}

View File

@@ -12,7 +12,7 @@ namespace Cysharp.Threading.Tasks.Internal
readonly PlayerLoopTiming timing;
SpinLock gate = new SpinLock();
SpinLock gate = new SpinLock(false);
bool dequing = false;
int actionListCount = 0;
@@ -70,13 +70,17 @@ namespace Cysharp.Threading.Tasks.Internal
}
}
public void Clear()
public int Clear()
{
var rest = actionListCount + waitingListCount;
actionListCount = 0;
actionList = new Action[InitialSize];
waitingListCount = 0;
waitingList = new Action[InitialSize];
return rest;
}
// delegate entrypoint.
@@ -128,6 +132,14 @@ namespace Cysharp.Threading.Tasks.Internal
case PlayerLoopTiming.LastPostLateUpdate:
LastPostLateUpdate();
break;
#if UNITY_2020_2_OR_NEWER
case PlayerLoopTiming.TimeUpdate:
TimeUpdate();
break;
case PlayerLoopTiming.LastTimeUpdate:
LastTimeUpdate();
break;
#endif
default:
break;
}
@@ -150,6 +162,10 @@ namespace Cysharp.Threading.Tasks.Internal
void LastPreLateUpdate() => RunCore();
void PostLateUpdate() => RunCore();
void LastPostLateUpdate() => RunCore();
#if UNITY_2020_2_OR_NEWER
void TimeUpdate() => RunCore();
void LastTimeUpdate() => RunCore();
#endif
[System.Diagnostics.DebuggerHidden]
void RunCore()
@@ -170,9 +186,9 @@ namespace Cysharp.Threading.Tasks.Internal
for (int i = 0; i < actionListCount; i++)
{
var action = actionList[i];
actionList[i] = null;
try
{
action();

View File

@@ -48,14 +48,24 @@ namespace Cysharp.Threading.Tasks.Internal
}
}
public void Clear()
public int Clear()
{
lock (arrayLock)
{
var rest = 0;
for (var index = 0; index < loopItems.Length; index++)
{
if (loopItems[index] != null)
{
rest++;
}
loopItems[index] = null;
}
tail = 0;
return rest;
}
}
@@ -108,6 +118,14 @@ namespace Cysharp.Threading.Tasks.Internal
case PlayerLoopTiming.LastPostLateUpdate:
LastPostLateUpdate();
break;
#if UNITY_2020_2_OR_NEWER
case PlayerLoopTiming.TimeUpdate:
TimeUpdate();
break;
case PlayerLoopTiming.LastTimeUpdate:
LastTimeUpdate();
break;
#endif
default:
break;
}
@@ -130,6 +148,10 @@ namespace Cysharp.Threading.Tasks.Internal
void LastPreLateUpdate() => RunCore();
void PostLateUpdate() => RunCore();
void LastPostLateUpdate() => RunCore();
#if UNITY_2020_2_OR_NEWER
void TimeUpdate() => RunCore();
void LastTimeUpdate() => RunCore();
#endif
[System.Diagnostics.DebuggerHidden]
void RunCore()
@@ -143,7 +165,6 @@ namespace Cysharp.Threading.Tasks.Internal
{
var j = tail - 1;
// eliminate array-bound check for i
for (int i = 0; i < loopItems.Length; i++)
{
var action = loopItems[i];

View File

@@ -7,7 +7,8 @@ namespace Cysharp.Threading.Tasks.Internal
{
static TaskPool<PooledDelegate<T>> pool;
public PooledDelegate<T> NextNode { get; set; }
PooledDelegate<T> nextNode;
public ref PooledDelegate<T> NextNode => ref nextNode;
static PooledDelegate()
{

View File

@@ -18,6 +18,8 @@ namespace Cysharp.Threading.Tasks.Internal
public TimeSpan Elapsed => TimeSpan.FromTicks(this.ElapsedTicks);
public bool IsInvalid => startTimestamp == 0;
public long ElapsedTicks
{
get

View File

@@ -63,6 +63,42 @@ namespace Cysharp.Threading.Tasks.Linq
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)
@@ -105,6 +141,46 @@ namespace Cysharp.Threading.Tasks.Linq
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)
@@ -147,6 +223,46 @@ namespace Cysharp.Threading.Tasks.Linq
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)
@@ -195,7 +311,14 @@ namespace Cysharp.Threading.Tasks.Linq
{
while (await e.MoveNextAsync())
{
onNext(e.Current);
try
{
onNext(e.Current);
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
onCompleted();
}
@@ -227,7 +350,14 @@ namespace Cysharp.Threading.Tasks.Linq
{
while (await e.MoveNextAsync())
{
onNext(e.Current).Forget();
try
{
onNext(e.Current).Forget();
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
onCompleted();
}
@@ -259,7 +389,14 @@ namespace Cysharp.Threading.Tasks.Linq
{
while (await e.MoveNextAsync())
{
onNext(e.Current, cancellationToken).Forget();
try
{
onNext(e.Current, cancellationToken).Forget();
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
onCompleted();
}
@@ -291,7 +428,14 @@ namespace Cysharp.Threading.Tasks.Linq
{
while (await e.MoveNextAsync())
{
observer.OnNext(e.Current);
try
{
observer.OnNext(e.Current);
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
observer.OnCompleted();
}
@@ -309,5 +453,84 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
}
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();
}
}
}
}
}

View File

@@ -19,8 +19,6 @@ namespace Cysharp.Threading.Tasks.Linq
{
internal static async UniTask<TSource[]> ToArrayAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
{
// UnityEngine.Debug.Log("Called ToArray");
var pool = ArrayPool<TSource>.Shared;
var array = pool.Rent(16);

View File

@@ -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);

View File

@@ -8,8 +8,10 @@ using System.Threading;
#if UNITY_2019_3_OR_NEWER
using UnityEngine.LowLevel;
using PlayerLoopType = UnityEngine.PlayerLoop;
#else
using UnityEngine.Experimental.LowLevel;
using PlayerLoopType = UnityEngine.Experimental.PlayerLoop;
#endif
#if UNITY_EDITOR
@@ -57,6 +59,13 @@ namespace Cysharp.Threading.Tasks
public struct UniTaskLoopRunnerLastYieldUpdate { };
public struct UniTaskLoopRunnerLastYieldPreLateUpdate { };
public struct UniTaskLoopRunnerLastYieldPostLateUpdate { };
#if UNITY_2020_2_OR_NEWER
public struct UniTaskLoopRunnerTimeUpdate { };
public struct UniTaskLoopRunnerLastTimeUpdate { };
public struct UniTaskLoopRunnerYieldTimeUpdate { };
public struct UniTaskLoopRunnerLastYieldTimeUpdate { };
#endif
}
public enum PlayerLoopTiming
@@ -80,7 +89,13 @@ namespace Cysharp.Threading.Tasks
LastPreLateUpdate = 11,
PostLateUpdate = 12,
LastPostLateUpdate = 13
LastPostLateUpdate = 13,
#if UNITY_2020_2_OR_NEWER
// Unity 2020.2 added TimeUpdate https://docs.unity3d.com/2020.2/Documentation/ScriptReference/PlayerLoop.TimeUpdate.html
TimeUpdate = 14,
LastTimeUpdate = 15,
#endif
}
public interface IPlayerLoopItem
@@ -101,7 +116,7 @@ namespace Cysharp.Threading.Tasks
static SynchronizationContext unitySynchronizationContetext;
static ContinuationQueue[] yielders;
static PlayerLoopRunner[] runners;
internal static bool IsEditorApplicationQuitting { get; private set; }
static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem,
Type loopRunnerYieldType, ContinuationQueue cq, Type lastLoopRunnerYieldType, ContinuationQueue lastCq,
Type loopRunnerType, PlayerLoopRunner runner, Type lastLoopRunnerType, PlayerLoopRunner lastRunner)
@@ -110,27 +125,32 @@ namespace Cysharp.Threading.Tasks
#if UNITY_EDITOR
EditorApplication.playModeStateChanged += (state) =>
{
if (state == PlayModeStateChange.EnteredEditMode || state == PlayModeStateChange.EnteredPlayMode)
if (state == PlayModeStateChange.EnteredEditMode || state == PlayModeStateChange.ExitingEditMode)
{
return;
}
IsEditorApplicationQuitting = true;
// run rest action before clear.
if (runner != null)
{
runner.Run();
runner.Clear();
}
if (lastRunner != null)
{
lastRunner.Run();
lastRunner.Clear();
}
if (runner != null)
{
runner.Clear();
}
if (lastRunner != null)
{
lastRunner.Clear();
}
if (cq != null)
{
cq.Clear();
}
if (lastCq != null)
{
lastCq.Clear();
if (cq != null)
{
cq.Run();
cq.Clear();
}
if (lastCq != null)
{
lastCq.Run();
lastCq.Clear();
}
IsEditorApplicationQuitting = false;
}
};
#endif
@@ -175,6 +195,32 @@ 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()
{
@@ -246,52 +292,90 @@ namespace Cysharp.Threading.Tasks
if (item != null) item.Run();
}
}
UniTaskSynchronizationContext.Run();
}
#endif
private static int FindLoopSystemIndex(PlayerLoopSystem[] playerLoopList, Type systemType)
{
for (int i = 0; i < playerLoopList.Length; i++)
{
if (playerLoopList[i].type == systemType)
{
return i;
}
}
throw new Exception("Target PlayerLoopSystem does not found. Type:" + systemType.FullName);
}
public static void Initialize(ref PlayerLoopSystem playerLoop)
{
#if UNITY_2020_2_OR_NEWER
yielders = new ContinuationQueue[16];
runners = new PlayerLoopRunner[16];
#else
yielders = new ContinuationQueue[14];
runners = new PlayerLoopRunner[14];
#endif
var copyList = playerLoop.subSystemList.ToArray();
// Initialization
copyList[0].subSystemList = InsertRunner(copyList[0], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), yielders[0] = new ContinuationQueue(PlayerLoopTiming.Initialization),
var i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Initialization));
copyList[i].subSystemList = InsertRunner(copyList[i], 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),
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.EarlyUpdate));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), yielders[2] = new ContinuationQueue(PlayerLoopTiming.EarlyUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldEarlyUpdate), yielders[3] = new ContinuationQueue(PlayerLoopTiming.LastEarlyUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerEarlyUpdate), runners[2] = new PlayerLoopRunner(PlayerLoopTiming.EarlyUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastEarlyUpdate), runners[3] = new PlayerLoopRunner(PlayerLoopTiming.LastEarlyUpdate));
// FixedUpdate
copyList[2].subSystemList = InsertRunner(copyList[2], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), yielders[4] = new ContinuationQueue(PlayerLoopTiming.FixedUpdate),
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.FixedUpdate));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), yielders[4] = new ContinuationQueue(PlayerLoopTiming.FixedUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldFixedUpdate), yielders[5] = new ContinuationQueue(PlayerLoopTiming.LastFixedUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerFixedUpdate), runners[4] = new PlayerLoopRunner(PlayerLoopTiming.FixedUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastFixedUpdate), runners[5] = new PlayerLoopRunner(PlayerLoopTiming.LastFixedUpdate));
// PreUpdate
copyList[3].subSystemList = InsertRunner(copyList[3], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), yielders[6] = new ContinuationQueue(PlayerLoopTiming.PreUpdate),
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.PreUpdate));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), yielders[6] = new ContinuationQueue(PlayerLoopTiming.PreUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreUpdate), yielders[7] = new ContinuationQueue(PlayerLoopTiming.LastPreUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreUpdate), runners[6] = new PlayerLoopRunner(PlayerLoopTiming.PreUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreUpdate), runners[7] = new PlayerLoopRunner(PlayerLoopTiming.LastPreUpdate));
// Update
copyList[4].subSystemList = InsertRunner(copyList[4], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), yielders[8] = new ContinuationQueue(PlayerLoopTiming.Update),
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Update));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), yielders[8] = new ContinuationQueue(PlayerLoopTiming.Update),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldUpdate), yielders[9] = new ContinuationQueue(PlayerLoopTiming.LastUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerUpdate), runners[8] = new PlayerLoopRunner(PlayerLoopTiming.Update),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastUpdate), runners[9] = new PlayerLoopRunner(PlayerLoopTiming.LastUpdate));
// PreLateUpdate
copyList[5].subSystemList = InsertRunner(copyList[5], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), yielders[10] = new ContinuationQueue(PlayerLoopTiming.PreLateUpdate),
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.PreLateUpdate));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), yielders[10] = new ContinuationQueue(PlayerLoopTiming.PreLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreLateUpdate), yielders[11] = new ContinuationQueue(PlayerLoopTiming.LastPreLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreLateUpdate), runners[10] = new PlayerLoopRunner(PlayerLoopTiming.PreLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreLateUpdate), runners[11] = new PlayerLoopRunner(PlayerLoopTiming.LastPreLateUpdate));
// PostLateUpdate
copyList[6].subSystemList = InsertRunner(copyList[6], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), yielders[12] = new ContinuationQueue(PlayerLoopTiming.PostLateUpdate),
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.PostLateUpdate));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), yielders[12] = new ContinuationQueue(PlayerLoopTiming.PostLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPostLateUpdate), yielders[13] = new ContinuationQueue(PlayerLoopTiming.LastPostLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), runners[12] = new PlayerLoopRunner(PlayerLoopTiming.PostLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPostLateUpdate), runners[13] = new PlayerLoopRunner(PlayerLoopTiming.LastPostLateUpdate));
#if UNITY_2020_2_OR_NEWER
// TimeUpdate
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.TimeUpdate));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldTimeUpdate), yielders[14] = new ContinuationQueue(PlayerLoopTiming.TimeUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldTimeUpdate), yielders[15] = new ContinuationQueue(PlayerLoopTiming.LastTimeUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerTimeUpdate), runners[14] = new PlayerLoopRunner(PlayerLoopTiming.TimeUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastTimeUpdate), runners[15] = new PlayerLoopRunner(PlayerLoopTiming.LastTimeUpdate));
#endif
// Insert UniTaskSynchronizationContext to Update loop
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Update));
copyList[i].subSystemList = InsertUniTaskSynchronizationContext(copyList[i]);
playerLoop.subSystemList = copyList;
PlayerLoop.SetPlayerLoop(playerLoop);
@@ -306,6 +390,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
}
}

View File

@@ -12,7 +12,9 @@ namespace Cysharp.Threading.Tasks
public static class TaskPool
{
internal static int MaxPoolSize;
static ConcurrentDictionary<Type, Func<int>> sizes = new ConcurrentDictionary<Type, Func<int>>();
// avoid to use ConcurrentDictionary for safety of WebGL build.
static Dictionary<Type, Func<int>> sizes = new Dictionary<Type, Func<int>>();
static TaskPool()
{
@@ -40,22 +42,27 @@ namespace Cysharp.Threading.Tasks
public static IEnumerable<(Type, int)> GetCacheSizeInfo()
{
foreach (var item in sizes)
lock (sizes)
{
yield return (item.Key, item.Value());
foreach (var item in sizes)
{
yield return (item.Key, item.Value());
}
}
}
public static void RegisterSizeGetter(Type type, Func<int> getSize)
{
sizes[type] = getSize;
lock (sizes)
{
sizes[type] = getSize;
}
}
}
public interface ITaskPoolNode<T>
{
T NextNode { get; set; }
ref T NextNode { get; }
}
// mutable struct, don't mark readonly.
@@ -77,8 +84,9 @@ namespace Cysharp.Threading.Tasks
var v = root;
if (!(v is null))
{
root = v.NextNode;
v.NextNode = null;
ref var nextNode = ref v.NextNode;
root = nextNode;
nextNode = null;
size--;
result = v;
Volatile.Write(ref gate, 0);

View File

@@ -1541,6 +1541,7 @@ namespace Cysharp.Threading.Tasks.Triggers
#endregion
#region MouseDown
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
public interface IAsyncOnMouseDownHandler
{
@@ -1597,9 +1598,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMouseDownHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseDownAsync();
}
}
#endif
#endregion
#region MouseDrag
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
public interface IAsyncOnMouseDragHandler
{
@@ -1656,9 +1659,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMouseDragHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseDragAsync();
}
}
#endif
#endregion
#region MouseEnter
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
public interface IAsyncOnMouseEnterHandler
{
@@ -1715,9 +1720,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMouseEnterHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseEnterAsync();
}
}
#endif
#endregion
#region MouseExit
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
public interface IAsyncOnMouseExitHandler
{
@@ -1774,9 +1781,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMouseExitHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseExitAsync();
}
}
#endif
#endregion
#region MouseOver
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
public interface IAsyncOnMouseOverHandler
{
@@ -1833,9 +1842,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMouseOverHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseOverAsync();
}
}
#endif
#endregion
#region MouseUp
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
public interface IAsyncOnMouseUpHandler
{
@@ -1892,9 +1903,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMouseUpHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseUpAsync();
}
}
#endif
#endregion
#region MouseUpAsButton
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
public interface IAsyncOnMouseUpAsButtonHandler
{
@@ -1951,6 +1964,7 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMouseUpAsButtonHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseUpAsButtonAsync();
}
}
#endif
#endregion
#region ParticleCollision

View File

@@ -25,13 +25,7 @@
("Update", "Update", "AsyncUnit", null, empty),
("FixedUpdate", "FixedUpdate", "AsyncUnit", null, empty),
("LateUpdate", "LateUpdate", "AsyncUnit", null, empty),
("MouseDown", "OnMouseDown", "AsyncUnit", null, empty),
("MouseDrag", "OnMouseDrag", "AsyncUnit", null, empty),
("MouseEnter", "OnMouseEnter", "AsyncUnit", null, empty),
("MouseExit", "OnMouseExit", "AsyncUnit", null, empty),
("MouseOver", "OnMouseOver", "AsyncUnit", null, empty),
("MouseUp", "OnMouseUp", "AsyncUnit", null, empty),
("MouseUpAsButton", "OnMouseUpAsButton", "AsyncUnit", null, empty),
("ParticleCollision", "OnParticleCollision", "GameObject", null, new []{ ("GameObject", "other") }),
("RectTransformDimensionsChange", "OnRectTransformDimensionsChange", "AsyncUnit", null, empty),
("RectTransformRemoved", "OnRectTransformRemoved", "AsyncUnit", null, empty),
@@ -47,6 +41,15 @@
("BecameInvisible", "OnBecameInvisible", "AsyncUnit", null, empty),
("BecameVisible", "OnBecameVisible", "AsyncUnit", null, empty),
// Mouse... #if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
("MouseDown", "OnMouseDown", "AsyncUnit", null, empty),
("MouseDrag", "OnMouseDrag", "AsyncUnit", null, empty),
("MouseEnter", "OnMouseEnter", "AsyncUnit", null, empty),
("MouseExit", "OnMouseExit", "AsyncUnit", null, empty),
("MouseOver", "OnMouseOver", "AsyncUnit", null, empty),
("MouseUp", "OnMouseUp", "AsyncUnit", null, empty),
("MouseUpAsButton", "OnMouseUpAsButton", "AsyncUnit", null, empty),
// new in v2
("ApplicationFocus", "OnApplicationFocus", "bool", null, new []{("bool", "hasFocus") }),
("ApplicationPause", "OnApplicationPause", "bool", null, new []{("bool", "pauseStatus") }),
@@ -104,6 +107,7 @@
Func<(string argType, string argName)[], string> BuildResultParameter = x => x.Length == 0 ? "AsyncUnit.Default" : "(" + string.Join(", ", x.Select(y => y.argName)) + ")";
Func<string, bool> Is2019_3 = x => x == "ParticleUpdateJobScheduled";
Func<string, bool> IsMouseTrigger = x => x.StartsWith("Mouse");
#>
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
@@ -117,6 +121,9 @@ namespace Cysharp.Threading.Tasks.Triggers
#region <#= t.triggerName #>
<# if(Is2019_3(t.triggerName)) { #>
#if UNITY_2019_3_OR_NEWER
<# } #>
<# if(IsMouseTrigger(t.triggerName)) { #>
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
<# } #>
public interface <#= ToInterfaceName(t.methodName) #>
@@ -174,7 +181,7 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((<#= ToInterfaceName(t.methodName) #>)new AsyncTriggerHandler<<#= t.returnType #>>(this, cancellationToken, true)).<#= t.methodName #>Async();
}
}
<# if(Is2019_3(t.triggerName)) { #>
<# if(Is2019_3(t.triggerName) || IsMouseTrigger(t.triggerName)) { #>
#endif
<# } #>
#endregion

View File

@@ -127,7 +127,8 @@ namespace Cysharp.Threading.Tasks
sealed class YieldPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<YieldPromise>
{
static TaskPool<YieldPromise> pool;
public YieldPromise NextNode { get; set; }
YieldPromise nextNode;
public ref YieldPromise NextNode => ref nextNode;
static YieldPromise()
{
@@ -215,7 +216,8 @@ namespace Cysharp.Threading.Tasks
sealed class NextFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<NextFramePromise>
{
static TaskPool<NextFramePromise> pool;
public NextFramePromise NextNode { get; set; }
NextFramePromise nextNode;
public ref NextFramePromise NextNode => ref nextNode;
static NextFramePromise()
{
@@ -309,7 +311,8 @@ namespace Cysharp.Threading.Tasks
sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise>
{
static TaskPool<DelayFramePromise> pool;
public DelayFramePromise NextNode { get; set; }
DelayFramePromise nextNode;
public ref DelayFramePromise NextNode => ref nextNode;
static DelayFramePromise()
{
@@ -424,7 +427,8 @@ namespace Cysharp.Threading.Tasks
sealed class DelayPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayPromise>
{
static TaskPool<DelayPromise> pool;
public DelayPromise NextNode { get; set; }
DelayPromise nextNode;
public ref DelayPromise NextNode => ref nextNode;
static DelayPromise()
{
@@ -534,7 +538,8 @@ namespace Cysharp.Threading.Tasks
sealed class DelayIgnoreTimeScalePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayIgnoreTimeScalePromise>
{
static TaskPool<DelayIgnoreTimeScalePromise> pool;
public DelayIgnoreTimeScalePromise NextNode { get; set; }
DelayIgnoreTimeScalePromise nextNode;
public ref DelayIgnoreTimeScalePromise NextNode => ref nextNode;
static DelayIgnoreTimeScalePromise()
{
@@ -644,7 +649,8 @@ namespace Cysharp.Threading.Tasks
sealed class DelayRealtimePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayRealtimePromise>
{
static TaskPool<DelayRealtimePromise> pool;
public DelayRealtimePromise NextNode { get; set; }
DelayRealtimePromise nextNode;
public ref DelayRealtimePromise NextNode => ref nextNode;
static DelayRealtimePromise()
{
@@ -720,6 +726,12 @@ namespace Cysharp.Threading.Tasks
return false;
}
if (stopwatch.IsInvalid)
{
core.TrySetResult(AsyncUnit.Default);
return false;
}
if (stopwatch.ElapsedTicks >= delayTimeSpanTicks)
{
core.TrySetResult(AsyncUnit.Default);

View File

@@ -341,10 +341,12 @@ namespace Cysharp.Threading.Tasks
public UniTaskStatus GetStatus(short token)
{
var f = Interlocked.Exchange(ref factory, null);
if (f == null) throw new InvalidOperationException("Can't call twice.");
if (f != null)
{
task = f();
awaiter = task.GetAwaiter();
}
task = f();
awaiter = task.GetAwaiter();
return task.Status;
}
@@ -383,10 +385,12 @@ namespace Cysharp.Threading.Tasks
public UniTaskStatus GetStatus(short token)
{
var f = Interlocked.Exchange(ref factory, null);
if (f == null) throw new InvalidOperationException("Can't call twice.");
if (f != null)
{
task = f();
awaiter = task.GetAwaiter();
}
task = f();
awaiter = task.GetAwaiter();
return task.Status;
}

View File

@@ -7,7 +7,11 @@ namespace Cysharp.Threading.Tasks
{
public partial struct UniTask
{
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
#region OBSOLETE_RUN
// Run is a confusing name, use only RunOnThreadPool in the future.
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask Run(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -35,7 +39,7 @@ namespace Cysharp.Threading.Tasks
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask Run(Action<object> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -63,7 +67,7 @@ namespace Cysharp.Threading.Tasks
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask Run(Func<UniTask> action, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -91,7 +95,7 @@ namespace Cysharp.Threading.Tasks
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask Run(Func<object, UniTask> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -119,7 +123,7 @@ namespace Cysharp.Threading.Tasks
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask<T> Run<T>(Func<T> func, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -146,7 +150,7 @@ namespace Cysharp.Threading.Tasks
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask<T> Run<T>(Func<UniTask<T>> func, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -176,7 +180,7 @@ namespace Cysharp.Threading.Tasks
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -203,7 +207,7 @@ namespace Cysharp.Threading.Tasks
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask<T> Run<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -232,6 +236,235 @@ namespace Cysharp.Threading.Tasks
return result;
}
}
#endregion
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask RunOnThreadPool(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
action();
}
finally
{
await UniTask.Yield();
}
}
else
{
action();
}
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask RunOnThreadPool(Action<object> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
action(state);
}
finally
{
await UniTask.Yield();
}
}
else
{
action(state);
}
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask RunOnThreadPool(Func<UniTask> action, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
await action();
}
finally
{
await UniTask.Yield();
}
}
else
{
await action();
}
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask RunOnThreadPool(Func<object, UniTask> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
await action(state);
}
finally
{
await UniTask.Yield();
}
}
else
{
await action(state);
}
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> RunOnThreadPool<T>(Func<T> func, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return func();
}
finally
{
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
return func();
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> RunOnThreadPool<T>(Func<UniTask<T>> func, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return await func();
}
finally
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
var result = await func();
cancellationToken.ThrowIfCancellationRequested();
return result;
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> RunOnThreadPool<T>(Func<object, T> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return func(state);
}
finally
{
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
return func(state);
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> RunOnThreadPool<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return await func(state);
}
finally
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
var result = await func(state);
cancellationToken.ThrowIfCancellationRequested();
return result;
}
}
}
}

View File

@@ -224,7 +224,8 @@ namespace Cysharp.Threading.Tasks
sealed class ThreadPoolWorkItem : IThreadPoolWorkItem, ITaskPoolNode<ThreadPoolWorkItem>
{
static TaskPool<ThreadPoolWorkItem> pool;
public ThreadPoolWorkItem NextNode { get; set; }
ThreadPoolWorkItem nextNode;
public ref ThreadPoolWorkItem NextNode => ref nextNode;
static ThreadPoolWorkItem()
{

View File

@@ -38,7 +38,8 @@ namespace Cysharp.Threading.Tasks
sealed class WaitUntilPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilPromise>
{
static TaskPool<WaitUntilPromise> pool;
public WaitUntilPromise NextNode { get; set; }
WaitUntilPromise nextNode;
public ref WaitUntilPromise NextNode => ref nextNode;
static WaitUntilPromise()
{
@@ -142,7 +143,8 @@ namespace Cysharp.Threading.Tasks
sealed class WaitWhilePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise>
{
static TaskPool<WaitWhilePromise> pool;
public WaitWhilePromise NextNode { get; set; }
WaitWhilePromise nextNode;
public ref WaitWhilePromise NextNode => ref nextNode;
static WaitWhilePromise()
{
@@ -246,7 +248,8 @@ namespace Cysharp.Threading.Tasks
sealed class WaitUntilCanceledPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilCanceledPromise>
{
static TaskPool<WaitUntilCanceledPromise> pool;
public WaitUntilCanceledPromise NextNode { get; set; }
WaitUntilCanceledPromise nextNode;
public ref WaitUntilCanceledPromise NextNode => ref nextNode;
static WaitUntilCanceledPromise()
{
@@ -334,7 +337,8 @@ namespace Cysharp.Threading.Tasks
sealed class WaitUntilValueChangedUnityObjectPromise<T, U> : IUniTaskSource<U>, IPlayerLoopItem, ITaskPoolNode<WaitUntilValueChangedUnityObjectPromise<T, U>>
{
static TaskPool<WaitUntilValueChangedUnityObjectPromise<T, U>> pool;
public WaitUntilValueChangedUnityObjectPromise<T, U> NextNode { get; set; }
WaitUntilValueChangedUnityObjectPromise<T, U> nextNode;
public ref WaitUntilValueChangedUnityObjectPromise<T, U> NextNode => ref nextNode;
static WaitUntilValueChangedUnityObjectPromise()
{
@@ -457,7 +461,8 @@ namespace Cysharp.Threading.Tasks
where T : class
{
static TaskPool<WaitUntilValueChangedStandardObjectPromise<T, U>> pool;
public WaitUntilValueChangedStandardObjectPromise<T, U> NextNode { get; set; }
WaitUntilValueChangedStandardObjectPromise<T, U> nextNode;
public ref WaitUntilValueChangedStandardObjectPromise<T, U> NextNode => ref nextNode;
static WaitUntilValueChangedStandardObjectPromise()
{

View File

@@ -10,6 +10,11 @@
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.modules.assetbundle",
"expression": "",
"define": "UNITASK_ASSETBUNDLE_SUPPORT"
}
],
"noEngineReferences": false
}

View File

@@ -115,8 +115,13 @@ namespace Cysharp.Threading.Tasks
var status = this.source.GetStatus(this.token);
if (status.IsCompletedSuccessfully())
{
this.source.GetResult(this.token);
return CompletedTasks.AsyncUnit;
}
else if(this.source is IUniTaskSource<AsyncUnit> asyncUnitSource)
{
return new UniTask<AsyncUnit>(asyncUnitSource, this.token);
}
return new UniTask<AsyncUnit>(new AsyncUnitSource(this.source), this.token);
}
@@ -422,6 +427,7 @@ namespace Cysharp.Threading.Tasks
var status = this.source.GetStatus(this.token);
if (status.IsCompletedSuccessfully())
{
this.source.GetResult(this.token);
return UniTask.CompletedTask;
}

View File

@@ -319,7 +319,8 @@ namespace Cysharp.Threading.Tasks
public class AutoResetUniTaskCompletionSource : IUniTaskSource, ITaskPoolNode<AutoResetUniTaskCompletionSource>, IPromise
{
static TaskPool<AutoResetUniTaskCompletionSource> pool;
public AutoResetUniTaskCompletionSource NextNode { get; set; }
AutoResetUniTaskCompletionSource nextNode;
public ref AutoResetUniTaskCompletionSource NextNode => ref nextNode;
static AutoResetUniTaskCompletionSource()
{
@@ -441,7 +442,8 @@ namespace Cysharp.Threading.Tasks
public class AutoResetUniTaskCompletionSource<T> : IUniTaskSource<T>, ITaskPoolNode<AutoResetUniTaskCompletionSource<T>>, IPromise<T>
{
static TaskPool<AutoResetUniTaskCompletionSource<T>> pool;
public AutoResetUniTaskCompletionSource<T> NextNode { get; set; }
AutoResetUniTaskCompletionSource<T> nextNode;
public ref AutoResetUniTaskCompletionSource<T> NextNode => ref nextNode;
static AutoResetUniTaskCompletionSource()
{

View File

@@ -189,6 +189,174 @@ namespace Cysharp.Threading.Tasks
return new AsyncLazy<T>(task);
}
/// <summary>
/// Ignore task result when cancel raised first.
/// </summary>
public static UniTask WithCancellation(this UniTask task, CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled)
{
return task;
}
if (cancellationToken.IsCancellationRequested)
{
return UniTask.FromCanceled(cancellationToken);
}
if (task.Status.IsCompleted())
{
return task;
}
return new UniTask(new WithCancellationSource(task, cancellationToken), 0);
}
/// <summary>
/// Ignore task result when cancel raised first.
/// </summary>
public static UniTask<T> WithCancellation<T>(this UniTask<T> task, CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled)
{
return task;
}
if (cancellationToken.IsCancellationRequested)
{
return UniTask.FromCanceled<T>(cancellationToken);
}
if (task.Status.IsCompleted())
{
return task;
}
return new UniTask<T>(new WithCancellationSource<T>(task, cancellationToken), 0);
}
sealed class WithCancellationSource : IUniTaskSource
{
static readonly Action<object> cancellationCallbackDelegate = CancellationCallback;
CancellationToken cancellationToken;
CancellationTokenRegistration tokenRegistration;
UniTaskCompletionSourceCore<AsyncUnit> core;
public WithCancellationSource(UniTask task, CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
this.tokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallbackDelegate, this);
RunTask(task).Forget();
}
async UniTaskVoid RunTask(UniTask task)
{
try
{
await task;
core.TrySetResult(AsyncUnit.Default);
}
catch (Exception ex)
{
core.TrySetException(ex);
}
finally
{
tokenRegistration.Dispose();
}
}
static void CancellationCallback(object state)
{
var self = (WithCancellationSource)state;
self.core.TrySetCanceled(self.cancellationToken);
}
public void GetResult(short token)
{
core.GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
}
sealed class WithCancellationSource<T> : IUniTaskSource<T>
{
static readonly Action<object> cancellationCallbackDelegate = CancellationCallback;
CancellationToken cancellationToken;
CancellationTokenRegistration tokenRegistration;
UniTaskCompletionSourceCore<T> core;
public WithCancellationSource(UniTask<T> task, CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
this.tokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallbackDelegate, this);
RunTask(task).Forget();
}
async UniTaskVoid RunTask(UniTask<T> task)
{
try
{
core.TrySetResult(await task);
}
catch (Exception ex)
{
core.TrySetException(ex);
}
finally
{
tokenRegistration.Dispose();
}
}
static void CancellationCallback(object state)
{
var self = (WithCancellationSource<T>)state;
self.core.TrySetCanceled(self.cancellationToken);
}
void IUniTaskSource.GetResult(short token)
{
core.GetResult(token);
}
public T GetResult(short token)
{
return core.GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
}
#if UNITY_2018_3_OR_NEWER
public static IEnumerator ToCoroutine<T>(this UniTask<T> task, Action<T> resultHandler = null, Action<Exception> exceptionHandler = null)
@@ -393,21 +561,23 @@ namespace Cysharp.Threading.Tasks
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
awaiter.SourceOnCompleted(state =>
else
{
using (var t = (StateTuple<UniTask.Awaiter>)state)
awaiter.SourceOnCompleted(state =>
{
try
using (var t = (StateTuple<UniTask.Awaiter>)state)
{
t.Item1.GetResult();
try
{
t.Item1.GetResult();
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
}, StateTuple.Create(awaiter));
}, StateTuple.Create(awaiter));
}
}
public static void Forget(this UniTask task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread = true)
@@ -461,21 +631,23 @@ namespace Cysharp.Threading.Tasks
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
awaiter.SourceOnCompleted(state =>
else
{
using (var t = (StateTuple<UniTask<T>.Awaiter>)state)
awaiter.SourceOnCompleted(state =>
{
try
using (var t = (StateTuple<UniTask<T>.Awaiter>)state)
{
t.Item1.GetResult();
try
{
t.Item1.GetResult();
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
}, StateTuple.Create(awaiter));
}, StateTuple.Create(awaiter));
}
}
public static void Forget<T>(this UniTask<T> task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread = true)
@@ -564,11 +736,51 @@ namespace Cysharp.Threading.Tasks
return await await task;
}
public static async UniTask Unwrap<T>(this UniTask<UniTask> task)
public static async UniTask Unwrap(this UniTask<UniTask> task)
{
await await task;
}
public static async UniTask<T> Unwrap<T>(this Task<UniTask<T>> task)
{
return await await task;
}
public static async UniTask<T> Unwrap<T>(this Task<UniTask<T>> task, bool continueOnCapturedContext)
{
return await await task.ConfigureAwait(continueOnCapturedContext);
}
public static async UniTask Unwrap(this Task<UniTask> task)
{
await await task;
}
public static async UniTask Unwrap(this Task<UniTask> task, bool continueOnCapturedContext)
{
await await task.ConfigureAwait(continueOnCapturedContext);
}
public static async UniTask<T> Unwrap<T>(this UniTask<Task<T>> task)
{
return await await task;
}
public static async UniTask<T> Unwrap<T>(this UniTask<Task<T>> task, bool continueOnCapturedContext)
{
return await (await task).ConfigureAwait(continueOnCapturedContext);
}
public static async UniTask Unwrap(this UniTask<Task> task)
{
await await task;
}
public static async UniTask Unwrap(this UniTask<Task> task, bool continueOnCapturedContext)
{
await (await task).ConfigureAwait(continueOnCapturedContext);
}
#if UNITY_2018_3_OR_NEWER
sealed class ToCoroutineEnumerator : IEnumerator

View File

@@ -0,0 +1,158 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public class UniTaskSynchronizationContext : SynchronizationContext
{
const int MaxArrayLength = 0X7FEFFFFF;
const int InitialSize = 16;
static SpinLock gate = new SpinLock(false);
static bool dequing = false;
static int actionListCount = 0;
static Callback[] actionList = new Callback[InitialSize];
static int waitingListCount = 0;
static Callback[] waitingList = new Callback[InitialSize];
static int opCount;
public override void Send(SendOrPostCallback d, object state)
{
d(state);
}
public override void Post(SendOrPostCallback d, object state)
{
bool lockTaken = false;
try
{
gate.Enter(ref lockTaken);
if (dequing)
{
// Ensure Capacity
if (waitingList.Length == waitingListCount)
{
var newLength = waitingListCount * 2;
if ((uint)newLength > MaxArrayLength) newLength = MaxArrayLength;
var newArray = new Callback[newLength];
Array.Copy(waitingList, newArray, waitingListCount);
waitingList = newArray;
}
waitingList[waitingListCount] = new Callback(d, state);
waitingListCount++;
}
else
{
// Ensure Capacity
if (actionList.Length == actionListCount)
{
var newLength = actionListCount * 2;
if ((uint)newLength > MaxArrayLength) newLength = MaxArrayLength;
var newArray = new Callback[newLength];
Array.Copy(actionList, newArray, actionListCount);
actionList = newArray;
}
actionList[actionListCount] = new Callback(d, state);
actionListCount++;
}
}
finally
{
if (lockTaken) gate.Exit(false);
}
}
public override void OperationStarted()
{
Interlocked.Increment(ref opCount);
}
public override void OperationCompleted()
{
Interlocked.Decrement(ref opCount);
}
public override SynchronizationContext CreateCopy()
{
return this;
}
// delegate entrypoint.
internal static void Run()
{
{
bool lockTaken = false;
try
{
gate.Enter(ref lockTaken);
if (actionListCount == 0) return;
dequing = true;
}
finally
{
if (lockTaken) gate.Exit(false);
}
}
for (int i = 0; i < actionListCount; i++)
{
var action = actionList[i];
actionList[i] = default;
action.Invoke();
}
{
bool lockTaken = false;
try
{
gate.Enter(ref lockTaken);
dequing = false;
var swapTempActionList = actionList;
actionListCount = waitingListCount;
actionList = waitingList;
waitingListCount = 0;
waitingList = swapTempActionList;
}
finally
{
if (lockTaken) gate.Exit(false);
}
}
}
[StructLayout(LayoutKind.Auto)]
readonly struct Callback
{
readonly SendOrPostCallback callback;
readonly object state;
public Callback(SendOrPostCallback callback, object state)
{
this.callback = callback;
this.state = state;
}
public void Invoke()
{
try
{
callback(state);
}
catch (Exception ex)
{
UnityEngine.Debug.LogException(ex);
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: abf3aae9813db2849bce518f8596e920
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,324 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#if UNITY_2018_4 || UNITY_2019_4_OR_NEWER
#if UNITASK_ASSETBUNDLE_SUPPORT
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using UnityEngine;
namespace Cysharp.Threading.Tasks
{
public static partial class UnityAsyncExtensions
{
public static AssetBundleRequestAllAssetsAwaiter AwaitForAllAssets(this AssetBundleRequest asyncOperation)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new AssetBundleRequestAllAssetsAwaiter(asyncOperation);
}
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object[]>(cancellationToken);
if (asyncOperation.isDone) return UniTask.FromResult<UnityEngine.Object[]>(asyncOperation.allAssets);
return new UniTask<UnityEngine.Object[]>(AssetBundleRequestAllAssetsWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
}
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object[]>(cancellationToken);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.allAssets);
return new UniTask<UnityEngine.Object[]>(AssetBundleRequestAllAssetsConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
}
public struct AssetBundleRequestAllAssetsAwaiter : ICriticalNotifyCompletion
{
AssetBundleRequest asyncOperation;
Action<AsyncOperation> continuationAction;
public AssetBundleRequestAllAssetsAwaiter(AssetBundleRequest asyncOperation)
{
this.asyncOperation = asyncOperation;
this.continuationAction = null;
}
public AssetBundleRequestAllAssetsAwaiter GetAwaiter()
{
return this;
}
public bool IsCompleted => asyncOperation.isDone;
public UnityEngine.Object[] GetResult()
{
if (continuationAction != null)
{
asyncOperation.completed -= continuationAction;
continuationAction = null;
var result = asyncOperation.allAssets;
asyncOperation = null;
return result;
}
else
{
var result = asyncOperation.allAssets;
asyncOperation = null;
return result;
}
}
public void OnCompleted(Action continuation)
{
UnsafeOnCompleted(continuation);
}
public void UnsafeOnCompleted(Action continuation)
{
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
continuationAction = PooledDelegate<AsyncOperation>.Create(continuation);
asyncOperation.completed += continuationAction;
}
}
sealed class AssetBundleRequestAllAssetsWithCancellationSource : IUniTaskSource<UnityEngine.Object[]>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestAllAssetsWithCancellationSource>
{
static TaskPool<AssetBundleRequestAllAssetsWithCancellationSource> pool;
AssetBundleRequestAllAssetsWithCancellationSource nextNode;
public ref AssetBundleRequestAllAssetsWithCancellationSource NextNode => ref nextNode;
static AssetBundleRequestAllAssetsWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(AssetBundleRequestAllAssetsWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
AssetBundleRequest asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<UnityEngine.Object[]> core;
AssetBundleRequestAllAssetsWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<UnityEngine.Object[]> Create(AssetBundleRequest asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<UnityEngine.Object[]>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AssetBundleRequestAllAssetsWithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return;
}
core.TrySetResult(asyncOperation.allAssets);
}
}
public UnityEngine.Object[] GetResult(short token)
{
return core.GetResult(token);
}
void IUniTaskSource.GetResult(short token)
{
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 bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AssetBundleRequestAllAssetsConfiguredSource : IUniTaskSource<UnityEngine.Object[]>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestAllAssetsConfiguredSource>
{
static TaskPool<AssetBundleRequestAllAssetsConfiguredSource> pool;
AssetBundleRequestAllAssetsConfiguredSource nextNode;
public ref AssetBundleRequestAllAssetsConfiguredSource NextNode => ref nextNode;
static AssetBundleRequestAllAssetsConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(AssetBundleRequestAllAssetsConfiguredSource), () => pool.Size);
}
AssetBundleRequest asyncOperation;
IProgress<float> progress;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<UnityEngine.Object[]> core;
AssetBundleRequestAllAssetsConfiguredSource()
{
}
public static IUniTaskSource<UnityEngine.Object[]> Create(AssetBundleRequest asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<UnityEngine.Object[]>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AssetBundleRequestAllAssetsConfiguredSource();
}
result.asyncOperation = asyncOperation;
result.progress = progress;
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public UnityEngine.Object[] GetResult(short token)
{
try
{
return core.GetResult(token);
}
finally
{
TryReturn();
}
}
void IUniTaskSource.GetResult(short token)
{
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 bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
if (progress != null)
{
progress.Report(asyncOperation.progress);
}
if (asyncOperation.isDone)
{
core.TrySetResult(asyncOperation.allAssets);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
progress = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
}
}
#endif
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e9147caba40da434da95b39709c13784
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -29,7 +29,8 @@ namespace Cysharp.Threading.Tasks
sealed class AsyncGPUReadbackRequestAwaiterConfiguredSource : IUniTaskSource<AsyncGPUReadbackRequest>, IPlayerLoopItem, ITaskPoolNode<AsyncGPUReadbackRequestAwaiterConfiguredSource>
{
static TaskPool<AsyncGPUReadbackRequestAwaiterConfiguredSource> pool;
public AsyncGPUReadbackRequestAwaiterConfiguredSource NextNode { get; set; }
AsyncGPUReadbackRequestAwaiterConfiguredSource nextNode;
public ref AsyncGPUReadbackRequestAwaiterConfiguredSource NextNode => ref nextNode;
static AsyncGPUReadbackRequestAwaiterConfiguredSource()
{

View File

@@ -4,6 +4,7 @@
using System;
using System.Threading;
using Unity.Jobs;
using UnityEngine;
namespace Cysharp.Threading.Tasks
{
@@ -85,7 +86,7 @@ namespace Cysharp.Threading.Tasks
public bool MoveNext()
{
if (jobHandle.IsCompleted)
if (jobHandle.IsCompleted | PlayerLoopHelper.IsEditorApplicationQuitting)
{
jobHandle.Complete();
core.TrySetResult(AsyncUnit.Default);

View File

@@ -80,7 +80,8 @@ namespace Cysharp.Threading.Tasks
sealed class AsyncOperationWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationWithCancellationSource>
{
static TaskPool<AsyncOperationWithCancellationSource> pool;
public AsyncOperationWithCancellationSource NextNode { get; set; }
AsyncOperationWithCancellationSource nextNode;
public ref AsyncOperationWithCancellationSource NextNode => ref nextNode;
static AsyncOperationWithCancellationSource()
{
@@ -198,7 +199,8 @@ namespace Cysharp.Threading.Tasks
sealed class AsyncOperationConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationConfiguredSource>
{
static TaskPool<AsyncOperationConfiguredSource> pool;
public AsyncOperationConfiguredSource NextNode { get; set; }
AsyncOperationConfiguredSource nextNode;
public ref AsyncOperationConfiguredSource NextNode => ref nextNode;
static AsyncOperationConfiguredSource()
{
@@ -374,7 +376,8 @@ namespace Cysharp.Threading.Tasks
sealed class ResourceRequestWithCancellationSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<ResourceRequestWithCancellationSource>
{
static TaskPool<ResourceRequestWithCancellationSource> pool;
public ResourceRequestWithCancellationSource NextNode { get; set; }
ResourceRequestWithCancellationSource nextNode;
public ref ResourceRequestWithCancellationSource NextNode => ref nextNode;
static ResourceRequestWithCancellationSource()
{
@@ -496,7 +499,8 @@ namespace Cysharp.Threading.Tasks
sealed class ResourceRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<ResourceRequestConfiguredSource>
{
static TaskPool<ResourceRequestConfiguredSource> pool;
public ResourceRequestConfiguredSource NextNode { get; set; }
ResourceRequestConfiguredSource nextNode;
public ref ResourceRequestConfiguredSource NextNode => ref nextNode;
static ResourceRequestConfiguredSource()
{
@@ -605,6 +609,7 @@ namespace Cysharp.Threading.Tasks
#endregion
#if UNITASK_ASSETBUNDLE_SUPPORT
#region AssetBundleRequest
public static AssetBundleRequestAwaiter GetAwaiter(this AssetBundleRequest asyncOperation)
@@ -676,7 +681,8 @@ namespace Cysharp.Threading.Tasks
sealed class AssetBundleRequestWithCancellationSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestWithCancellationSource>
{
static TaskPool<AssetBundleRequestWithCancellationSource> pool;
public AssetBundleRequestWithCancellationSource NextNode { get; set; }
AssetBundleRequestWithCancellationSource nextNode;
public ref AssetBundleRequestWithCancellationSource NextNode => ref nextNode;
static AssetBundleRequestWithCancellationSource()
{
@@ -798,7 +804,8 @@ namespace Cysharp.Threading.Tasks
sealed class AssetBundleRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestConfiguredSource>
{
static TaskPool<AssetBundleRequestConfiguredSource> pool;
public AssetBundleRequestConfiguredSource NextNode { get; set; }
AssetBundleRequestConfiguredSource nextNode;
public ref AssetBundleRequestConfiguredSource NextNode => ref nextNode;
static AssetBundleRequestConfiguredSource()
{
@@ -906,7 +913,9 @@ namespace Cysharp.Threading.Tasks
}
#endregion
#endif
#if UNITASK_ASSETBUNDLE_SUPPORT
#region AssetBundleCreateRequest
public static AssetBundleCreateRequestAwaiter GetAwaiter(this AssetBundleCreateRequest asyncOperation)
@@ -978,7 +987,8 @@ namespace Cysharp.Threading.Tasks
sealed class AssetBundleCreateRequestWithCancellationSource : IUniTaskSource<AssetBundle>, IPlayerLoopItem, ITaskPoolNode<AssetBundleCreateRequestWithCancellationSource>
{
static TaskPool<AssetBundleCreateRequestWithCancellationSource> pool;
public AssetBundleCreateRequestWithCancellationSource NextNode { get; set; }
AssetBundleCreateRequestWithCancellationSource nextNode;
public ref AssetBundleCreateRequestWithCancellationSource NextNode => ref nextNode;
static AssetBundleCreateRequestWithCancellationSource()
{
@@ -1100,7 +1110,8 @@ namespace Cysharp.Threading.Tasks
sealed class AssetBundleCreateRequestConfiguredSource : IUniTaskSource<AssetBundle>, IPlayerLoopItem, ITaskPoolNode<AssetBundleCreateRequestConfiguredSource>
{
static TaskPool<AssetBundleCreateRequestConfiguredSource> pool;
public AssetBundleCreateRequestConfiguredSource NextNode { get; set; }
AssetBundleCreateRequestConfiguredSource nextNode;
public ref AssetBundleCreateRequestConfiguredSource NextNode => ref nextNode;
static AssetBundleCreateRequestConfiguredSource()
{
@@ -1208,6 +1219,7 @@ namespace Cysharp.Threading.Tasks
}
#endregion
#endif
#if ENABLE_UNITYWEBREQUEST
#region UnityWebRequestAsyncOperation
@@ -1303,7 +1315,8 @@ namespace Cysharp.Threading.Tasks
sealed class UnityWebRequestAsyncOperationWithCancellationSource : IUniTaskSource<UnityWebRequest>, IPlayerLoopItem, ITaskPoolNode<UnityWebRequestAsyncOperationWithCancellationSource>
{
static TaskPool<UnityWebRequestAsyncOperationWithCancellationSource> pool;
public UnityWebRequestAsyncOperationWithCancellationSource NextNode { get; set; }
UnityWebRequestAsyncOperationWithCancellationSource nextNode;
public ref UnityWebRequestAsyncOperationWithCancellationSource NextNode => ref nextNode;
static UnityWebRequestAsyncOperationWithCancellationSource()
{
@@ -1434,7 +1447,8 @@ namespace Cysharp.Threading.Tasks
sealed class UnityWebRequestAsyncOperationConfiguredSource : IUniTaskSource<UnityWebRequest>, IPlayerLoopItem, ITaskPoolNode<UnityWebRequestAsyncOperationConfiguredSource>
{
static TaskPool<UnityWebRequestAsyncOperationConfiguredSource> pool;
public UnityWebRequestAsyncOperationConfiguredSource NextNode { get; set; }
UnityWebRequestAsyncOperationConfiguredSource nextNode;
public ref UnityWebRequestAsyncOperationConfiguredSource NextNode => ref nextNode;
static UnityWebRequestAsyncOperationConfiguredSource()
{

View File

@@ -17,6 +17,7 @@
Func<string, string> ToUniTaskReturnType = x => (x == "void") ? "UniTask" : $"UniTask<{x}>";
Func<string, string> ToIUniTaskSourceReturnType = x => (x == "void") ? "IUniTaskSource" : $"IUniTaskSource<{x}>";
Func<(string typeName, string returnType, string returnField), bool> IsUnityWebRequest = x => x.returnType == "UnityWebRequest";
Func<(string typeName, string returnType, string returnField), bool> IsAssetBundleModule = x => x.typeName == "AssetBundleRequest" || x.typeName == "AssetBundleCreateRequest";
Func<(string typeName, string returnType, string returnField), bool> IsVoid = x => x.returnType == "void";
#>
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
@@ -37,6 +38,8 @@ namespace Cysharp.Threading.Tasks
<# foreach(var t in types) { #>
<# if(IsUnityWebRequest(t)) { #>
#if ENABLE_UNITYWEBREQUEST
<# } else if(IsAssetBundleModule(t)) { #>
#if UNITASK_ASSETBUNDLE_SUPPORT
<# } #>
#region <#= t.typeName #>
@@ -151,7 +154,8 @@ namespace Cysharp.Threading.Tasks
sealed class <#= t.typeName #>WithCancellationSource : <#= ToIUniTaskSourceReturnType(t.returnType) #>, IPlayerLoopItem, ITaskPoolNode<<#= t.typeName #>WithCancellationSource>
{
static TaskPool<<#= t.typeName #>WithCancellationSource> pool;
public <#= t.typeName #>WithCancellationSource NextNode { get; set; }
<#= t.typeName #>WithCancellationSource nextNode;
public ref <#= t.typeName #>WithCancellationSource NextNode => ref nextNode;
static <#= t.typeName #>WithCancellationSource()
{
@@ -294,7 +298,8 @@ namespace Cysharp.Threading.Tasks
sealed class <#= t.typeName #>ConfiguredSource : <#= ToIUniTaskSourceReturnType(t.returnType) #>, IPlayerLoopItem, ITaskPoolNode<<#= t.typeName #>ConfiguredSource>
{
static TaskPool<<#= t.typeName #>ConfiguredSource> pool;
public <#= t.typeName #>ConfiguredSource NextNode { get; set; }
<#= t.typeName #>ConfiguredSource nextNode;
public ref <#= t.typeName #>ConfiguredSource NextNode => ref nextNode;
static <#= t.typeName #>ConfiguredSource()
{
@@ -422,7 +427,7 @@ namespace Cysharp.Threading.Tasks
}
#endregion
<# if(IsUnityWebRequest(t)) { #>
<# if(IsUnityWebRequest(t) || IsAssetBundleModule(t)) { #>
#endif
<# } #>

View File

@@ -1,6 +1,7 @@
#if ENABLE_UNITYWEBREQUEST
using System;
using System.Collections.Generic;
using UnityEngine.Networking;
namespace Cysharp.Threading.Tasks
@@ -17,6 +18,7 @@ namespace Cysharp.Threading.Tasks
public string Error { get; }
public string Text { get; }
public long ResponseCode { get; }
public Dictionary<string, string> ResponseHeaders { get; }
string msg;
@@ -38,6 +40,7 @@ namespace Cysharp.Threading.Tasks
this.Text = dhb.text;
}
}
this.ResponseHeaders = unityWebRequest.GetResponseHeaders();
}
public override string Message

View File

@@ -1,7 +1,7 @@
{
"name": "com.cysharp.unitask",
"displayName": "UniTask",
"version": "2.0.26",
"version": "2.1.1",
"unity": "2018.4",
"description": "Provides an efficient async/await integration to Unity.",
"keywords": [ "async/await", "async", "Task", "UniTask" ],

View File

@@ -0,0 +1,41 @@
#if UNITY_EDITOR
using System;
using Cysharp.Threading.Tasks;
using UnityEditor;
using UnityEngine;
public class Test1
{
[MenuItem("Test/Test1")]
public static async UniTaskVoid TestFunc()
{
await DoSomeThing();
string[] scenes = new string[]
{
"Assets/Scenes/SandboxMain.unity",
};
try
{
Debug.Log("Build Begin");
BuildPipeline.BuildPlayer(scenes, Application.dataPath + "../target", BuildTarget.StandaloneWindows, BuildOptions.CompressWithLz4);
Debug.Log("Build After");
}
catch (Exception e)
{
Debug.LogError(e.Message);
}
}
public static async UniTask DoSomeThing()
{
Debug.Log("Dosomething");
await UniTask.Delay(1500, DelayType.Realtime);
Debug.Log("Dosomething 2");
await UniTask.Delay(1000, DelayType.Realtime);
Debug.Log("Dosomething 3");
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 48003021292963e48b2493e915dca5ac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using UnityEngine;
// using UnityEngine.AddressableAssets;
/*UNniTastWhenAnyTester*/
@@ -49,4 +50,9 @@ public class ExceptionExamples : MonoBehaviour
await UniTask.Delay(100);
return taskIndex;
}
//void AddressablesTest()
//{
// Addressables.ClearDependencyCacheAsync("key", true);
//}
}

View File

@@ -0,0 +1,471 @@
using Cysharp.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace Cysharp.Threading.Tasks.Sample
{
//public class Sample2
//{
// public Sample2()
// {
// // デコレーターの詰まったClientを生成これは一度作ったらフィールドに保存可
// var client = new NetworkClient("http://localhost", TimeSpan.FromSeconds(10),
// new QueueRequestDecorator(),
// new LoggingDecorator(),
// new AppendTokenDecorator(),
// new SetupHeaderDecorator());
// await client.PostAsync("/User/Register", new { Id = 100 });
// }
//}
public class ReturnToTitleDecorator : IAsyncDecorator
{
public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next)
{
try
{
return await next(context, cancellationToken);
}
catch (Exception ex)
{
if (ex is OperationCanceledException)
{
// キャンセルはきっと想定されている処理なのでそのまんまスルー呼び出し側でOperationCanceledExceptionとして飛んでいく)
throw;
}
if (ex is UnityWebRequestException uwe)
{
// ステータスコードを使って、タイトルに戻す例外です、とかリトライさせる例外です、とかハンドリングさせると便利
// if (uwe.ResponseCode) { }...
}
// サーバー例外のMessageを直接出すなんて乱暴なことはデバッグ時だけですよ勿論。
var result = await MessageDialog.ShowAsync(ex.Message);
// OK か Cancelかで分岐するなら。今回はボタン一個、OKのみの想定なので無視
// if (result == DialogResult.Ok) { }...
// シーン呼び出しはawaitしないことawaitして正常終了しちゃうと、この通信の呼び出し元に処理が戻って続行してしまいます
// のでForget。
SceneManager.LoadSceneAsync("TitleScene").ToUniTask().Forget();
// そしてOperationCanceledExceptionを投げて、この通信の呼び出し元の処理はキャンセル扱いにして終了させる
throw new OperationCanceledException();
}
}
}
public enum DialogResult
{
Ok,
Cancel
}
public static class MessageDialog
{
public static async UniTask<DialogResult> ShowAsync(string message)
{
// (例えば)Prefabで作っておいたダイアログを生成する
var view = await Resources.LoadAsync("Prefabs/Dialog");
// Ok, Cancelボタンのどちらかが押されるのを待機
return await (view as GameObject).GetComponent<MessageDialogView>().ClickResult;
}
}
public class MessageDialogView : MonoBehaviour
{
[SerializeField] Button okButton = default;
[SerializeField] Button closeButton = default;
UniTaskCompletionSource<DialogResult> taskCompletion;
// これでどちらかが押されるまで無限に待つを表現
public UniTask<DialogResult> ClickResult => taskCompletion.Task;
private void Start()
{
taskCompletion = new UniTaskCompletionSource<DialogResult>();
okButton.onClick.AddListener(() =>
{
taskCompletion.TrySetResult(DialogResult.Ok);
});
closeButton.onClick.AddListener(() =>
{
taskCompletion.TrySetResult(DialogResult.Cancel);
});
}
// もしボタンが押されずに消滅した場合にネンノタメ。
private void OnDestroy()
{
taskCompletion.TrySetResult(DialogResult.Cancel);
}
}
public class MockDecorator : IAsyncDecorator
{
Dictionary<string, object> mock;
// Pathと型を1:1にして事前定義したオブジェクトを返す辞書を渡す
public MockDecorator(Dictionary<string, object> mock)
{
this.mock = mock;
}
public UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next)
{
if (mock.TryGetValue(context.Path, out var value))
{
// 一致したものがあればそれを返す(実際の通信は行わない)
return new UniTask<ResponseContext>(new ResponseContext(value));
}
else
{
return next(context, cancellationToken);
}
}
}
//public class LoggingDecorator : IAsyncDecorator
//{
// public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next)
// {
// var sw = Stopwatch.StartNew();
// try
// {
// UnityEngine.Debug.Log("Start Network Request:" + context.Path);
// var response = await next(context, cancellationToken);
// UnityEngine.Debug.Log($"Complete Network Request: {context.Path} , Elapsed: {sw.Elapsed}, Size: {response.GetRawData().Length}");
// return response;
// }
// catch (Exception ex)
// {
// if (ex is OperationCanceledException)
// {
// UnityEngine.Debug.Log("Request Canceled:" + context.Path);
// }
// else if (ex is TimeoutException)
// {
// UnityEngine.Debug.Log("Request Timeout:" + context.Path);
// }
// else if (ex is UnityWebRequestException webex)
// {
// if (webex.IsHttpError)
// {
// UnityEngine.Debug.Log($"Request HttpError: {context.Path} Code:{webex.ResponseCode} Message:{webex.Message}");
// }
// else if (webex.IsNetworkError)
// {
// UnityEngine.Debug.Log($"Request NetworkError: {context.Path} Code:{webex.ResponseCode} Message:{webex.Message}");
// }
// }
// throw;
// }
// finally
// {
// /* log other */
// }
// }
//}
public class SetupHeaderDecorator : IAsyncDecorator
{
public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next)
{
context.RequestHeaders["x-app-timestamp"] = context.Timestamp.ToString();
context.RequestHeaders["x-user-id"] = "132141411"; // どこかから持ってくる
context.RequestHeaders["x-access-token"] = "fafafawfafewaea"; // どこかから持ってくる2
var respsonse = await next(context, cancellationToken);
var nextToken = respsonse.ResponseHeaders["token"];
// UserProfile.Token = nextToken; // どこかにセットするということにする
return respsonse;
}
}
public class AppendTokenDecorator : IAsyncDecorator
{
public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next)
{
string token = "token"; // どっかから取ってくるということにする
RETRY:
try
{
context.RequestHeaders["x-accesss-token"] = token;
return await next(context, cancellationToken);
}
catch (UnityWebRequestException ex)
{
// 例えば700はTokenを再取得してください的な意味だったとする
if (ex.ResponseCode == 700)
{
// 別口でTokenを取得します的な処理
var newToken = await new NetworkClient(context.BasePath, context.Timeout).PostAsync<string>("/Auth/GetToken", "access_token", cancellationToken);
context.Reset(this);
goto RETRY;
}
goto RETRY;
}
}
}
public class QueueRequestDecorator : IAsyncDecorator
{
readonly Queue<(UniTaskCompletionSource<ResponseContext>, RequestContext, CancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>>)> q = new Queue<(UniTaskCompletionSource<ResponseContext>, RequestContext, CancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>>)>();
bool running;
public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next)
{
if (q.Count == 0)
{
return await next(context, cancellationToken);
}
else
{
var completionSource = new UniTaskCompletionSource<ResponseContext>();
q.Enqueue((completionSource, context, cancellationToken, next));
if (!running)
{
Run().Forget();
}
return await completionSource.Task;
}
}
async UniTaskVoid Run()
{
running = true;
try
{
while (q.Count != 0)
{
var (tcs, context, cancellationToken, next) = q.Dequeue();
try
{
var response = await next(context, cancellationToken);
tcs.TrySetResult(response);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
}
finally
{
running = false;
}
}
}
public class RequestContext
{
int decoratorIndex;
readonly IAsyncDecorator[] decorators;
Dictionary<string, string> headers;
public string BasePath { get; }
public string Path { get; }
public object Value { get; }
public TimeSpan Timeout { get; }
public DateTimeOffset Timestamp { get; private set; }
public IDictionary<string, string> RequestHeaders
{
get
{
if (headers == null)
{
headers = new Dictionary<string, string>();
}
return headers;
}
}
public RequestContext(string basePath, string path, object value, TimeSpan timeout, IAsyncDecorator[] filters)
{
this.decoratorIndex = -1;
this.decorators = filters;
this.BasePath = basePath;
this.Path = path;
this.Value = value;
this.Timeout = timeout;
this.Timestamp = DateTimeOffset.UtcNow;
}
internal Dictionary<string, string> GetRawHeaders() => headers;
internal IAsyncDecorator GetNextDecorator() => decorators[++decoratorIndex];
public void Reset(IAsyncDecorator currentFilter)
{
decoratorIndex = Array.IndexOf(decorators, currentFilter);
if (headers != null)
{
headers.Clear();
}
Timestamp = DateTimeOffset.UtcNow;
}
}
public class ResponseContext
{
bool hasValue;
object value;
readonly byte[] bytes;
public long StatusCode { get; }
public Dictionary<string, string> ResponseHeaders { get; }
public ResponseContext(object value, Dictionary<string, string> header = null)
{
this.hasValue = true;
this.value = value;
this.StatusCode = 200;
this.ResponseHeaders = (header ?? new Dictionary<string, string>());
}
public ResponseContext(byte[] bytes, long statusCode, Dictionary<string, string> responseHeaders)
{
this.hasValue = false;
this.bytes = bytes;
this.StatusCode = statusCode;
this.ResponseHeaders = responseHeaders;
}
public byte[] GetRawData() => bytes;
public T GetResponseAs<T>()
{
if (hasValue)
{
return (T)value;
}
value = JsonUtility.FromJson<T>(Encoding.UTF8.GetString(bytes));
hasValue = true;
return (T)value;
}
}
public interface IAsyncDecorator
{
UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next);
}
public class NetworkClient : IAsyncDecorator
{
readonly Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next;
readonly IAsyncDecorator[] decorators;
readonly TimeSpan timeout;
readonly IProgress<float> progress;
readonly string basePath;
public NetworkClient(string basePath, TimeSpan timeout, params IAsyncDecorator[] decorators)
: this(basePath, timeout, null, decorators)
{
}
public NetworkClient(string basePath, TimeSpan timeout, IProgress<float> progress, params IAsyncDecorator[] decorators)
{
this.next = InvokeRecursive; // setup delegate
this.basePath = basePath;
this.timeout = timeout;
this.progress = progress;
this.decorators = new IAsyncDecorator[decorators.Length + 1];
Array.Copy(decorators, this.decorators, decorators.Length);
this.decorators[this.decorators.Length - 1] = this;
}
public async UniTask<T> PostAsync<T>(string path, T value, CancellationToken cancellationToken = default)
{
var request = new RequestContext(basePath, path, value, timeout, decorators);
var response = await InvokeRecursive(request, cancellationToken);
return response.GetResponseAs<T>();
}
UniTask<ResponseContext> InvokeRecursive(RequestContext context, CancellationToken cancellationToken)
{
return context.GetNextDecorator().SendAsync(context, cancellationToken, next); // マジカル再帰処理
}
async UniTask<ResponseContext> IAsyncDecorator.SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> _)
{
// Postしか興味ないからPostにしかしないよ
// パフォーマンスを最大限にしたい場合はuploadHandler, downloadHandlerをカスタマイズすること
// JSONでbodyに送るというパラメータで送るという雑設定。
var data = JsonUtility.ToJson(context.Value);
var formData = new Dictionary<string, string> { { "body", data } };
using (var req = UnityWebRequest.Post(basePath + context.Path, formData))
{
var header = context.GetRawHeaders();
if (header != null)
{
foreach (var item in header)
{
req.SetRequestHeader(item.Key, item.Value);
}
}
// Timeout処理はCancellationTokenSourceのCancelAfterSlim(UniTask拡張)を使ってサクッと処理
var linkToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
linkToken.CancelAfterSlim(timeout);
try
{
// 完了待ちや終了処理はUniTaskの拡張自体に丸投げ
await req.SendWebRequest().ToUniTask(progress: progress, cancellationToken: linkToken.Token);
}
catch (OperationCanceledException)
{
// 元キャンセレーションソースがキャンセルしてなければTimeoutによるものと判定
if (!cancellationToken.IsCancellationRequested)
{
throw new TimeoutException();
}
}
finally
{
// Timeoutに引っかからなかった場合にてるのでCancelAfterSlimの裏で回ってるループをこれで終わらせとく
if (!linkToken.IsCancellationRequested)
{
linkToken.Cancel();
}
}
// UnityWebRequestを先にDisposeしちゃうので先に必要なものを取得しておく性能的には無駄なのでパフォーマンスを最大限にしたい場合は更に一工夫を
return new ResponseContext(req.downloadHandler.data, req.responseCode, req.GetResponseHeaders());
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7fc39a4b35a8db44592cddc0b365942f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -17,6 +17,8 @@ using UnityEngine.UI;
using UnityEngine.SceneManagement;
using UnityEngine.Rendering;
using System.IO;
using System.Linq.Expressions;
using Cysharp.Threading.Tasks.Sample;
// using DG.Tweening;
@@ -133,6 +135,7 @@ public class SandboxMain : MonoBehaviour
UniTaskCompletionSource ucs;
async UniTask<int> FooAsync()
{
// use F10, will crash.
var loop = int.Parse("9");
await UniTask.DelayFrame(loop);
@@ -190,6 +193,9 @@ public class SandboxMain : MonoBehaviour
async UniTask RunStandardDelayAsync()
{
UnityEngine.Debug.Log("DEB");
await UniTask.DelayFrame(30);
@@ -220,6 +226,11 @@ public class SandboxMain : MonoBehaviour
async UniTaskVoid Update2()
{
UnityEngine.Debug.Log("async linq!");
await UniTaskAsyncEnumerable.Range(1, 10)
@@ -417,15 +428,15 @@ public class SandboxMain : MonoBehaviour
Debug.Log("after");
}
private async UniTaskVoid ExecuteAsync()
{
var req = UnityWebRequest.Get("https://google.com/");
//private async UniTaskVoid ExecuteAsync()
//{
// var req = UnityWebRequest.Get("https://google.com/");
var v = await req.SendWebRequest().ToUniTask();
// req.Dispose();
Debug.Log($"{v.isDone} {v.isHttpError} {v.isNetworkError}");
Debug.Log(v.downloadHandler.text);
}
// var v = await req.SendWebRequest().ToUniTask();
// // req.Dispose();
// Debug.Log($"{v.isDone} {v.isHttpError} {v.isNetworkError}");
// Debug.Log(v.downloadHandler.text);
//}
private async void Go()
{
await UniTask.DelayFrame(0);
@@ -455,7 +466,7 @@ public class SandboxMain : MonoBehaviour
private void Awake()
{
PlayerLoopInfo.Inject();
// PlayerLoopInfo.Inject();
PrepareCamera();
}
@@ -473,226 +484,82 @@ public class SandboxMain : MonoBehaviour
});
}
async void RunStandardTaskAsync()
{
Debug.Log("Wait 3 seconds");
await Task.Delay(TimeSpan.FromSeconds(3));
Debug.Log("Current SyncContext:" + SynchronizationContext.Current.GetType().FullName);
}
async UniTask QuitCheck()
{
try
{
await UniTask.Delay(TimeSpan.FromMinutes(1), cancellationToken: quitSource.Token);
}
finally
{
Debug.Log("End QuitCheck async");
}
}
CancellationTokenSource quitSource = new CancellationTokenSource();
IEnumerator TestCor()
{
Debug.Log("start cor");
yield return null;
yield return new WaitForEndOfFrame();
Debug.Log("end cor");
}
IEnumerator LastYieldCore()
{
Debug.Log("YieldBegin:" + Time.frameCount);
yield return new WaitForEndOfFrame();
Debug.Log("YieldEnd:" + Time.frameCount);
}
private static async UniTask TestAsync(CancellationToken ct)
{
Debug.Log("TestAsync Start.");
var count = 0;
while (!ct.IsCancellationRequested)
{
try
{
Debug.Log($"TestAsync try count:{++count}");
var task1 = new WaitUntil(() => UnityEngine.Random.Range(0, 10) == 0).ToUniTask();
var task2 = new WaitUntil(() => UnityEngine.Random.Range(0, 10) == 0).ToUniTask();
var task3 = new WaitUntil(() => UnityEngine.Random.Range(0, 10) == 0).ToUniTask();
await UniTask.WhenAny(task1, task2, task3);
}
catch (Exception e)
{
Debug.LogError(e);
return;
}
}
Debug.Log("TestAsync Finished.");
}
async UniTaskVoid Start()
{
var url = "http://google.com/404";
var webRequestAsyncOperation = UnityWebRequest.Get(url).SendWebRequest();
await webRequestAsyncOperation.ToUniTask();
var cts = new CancellationTokenSource();
//PlayerLoopInfo.Inject();
//_ = AsyncFixedUpdate();
//StartCoroutine(CoroutineFixedUpdate());
//StartCoroutine(TestCoroutine().ToCoroutine());
// Application.logMessageReceived += Application_logMessageReceived;
// var rp = new AsyncReactiveProperty<int>();
// rp.AddTo(this.GetCancellationTokenOnDestroy());
//var cts = new CancellationTokenSource();
// UniTask.Post(
// CancellationToken.
//UniTask.Delay(TimeSpan.FromSeconds(3)).
//okButton.onClick.AddListener(UniTask.UnityAction(async () =>
//{
// _ = ExecuteAsync();
// await UniTask.Yield();
// //await DelayCheck();
// /*
// UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType);
// StartCoroutine(CoroutineRun());
// StartCoroutine(CoroutineRun2());
// _ = AsyncRun();
// _ = AsyncLastUpdate();
// _ = AsyncLastLast();
// */
// //await UniTask.Yield();
// //_ = Test2();
// // EarlyUpdate.ExecuteMainThreadJobs
// // _ = Test2();
// //var t = await Resources.LoadAsync<TextAsset>(Application.streamingAssetsPath + "test.txt");
// //Debug.Log("LoadEnd" + PlayerLoopInfo.CurrentLoopType + ", " + (t != null));
// //Debug.Log("LoadEnd" + PlayerLoopInfo.CurrentLoopType + ", " + ((TextAsset)t).text);
// //await UniTask.Yield(PlayerLoopTiming.LastUpdate);
// //UnityEngine.Debug.Log("after update:" + Time.frameCount);
// ////await UniTask.NextFrame();
// ////await UniTask.Yield();
// ////UnityEngine.Debug.Log("after update nextframe:" + Time.frameCount);
// //StartCoroutine(CoroutineRun2());
// ////StartCoroutine(CoroutineRun());
// //UnityEngine.Debug.Log("FOO?");
// //_ = DelayFrame3_Pre();
// //await UniTask.Yield();
//}));
//cancelButton.onClick.AddListener(UniTask.UnityAction(async () =>
//{
// _ = DelayFrame3_Post();
// await UniTask.Yield();
// //await UniTask.Yield(PlayerLoopTiming.LastPreUpdate);
// //UnityEngine.Debug.Log("before update:" + Time.frameCount);
// //await UniTask.NextFrame();
// //await UniTask.Yield();
// //UnityEngine.Debug.Log("before update nextframe:" + Time.frameCount);
// //StartCoroutine(CoroutineRun());
// //UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType);
// //_ = Yieldding();
// //var cts = new CancellationTokenSource();
// //UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType + ":" + Time.frameCount);
// //var la = SceneManager.LoadSceneAsync("Scenes/ExceptionExamples").WithCancellation(cts.Token);
// ////cts.Cancel();
// //await la;
// //UnityEngine.Debug.Log("End LoadSceneAsync" + PlayerLoopInfo.CurrentLoopType + ":" + Time.frameCount);
//}));
//return;
//await UniTask.SwitchToMainThread();
//UniTaskAsyncEnumerable.EveryValueChanged(mcc, x => x.MyProperty)
// .Do(_ => { }, () => Debug.Log("COMPLETED"))
// .ForEachAsync(x =>
// {
// Debug.Log("VALUE_CHANGED:" + x);
// })
// .Forget();
//_ = Test1();
//Test2().Forget();
//StartCoroutine(Test3("https://bing.com/"));
//bool flip = false;
//var rect = cancelButton.GetComponent<RectTransform>();
//var cts = new CancellationTokenSource();
//var ct = cts.Token;
//okButton.onClick.AddListener(UniTask.UnityAction(async () =>
//{
// await rect.DOMoveX(10f * (flip ? -1 : 1), 3).OnUpdate(() => { Debug.Log("UPDATE YEAH"); }).WithCancellation(ct);
// flip = !flip;
// // ok.
//}));
//cancelButton.onClick.AddListener(() =>
//{
// cts.Cancel();
//});
// DG.Tweening.Core.TweenerCore<int>
//Debug.Log("GO MOVEX");
//await okButton.GetComponent<RectTransform>().DOMoveX(-10.2f, 3).WithCancellation(CancellationToken.None);
//Debug.Log("END MOVEX");
//Debug.Log("AGAIN MOVE");
//await okButton.GetComponent<RectTransform>().DOMoveY(10.2f, 3).WithCancellation(CancellationToken.None);
//Debug.Log("AGAIN END MOVE");
//Debug.Log(Test().GetType().FullName);
// check stacktrace
// await UniTaskAsyncEnumerable.EveryUpdate().Where((x, i) => i % 2 == 0).Select(x => x).DistinctUntilChanged().ForEachAsync(x =>
//{
// Debug.Log("test");
//});
//// DOTween.To(
//var cts = new CancellationTokenSource();
////var tween = okButton.GetComponent<RectTransform>().DOLocalMoveX(100, 5.0f);
//cancelButton.OnClickAsAsyncEnumerable().ForEachAsync(_ =>
//{
// cts.Cancel();
//}).Forget();
//// await tween.ToUniTask(TweenCancelBehaviour.KillAndCancelAwait, cts.Token);
////tween.SetRecyclable(true);
//Debug.Log("END");
//// tween.Play();
//// DOTween.
//// DOVirtual.Float(0, 1, 1, x => { }).ToUniTask();
//await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate())
//{
// Debug.Log("Update() " + Time.frameCount);
//}
//await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsync(_ =>
//{
//});
//okButton.OnClickAsAsyncEnumerable().ForEachAsync(_ =>
//{
//foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
//{
// Debug.Log(type + ":" + size);
//}
//}).Forget();
//CloseAsync(this.GetCancellationTokenOnDestroy()).Forget();
//okButton.onClick.AddListener(UniTask.UnityAction(async () => await UniTask.Yield()));
//UpdateUniTask().Forget();
//StartCoroutine(Coroutine());
//await UniTask.Delay(TimeSpan.FromSeconds(1));
// _ = ReturnToMainThreadTest();
//GameObject.Destroy(this.gameObject);
TestAsync(cts.Token).Forget();
okButton.onClick.AddListener(UniTask.UnityAction(async () =>
{
cts.Cancel();
await UniTask.Yield();
}));
await UniTask.Yield();
}
private void Application_logMessageReceived2(string condition, string stackTrace, LogType type)
@@ -1051,6 +918,17 @@ public class SandboxMain : MonoBehaviour
}
}
public class SyncContextInjecter
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
public static void Inject()
{
SynchronizationContext.SetSynchronizationContext(new UniTaskSynchronizationContext());
}
}
public class PlayerLoopInfo
{
// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
@@ -1085,7 +963,7 @@ public class PlayerLoopInfo
public static Type CurrentLoopType { get; private set; }
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
public static void Inject()
{
var system = PlayerLoop.GetCurrentPlayerLoop();

View File

@@ -55,6 +55,8 @@ namespace Cysharp.Threading.TasksTests
}
}
#if !UNITY_WEBGL
[UnityTest]
public IEnumerator DelayAnd() => UniTask.ToCoroutine(async () =>
{
@@ -76,6 +78,8 @@ namespace Cysharp.Threading.TasksTests
}
});
#endif
[UnityTest]
public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
{
@@ -183,6 +187,8 @@ namespace Cysharp.Threading.TasksTests
diff.Should().Be(11);
});
#if !UNITY_WEBGL
[UnityTest]
public IEnumerator SwitchTo() => UniTask.ToCoroutine(async () =>
{
@@ -215,6 +221,8 @@ namespace Cysharp.Threading.TasksTests
currentThreadId.Should().Be(switchedThreadId2);
});
#endif
//[UnityTest]
//public IEnumerator ObservableConversion() => UniTask.ToCoroutine(async () =>
//{

View File

@@ -0,0 +1,196 @@
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine.UI;
using UnityEngine.Scripting;
using Cysharp.Threading.Tasks;
using Unity.Collections;
using System.Threading;
using NUnit.Framework;
using UnityEngine.TestTools;
using FluentAssertions;
namespace Cysharp.Threading.TasksTests
{
public class CoroutineToUniTaskTest
{
[UnityTest]
public IEnumerator EarlyUpdate() => UniTask.ToCoroutine(async () =>
{
await UniTask.Yield(PlayerLoopTiming.EarlyUpdate);
var l = new List<(int, int)>();
var currentFrame = Time.frameCount;
var t = Worker(l).ToUniTask();
l.Count.Should().Be(1);
l[0].Should().Be((0, currentFrame));
await t;
l[1].Should().Be((1, Time.frameCount));
l[1].Item2.Should().NotBe(currentFrame);
});
[UnityTest]
public IEnumerator LateUpdate() => UniTask.ToCoroutine(async () =>
{
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
var l = new List<(int, int)>();
var currentFrame = Time.frameCount;
var t = Worker(l).ToUniTask();
l.Count.Should().Be(1);
l[0].Should().Be((0, currentFrame));
await t;
l[1].Should().Be((1, Time.frameCount));
l[1].Item2.Should().NotBe(currentFrame);
});
//[UnityTest]
//public IEnumerator TestCoroutine()
//{
// yield return UniTask.Yield(PlayerLoopTiming.EarlyUpdate).ToUniTask().ToCoroutine();
// var nanika = (UnityEngine.MonoBehaviour)GameObject.FindObjectOfType(typeof(UnityEngine.MonoBehaviour));
// var l = new List<(int, int)>();
// var currentFrame = Time.frameCount;
// var t = nanika.StartCoroutine(Worker(l));
// l.Count.Should().Be(1);
// l[0].Should().Be((0, currentFrame));
// yield return t;
// l[1].Should().Be((1, Time.frameCount));
// l[1].Item2.Should().NotBe(currentFrame);
//}
//[UnityTest]
//public IEnumerator TestCoroutine2()
//{
// yield return UniTask.Yield(PlayerLoopTiming.PostLateUpdate).ToUniTask().ToCoroutine();
// var nanika = (UnityEngine.MonoBehaviour)GameObject.FindObjectOfType(typeof(UnityEngine.MonoBehaviour));
// var l = new List<(int, int)>();
// var currentFrame = Time.frameCount;
// var t = nanika.StartCoroutine(Worker(l));
// l.Count.Should().Be(1);
// l[0].Should().Be((0, currentFrame));
// yield return t;
// l[1].Should().Be((1, Time.frameCount));
// l[1].Item2.Should().NotBe(currentFrame);
//}
[UnityTest]
public IEnumerator ImmediateRunTest() => UniTask.ToCoroutine(async () =>
{
var l = new List<int>();
var x1 = Immediate(l);
var x2 = Immediate(l);
var x3 = DelayOne(l);
var t1 = x1.ToUniTask();
CollectionAssert.AreEqual(l, new[] { 1, 2, 3 });
await t1;
var t2 = x2.ToUniTask();
CollectionAssert.AreEqual(l, new[] { 1, 2, 3, 1, 2, 3 });
var t3 = x3.ToUniTask();
CollectionAssert.AreEqual(l, new[] { 1, 2, 3, 1, 2, 3, 10 });
await UniTask.WhenAll(t2, t3);
CollectionAssert.AreEqual(l, new[] { 1, 2, 3, 1, 2, 3, 10, 20, 30 });
});
IEnumerator Immediate(List<int> l)
{
l.Add(1);
l.Add(2);
l.Add(3);
yield break;
}
IEnumerator DelayOne(List<int> l)
{
l.Add(10);
yield return null;
l.Add(20);
l.Add(30);
}
#if !UNITY_WEBGL
[UnityTest]
public IEnumerator WaitForSecondsTest() => UniTask.ToCoroutine(async () =>
{
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
Time.timeScale = 0.5f;
try
{
var now = DateTimeOffset.UtcNow;
await WaitFor();
var elapsed = DateTimeOffset.UtcNow - now;
(5.8f <= elapsed.TotalSeconds && elapsed.TotalSeconds <= 6.2f).Should().BeTrue();
}
finally
{
Time.timeScale = 1.0f;
}
});
IEnumerator WaitFor()
{
yield return new WaitForSeconds(3.0f);
}
#endif
IEnumerator Worker(List<(int, int)> l)
{
l.Add((0, Time.frameCount));
yield return null;
l.Add((1, Time.frameCount));
}
public async UniTask Foo()
{
var tasks = new List<UniTask>();
var t = Bar<int>();
tasks.Add(t);
t = Bar<int>();
tasks.Add(t);
await UniTask.WhenAll(tasks);
}
public async UniTask<T> Bar<T>()
{
await UniTask.Yield();
return default(T);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 43ffb719370515746932af3732ce073e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -175,6 +175,7 @@ namespace Cysharp.Threading.TasksTests
}
});
#if !UNITY_WEBGL
[UnityTest]
public IEnumerator DelayInThreadPool() => UniTask.ToCoroutine(async () =>
@@ -185,6 +186,8 @@ namespace Cysharp.Threading.TasksTests
});
});
#endif
[UnityTest]
public IEnumerator DelayRealtime() => UniTask.ToCoroutine(async () =>
{
@@ -200,5 +203,16 @@ namespace Cysharp.Threading.TasksTests
okay1.Should().Be(true);
okay2.Should().Be(true);
});
[UnityTest]
public IEnumerator LoopTest() => UniTask.ToCoroutine(async () =>
{
for (int i = 0; i < 20; ++i)
{
UniTask.DelayFrame(100).Forget();
await UniTask.DelayFrame(1);
}
});
}
}

View File

@@ -37,6 +37,7 @@ namespace Cysharp.Threading.TasksTests
{
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
#if !UNITY_WSA
#if !UNITY_WEBGL
//[UnityTest]
//public IEnumerator RunThread() => UniTask.ToCoroutine(async () =>
@@ -88,7 +89,7 @@ namespace Cysharp.Threading.TasksTests
}
});
#endif
#endif
#endif
}

View File

@@ -0,0 +1,43 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &1
MonoBehaviour:
m_ObjectHideFlags: 61
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_EnablePreviewPackages: 0
m_EnablePackageDependencies: 0
m_AdvancedSettingsExpanded: 1
m_ScopedRegistriesSettingsExpanded: 1
oneTimeWarningShown: 0
m_Registries:
- m_Id: main
m_Name:
m_Url: https://packages.unity.com
m_Scopes: []
m_IsDefault: 1
m_Capabilities: 7
m_UserSelectedRegistryName:
m_UserAddingNewScopedRegistry: 0
m_RegistryInfoDraft:
m_ErrorMessage:
m_Original:
m_Id:
m_Name:
m_Url:
m_Scopes: []
m_IsDefault: 0
m_Capabilities: 0
m_Modified: 0
m_Name:
m_Url:
m_Scopes:
-
m_SelectedScopeIndex: 0

View File

@@ -111,6 +111,8 @@ PlayerSettings:
switchNVNShaderPoolsGranularity: 33554432
switchNVNDefaultPoolsGranularity: 16777216
switchNVNOtherPoolsGranularity: 16777216
stadiaPresentMode: 0
stadiaTargetFramerate: 0
vulkanNumSwapchainBuffers: 3
vulkanEnableSetSRGBWrite: 0
m_SupportedAspectRatios:
@@ -191,22 +193,6 @@ PlayerSettings:
uIStatusBarHidden: 1
uIExitOnSuspend: 0
uIStatusBarStyle: 0
iPhoneSplashScreen: {fileID: 0}
iPhoneHighResSplashScreen: {fileID: 0}
iPhoneTallHighResSplashScreen: {fileID: 0}
iPhone47inSplashScreen: {fileID: 0}
iPhone55inPortraitSplashScreen: {fileID: 0}
iPhone55inLandscapeSplashScreen: {fileID: 0}
iPhone58inPortraitSplashScreen: {fileID: 0}
iPhone58inLandscapeSplashScreen: {fileID: 0}
iPadPortraitSplashScreen: {fileID: 0}
iPadHighResPortraitSplashScreen: {fileID: 0}
iPadLandscapeSplashScreen: {fileID: 0}
iPadHighResLandscapeSplashScreen: {fileID: 0}
iPhone65inPortraitSplashScreen: {fileID: 0}
iPhone65inLandscapeSplashScreen: {fileID: 0}
iPhone61inPortraitSplashScreen: {fileID: 0}
iPhone61inLandscapeSplashScreen: {fileID: 0}
appleTVSplashScreen: {fileID: 0}
appleTVSplashScreen2x: {fileID: 0}
tvOSSmallIconLayers: []
@@ -557,7 +543,7 @@ PlayerSettings:
platformArchitecture: {}
scriptingBackend:
Android: 1
Standalone: 0
Standalone: 1
il2cppCompilerConfiguration: {}
managedStrippingLevel: {}
incrementalIl2cppBuild: {}
@@ -566,7 +552,8 @@ PlayerSettings:
scriptingRuntimeVersion: 1
gcIncremental: 0
gcWBarrierValidation: 0
apiCompatibilityLevelPerPlatform: {}
apiCompatibilityLevelPerPlatform:
Standalone: 6
m_RenderingPath: 1
m_MobileRenderingPath: 1
metroPackageName: Template_2D
@@ -621,6 +608,7 @@ PlayerSettings:
XboxOnePersistentLocalStorageSize: 0
XboxOneXTitleMemory: 8
XboxOneOverrideIdentityName:
XboxOneOverrideIdentityPublisher:
vrEditorSettings:
daydream:
daydreamIconForeground: {fileID: 0}

View File

@@ -1,2 +1,2 @@
m_EditorVersion: 2019.3.9f1
m_EditorVersionWithRevision: 2019.3.9f1 (e6e740a1c473)
m_EditorVersion: 2020.2.1f1
m_EditorVersionWithRevision: 2020.2.1f1 (270dd8c3da1c)

View File

@@ -0,0 +1,8 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!890905787 &1
VersionControlSettings:
m_ObjectHideFlags: 0
m_Mode: Visible Meta Files
m_CollabEditorSettings:
inProgressEnabled: 1

View File

@@ -0,0 +1,24 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!162 &1
EditorUserSettings:
m_ObjectHideFlags: 0
serializedVersion: 4
m_ConfigSettings:
RecentlyUsedScenePath-0:
value: 22424703114646680e0b0227036c6c1118131a25340527392367083debf42d
flags: 0
vcSharedLogLevel:
value: 0d5e400f0650
flags: 0
m_VCAutomaticAdd: 1
m_VCDebugCom: 0
m_VCDebugCmd: 0
m_VCDebugOut: 0
m_SemanticMergeMode: 2
m_VCShowFailedCheckout: 1
m_VCOverwriteFailedCheckoutAssets: 1
m_VCProjectOverlayIcons: 1
m_VCHierarchyOverlayIcons: 1
m_VCOtherOverlayIcons: 1
m_VCAllowAsyncUpdate: 0