Compare commits

..

501 Commits

Author SHA1 Message Date
Ikiru Yoshizaki 06067cd4c8 ci: use Cysharp/Actions checkout instead of 3rd party directly 2025-03-19 15:44:16 +09:00
Ikiru Yoshizaki d9983cfe27
Merge pull request #654 from Cysharp/feature/pin_action
ci: Pinning third party GitHub Actions sha
2025-03-18 17:33:39 +09:00
Ikiru Yoshizaki 70eb7cd3ee ci: Pinning third party GitHub Actions sha 2025-03-18 16:58:33 +09:00
Ikiru Yoshizaki cc3c70af90 ci: update vault 2025-03-11 12:55:51 +09:00
Ikiru Yoshizaki 8042b29ff8 ci: extend timeout 2025-01-07 12:29:34 +09:00
Yoshifumi Kawai b0d01ca75f
Merge pull request #641 from hmkc/dev
Update README_CN.md
2024-12-12 16:35:32 +09:00
hmkc 7a63ab7088 Update README_CN.md
Re-translated the documentation.
2024-12-11 17:13:32 +08:00
Yoshifumi Kawai bdf102f145
Merge pull request #639 from hmkc/dev
Update README_CN.md
2024-12-09 20:18:43 +09:00
hmkc 41cea030ab Update README_CN.md 2024-12-09 18:39:08 +08:00
Yoshifumi Kawai f9fd769be7
Update README.md 2024-10-09 14:46:23 +09:00
neuecc 579304fe47 chore(docs): update TOC 2024-10-08 14:02:41 +00:00
Yoshifumi Kawai 740ca7ef01
Awaitable notes 2024-10-08 23:02:26 +09:00
github-actions[bot] 7c0f199fe0 feat: Update package.json to 2.5.10 2024-10-03 00:42:37 +00:00
neuecc 9a3ec31533 Merge remote-tracking branch 'origin/master' 2024-10-03 09:41:47 +09:00
neuecc 37d8f4f48e C# 8 #624 2024-10-03 09:41:30 +09:00
github-actions[bot] 27a0c06ede feat: Update package.json to 2.5.9 2024-10-01 06:16:52 +00:00
neuecc e4082ecd75 Merge remote-tracking branch 'origin/master' 2024-10-01 14:59:02 +09:00
neuecc 3b0fd784ff meta 2024-10-01 14:58:57 +09:00
neuecc dc9ebfd765 Add AsyncInstantiateOperation.WithCancellation, ToUniTask for Unity 2022.3.20/Unity 2022.3 or newer 2024-10-01 14:50:13 +09:00
Yoshifumi Kawai 005c83fbd7
Merge pull request #623 from Cysharp/feature/ci
ci: remove Unity 2021 LTS from Build matrix
2024-10-01 10:44:30 +09:00
github-actions[bot] 5984b67ecb feat: Update package.json to 2.5.8 2024-09-30 11:45:57 +00:00
neuecc 05fdf48058 Merge remote-tracking branch 'origin/master' 2024-09-30 20:43:06 +09:00
Yoshifumi Kawai 647ed6ff82
Merge pull request #621 from dvsilch/master
add generic type UnityAction support & fix typo
2024-09-30 20:42:57 +09:00
neuecc 0826b7e976 Add UniTask.WhenEach 2024-09-30 20:42:17 +09:00
dvsilch a51632cd4b fix: add overloads for CancellationToken 2024-09-30 10:13:29 +08:00
dvsilch bf945a7ef4 fix: rename parameters and type parameters 2024-09-30 10:11:36 +08:00
dvsilch 353f15e94f fix: add .Forget() call and rename paramters to keep coding style consistent 2024-09-27 23:45:05 +08:00
dvsilch cf19f18662 fix: typo 2024-09-27 22:57:36 +08:00
dvsilch fdb9d1cf95 feature: add overload in UniTask.UnityAction to support generic type UnityAction 2024-09-27 22:57:05 +08:00
Yoshifumi Kawai 2e0917428b
Merge pull request #620 from albermotion/master
Added a note for users using Unity 2023.1 or newer to prevent compilation error
2024-09-27 09:48:04 +09:00
Alberto 0b16005f4b Added a note for users using Unity 2023.1 or newer 2024-09-26 22:33:37 +02:00
github-actions[bot] 74bbe87b58 feat: Update package.json to 2.5.7 2024-09-26 06:24:48 +00:00
Yoshifumi Kawai 6f4131539b
Merge pull request #619 from kochounoyume/add-state-argument
Add overload in UniTask.WaitUntil, UniTask.WaitWhile and UniTask.Defer
2024-09-26 12:52:56 +09:00
Kochoyume 06283f0ffb Add to the code that I forgot to write 2024-09-24 23:58:28 +09:00
Kochoyume dfe5ee43c2 Add overload in UniTask.WaitUntil, UniTask.WaitWhile and UniTask.Defer to avoid closure allocation 2024-09-24 23:37:44 +09:00
Ikiru Yoshizaki eaa553dc83 ci: remove Unity 2021 LTS from Build matrix 2024-09-24 18:30:54 +09:00
github-actions[bot] 83d8a2b424 feat: Update package.json to 2.5.6 2024-09-24 09:20:07 +00:00
Yoshifumi Kawai 4d204e4aa6
Update build-debug.yml 2024-09-24 17:49:34 +09:00
Yoshifumi Kawai 87e164e275
Merge pull request #600 from Iblis/textmeshpro_unity6_adoptions
TextMeshPro Unity6 adoptions
2024-09-24 17:16:20 +09:00
Yoshifumi Kawai b63eb8d090
Merge pull request #610 from LucianoPC/forget-ignored-task
Forget ignored task
2024-09-24 17:14:40 +09:00
neuecc 75119acb50 Fix WaitWhile(bool cancelImmediately = true) does not work correctly
, and when cancelImmediately = true and canceled task remove Trakcking immediately
2024-09-24 17:12:50 +09:00
Ikiru Yoshizaki f7b3c2fbe1 ci: update release unity version to 2022.3.39f1 [skip ci] 2024-09-18 17:06:00 +09:00
Ikiru Yoshizaki b317ecfa01 ci: set max-parallel to restrict up to 2 run [skip ci] 2024-09-18 16:27:42 +09:00
Ikiru Yoshizaki 7b05569ef7 ci: single job 2024-09-18 16:27:42 +09:00
Luciano Prestes Cavalcanti e0465c6c2c Forget ignored task 2024-08-19 08:18:57 -03:00
Yoshifumi Kawai 9057452c86
Merge pull request #607 from Redpenguine/master
fix: incorrect download progress reporting
2024-08-14 19:54:15 +09:00
Mykyta Myronenko a2f6f84bde
fix: incorrect download progress reporting 2024-08-10 19:29:54 +03:00
Ikiru Yoshizaki f057abff0f chore: remove Unity UserSettigs from git handling 2024-07-31 16:11:03 +09:00
Ikiru Yoshizaki c61a7d9961
Merge pull request #605 from Cysharp/dependabot/github_actions/peaceiris/actions-gh-pages-4
chore(deps): bump peaceiris/actions-gh-pages from 3 to 4
2024-07-30 17:09:56 +09:00
Ikiru Yoshizaki 9587f2eeec
Merge pull request #606 from Cysharp/dependabot/github_actions/technote-space/toc-generator-4.3.1
chore(deps): bump technote-space/toc-generator from 2.4.0 to 4.3.1
2024-07-30 17:09:49 +09:00
Ikiru Yoshizaki 550784f31c ci: check not dependabot 2024-07-30 17:07:43 +09:00
Ikiru Yoshizaki 11b3282b3d ci: Don't allow dependabot to run Unity CI 2024-07-30 17:01:09 +09:00
dependabot[bot] b2532b0798
chore(deps): bump technote-space/toc-generator from 2.4.0 to 4.3.1
Bumps [technote-space/toc-generator](https://github.com/technote-space/toc-generator) from 2.4.0 to 4.3.1.
- [Release notes](https://github.com/technote-space/toc-generator/releases)
- [Changelog](https://github.com/technote-space/toc-generator/blob/main/.releasegarc)
- [Commits](https://github.com/technote-space/toc-generator/compare/v2.4.0...v4.3.1)

---
updated-dependencies:
- dependency-name: technote-space/toc-generator
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-30 06:42:27 +00:00
dependabot[bot] 4fc41ecb17
chore(deps): bump peaceiris/actions-gh-pages from 3 to 4
Bumps [peaceiris/actions-gh-pages](https://github.com/peaceiris/actions-gh-pages) from 3 to 4.
- [Release notes](https://github.com/peaceiris/actions-gh-pages/releases)
- [Changelog](https://github.com/peaceiris/actions-gh-pages/blob/main/CHANGELOG.md)
- [Commits](https://github.com/peaceiris/actions-gh-pages/compare/v3...v4)

---
updated-dependencies:
- dependency-name: peaceiris/actions-gh-pages
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-30 06:42:24 +00:00
Ikiru Yoshizaki e52663cef6
Merge pull request #570 from Cysharp/ci/dependabot
ci: add depndabot.yaml to update github-actions
2024-07-30 15:41:57 +09:00
Ikiru Yoshizaki 1827be2de7
Merge pull request #603 from Cysharp/feature/test
ci: Change Unity UnitTest from Mono to IL2CPP and enabled on CI
2024-07-30 15:41:47 +09:00
Ikiru Yoshizaki 8560561ef3 chore: remove Layouts for Unity2022 2024-07-30 15:17:05 +09:00
Ikiru Yoshizaki 2019f1fa7f ci: update CI Unity 2024-07-30 15:16:54 +09:00
Ikiru Yoshizaki 9e2265d148 ci: split job for build and test 2024-07-30 14:46:59 +09:00
Ikiru Yoshizaki 9d02279822 ci: extend Unity Build timeout to 20m 2024-07-30 14:25:31 +09:00
Ikiru Yoshizaki 17ce06d93c chore: remove com.unity.ai.navigation as 2021.3.39f1 failed and not inuse 2024-07-30 14:07:59 +09:00
Ikiru Yoshizaki 7b810413fe fix: missing include Packages folder for Unity 2024-07-30 14:00:06 +09:00
Ikiru Yoshizaki d78c0d6c02 ci: remove garbage folder 2024-07-30 12:36:34 +09:00
Ikiru Yoshizaki d248acc7d1 chore: bump RuntimeUnitTestToolkit to 2.6.0 to adapt Unity 2022 Build API change 2024-07-30 12:34:46 +09:00
Ikiru Yoshizaki 4c0b1f753a chore: bump UniTask unity version to 2022.3.39f1 2024-07-24 20:11:26 +09:00
Ikiru Yoshizaki 7bb6feda55 chore: switch to src/UniTask/*.cproj 2024-07-24 20:10:17 +09:00
Ikiru Yoshizaki f8a501290a chore: upload 2024-07-24 20:08:11 +09:00
Ikiru Yoshizaki cb497c9eb5 ci: bump Unity version to LTS, Unity Unit Test with LTS. 2024-07-24 19:35:31 +09:00
Ikiru Yoshizaki 342a37a074 ci: add strategy.fail-fast: false 2024-07-24 17:44:49 +09:00
Ikiru Yoshizaki c3146ec74f ci: change Unit Test to IL2CPP 2024-07-24 17:24:01 +09:00
Ikiru Yoshizaki df16813fae ci: bump actions/checkout@v3 -> v4 2024-07-24 17:22:28 +09:00
Philipp Walser 8d98bbc7ba
added version contraint, tmp is part of ugui since v2.0.0 2024-07-12 19:05:38 +02:00
Philipp Walser bb095697f7
added versionDefine to set TMP support if ugui is found, too 2024-07-12 19:01:59 +02:00
github-actions[bot] cdf88c6a6a feat: Update package.json to 2.5.5 2024-07-01 06:47:55 +00:00
Yoshifumi Kawai 4eee2c9270
Merge pull request #599 from xfvvvv/patch-1
Fix onTextSelection and onEndTextSelection stack overflow issue
2024-07-01 15:45:30 +09:00
Chelal 222b1401f5
Fix onTextSelection and onEndTextSelection stack overflow issue 2024-06-29 21:51:27 +08:00
Ikiru Yoshizaki fbe0bf8515
Merge pull request #579 from Cysharp/feature/artifact
ci: change upload-artifact & download-artifact to Cysharp/Actions
2024-04-30 14:36:53 +09:00
Ikiru Yoshizaki 626685292c ci: set upload-artifact retention-period: 1 2024-04-30 14:06:49 +09:00
Ikiru Yoshizaki 67794f5cdd ci: change upload-artifact & download-artifact to Cysharp/Actions 2024-04-30 12:43:17 +09:00
Mayuki Sawatari 2e225fb841
Merge pull request #571 from Cysharp/feature/secret
feat: change load secret from op
2024-04-17 18:29:49 +09:00
Ikiru Yoshizaki 935523f25c feat: change load secret from op 2024-04-17 16:09:55 +09:00
Ikiru Yoshizaki 3b3f7ebd3e ci: add depndabot.yaml to update github-actions 2024-04-16 12:14:52 +09:00
Yoshifumi Kawai 88ecfa2992
Merge pull request #565 from divdeploy/master
chore: fix comment
2024-04-08 17:03:08 +09:00
divdeploy aa70fe5c5a chore: fix comment
Signed-off-by: divdeploy <chenguangxue@outlook.com>
2024-04-05 23:28:56 +08:00
Yoshifumi Kawai 0c8057c668
Merge pull request #538 from kochounoyume/argument-allowed-factory
Factory method allowed argument
2024-04-05 13:30:39 +09:00
Kochoyume 28867646b0 Fix T state as the first argument 2024-03-31 00:05:18 +09:00
Kochoyume ca60864021 Fix comment 2024-03-30 23:57:43 +09:00
Kochoyume 1e4561da22 Fix comment 2024-03-30 23:57:43 +09:00
Kochoyume 1496cc990a Fix misspelling 2024-03-30 23:57:43 +09:00
Kochoyume 7bb3bf8d0e Update UniTask.Create, Unitask.Action and UniTask.UnityAction 2024-03-30 23:57:43 +09:00
github-actions[bot] d057074f17 feat: Update package.json to 2.5.4 2024-03-28 06:22:15 +00:00
hadashiA b724a2aa84
Merge pull request #557 from Cysharp/hadashiA/fix-cancel-immediate
Fix unintended returning to pool with cancelImmediately
2024-03-28 15:21:40 +09:00
hadashiA 938ddd5356 Tweaks 2024-03-28 15:15:55 +09:00
hadashiA 911c37d4d8 Tweaks 2024-03-28 15:14:10 +09:00
hadashiA caccccb0b5 Fix timing to returning to pool 2024-03-28 15:00:41 +09:00
hadashiA a48f11d31b Fix unintended returning to pool with cancelImmediately (WaitUntil,etc) 2024-03-28 14:46:37 +09:00
hadashiA fcd93feb56 Fix unintended returning to pool with cancelImmediately (AsyncOperation) 2024-03-28 14:45:55 +09:00
hadashiA b472b23773 Fix unintended returning to pool with cancelImmediately (UniTask.Delay) 2024-03-28 14:29:30 +09:00
Yoshifumi Kawai 01c8fada1f
Merge pull request #542 from Cysharp/feature/new_release
ci: Cysharp/Actions/.github/workflows/create-release.yaml
2024-03-25 18:30:50 +09:00
Ikiru Yoshizaki 10cd137126 ci: Cysharp/Actions/.github/workflows/create-release.yaml 2024-02-07 21:04:48 +09:00
Yoshifumi Kawai 809d23edae
Merge pull request #541 from TORISOUP/fix_ToArray
Optimization of ToArray in Merge method
2024-02-04 23:19:47 +09:00
TORISOUP d38731bc44 fix call ToArray 2024-02-04 20:51:21 +09:00
github-actions[bot] 64792b672d feat: Update package.json to 2.5.3 2024-01-25 12:52:16 +00:00
neuecc 3892cc2299 Merge branch 'master' of https://github.com/Cysharp/UniTask 2024-01-25 21:51:45 +09:00
neuecc 5bfccaa3b6 autoReleaseWhenCancelled -> autoReleaseWhenCanceled 2024-01-25 21:51:40 +09:00
github-actions[bot] b49b7332bb feat: Update package.json to 2.5.2 2024-01-25 11:43:24 +00:00
neuecc 08184af737 simplify UNITASK define 2024-01-25 20:42:42 +09:00
Yoshifumi Kawai 0ef6c59385
Merge pull request #535 from kochounoyume/valuetask-error-netframework
UniTask does not work in .NET Framework environment after adding UniTask.AsValueTask
2024-01-25 20:26:21 +09:00
Yoshifumi Kawai 4b0bd3b509
Merge pull request #536 from Cysharp/hadashiA/release-handle
Fix "Release handle when cancellation is requested"
2024-01-25 20:26:13 +09:00
Kochoyume 9a4720d180 Fix:Addressed generic parameter attributes error in the .Net Framework 2024-01-25 20:05:45 +09:00
hadashiA b99646558c Add a flag autoReleaseWhenCancelled to addressable extensionhs 2024-01-25 19:03:00 +09:00
hadashiA ee12dd9ae7 Fix omissions in Addressable.Release and IsValid checks 2024-01-25 17:47:06 +09:00
Kochoyume 006e0f2c81 Revert "Fix conditional compilation to work with .NET Framework"
This reverts commit 7d31299b5c.
2024-01-25 16:16:01 +09:00
Kochoyume 7d31299b5c Fix conditional compilation to work with .NET Framework 2024-01-25 01:02:57 +09:00
Yoshifumi Kawai fe8bf834e6
Update README.md 2024-01-24 10:47:31 +09:00
github-actions[bot] 5843258e8c feat: Update package.json to 2.5.1 2024-01-24 01:44:21 +00:00
Yoshifumi Kawai 6cd002645e
Merge pull request #533 from doyasu24/feature/add-unitask-asvaluetask-for-unity
add UniTask.AsValueTask for Unity
2024-01-24 10:41:39 +09:00
doyasu24 4fe0861714
fix: use UNITY_2018_3_OR_NEWER instead of UNITY_2018_1_OR_NEWER 2024-01-19 23:42:17 +09:00
doyasu24 fbbba061dd
ValueTask is available in Unity 2021_2_OR_NEWER or .NET Core 2024-01-19 23:40:28 +09:00
doyasu24 81f2e37ea5
add UniTask.AsValueTask for Unity 2024-01-19 00:29:21 +09:00
Yoshifumi Kawai 66de0d3a58
Merge pull request #519 from Cysharp/guitarrapc-patch-1
Use Cysharp/Actions/setup-dotnet default version
2024-01-12 15:34:54 +09:00
Yoshifumi Kawai beb10abbf7
Merge pull request #524 from Saismirk/master
Fixed typo in TimeoutWithoutException summary.
2024-01-12 15:22:35 +09:00
Saismirk d60f64761b Fixed typo in TimeoutWithoutException summary. 2023-12-10 12:53:52 -05:00
Luciano Prestes Cavalcanti 36ac0863ad Release handle when cancellation is requested 2023-11-22 08:09:03 -03:00
Ikiru Yoshizaki 104f8e09ca
Update build-release.yml 2023-11-15 12:09:34 +09:00
Ikiru Yoshizaki cfbff008c4
Use Cysharp/Actions/setup-dotnet default version
## tl;dr;

It support both .NET 6,7 and 8. No need specify version.
2023-11-15 11:55:27 +09:00
Yoshifumi Kawai 5cc97c7f00
ReadMe 2023-11-02 18:23:43 +09:00
github-actions[bot] 5666292496 feat: Update package.json to 2.5.0 2023-11-02 05:03:47 +00:00
hadashiA 1288cbc128
Merge pull request #518 from Cysharp/hadashiA/awaitable-to-unitask
Add Awaitable.AsUniTask()
2023-11-02 14:02:36 +09:00
hadashiA 5f3aa18f38 Add Awaitable<T>.ToUniTask 2023-11-02 13:54:55 +09:00
hadashiA 0970ae8c31
Merge pull request #517 from Cysharp/hadashiA/cancel-immediately-flag
Add a flag to cancel immediately instead of player loop
2023-11-02 13:52:12 +09:00
hadashiA ad23f7fb29 Fix `#if` directive that should be in .tt 2023-11-02 12:54:59 +09:00
hadashiA a4be8f316e Add Awaitable.AsUniTask() 2023-11-02 12:35:05 +09:00
hadashiA 370425578f Fix AssetBundleRequestAll 2023-11-02 12:24:41 +09:00
hadashiA 1b76f77608 Add test 2023-11-02 12:24:41 +09:00
hadashiA 0579984355 Use AsyncOperation.completed callback usually 2023-11-02 12:24:41 +09:00
hadashiA 39cf81d2ab Add test for unity AsyncOperation 2023-11-02 12:24:41 +09:00
hadashiA 0a203c8db9 Cache Action in advance 2023-11-02 12:24:41 +09:00
hadashiA 55be4dba82 Add callback handler to AsyncOperationConfiguredSource instead to create other sources 2023-11-02 12:24:41 +09:00
hadashiA f0adf36633 Use AsyncOperation.completed handler with new optional argument 2023-11-02 12:24:41 +09:00
hadashiA 24afc4f3eb Add cancelImmediately flag for assetbundle extensions 2023-11-02 12:24:41 +09:00
hadashiA 2cf06af433 Change AsyncEnumerable factory to use OperationCanceledException 2023-11-02 12:24:36 +09:00
hadashiA 94be2e748b Add cancelImmediately flag for addressable extensions 2023-10-27 15:06:12 +09:00
hadashiA 7f582e5e29 Update README 2023-10-27 14:52:58 +09:00
hadashiA 3f042c8886 Add cancel immediate flag 2023-10-27 12:42:12 +09:00
Yoshifumi Kawai 6f5d818544
Merge pull request #515 from ananttheant/fix/readme-typo
Fix typo in README.md
2023-10-26 13:11:42 +09:00
Anant Sharma 2ccb37cb02
Fix typo in README.md 2023-10-25 15:24:27 +01:00
github-actions[bot] cfe509a556 feat: Update package.json to 2.4.1 2023-09-21 03:22:58 +00:00
hadashiA a46a4cac01
Merge pull request #509 from Cysharp/ku/fix-dotween-registration
Fix a bug in Dotween after returning to the pool of `CancellationToken.Register`
2023-09-21 12:20:43 +09:00
hadashiA 3ed28e534a Tweaks 2023-09-21 10:00:27 +09:00
hadashiA be8dbe8804 Fixed a bug in Dotween after returning to the pool of CancellationToken.Register 2023-09-20 17:35:04 +09:00
github-actions[bot] 64f7eec4e9 feat: Update package.json to 2.4.0 2023-09-14 07:43:41 +00:00
hadashiA 3a93f4a49f
Update README.md 2023-09-14 16:40:42 +09:00
hadashiA 71958adc3d
Merge pull request #498 from Cysharp/hadashiA/async-linq-merge
Add UniTaskAsyncEnumerable.Merge
2023-09-14 16:28:05 +09:00
hadashiA acc71550c9
Merge pull request #496 from Cysharp/hadashiA/awaitable
Add EndOfFrame implementation using `UnityEngine.Awaitable`
2023-09-14 16:26:40 +09:00
hadashiA 3ba64412f8 Reduce the lock 2023-09-14 16:26:13 +09:00
hadashiA 90c5e5a6ad
Merge pull request #500 from Cysharp/hadashiA/inner-ex2
Use always innerException for Task.AsUniTask
2023-09-14 16:22:09 +09:00
hadashiA 8a022ee02d
Merge pull request #499 from Cysharp/hadashiA/fix-completion-source-retval
Fix conditions for UniTaskCompletionSourceCore.TrySet* to be true
2023-09-14 16:12:32 +09:00
hadashiA 6a89ea8139
Merge pull request #503 from Cysharp/hadashiA/fix-auto-reset-source
Add check to that AutoResetUniTaskSource already returned to the pool
2023-09-14 16:10:42 +09:00
hadashiA 90c81613ac Add check to that innerException is empty 2023-09-14 16:08:32 +09:00
hadashiA 7c62904a74
Merge pull request #489 from Merglasch/EarlyInit
Earlier intialization for Unitask, depending on unity version
2023-09-14 15:43:34 +09:00
hadashiA 4f6344a12f Add check to AutoResetUniTaskSource already returned to the pool 2023-09-14 10:36:31 +09:00
hadashiA 937d3adf66 Fix race condition 2023-09-12 14:34:53 +09:00
hadashiA 3bac16229f Reduce lock 2023-09-11 00:22:21 +09:00
hadashiA ea57847c97 Add dispose 2023-09-09 17:04:02 +09:00
hadashiA 730d68132d Tweaks 2023-09-09 14:27:06 +09:00
hadashiA 6db872236e Fix test 2023-09-09 10:16:01 +09:00
hadashiA ba7e676c6f Fix test 2023-09-09 10:01:58 +09:00
hadashiA 6e99accf99 Fix race condition (todo: too wide lock range?) 2023-09-09 08:49:52 +09:00
hadashiA b195df9773 Update README 2023-09-08 20:06:02 +09:00
hadashiA f303d9d7e8 Add UniTaskAsyncEnumerable.Merge 2023-09-08 20:05:51 +09:00
hadashiA 62a2a2e8f9 Use always innerException for Task.AsUniTask 2023-09-08 18:43:41 +09:00
hadashiA ffbadbcc4c Update README about WaitForEndOfFrame 2023-09-08 18:00:39 +09:00
hadashiA 50ad2ee9d6 Fix conditions for UniTaskCompletionSourceCore.TrySet* to be true 2023-09-08 17:31:19 +09:00
hadashiA c170af5642
Merge pull request #487 from Cysharp/hadashiA/fix-test
Fix TriggerEvent problem with iterate breaking on Remove when it has multiple handlers
2023-09-08 17:22:16 +09:00
hadashiA 242bceecd3
Merge pull request #492 from Cysharp/hadashiA/readme
Update README about DOTween
2023-09-08 17:19:30 +09:00
hadashiA 06346b8a2a
Merge pull request #493 from Cysharp/hadashiA/monitor
Reduce the times of AwakeMonitor checking
2023-09-08 17:18:50 +09:00
hadashiA b071eeadfb
Merge pull request #486 from Cysharp/hadashiA/inner-ex
Use innerException for `Task.AsUniTask`
2023-09-08 17:17:28 +09:00
hadashiA e7f23d8328
Merge pull request #497 from Cysharp/hadashiA/net6
Use dotnet >= 6.0
2023-09-08 17:16:36 +09:00
hadashiA 47a3f09abf
Merge pull request #485 from Cysharp/hadashiA/fix-dotween
Fix a problem in dotween where an extra update would run after canceling
2023-09-08 17:16:20 +09:00
hadashiA a7a6af0a68 Fix AwakeMonitor to not duplicates 2023-09-08 09:36:42 +09:00
hadashiA f203b6c051
Merge pull request #494 from Cysharp/hadashiA/web-req-null-check
Add check if UnityWebRequest was destroyed
2023-09-07 18:13:46 +09:00
hadashiA 07211f1fc3 Bump dotnet version to 6.0/7.0 2023-09-07 18:12:47 +09:00
hadashiA bc27f6c0d8 Fix test 2023-09-07 17:43:40 +09:00
hadashiA c65f9c3497 Add EndOfFrame implementation using UnityEngine.Awaitable 2023-09-07 17:36:06 +09:00
hadashiA afe5f57adc Use CancellationToken.Register to cancel DOTWeen 2023-09-07 17:05:11 +09:00
hadashiA 9135c7ce56 Add check if UnityWebRequest was destroyed 2023-09-07 10:35:48 +09:00
hadashiA 7fae415689 Reduce the times of AwakeMonitor checking 2023-09-07 10:03:55 +09:00
hadashiA e5cc8667ac docs: update TOC 2023-09-07 00:58:51 +00:00
hadashiA 0ea18d0e16 Update README about DOTween 2023-09-07 09:58:00 +09:00
hadashiA 2d674999f0 Use InnerException when only be one 2023-09-07 09:11:47 +09:00
David Klein 3121903fa3 Earlier intialization for Unitask, depending on unity version 2023-09-04 11:00:50 +02:00
hadashiA af2e49aa29 Fix TriggerEvent.SetCancel 2023-09-03 20:40:04 +09:00
hadashiA 22940635fe Fix TriggerEvent problem with iterate breaking on Remove when it has multiple handlers 2023-09-02 22:26:09 +09:00
hadashiA c1042b32b7
Merge pull request #488 from Cysharp/revert-445-fix-wait-async
Revert "Fixed https://github.com/Cysharp/UniTask/issues/444"
2023-09-01 20:08:55 +09:00
hadashiA 29a144694d
Revert "Fixed https://github.com/Cysharp/UniTask/issues/444" 2023-09-01 19:52:22 +09:00
hadashiA 548d56e654
Merge pull request #445 from faveris/fix-wait-async
Fixed https://github.com/Cysharp/UniTask/issues/444
2023-09-01 17:06:31 +09:00
hadashiA 6fb4f2d6d2 Support exception unwrapping for AsUniTask of Task.WhenAll 2023-09-01 16:58:11 +09:00
hadashiA 8eac07ad24 Use innerException for Task.AsUniTask 2023-09-01 10:49:43 +09:00
hadashiA 716402a180 Fix a problem in dotween where an extra update would run after canceling 2023-08-31 19:22:17 +09:00
hadashiA 4c3d6938ed
Merge pull request #484 from Cysharp/hadashiA/fix-async-enumerable
Fix problem with finally in UniTaskAsyncEnumerable.Create not being executed
2023-08-31 19:17:31 +09:00
hadashiA b4486802f2 Fix problem with part of await foreach not executing on break 2023-08-31 12:42:53 +09:00
Yoshifumi Kawai d210e3d76a
Merge pull request #457 from sgaumin/wait-for-seconds
Add WaitForSeconds method declarations #371
2023-06-23 19:04:48 +09:00
Ikiru Yoshizaki f2773f585e Revert "Revert "Merge pull request #459 from Cysharp/feature/prevent""
This reverts commit 305695ad5d.
2023-05-08 12:22:51 +09:00
Ikiru Yoshizaki 305695ad5d Revert "Merge pull request #459 from Cysharp/feature/prevent"
This reverts commit 356a4ee62e.
2023-04-26 14:14:51 +09:00
Ikiru Yoshizaki 418ab36a72
Merge pull request #460 from Cysharp/feature/unity
chore: change unity build to cysharp actions
2023-04-26 12:35:55 +09:00
Ikiru Yoshizaki 563b4fbbd5 chore: remove license 2023-04-25 19:48:54 +09:00
Ikiru Yoshizaki 878d33115f chore: change unity build to cysharp actions 2023-04-25 19:39:40 +09:00
Ikiru Yoshizaki 356a4ee62e
Merge pull request #459 from Cysharp/feature/prevent
chote: prevent github workflow change
2023-04-25 18:50:19 +09:00
Ikiru Yoshizaki eb32ae25e0 chote: prevent github workflow change 2023-04-25 16:56:50 +09:00
Sébastien Gaumin 3ca4062536 Add WaitForSeconds method declarations #371 2023-04-07 00:33:31 +09:00
Artem Kolesnykov 019f8aaf30 Deleted preserveRemoveSelf because Remove() should always remove a trigger for pooling to work correctly 2023-02-12 17:33:19 +02:00
Artem Kolesnykov 663fa737f3 Added a test case for https://github.com/Cysharp/UniTask/issues/444 2023-02-12 17:33:19 +02:00
Yoshifumi Kawai 73d86259ce
f 2022-12-26 06:02:57 +09:00
Yoshifumi Kawai c7eedf85c7
Merge pull request #421 from ivribalko/master
fixed typo in README.md (AysncLocal)
2022-11-21 12:48:07 +09:00
Ivan Rybalko 8dc3ffd552 fixed typo (AysncLocal) 2022-11-10 20:28:07 +00:00
github-actions[bot] b992a061fb feat: Update package.json to 2.3.3 2022-11-01 11:41:05 +00:00
neuecc 4fc09a6f61 more 2022-11-01 20:40:24 +09:00
neuecc e57176a43c Merge remote-tracking branch 'origin/master' 2022-11-01 20:39:37 +09:00
Yoshifumi Kawai 710d0d9012
Merge pull request #418 from adarapata/fix-asyncenumerable-cancel
Fix UnityEventHandlerAsyncEnumerator cancellation
2022-11-01 20:39:22 +09:00
neuecc 039de3ef65 Text -> UnityEngine.UI.Text #401 2022-11-01 20:33:24 +09:00
imo de38f63a55 Token was incorrectly specified. 2022-11-01 20:12:55 +09:00
Yoshifumi Kawai 38f8193199
Update README.md 2022-10-25 13:51:57 +09:00
neuecc 15cffb7357 docs: update TOC 2022-10-25 04:50:13 +00:00
Yoshifumi Kawai d55748e05b
Merge pull request #374 from wqaetly/readme_cn
doc:provide simple chinese readme
2022-10-25 13:50:01 +09:00
NKG丶MadLife 5602861dd4
Update README_CN.md 2022-10-25 12:10:28 +08:00
github-actions[bot] d2245bc38b feat: Update package.json to 2.3.2 2022-10-24 12:59:01 +00:00
neuecc f092f6a112 Fix AwaitForAllAssets stackoverflow #394 2022-10-24 21:57:42 +09:00
neuecc ed617a04a6 if UNITY_2022_2_OR_NEWER, use destroyCancellationToken 2022-10-24 21:56:03 +09:00
neuecc 340736795c UnityEvent.AsAsyncEnumerable handle cancel correctly #365 2022-10-24 21:51:23 +09:00
neuecc dc804ffb13 breaking changes, UniTask.WaitForFixedUpdate wait at LastFixedUpdate #377 2022-10-24 21:18:36 +09:00
neuecc a879989d1c Unity 2023.1.0a15, remove AsyncOperation.GetAwaiter to avoid conflict 2022-10-24 21:11:49 +09:00
Yoshifumi Kawai ee54559532
Merge pull request #413 from battleroy/battleroy/observable-extension-cancellation-token-forwarding
Fix cancellation token forwarding
2022-10-12 10:53:30 +09:00
Evgeny Chasovitin 4a72ec2a1a Fix cancellation token forwarding at FirstValueToUniTaskObserver and ToUniTaskObserver; 2022-10-11 23:01:19 +04:00
Yoshifumi Kawai e999268305
Merge pull request #395 from kroonhorstdino/diagnostics_helper_null_check
Diagnostics helper null check
2022-09-30 18:49:10 +09:00
Yoshifumi Kawai 8300c1b1e6
Merge pull request #399 from shiena/fix/prevent-nre
fix: prevent NullReferenceException in TimeoutController
2022-09-30 18:04:57 +09:00
KOGA Mitsuhiro daa0c7b9a0 fix: prevent NullReferenceException in TimeoutController 2022-09-14 02:56:06 +09:00
KOGA Mitsuhiro 9b9a4ec76a fix: prevent NullReferenceException in TimeoutController 2022-09-08 15:12:05 +09:00
Christoph Hüter 3f8e43b83f
Remove empty line with spaces 2022-08-31 03:00:22 +02:00
Christoph Hüter cf6f0799e9
Remove spaces in empty line 2022-08-25 13:51:44 +02:00
Christoph Hüter 91ba4f003a
Add null checks to diagnostics helper functions 2022-08-25 13:48:52 +02:00
Yoshifumi Kawai f48cb4b03e
Merge pull request #392 from Ryuu-64/master
Typo in TaskPool.cs annotation internaly -> internally
2022-08-23 10:38:44 +09:00
DESKTOP-FQ830JM\Ryuu 70a243d978 typo in TaskPool.cs internaly -> internally 2022-08-22 12:20:52 +08:00
Yoshifumi Kawai 9b95f3b9f6
Merge pull request #385 from fpagyu/support-addressablesCN
re-define UNITASK_ADDRESSABLE_SUPPORT for supporting Addressables.CN
2022-08-02 10:46:39 +09:00
yuzj a25adb601b re-define UNITASK_ADDRESSABLE_SUPPORT for supporting Addressables.CN 2022-08-01 18:21:04 +08:00
Ikiru Yoshizaki e6240879c4 chore: checkout@v3 2022-07-26 15:34:50 +09:00
Ikiru Yoshizaki a94e8cceac
Merge pull request #381 from Cysharp/feature/actions
feat: use Cysharp/Actions reusable workflows/actions
2022-07-26 13:59:46 +09:00
Ikiru Yoshizaki 0595a4182a feat: use Cysharp/Actions for release 2022-07-26 13:10:40 +09:00
Ikiru Yoshizaki df96c119c1 chore: set timeout 2022-07-26 13:10:32 +09:00
Ikiru Yoshizaki 534f4a2588 chore: use Cysharp/Actions reusable workflow stale 2022-07-26 13:10:22 +09:00
Ikiru Yoshizaki 873485ad1a chore: IsPackable marking 2022-07-26 13:10:10 +09:00
NKG丶MadLife f6037d6c9b doc:provide simple chinese readme 2022-07-16 10:02:16 +08:00
Yoshifumi Kawai 78db78c7bd
Merge pull request #369 from nolimet/bugfix/issue#368
Fix for issue #368 UnityWebRequestException extra line in exception message
2022-06-23 17:46:29 +09:00
Jesse Stam 226e272787 Added simple check to see if there is text.
Add this so we don't add a new line we don't need.
2022-06-23 09:31:05 +02:00
neuecc 9e2163616b Merge remote-tracking branch 'origin/master' 2022-03-03 08:14:22 +09:00
neuecc 52df6fbf3f t 2022-03-03 08:14:18 +09:00
github-actions[bot] 33d32baea4 feat: Update package.json to 2.3.1 2022-03-02 23:02:44 +00:00
neuecc b0250cfe75 Merge remote-tracking branch 'origin/master' 2022-03-03 08:02:10 +09:00
neuecc 7a35f121cd build and analyzer 2022-03-03 08:02:06 +09:00
github-actions[bot] 79f1566fdb feat: Update package.json to 2.3.0 2022-03-02 22:56:07 +00:00
neuecc 364b67805d test ignore 2022-03-03 07:53:14 +09:00
neuecc 62ca0c6e92 Fix IObservable.ToUniTask does not propagate unobserved exception 2022-03-03 07:40:28 +09:00
neuecc f63212aa17 ReadMe more 2022-03-03 07:34:49 +09:00
neuecc 768fd16e60 UniTask,Run obsolete 2022-03-03 07:32:15 +09:00
neuecc 3c99010ba0 Add UniTask.WaitForEndOfFrame(MonoBehaviour), Obsolete no args. 2022-03-03 07:22:58 +09:00
neuecc 5c668717d8 Run DelayFrame on UnityEditor 2022-03-03 06:23:34 +09:00
Yoshifumi Kawai b089f74c65
Merge pull request #323 from SolidAlloy/throw-exception-fix
Fix exceptions never being reported when UniTask is executed without await and Forget()
2022-03-03 06:04:55 +09:00
Yoshifumi Kawai fcf4f21cc1
Merge pull request #300 from satanabe1/fix-bad_image_format_exception
Fix BadImageFormatException when StackTrace captured on UniTaskTracker
2022-03-03 05:34:07 +09:00
Yoshifumi Kawai ee2fd3e91d
Merge pull request #280 from yellowisher/master
[DoTween]: fix "AwaitFor~" extensions ignore original callback
2022-03-03 05:31:33 +09:00
Yoshifumi Kawai 27604496ca
Merge pull request #327 from cjaligaga/patch-1
Update README.md
2022-02-08 10:42:27 +09:00
cjaligaga 0d01034a57
Update README.md
grammar correction to avoid confusion
2021-11-09 13:00:26 +08:00
neuecc 50a67d8f41 r 2021-10-26 19:48:28 +09:00
Yoshifumi Kawai 2a23a85cdd
Update README.md 2021-10-26 00:31:02 +09:00
Yoshifumi Kawai e127d9976e
Update README.md 2021-10-25 22:51:58 +09:00
Artem Perepelitsa c31b78e45e Add a unit test to verify that an unawaited task reports exceptions 2021-10-16 12:18:52 +03:00
Artem Perepelitsa b6b0b4000d Add unobserved task exception publishing if an exception inside ExceptionResultSource is never thrown 2021-10-16 11:55:30 +03:00
neuecc 69be818a46 AnalyzeSymbol -> AnalyzeOperation 2021-08-27 16:51:39 +09:00
neuecc a1dee8b54f add experimental analyzer 2021-08-27 16:44:05 +09:00
Yoshifumi Kawai 4f6166102d
Merge pull request #285 from Cysharp/chore/xml
chore: add intellisense xml to nuget
2021-07-27 06:26:56 +09:00
Yoshifumi Kawai 6b1d2c231a
Merge pull request #284 from Cysharp/chore/setup_dotnet
chore: remove setup-dotnet
2021-07-27 06:26:47 +09:00
watanabe_satoshi 0715dd31bf fix BadImageFormatException 2021-07-05 18:41:31 +09:00
Ikiru Yoshizaki 26dbfa3655 chore: add intellisense xml to nuget 2021-05-24 14:38:36 +09:00
Ikiru Yoshizaki 6ac55e37a1 chore: remove setup-dotnet 2021-05-24 11:01:36 +09:00
Yoshifumi Kawai 60bfbae787
Update package.json 2021-05-11 10:07:23 +09:00
yellowisher fc9ddeb15c Fix typo 2021-05-08 14:40:02 +09:00
yellowisher dfe18d11ff [DoTween]: call original complete callback as well 2021-05-08 14:39:26 +09:00
Yoshifumi Kawai 958a8e11ab
Merge pull request #266 from oktomus/patch-1
Fix wording and code instructions for -batchmode usage
2021-04-21 16:51:30 +09:00
Kevin Masson 257186313b
Fix wording and code instructions for -batchmode usage
`Environment.Exit(0)` didn't work for me, the process was still running. `EditorApplication.Exit(0);` works and kills the `Unity Editor` process correctly.
2021-04-20 14:31:16 +02:00
Ikiru Yoshizaki 5bd508b31c shore: skip ci supported by default 2021-04-19 18:47:10 +09:00
Yoshifumi Kawai 9e18ba332e
Update README.md 2021-04-18 11:26:35 +09:00
Yoshifumi Kawai 946b9003f0
Update README.md 2021-04-13 19:03:58 +09:00
Yoshifumi Kawai ffa55becf3
Update README.md 2021-04-13 14:09:32 +09:00
github-actions[bot] 72e620d169 feat: Update package.json to 2.2.5 2021-04-06 04:33:51 +00:00
github-actions[bot] 10ebddf892 Revert "feat: Update package.json to 2.2.5"
This reverts commit 4fc9ca315e.
2021-04-06 13:33:09 +09:00
neuecc c51e45ee21 Merge remote-tracking branch 'origin/master' 2021-04-06 13:32:36 +09:00
neuecc 6968faf35b remove remove 2021-04-06 13:32:31 +09:00
github-actions[bot] 4fc9ca315e feat: Update package.json to 2.2.5 2021-04-06 04:24:55 +00:00
neuecc aaf1c0eaa1 assembly signed 2021-04-06 13:23:56 +09:00
neuecc 44ce3c96bb en-us docs 2021-04-06 13:17:11 +09:00
Yoshifumi Kawai 03097f08e2
Merge pull request #259 from hadoumune/patch-1
fix typo unitySynchronizationContext
2021-04-06 13:11:28 +09:00
Yoshifumi Kawai af82dd719e
Merge pull request #260 from euglenach/master
fix typo in README.md
2021-04-06 13:11:03 +09:00
neuecc ecd3625a08 Fix UniTaskAsyncEnumerable.Prepend does not work, #251 2021-04-06 13:08:18 +09:00
neuecc da8f599ccb fix unittest more 2021-04-06 13:05:52 +09:00
neuecc aa3216e48d fix invalid unit tests(BeEquilvalentTo -> Equal) 2021-04-06 13:02:50 +09:00
Euglenach 5f7148419f fix typo in README.md
Enumerbale → Enumerable
2021-04-06 00:31:18 +09:00
Ikiru Yoshizaki 89ae106ea7 fix: release tag update when package.json has change 2021-04-05 11:06:40 +09:00
hatomune 721a7d9e4e
fix typo unitySynchronizationContext
When I was following the context switch flow, I found a typo, so I fixed it.
2021-04-04 01:17:23 +09:00
github-actions[bot] 18f2746f0d feat: Update package.json to 2.2.4 2021-03-12 07:36:32 +00:00
Yoshifumi Kawai 8a10f2191f
Merge pull request #243 from Cysharp/playerloop-timer
Add PlayerLoopTimer
2021-03-12 16:07:11 +09:00
neuecc 8b3c8d15c4 PlayerLoopTimer unit test and fixes 2021-03-12 16:06:42 +09:00
neuecc 49ca9364f7 TimeoutController uses PlayerLoopTimer(WIP, needs test) 2021-03-12 13:01:46 +09:00
neuecc 62f6429b60 add PlayerLoopTimer 2021-03-12 11:35:46 +09:00
neuecc b6a9836e81 WIP, playerlooptimer 2021-03-11 17:56:41 +09:00
github-actions[bot] be34d8abf4 feat: Update package.json to 2.2.3 2021-03-02 06:19:55 +00:00
neuecc 9af15d7ab3 Merge remote-tracking branch 'origin/master' 2021-03-02 15:16:07 +09:00
neuecc 308fef2859 Fix: UniTask(not generics).ToObservable does not return error when task
Fix: UniTask(not generics).ToObservable does not return OnError when task status is already faulted or canceled
2021-03-02 15:16:00 +09:00
github-actions[bot] 3cc0c80b1e feat: Update package.json to 2.2.2 2021-03-02 02:02:01 +00:00
neuecc cdda33a98e Add UniTask.Yield(CancellationToken), NextFrame(CancellationToken) 2021-03-01 19:08:40 +09:00
github-actions[bot] e57a4332ec feat: Update package.json to 2.2.1 2021-02-26 09:52:12 +00:00
neuecc e88e553cc9 2.2.1 2021-02-26 18:51:35 +09:00
github-actions[bot] a1a38d0d7c feat: Update package.json to 2.2.0 2021-02-26 07:47:03 +00:00
Yoshifumi Kawai c9bebd6550
Merge pull request #238 from Cysharp/custom-loop-injector
Ver 2.2.0
2021-02-26 16:42:54 +09:00
neuecc 42047070dd more ci 2021-02-26 11:42:30 +09:00
neuecc 316f3bd963 cm 2021-02-26 11:00:47 +09:00
neuecc 5f96e646d4 netcore 2021-02-26 10:27:22 +09:00
neuecc 186114996c docs: update TOC 2021-02-25 13:13:40 +00:00
neuecc 841b6e85ae fix 2021-02-25 22:13:14 +09:00
neuecc 7ac9853cf6 (BreakingChange)AsyncOperation.WithCancellation same ToUniTask(ctoken) 2021-02-25 22:11:18 +09:00
neuecc 0ec45b9da6 (Breaking Changed)UniTask.WithCancellation -> IgnoreWhenCanceled 2021-02-25 21:56:52 +09:00
neuecc dfd0fe9fe4 Add TimeoutController 2021-02-25 20:30:44 +09:00
neuecc 4710268e0a Add PlayerLoopHelper.Initialize(InbjectPlayerLoopTimings) 2021-02-25 19:26:37 +09:00
neuecc cae512e4de Delay automatically fallback to Realtime when run on EditMode #234 2021-02-25 19:25:48 +09:00
neuecc 6351d4c5a4 test1 2021-02-25 11:12:15 +09:00
github-actions[bot] 1173bb4f34 feat: Update package.json to 2.1.2 2021-02-08 10:44:53 +00:00
neuecc e4272b5337 one more back to 2.1.1 2021-02-08 19:43:57 +09:00
neuecc b660506e31 com.unity.modules.unitywebrequest 2021-02-08 19:43:32 +09:00
github-actions[bot] 2c101aef08 feat: Update package.json to 2.1.2 2021-02-08 10:37:58 +00:00
neuecc 498b2a4f81 Merge remote-tracking branch 'origin/master' 2021-02-08 19:37:25 +09:00
neuecc a8c2fd420a back to 2.1.1 2021-02-08 19:37:09 +09:00
Yoshifumi Kawai dade7fdc76
Merge pull request #230 from Cysharp/fix/checkout_version
chore: fix checkout version on unity
2021-02-08 19:34:19 +09:00
Ikiru Yoshizaki 1c8b16f798 chore: fix checkout version on unity 2021-02-08 19:32:53 +09:00
neuecc 89649d8777 remove unity generate csproj 2021-02-08 19:22:54 +09:00
github-actions[bot] 9894bf875b feat: Update package.json to 2.1.2 2021-02-08 10:14:07 +00:00
Yoshifumi Kawai 45a800330b
Merge pull request #229 from Cysharp/moreasmdef
define UNITASK_PHYSICS_SUPPORT, UNITASK_PHYSICS2D_SUPPORT, UNITASK_PARTICLESYSTEM_SUPPORT, UNITASK_UGUI_SUPPORT, UNITASK_WEBREQUEST_SUPPORT
2021-02-08 19:11:00 +09:00
neuecc a35e5f929d UNITASK_WEBREQUEST_SUPPORT 2021-02-08 19:09:02 +09:00
neuecc 9a3f10d4bf versionDefines for physics etc. 2021-02-08 18:08:56 +09:00
Ikiru Yoshizaki 19b5f921a1 chore: add original cli command 2021-02-08 16:53:46 +09:00
Yoshifumi Kawai 8cd577904e
Merge pull request #228 from Cysharp/chore/image
chore: new docker image to build
2021-02-08 16:16:56 +09:00
Ikiru Yoshizaki 309d661bcc fix: actions name update to game-ci/unity-builder 2021-02-08 16:08:43 +09:00
Ikiru Yoshizaki 8736024d9b Merge branch 'master' into chore/image 2021-02-08 15:54:18 +09:00
Ikiru Yoshizaki 97680e57a8 chore: new unity build with unityci/editor 2021-02-08 15:54:07 +09:00
Yoshifumi Kawai 638522600a
Merge pull request #220 from taigacon/master
Fix using AsyncUnityEventHandler with a already canceled token
2021-02-08 14:51:21 +09:00
Yoshifumi Kawai 081113a62c
Merge pull request #224 from neilsarkar/master
Grammar edits for README.md
2021-02-08 14:01:03 +09:00
Ikiru Yoshizaki 7535c84581 chore: use unity-editor 2021-02-08 11:47:14 +09:00
Ikiru Yoshizaki 690f3a2ead chore: move 2020.1 beta to ga image 2021-02-08 11:36:39 +09:00
Ikiru Yoshizaki 88371d08b9 chore: new docker image to build 2021-02-08 11:26:31 +09:00
Yoshifumi Kawai 6d382450cf
Merge pull request #226 from Cysharp/chore/release
chore: release on workflow_dispatch
2021-02-08 11:22:27 +09:00
Ikiru Yoshizaki 19f2a6f282 chore: pr build should run for master only 2021-02-04 03:01:21 +09:00
Ikiru Yoshizaki 7ed0b016ea chore: release on workflow_dispatch 2021-02-04 02:59:52 +09:00
Ikiru Yoshizaki f72a51e13d chore: detect forgotten .meta file commit 2021-02-04 02:58:29 +09:00
Neil Sarkar 0e2dbbe754
Minor copyedit
`AsyncOpereationHandle<T>` -> `AsyncOperationHandle<T>`
2021-02-03 10:25:35 -05:00
Neil Sarkar cff51a0425
Minor copyedit
oldest version -> lowest version
2021-02-03 10:18:32 -05:00
Neil Sarkar 7cac4bbbab
Grammar fixes for README.md 2021-02-03 10:07:32 -05:00
taigacon 3f4a46bca6 Fix using AsyncUnityEventHandler with a already canceled token 2021-01-25 20:02:56 +08:00
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
neuecc 547b700ba7 AsyncOperation/Addressables returns Cacnceled UniTask when already canceled. 2020-07-16 06:12:39 +09:00
neuecc 6b87d5d2b0 2.0.26 2020-07-16 06:05:04 +09:00
neuecc 023894d45e Merge remote-tracking branch 'origin/master' 2020-07-16 05:37:08 +09:00
Yoshifumi Kawai 009715c0da
Merge pull request #115 from IllusionCui/master
[addressable]:fix CancellationToken can't stop UniTask #114
2020-07-16 05:37:01 +09:00
neuecc c2824027d4 more strict handle cancel timing on asyncOperation.WithCancellation() 2020-07-16 05:36:21 +09:00
cuibeibei 65b6553a1a [addressable]:fix CancellationToken can't stop UniTask #114 2020-07-15 15:14:08 +08:00
neuecc 9d3b7adc8e Add CancellationToken cancellationToken = default to UniTask.Run #113 2020-07-14 11:25:30 +09:00
neuecc 3724fc204c memo of IL2CPP VM bug. 2020-07-14 06:40:32 +09:00
neuecc b97451a915 UnityWebRequest, Addressables returns exception when already isDone and AsyncOperation has error. 2020-07-13 09:34:43 +09:00
neuecc 9ddcac4c6c ignore1 2020-07-13 01:00:16 +09:00
neuecc b61e3c347f remove unity csproj 2020-07-11 02:19:40 +09:00
neuecc 0bb44066c0 2.0.25 2020-07-09 22:39:48 +09:00
neuecc 305c4aaa07 fix UnityWebRequestException String access is not supported #110 2020-07-09 22:17:49 +09:00
neuecc 42d627f3ba fix again #108 2020-07-09 17:29:04 +09:00
neuecc 6dd2b464a3 NRE on head request with invalid url #108 2020-07-09 01:31:27 +09:00
neuecc 32f24cf8f8 cleanup unused code 2020-07-04 22:08:48 +09:00
neuecc 4a89e3ea86 r 2020-07-04 06:44:57 +09:00
neuecc 887db5b281 2.0.24 2020-07-04 06:36:14 +09:00
neuecc fee5518a82 Improve, Select/Where add UniTaskTracker 2020-07-04 06:29:50 +09:00
neuecc 551128e64c Add UniTaskAsyncEnumerable.Create 2020-07-04 06:29:33 +09:00
neuecc c65ae8d18e error detail 2020-07-04 06:29:08 +09:00
neuecc c1f75d9ebd Add UniTask.Never 2020-06-30 04:01:00 +09:00
neuecc 73a5ff6648 r 2020-06-29 03:24:10 +09:00
neuecc 1c264f380e 2.0.23 2020-06-29 03:22:33 +09:00
neuecc f02bfa0a1e Fix lose exception stacktrace info when task is sync completed 2020-06-29 03:20:13 +09:00
neuecc 9d684006fc test 2020-06-29 03:02:23 +09:00
neuecc c49f1ed028 r 2020-06-29 01:18:16 +09:00
164 changed files with 11334 additions and 8536 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
github: [neuecc]

12
.github/dependabot.yaml vendored Normal file
View File

@ -0,0 +1,12 @@
# ref: https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly" # Check for updates to GitHub Actions every week
ignore:
# I just want update action when major/minor version is updated. patch updates are too noisy.
- dependency-name: '*'
update-types:
- version-update:semver-patch

View File

@ -3,72 +3,86 @@ name: Build-Debug
on: on:
push: push:
branches: branches:
- "**" - "master"
tags:
- "!*" # not a tag push
pull_request: pull_request:
types: branches:
- opened - "master"
- synchronize
jobs: jobs:
build-dotnet: build-dotnet:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: timeout-minutes: 10
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
NUGET_XMLDOC_MODE: skip
steps: steps:
- uses: actions/checkout@v2 - uses: Cysharp/Actions/.github/actions/checkout@main
- uses: actions/setup-dotnet@v1 - uses: Cysharp/Actions/.github/actions/setup-dotnet@main
with: - run: dotnet build -c Debug
dotnet-version: 3.1.101 - run: dotnet test -c Debug
- run: dotnet test -c Debug ./src/UniTask.NetCoreTests/UniTask.NetCoreTests.csproj
build-unity: build-unity:
if: "(github.event == 'push' && github.repository_owner == 'Cysharp') || startsWith(github.event.pull_request.head.label, 'Cysharp:')" if: ${{ ((github.event_name == 'push' && github.repository_owner == 'Cysharp') || startsWith(github.event.pull_request.head.label, 'Cysharp:')) && github.triggering_actor != 'dependabot[bot]' }}
strategy: strategy:
fail-fast: false
max-parallel: 2
matrix: matrix:
unity: ["2019.3.9f1", "2020.1.0b5"] unity: ["2022.3.39f1", "6000.0.12f1"] # Test with LTS
include:
- unity: 2019.3.9f1
license: UNITY_2019_3
- unity: 2020.1.0b5
license: UNITY_2020_1
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: timeout-minutes: 30 # Unity build takes more than 20min.
# with linux-il2cpp. image from https://hub.docker.com/r/gableroux/unity3d/tags
image: gableroux/unity3d:${{ matrix.unity }}-linux-il2cpp
steps: steps:
- run: apt update && apt install git -y - name: Load secrets
- uses: actions/checkout@v2 id: op-load-secret
# create unity activation file and store to artifacts. uses: 1password/load-secrets-action@581a835fb51b8e7ec56b71cf2ffddd7e68bb25e0 # v2.0.0
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -logFile -createManualActivationFile || exit 0
- uses: actions/upload-artifact@v1
with: with:
name: Unity_v${{ matrix.unity }}.alf export-env: false
path: ./Unity_v${{ matrix.unity }}.alf
# activate Unity from manual license file(ulf)
- run: echo -n "$UNITY_LICENSE" >> .Unity.ulf
env: env:
UNITY_LICENSE: ${{ secrets[matrix.license] }} OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }}
- name: Activate Unity, always returns a success. But if a subsequent run fails, the activation may have failed(if succeeded, shows `Next license update check is after` and not shows other message(like GUID != GUID). If fails not). In that case, upload the artifact's .alf file to https://license.unity3d.com/manual to get the .ulf file and set it to secrets. UNITY_EMAIL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/username"
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -manualLicenseFile .Unity.ulf || exit 0 UNITY_PASSWORD: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/credential"
UNITY_SERIAL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/serial"
# Execute scripts: RuntimeUnitTestToolkit - uses: Cysharp/Actions/.github/actions/checkout@main
- name: Build UnitTest(Linux64, mono)
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend mono /BuildTarget StandaloneLinux64
working-directory: src/UniTask
- name: Execute UnitTest
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_Mono2x/test
# Execute scripts: Export Package # Execute scripts: Export Package
- name: Export unitypackage # /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export - name: Build Unity (.unitypacakge)
working-directory: src/UniTask if: ${{ startsWith(matrix.unity, '2022') }} # only execute once
uses: Cysharp/Actions/.github/actions/unity-builder@main
env:
UNITY_EMAIL: ${{ steps.op-load-secret.outputs.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ steps.op-load-secret.outputs.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ steps.op-load-secret.outputs.UNITY_SERIAL }}
with:
projectPath: src/UniTask
unityVersion: ${{ matrix.unity }}
targetPlatform: StandaloneLinux64
buildMethod: PackageExporter.Export
# Execute UnitTest
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend IL2CPP /BuildTarget StandaloneLinux64
- name: Build UnitTest (IL2CPP)
uses: Cysharp/Actions/.github/actions/unity-builder@main
env:
UNITY_EMAIL: ${{ steps.op-load-secret.outputs.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ steps.op-load-secret.outputs.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ steps.op-load-secret.outputs.UNITY_SERIAL }}
with:
projectPath: src/UniTask
unityVersion: ${{ matrix.unity }}
targetPlatform: StandaloneLinux64
buildMethod: UnitTestBuilder.BuildUnitTest
customParameters: "/headless /ScriptBackend IL2CPP"
- name: Check UnitTest file is generated
run: ls -lR ./src/UniTask/bin/UnitTest
- name: Execute UnitTest
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_IL2CPP/test
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files
with:
directory: src/UniTask
# Store artifacts. # Store artifacts.
- uses: actions/upload-artifact@v2 - uses: Cysharp/Actions/.github/actions/upload-artifact@main
if: ${{ startsWith(matrix.unity, '2021') }} # only execute 2021
with: with:
name: UniTask.unitypackage.zip name: UniTask.unitypackage-${{ matrix.unity }}.zip
path: ./src/UniTask/*.unitypackage path: ./src/UniTask/*.unitypackage
retention-days: 1

View File

@ -8,24 +8,24 @@ on:
jobs: jobs:
run-docfx: run-docfx:
if: "!(contains(github.event.head_commit.message, '[skip ci]') || contains(github.event.head_commit.message, '[ci skip]'))"
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 10
steps: steps:
- uses: actions/checkout@v2 - uses: Cysharp/Actions/.github/actions/checkout@main
- uses: actions/checkout@v2 - uses: Cysharp/Actions/.github/actions/checkout@main
with: with:
repository: Cysharp/DocfxTemplate repository: Cysharp/DocfxTemplate
path: docs/_DocfxTemplate path: docs/_DocfxTemplate
- uses: Kirbyrawr/docfx-action@master - uses: Kirbyrawr/docfx-action@db9a22c8fe1e8693a2a21be54cb0b87dfaa72cc4
name: Docfx metadata name: Docfx metadata
with: with:
args: metadata docs/docfx.json args: metadata docs/docfx.json
- uses: Kirbyrawr/docfx-action@master - uses: Kirbyrawr/docfx-action@db9a22c8fe1e8693a2a21be54cb0b87dfaa72cc4
name: Docfx build name: Docfx build
with: with:
args: build docs/docfx.json args: build docs/docfx.json
- name: Publish to GitHub Pages - name: Publish to GitHub Pages
uses: peaceiris/actions-gh-pages@v3 uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/_site publish_dir: docs/_site

View File

@ -1,105 +1,110 @@
name: Build-Release name: build-release
on: on:
push: workflow_dispatch:
tags: inputs:
- "[0-9]+.[0-9]+.[0-9]+*" tag:
description: "tag: git tag you want create. (sample 1.0.0)"
required: true
dry-run:
description: "dry-run: true will never create relase/nuget."
required: true
default: false
type: boolean
jobs: jobs:
build-dotnet: update-packagejson:
runs-on: ubuntu-latest uses: Cysharp/Actions/.github/workflows/update-packagejson.yaml@main
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
NUGET_XMLDOC_MODE: skip
steps:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1
with: with:
dotnet-version: 3.1.101 file-path: ./src/UniTask/Assets/Plugins/UniTask/package.json
# set release tag(*.*.*) to env.GIT_TAG tag: ${{ inputs.tag }}
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/} dry-run: ${{ inputs.dry-run }}
build-dotnet:
needs: [update-packagejson]
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- run: echo ${{ needs.update-packagejson.outputs.sha }}
- uses: Cysharp/Actions/.github/actions/checkout@main
with:
ref: ${{ needs.update-packagejson.outputs.sha }}
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
# build and pack # build and pack
- run: dotnet build -c Release -p:Version=${{ env.GIT_TAG }} - run: dotnet build -c Release -p:Version=${{ inputs.tag }}
- run: dotnet test -c Release --no-build - run: dotnet test -c Release --no-build
- run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ env.GIT_TAG }} - run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ inputs.tag }} -o ./publish
# Store artifacts. # Store artifacts.
- uses: actions/upload-artifact@v1 - uses: Cysharp/Actions/.github/actions/upload-artifact@main
with: with:
name: nuget name: nuget
path: ./src/UniTask.NetCore/bin/Release/UniTask.${{ env.GIT_TAG }}.nupkg path: ./publish/
retention-days: 1
build-unity: build-unity:
needs: [update-packagejson]
strategy: strategy:
matrix: matrix:
unity: ['2019.3.9f1'] unity: ["2022.3.39f1"]
include:
- unity: 2019.3.9f1
license: UNITY_2019_3
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: timeout-minutes: 15
# with linux-il2cpp. image from https://hub.docker.com/r/gableroux/unity3d/tags
image: gableroux/unity3d:${{ matrix.unity }}-linux-il2cpp
steps: steps:
- run: apt update && apt install git -y - name: Load secrets
- uses: actions/checkout@v2 id: op-load-secret
- run: echo -n "$UNITY_LICENSE" >> .Unity.ulf uses: 1password/load-secrets-action@581a835fb51b8e7ec56b71cf2ffddd7e68bb25e0 # v2.0.0
with:
export-env: false
env: env:
UNITY_LICENSE: ${{ secrets[matrix.license] }} OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }}
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -manualLicenseFile .Unity.ulf || exit 0 UNITY_EMAIL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/username"
UNITY_PASSWORD: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/credential"
# set release tag(*.*.*) to env.GIT_TAG UNITY_SERIAL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/serial"
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
- run: echo ${{ needs.update-packagejson.outputs.sha }}
- uses: Cysharp/Actions/.github/actions/checkout@main
with:
ref: ${{ needs.update-packagejson.outputs.sha }}
# Execute scripts: Export Package # Execute scripts: Export Package
- name: Export unitypackage # /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export - name: Build Unity (.unitypacakge)
working-directory: src/UniTask uses: Cysharp/Actions/.github/actions/unity-builder@main
env:
UNITY_EMAIL: ${{ steps.op-load-secret.outputs.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ steps.op-load-secret.outputs.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ steps.op-load-secret.outputs.UNITY_SERIAL }}
with:
projectPath: src/UniTask
unityVersion: ${{ matrix.unity }}
targetPlatform: StandaloneLinux64
buildMethod: PackageExporter.Export
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files
with:
directory: src/UniTask
# Store artifacts. # Store artifacts.
- uses: actions/upload-artifact@v2 - uses: Cysharp/Actions/.github/actions/upload-artifact@main
with: with:
name: UniTask.${{ env.GIT_TAG }}.unitypackage name: UniTask.${{ inputs.tag }}.unitypackage
path: ./src/UniTask/UniTask.${{ env.GIT_TAG }}.unitypackage path: ./src/UniTask/UniTask.${{ inputs.tag }}.unitypackage
retention-days: 1
# release
create-release: create-release:
needs: [build-dotnet, build-unity] needs: [update-packagejson, build-dotnet, build-unity]
runs-on: ubuntu-latest uses: Cysharp/Actions/.github/workflows/create-release.yaml@main
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
NUGET_XMLDOC_MODE: skip
steps:
# setup dotnet for nuget push
- uses: actions/setup-dotnet@v1
with: with:
dotnet-version: 3.1.101 commit-id: ${{ needs.update-packagejson.outputs.sha }}
# set release tag(*.*.*) to env.GIT_TAG dry-run: ${{ inputs.dry-run }}
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/} tag: ${{ inputs.tag }}
nuget-push: true
release-upload: true
release-asset-path: ./UniTask.${{ inputs.tag }}.unitypackage/UniTask.${{ inputs.tag }}.unitypackage
secrets: inherit
# Create Releases cleanup:
- uses: actions/create-release@v1 if: ${{ needs.update-packagejson.outputs.is-branch-created == 'true' }}
id: create_release needs: [update-packagejson, build-dotnet, build-unity]
env: uses: Cysharp/Actions/.github/workflows/clean-packagejson-branch.yaml@main
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
tag_name: ${{ github.ref }} branch: ${{ needs.update-packagejson.outputs.branch-name }}
release_name: Ver.${{ github.ref }}
# 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 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

View File

@ -0,0 +1,10 @@
name: Prevent github change
on:
pull_request:
paths:
- ".github/**/*.yaml"
- ".github/**/*.yml"
jobs:
detect:
uses: Cysharp/Actions/.github/workflows/prevent-github-change.yaml@main

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

@ -0,0 +1,10 @@
name: "Close stale issues"
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * *"
jobs:
stale:
uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main

View File

@ -8,8 +8,7 @@ on:
jobs: jobs:
generateTOC: generateTOC:
name: TOC Generator name: TOC Generator
runs-on: ubuntu-latest uses: Cysharp/Actions/.github/workflows/toc-generator.yaml@main
steps:
- uses: technote-space/toc-generator@v2.4.0
with: with:
TOC_TITLE: "## Table of Contents" TOC_TITLE: "## Table of Contents"
secrets: inherit

85
.gitignore vendored
View File

@ -130,78 +130,15 @@ UpgradeLog*.XML
Assets/WSATestCertificate.pfx Assets/WSATestCertificate.pfx
.vs/ .vs/
Assembly-CSharp\.csproj # Unity
UniRx\.Async\.csproj # Unity
.vsconfig
UniRx\.Async\.Editor\.csproj src/UniTask/Library/*
src/UniTask/Temp/*
UniRx\.Async\.Tests\.csproj src/UniTask/Logs/*
src/UniTask/[Uu]ser[Ss]ettings/
UniTask\.sln src/UniTask/*.sln
src/UniTask/*.csproj
RuntimeUnitTestToolkit\.csproj src/UniTask/*.unitypackage
!src/UniTask/Packages/
Assembly-CSharp-Editor\.csproj
UniRx\.Async\.unitypackage
UniRx.Async.Tests.Editor.csproj
src/UniTask/UniTask.csproj
src/UniTask/UniTask.Editor.csproj
src/UniTask/UniTask.Tests.csproj
src/UniTask/UniTask.Tests.Editor.csproj
src/UniTask/UniTask.*.unitypackage
src/UniTask/UniTask.Linq.csproj
src/UniTask/DOTween.Modules.csproj
src/UniTask/Unity.Addressables.csproj
src/UniTask/Unity.Addressables.Editor.csproj
src/UniTask/Unity.Analytics.DataPrivacy.csproj
src/UniTask/Unity.Recorder.csproj
src/UniTask/Unity.Recorder.Editor.csproj
src/UniTask/Unity.ResourceManager.csproj
src/UniTask/Unity.Rider.Editor.csproj
src/UniTask/Unity.ScriptableBuildPipeline.csproj
src/UniTask/Unity.ScriptableBuildPipeline.Editor.csproj
src/UniTask/Unity.TextMeshPro.csproj
src/UniTask/Unity.TextMeshPro.Editor.csproj
src/UniTask/Unity.Timeline.csproj
src/UniTask/Unity.Timeline.Editor.csproj
src/UniTask/Unity.VisualStudio.Editor.csproj
src/UniTask/Unity.VSCode.Editor.csproj
src/UniTask/UnityEditor.CacheServer.csproj
src/UniTask/UnityEditor.TestRunner.csproj
src/UniTask/UnityEditor.UI.csproj
src/UniTask/UnityEngine.Advertisements.csproj
src/UniTask/UnityEngine.Monetization.csproj
src/UniTask/UnityEngine.TestRunner.csproj
src/UniTask/UnityEngine.UI.csproj

545
README.md
View File

@ -1,20 +1,21 @@
UniTask UniTask
=== ===
[![GitHub Actions](https://github.com/Cysharp/UniTask/workflows/Build-Debug/badge.svg)](https://github.com/Cysharp/UniTask/actions) [![Releases](https://img.shields.io/github/release/Cysharp/UniTask.svg)](https://github.com/Cysharp/UniTask/releases) [![GitHub Actions](https://github.com/Cysharp/UniTask/workflows/Build-Debug/badge.svg)](https://github.com/Cysharp/UniTask/actions) [![Releases](https://img.shields.io/github/release/Cysharp/UniTask.svg)](https://github.com/Cysharp/UniTask/releases) [![Readme_CN](https://img.shields.io/badge/UniTask-%E4%B8%AD%E6%96%87%E6%96%87%E6%A1%A3-red)](https://github.com/Cysharp/UniTask/blob/master/README_CN.md)
Provides an efficient allocation free async/await integration to Unity. Provides an efficient allocation free async/await integration for Unity.
* Struct based `UniTask<T>` and custom AsyncMethodBuilder to achive zero allocation * Struct based `UniTask<T>` and custom AsyncMethodBuilder to achieve zero allocation
* All Unity AsyncOperations and Coroutine to awaitable * Makes all Unity AsyncOperations and Coroutines awaitable
* PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc..) that enable to replace all coroutine operation * PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc..) that enable replacing all coroutine operations
* MonoBehaviour Message Events and uGUI Events as awaitable/async-enumerable * MonoBehaviour Message Events and uGUI Events as awaitable/async-enumerable
* Completely run on Unity's PlayerLoop so don't use thread and run on WebGL, wasm, etc. * Runs completely on Unity's PlayerLoop so doesn't use threads and runs on WebGL, wasm, etc.
* Asynchronous LINQ, with Channel and AsyncReactiveProperty * Asynchronous LINQ, with Channel and AsyncReactiveProperty
* TaskTracker window to prevent memory leak * TaskTracker window to prevent memory leaks
* Highly compatible behaviour with Task/ValueTask/IValueTaskSource * Highly compatible behaviour with Task/ValueTask/IValueTaskSource
Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await for Unity, with Asynchronous LINQ For 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) ](https://medium.com/@neuecc/unitask-v2-zero-allocation-async-await-for-unity-with-asynchronous-linq-1aa9c96aa7dd)
For 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 --> <!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
@ -23,6 +24,7 @@ Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await f
- [Getting started](#getting-started) - [Getting started](#getting-started)
- [Basics of UniTask and AsyncOperation](#basics-of-unitask-and-asyncoperation) - [Basics of UniTask and AsyncOperation](#basics-of-unitask-and-asyncoperation)
- [Cancellation and Exception handling](#cancellation-and-exception-handling) - [Cancellation and Exception handling](#cancellation-and-exception-handling)
- [Timeout handling](#timeout-handling)
- [Progress](#progress) - [Progress](#progress)
- [PlayerLoop](#playerloop) - [PlayerLoop](#playerloop)
- [async void vs async UniTaskVoid](#async-void-vs-async-unitaskvoid) - [async void vs async UniTaskVoid](#async-void-vs-async-unitaskvoid)
@ -31,13 +33,18 @@ Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await f
- [AsyncEnumerable and Async LINQ](#asyncenumerable-and-async-linq) - [AsyncEnumerable and Async LINQ](#asyncenumerable-and-async-linq)
- [Awaitable Events](#awaitable-events) - [Awaitable Events](#awaitable-events)
- [Channel](#channel) - [Channel](#channel)
- [vs Awaitable](#vs-awaitable)
- [For Unit Testing](#for-unit-testing) - [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) - [Compare with Standard Task API](#compare-with-standard-task-api)
- [Pooling Configuration](#pooling-configuration) - [Pooling Configuration](#pooling-configuration)
- [Allocation on Profiler](#allocation-on-profiler)
- [UniTaskSynchronizationContext](#unitasksynchronizationcontext)
- [API References](#api-references) - [API References](#api-references)
- [UPM Package](#upm-package) - [UPM Package](#upm-package)
- [Install via git URL](#install-via-git-url) - [Install via git URL](#install-via-git-url)
- [Install via OpenUPM](#install-via-openupm)
- [.NET Core](#net-core) - [.NET Core](#net-core)
- [License](#license) - [License](#license)
@ -45,7 +52,7 @@ Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await f
Getting started Getting started
--- ---
Install via [UPM package](#upm-package) or asset package(`UniTask.*.*.*.unitypackage`) available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases) page. Install via [UPM package](#upm-package) with git reference or asset package(`UniTask.*.*.*.unitypackage`) available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases).
```csharp ```csharp
// extension awaiter/methods can be used by this namespace // extension awaiter/methods can be used by this namespace
@ -61,12 +68,13 @@ async UniTask<string> DemoAsync()
await SceneManager.LoadSceneAsync("scene2"); await SceneManager.LoadSceneAsync("scene2");
// .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject // .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
// after Unity 2022.2, you can use `destroyCancellationToken` in MonoBehaviour
var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy()); var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy());
// .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T> // .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x))); var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x)));
// await frame-based operation like coroutine // await frame-based operation like a coroutine
await UniTask.DelayFrame(100); await UniTask.DelayFrame(100);
// replacement of yield return new WaitForSeconds/WaitForSecondsRealtime // replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
@ -79,8 +87,13 @@ async UniTask<string> DemoAsync()
await UniTask.Yield(); await UniTask.Yield();
await UniTask.NextFrame(); await UniTask.NextFrame();
// replacement of WaitForEndOfFrame(same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate)) // replacement of WaitForEndOfFrame
#if UNITY_2023_1_OR_NEWER
await UniTask.WaitForEndOfFrame(); await UniTask.WaitForEndOfFrame();
#else
// requires MonoBehaviour(CoroutineRunner))
await UniTask.WaitForEndOfFrame(this); // this is MonoBehaviour
#endif
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate)) // replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
await UniTask.WaitForFixedUpdate(); await UniTask.WaitForFixedUpdate();
@ -91,10 +104,10 @@ async UniTask<string> DemoAsync()
// special helper of WaitUntil // special helper of WaitUntil
await UniTask.WaitUntilValueChanged(this, x => x.isActive); await UniTask.WaitUntilValueChanged(this, x => x.isActive);
// You can await IEnumerator coroutine // You can await IEnumerator coroutines
await FooCoroutineEnumerator(); await FooCoroutineEnumerator();
// You can await standard task // You can await a standard task
await Task.Run(() => 100); await Task.Run(() => 100);
// Multithreading, run on ThreadPool under this code // Multithreading, run on ThreadPool under this code
@ -116,15 +129,12 @@ async UniTask<string> DemoAsync()
var task2 = GetTextAsync(UnityWebRequest.Get("http://bing.com")); var task2 = GetTextAsync(UnityWebRequest.Get("http://bing.com"));
var task3 = GetTextAsync(UnityWebRequest.Get("http://yahoo.com")); var task3 = GetTextAsync(UnityWebRequest.Get("http://yahoo.com"));
// concurrent async-wait and get result easily by tuple syntax // concurrent async-wait and get results easily by tuple syntax
var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3); var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3);
// shorthand of WhenAll, tuple can await directly // shorthand of WhenAll, tuple can await directly
var (google2, bing2, yahoo2) = await (task1, task2, task3); var (google2, bing2, yahoo2) = await (task1, task2, task3);
// You can handle timeout easily
await GetTextAsync(UnityWebRequest.Get("http://unity.com")).Timeout(TimeSpan.FromMilliseconds(300));
// return async-value.(or you can use `UniTask`(no result), `UniTaskVoid`(fire and forget)). // return async-value.(or you can use `UniTask`(no result), `UniTaskVoid`(fire and forget)).
return (asset as TextAsset)?.text ?? throw new InvalidOperationException("Asset not found"); return (asset as TextAsset)?.text ?? throw new InvalidOperationException("Asset not found");
} }
@ -132,9 +142,9 @@ async UniTask<string> DemoAsync()
Basics of UniTask and AsyncOperation Basics of UniTask and AsyncOperation
--- ---
UniTask feature rely on C# 7.0([task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)) so required Unity version is after `Unity 2018.3`, officialy lower support version is `Unity 2018.4.13f1`. UniTask features rely on C# 7.0([task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)) so the required Unity version is after `Unity 2018.3`, the official lowest version supported is `Unity 2018.4.13f1`.
Why UniTask(custom task-like object) is required? Because Task is too heavy, not matched to Unity threading(single-thread). UniTask does not use thread and SynchronizationContext/ExecutionContext because almost Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It acquires more fast and more less allocation, completely integrated with Unity. Why is UniTask(custom task-like object) required? Because Task is too heavy and not matched to Unity threading (single-thread). UniTask does not use threads and SynchronizationContext/ExecutionContext because Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It achieves faster and lower allocation, and is completely integrated with Unity.
You can await `AsyncOperation`, `ResourceRequest`, `AssetBundleRequest`, `AssetBundleCreateRequest`, `UnityWebRequestAsyncOperation`, `AsyncGPUReadbackRequest`, `IEnumerator` and others when `using Cysharp.Threading.Tasks;`. You can await `AsyncOperation`, `ResourceRequest`, `AssetBundleRequest`, `AssetBundleCreateRequest`, `UnityWebRequestAsyncOperation`, `AsyncGPUReadbackRequest`, `IEnumerator` and others when `using Cysharp.Threading.Tasks;`.
@ -146,16 +156,16 @@ UniTask provides three pattern of extension methods.
* .ToUniTask(IProgress, PlayerLoopTiming, CancellationToken); * .ToUniTask(IProgress, PlayerLoopTiming, CancellationToken);
``` ```
`WithCancellation` is a simple version of `ToUniTask`, both returns `UniTask`. Details of cancellation, see: [Cancellation and Exception handling](#cancellation-and-exception-handling) section. `WithCancellation` is a simple version of `ToUniTask`, both return `UniTask`. For details of cancellation, see: [Cancellation and Exception handling](#cancellation-and-exception-handling) section.
> Note: WithCancellation is returned from native timing of PlayerLoop but ToUniTask is returned from specified PlayerLoopTiming. Details of timing, see: [PlayerLoop](#playerloop) section. > Note: await directly is returned from native timing of PlayerLoop but WithCancellation and ToUniTask are returned from specified PlayerLoopTiming. For details of timing, see: [PlayerLoop](#playerloop) section.
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. > Note: AssetBundleRequest has `asset` and `allAssets`, default await returns `asset`. If you want to get `allAssets`, you can use `AwaitForAllAssets()` method.
The type of `UniTask` can use utilities like `UniTask.WhenAll`, `UniTask.WhenAny`, `UniTask.WhenEach`. They are like `Task.WhenAll`/`Task.WhenAny` but the return type is more useful. They return value tuples so you can deconstruct each result and pass multiple types.
```csharp ```csharp
public class SceneAssets public async UniTaskVoid LoadManyAsync()
{
public SceneAssets()
{ {
// parallel load. // parallel load.
var (a, b, c) = await UniTask.WhenAll( var (a, b, c) = await UniTask.WhenAll(
@ -169,10 +179,9 @@ public class SceneAssets
var resource = await Resources.LoadAsync<Sprite>(path); var resource = await Resources.LoadAsync<Sprite>(path);
return (resource as Sprite); return (resource as Sprite);
} }
}
``` ```
If you want to convert callback to UniTask, you can use `UniTaskCompletionSource<T>` that is the lightweight edition of `TaskCompletionSource<T>`. If you want to convert a callback to UniTask, you can use `UniTaskCompletionSource<T>` which is a lightweight edition of `TaskCompletionSource<T>`.
```csharp ```csharp
public UniTask<int> WrapByUniTaskCompletionSource() public UniTask<int> WrapByUniTaskCompletionSource()
@ -189,7 +198,7 @@ public UniTask<int> WrapByUniTaskCompletionSource()
You can convert Task -> UniTask: `AsUniTask`, `UniTask` -> `UniTask<AsyncUnit>`: `AsAsyncUnitUniTask`, `UniTask<T>` -> `UniTask`: `AsUniTask`. `UniTask<T>` -> `UniTask`'s conversion cost is free. You can convert Task -> UniTask: `AsUniTask`, `UniTask` -> `UniTask<AsyncUnit>`: `AsAsyncUnitUniTask`, `UniTask<T>` -> `UniTask`: `AsUniTask`. `UniTask<T>` -> `UniTask`'s conversion cost is free.
If you want to convert async to coroutine, you can use `.ToCoroutine()`, this is useful to use only allow coroutine system. If you want to convert async to coroutine, you can use `.ToCoroutine()`, this is useful if you want to only allow using the coroutine system.
UniTask can not await twice. This is a similar constraint to the [ValueTask/IValueTaskSource](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1?view=netcore-3.1) introduced in .NET Standard 2.1. UniTask can not await twice. This is a similar constraint to the [ValueTask/IValueTaskSource](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1?view=netcore-3.1) introduced in .NET Standard 2.1.
@ -208,11 +217,13 @@ await task;
await task; // NG, throws Exception 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. Store to the class field, you can use `UniTask.Lazy` that supports calling multiple times. `.Preserve()` allows for multiple calls (internally cached results). This is useful when there are multiple calls in a function scope.
Also `UniTaskCompletionSource` can await multiple times and await from many callers.
Cancellation and Exception handling 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. Some UniTask factory methods have a `CancellationToken cancellationToken = default` parameter. Also some async operations for Unity have `WithCancellation(CancellationToken)` and `ToUniTask(..., CancellationToken cancellation = default)` extension methods.
You can pass `CancellationToken` to parameter by standard [`CancellationTokenSource`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource). You can pass `CancellationToken` to parameter by standard [`CancellationTokenSource`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource).
@ -229,18 +240,68 @@ await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().WithCancellati
await UniTask.DelayFrame(1000, cancellationToken: cts.Token); await UniTask.DelayFrame(1000, cancellationToken: cts.Token);
``` ```
CancellationToken can create by `CancellationTokenSource` or MonoBehaviour's extension method `GetCancellationTokenOnDestroy`. CancellationToken can be created by `CancellationTokenSource` or MonoBehaviour's extension method `GetCancellationTokenOnDestroy`.
```csharp ```csharp
// this CancellationToken lifecycle is same as GameObject. // this CancellationToken lifecycle is same as GameObject.
await UniTask.DelayFrame(1000, cancellationToken: this.GetCancellationTokenOnDestroy()); await UniTask.DelayFrame(1000, cancellationToken: this.GetCancellationTokenOnDestroy());
``` ```
When detect cancellation, all methods throws `OperationCanceledException` and propagate to upstream. `OperationCanceledException` is special exception, if not handled this exception, finally it is propagated to `UniTaskScheduler.UnobservedTaskException`. For propagate Cancellation, all async method recommend to accept `CancellationToken cancellationToken` at last argument, and pass `CancellationToken` from root to end.
Default behaviour of received unhandled exception is write log as exception. Log level can change by `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to change custom beavhiour, set action to `UniTaskScheduler.UnobservedTaskException.` ```csharp
await FooAsync(this.GetCancellationTokenOnDestroy());
If you want to cancel behaviour in async UniTask method, throws `OperationCanceledException` manually. // ---
async UniTask FooAsync(CancellationToken cancellationToken)
{
await BarAsync(cancellationToken);
}
async UniTask BarAsync(CancellationToken cancellationToken)
{
await UniTask.Delay(TimeSpan.FromSeconds(3), cancellationToken);
}
```
`CancellationToken` means lifecycle of async. You can hold your own lifecycle insteadof default CancellationTokenOnDestroy.
```csharp
public class MyBehaviour : MonoBehaviour
{
CancellationTokenSource disableCancellation = new CancellationTokenSource();
CancellationTokenSource destroyCancellation = new CancellationTokenSource();
private void OnEnable()
{
if (disableCancellation != null)
{
disableCancellation.Dispose();
}
disableCancellation = new CancellationTokenSource();
}
private void OnDisable()
{
disableCancellation.Cancel();
}
private void OnDestroy()
{
destroyCancellation.Cancel();
destroyCancellation.Dispose();
}
}
```
After Unity 2022.2, Unity adds CancellationToken in [MonoBehaviour.destroyCancellationToken](https://docs.unity3d.com/ScriptReference/MonoBehaviour-destroyCancellationToken.html) and [Application.exitCancellationToken](https://docs.unity3d.com/ScriptReference/Application-exitCancellationToken.html).
When cancellation is detected, all methods throw `OperationCanceledException` and propagate upstream. When exception(not limited to `OperationCanceledException`) is not handled in async method, it is propagated finally to `UniTaskScheduler.UnobservedTaskException`. The default behaviour of received unhandled exception is to write log as exception. Log level can be changed using `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to use custom behaviour, set an action to `UniTaskScheduler.UnobservedTaskException.`
And also `OperationCanceledException` is a special exception, this is silently ignored at `UnobservedTaskException`.
If you want to cancel behaviour in an async UniTask method, throw `OperationCanceledException` manually.
```csharp ```csharp
public async UniTask<int> FooAsync() public async UniTask<int> FooAsync()
@ -250,7 +311,7 @@ public async UniTask<int> FooAsync()
} }
``` ```
If you handle exception but want to ignore(propagete to global cancellation handling), use exception filter. If you handle an exception but want to ignore(propagate to global cancellation handling), use an exception filter.
```csharp ```csharp
public async UniTask<int> BarAsync() public async UniTask<int> BarAsync()
@ -260,14 +321,14 @@ public async UniTask<int> BarAsync()
var x = await FooAsync(); var x = await FooAsync();
return x * 2; return x * 2;
} }
catch (Exception ex) when (!(ex is OperationCanceledException)) catch (Exception ex) when (!(ex is OperationCanceledException)) // when (ex is not OperationCanceledException) at C# 9.0
{ {
return -1; return -1;
} }
} }
``` ```
throws/catch `OperationCanceledException` is slightly heavy, if you want to care performance, use `UniTask.SuppressCancellationThrow` to avoid OperationCanceledException throw. It returns `(bool IsCanceled, T Result)` instead of throw. throws/catch `OperationCanceledException` is slightly heavy, so if performance is a concern, use `UniTask.SuppressCancellationThrow` to avoid OperationCanceledException throw. It returns `(bool IsCanceled, T Result)` instead of throwing.
```csharp ```csharp
var (isCanceled, _) = await UniTask.DelayFrame(10, cancellationToken: cts.Token).SuppressCancellationThrow(); var (isCanceled, _) = await UniTask.DelayFrame(10, cancellationToken: cts.Token).SuppressCancellationThrow();
@ -277,11 +338,117 @@ if (isCanceled)
} }
``` ```
Note: Only suppress throws if you call it directly into the most source method. Otherwise, the return value will be converted, but the entire pipeline will not be suppressed throws. Note: Only suppress throws if you call directly into the most source method. Otherwise, the return value will be converted, but the entire pipeline will not suppress throws.
Some features that use Unity's player loop, such as `UniTask.Yield` and `UniTask.Delay` etc, determines CancellationToken state on the player loop.
This means it does not cancel immediately upon `CancellationToken` fired.
If you want to change this behaviour, the cancellation to be immediate, set the `cancelImmediately` flag as an argument.
```csharp
await UniTask.Yield(cancellationToken, cancelImmediately: true);
```
Note: Setting `cancelImmediately` to true and detecting an immediate cancellation is more costly than the default behavior.
This is because it uses `CancellationToken.Register`; it is heavier than checking CancellationToken on the player loop.
Timeout handling
---
Timeout is a variation of cancellation. You can set timeout by `CancellationTokenSouce.CancelAfterSlim(TimeSpan)` and pass CancellationToken to async methods.
```csharp
var cts = new CancellationTokenSource();
cts.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 5sec timeout.
try
{
await UnityWebRequest.Get("http://foo").SendWebRequest().WithCancellation(cts.Token);
}
catch (OperationCanceledException ex)
{
if (ex.CancellationToken == cts.Token)
{
UnityEngine.Debug.Log("Timeout");
}
}
```
> `CancellationTokenSouce.CancelAfter` is a standard api. However in Unity you should not use it because it depends threading timer. `CancelAfterSlim` is UniTask's extension methods, it uses PlayerLoop instead.
If you want to use timeout with other source of cancellation, use `CancellationTokenSource.CreateLinkedTokenSource`.
```csharp
var cancelToken = new CancellationTokenSource();
cancelButton.onClick.AddListener(() =>
{
cancelToken.Cancel(); // cancel from button click.
});
var timeoutToken = new CancellationTokenSource();
timeoutToken.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 5sec timeout.
try
{
// combine token
var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancelToken.Token, timeoutToken.Token);
await UnityWebRequest.Get("http://foo").SendWebRequest().WithCancellation(linkedTokenSource.Token);
}
catch (OperationCanceledException ex)
{
if (timeoutToken.IsCancellationRequested)
{
UnityEngine.Debug.Log("Timeout.");
}
else if (cancelToken.IsCancellationRequested)
{
UnityEngine.Debug.Log("Cancel clicked.");
}
}
```
Optimize for reduce allocation of CancellationTokenSource for timeout per call async method, you can use UniTask's `TimeoutController`.
```csharp
TimeoutController timeoutController = new TimeoutController(); // setup to field for reuse.
async UniTask FooAsync()
{
try
{
// you can pass timeoutController.Timeout(TimeSpan) to cancellationToken.
await UnityWebRequest.Get("http://foo").SendWebRequest()
.WithCancellation(timeoutController.Timeout(TimeSpan.FromSeconds(5)));
timeoutController.Reset(); // call Reset(Stop timeout timer and ready for reuse) when succeed.
}
catch (OperationCanceledException ex)
{
if (timeoutController.IsTimeout())
{
UnityEngine.Debug.Log("timeout");
}
}
}
```
If you want to use timeout with other source of cancellation, use `new TimeoutController(CancellationToken)`.
```csharp
TimeoutController timeoutController;
CancellationTokenSource clickCancelSource;
void Start()
{
this.clickCancelSource = new CancellationTokenSource();
this.timeoutController = new TimeoutController(clickCancelSource);
}
```
Note: UniTask has `.Timeout`, `.TimeoutWithoutException` methods however, if possible, do not use these, please pass `CancellationToken`. Because `.Timeout` work from external of task, can not stop timeoutted task. `.Timeout` means ignore result when timeout. If you pass a `CancellationToken` to the method, it will act from inside of the task, so it is possible to stop a running task.
Progress Progress
--- ---
Some async operation for unity have `ToUniTask(IProgress<float> progress = null, ...)` extension methods. Some async operations for unity have `ToUniTask(IProgress<float> progress = null, ...)` extension methods.
```csharp ```csharp
var progress = Progress.Create<float>(x => Debug.Log(x)); var progress = Progress.Create<float>(x => Debug.Log(x));
@ -291,9 +458,9 @@ var request = await UnityWebRequest.Get("http://google.co.jp")
.ToUniTask(progress: progress); .ToUniTask(progress: progress);
``` ```
You should not use standard `new System.Progress<T>`, because it causes allocation every times. Use `Cysharp.Threading.Tasks.Progress` instead. This progress factory has two methods, `Create` and `CreateOnlyValueChanged`. `CreateOnlyValueChanged` calls only when progress value changed. You should not use standard `new System.Progress<T>`, because it causes allocation every time. Use `Cysharp.Threading.Tasks.Progress` instead. This progress factory has two methods, `Create` and `CreateOnlyValueChanged`. `CreateOnlyValueChanged` calls only when the progress value has changed.
Implements IProgress interface to caller is more better, there is no allocation of lambda. Implementing IProgress interface to caller is better as there is no lambda allocation.
```csharp ```csharp
public class Foo : MonoBehaviour, IProgress<float> public class Foo : MonoBehaviour, IProgress<float>
@ -314,7 +481,7 @@ public class Foo : MonoBehaviour, IProgress<float>
PlayerLoop PlayerLoop
--- ---
UniTask is run on custom [PlayerLoop](https://docs.unity3d.com/ScriptReference/LowLevel.PlayerLoop.html). UniTask's playerloop based method(such as `Delay`, `DelayFrame`, `asyncOperation.ToUniTask`, etc...) accepts this `PlayerLoopTiming`. UniTask is run on a custom [PlayerLoop](https://docs.unity3d.com/ScriptReference/LowLevel.PlayerLoop.html). UniTask's playerloop based methods (such as `Delay`, `DelayFrame`, `asyncOperation.ToUniTask`, etc...) accept this `PlayerLoopTiming`.
```csharp ```csharp
public enum PlayerLoopTiming public enum PlayerLoopTiming
@ -339,28 +506,40 @@ public enum PlayerLoopTiming
PostLateUpdate = 12, PostLateUpdate = 12,
LastPostLateUpdate = 13 LastPostLateUpdate = 13
#if UNITY_2020_2_OR_NEWER
TimeUpdate = 14,
LastTimeUpdate = 15,
#endif
} }
``` ```
It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop. It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop.
`PlayerLoopTiming.Update` is similar as `yield return null` in coroutine, but it is called before Update(Update and uGUI events(button.onClick, etc...) are called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). `PlayerLoopTiming.FixedUpdate` is similar as `WaitForFixedUpdate`, `PlayerLoopTiming.LastPostLateUpdate` is similar as `WaitForEndOfFrame` in coroutine. `PlayerLoopTiming.Update` is similar to `yield return null` in a 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 to `WaitForFixedUpdate`.
`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`. > `PlayerLoopTiming.LastPostLateUpdate` is not equivalent 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(`Texture2D.ReadPixels`, `ScreenCapture.CaptureScreenshotAsTexture`, `CommandBuffer`, etc) do not work correctly when replaced with async/await. In these cases, pass MonoBehaviour(coroutine runnner) to `UniTask.WaitForEndOfFrame`. For example, `await UniTask.WaitForEndOfFrame(this);` is lightweight allocation free alternative of `yield return new WaitForEndOfFrame()`.
>
> Note: In Unity 2023.1 or newer, `await UniTask.WaitForEndOfFrame();` no longer requires MonoBehaviour. It uses `UnityEngine.Awaitable.EndOfFrameAsync`.
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and run on YieldRunner. It is most lightweight and faster. `yield return null` and `UniTask.Yield` are similar but different. `yield return null` always returns next frame but `UniTask.Yield` returns next called. That is, call `UniTask.Yield(PlayerLoopTiming.Update)` on `PreUpdate`, it returns same frame. `UniTask.NextFrame()` guarantees return next frame, you can expect this to behave exactly the same as `yield return null`.
AsyncOperation is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after called, loaded scene's `Start` called from `EarlyUpdate.ScriptRunDelayedStartupFrame`. Also `await UnityWebRequest` is returned from `EarlyUpdate.ExecuteMainThreadJobs`. > UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and runs on YieldRunner. It is the most lightweight and fastest.
In UniTask, await directly and `WithCancellation` use native timing, `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, causes different order of Start and continuation after await. so recommend not to use `LoadSceneAsync.ToUniTask`. `AsyncOperation` is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after being called, the loaded scene's `Start` is called from `EarlyUpdate.ScriptRunDelayedStartupFrame`. Also `await UnityWebRequest` is returned from `EarlyUpdate.ExecuteMainThreadJobs`.
In stacktrace, you can check where is running in playerloop. In UniTask, await directly uses native timing, while `WithCancellation` and `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, it causes a different order of Start and continuation after await. So it is recommended not to use `LoadSceneAsync.ToUniTask`.
> Note: When using Unity 2023.1 or newer, ensure you have `using UnityEngine;` in the using statements of your file when working with new `UnityEngine.Awaitable` methods like `SceneManager.LoadSceneAsync`.
> This prevents compilation errors by avoiding the use of the `UnityEngine.AsyncOperation` version.
In the stacktrace, you can check where it is running in playerloop.
![image](https://user-images.githubusercontent.com/46207/83735571-83caea80-a68b-11ea-8d22-5e22864f0d24.png) ![image](https://user-images.githubusercontent.com/46207/83735571-83caea80-a68b-11ea-8d22-5e22864f0d24.png)
In default, UniTask's PlayerLoop is initialized at `[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]`. By default, UniTask's PlayerLoop is initialized at `[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]`.
The order in which methods are called in BeforeSceneLoad is indeterminate, so if you want to use UniTask in other BeforeSceneLoad methods, you should try to initialize it before this. The order in which methods are called in BeforeSceneLoad is nondeterministic, so if you want to use UniTask in other BeforeSceneLoad methods, you should try to initialize it before this.
```csharp ```csharp
// AfterAssembliesLoaded is called before BeforeSceneLoad // AfterAssembliesLoaded is called before BeforeSceneLoad
@ -372,9 +551,9 @@ public static void InitUniTaskLoop()
} }
``` ```
If you import Unity's `Entities` package, that reset custom player loop to default at `BeforeSceneLoad` and inject ECS's loop. When Unity call ECS's inject method after UniTask's initialize method, UniTask will no longer work. If you import Unity's `Entities` package, that resets the custom player loop to default at `BeforeSceneLoad` and injects ECS's loop. When Unity calls ECS's inject method after UniTask's initialize method, UniTask will no longer work.
To solve this issue, you can re-initialize UniTask PlayerLoop after ECS initialized. To solve this issue, you can re-initialize the UniTask PlayerLoop after ECS is initialized.
```csharp ```csharp
// Get ECS Loop. // Get ECS Loop.
@ -384,9 +563,50 @@ var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
PlayerLoopHelper.Initialize(ref playerLoop); PlayerLoopHelper.Initialize(ref playerLoop);
``` ```
You can diagnose whether UniTask's player loop is ready by calling `PlayerLoopHelper.IsInjectedUniTaskPlayerLoop()`. And also `PlayerLoopHelper.DumpCurrentPlayerLoop` logs all current playerloops to console.
```csharp
void Start()
{
UnityEngine.Debug.Log("UniTaskPlayerLoop ready? " + PlayerLoopHelper.IsInjectedUniTaskPlayerLoop());
PlayerLoopHelper.DumpCurrentPlayerLoop();
}
```
You can optimize loop cost slightly by remove unuse PlayerLoopTiming injection. You can call `PlayerLoopHelper.Initialize(InjectPlayerLoopTimings)` on initialize.
```csharp
var loop = PlayerLoop.GetCurrentPlayerLoop();
PlayerLoopHelper.Initialize(ref loop, InjectPlayerLoopTimings.Minimum); // minimum is Update | FixedUpdate | LastPostLateUpdate
```
`InjectPlayerLoopTimings` has three preset, `All` and `Standard`(All without last except LastPostLateUpdate), `Minimum`(`Update | FixedUpdate | LastPostLateUpdate`). Default is All and you can combine custom inject timings like `InjectPlayerLoopTimings.Update | InjectPlayerLoopTimings.FixedUpdate | InjectPlayerLoopTimings.PreLateUpdate`.
You can make error to use uninjected `PlayerLoopTiming` by [Microsoft.CodeAnalysis.BannedApiAnalyzers](https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md). For example, you can setup `BannedSymbols.txt` like this for `InjectPlayerLoopTimings.Minimum`.
```txt
F:Cysharp.Threading.Tasks.PlayerLoopTiming.Initialization; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastInitialization; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.EarlyUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastEarlyUpdate; Isn't injected this PlayerLoop in this project.d
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastFixedUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.PreUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastPreUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.PreLateUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastPreLateUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.PostLateUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.TimeUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastTimeUpdate; Isn't injected this PlayerLoop in this project.
```
You can configure `RS0030` severity to error.
![image](https://user-images.githubusercontent.com/46207/109150837-bb933880-77ac-11eb-85ba-4fd15819dbd0.png)
async void vs async UniTaskVoid 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()`. `async void` is a standard C# task system so it does not run on UniTask systems. It is better not to use it. `async UniTaskVoid` is a lightweight version of `async UniTask` because it does not have awaitable completion and reports errors immediately to `UniTaskScheduler.UnobservedTaskException`. If you don't require awaiting (fire and forget), using `UniTaskVoid` is better. Unfortunately to dismiss warning, you're required to call `Forget()`.
```csharp ```csharp
public async UniTaskVoid FireAndForgetMethod() public async UniTaskVoid FireAndForgetMethod()
@ -401,7 +621,7 @@ public void Caller()
} }
``` ```
Also UniTask have `Forget` method, it is similar with UniTaskVoid and same effects with it. However still UniTaskVoid is more efficient if completely do not use await Also UniTask has the `Forget` method, it is similar to `UniTaskVoid` and has the same effects. However `UniTaskVoid` is more efficient if you completely don't use `await`
```csharp ```csharp
public async UniTask DoAsync() public async UniTask DoAsync()
@ -416,7 +636,7 @@ public void Caller()
} }
``` ```
Using async lambda in register event, it is used `async void`. To avoid it, you can use `UniTask.Action` or `UniTask.UnityAction` that creates delegate via `async UniTaskVoid` lambda. To use an async lambda registered to an event, don't use `async void`. Instead you can use `UniTask.Action` or `UniTask.UnityAction`, both of which create a delegate via `async UniTaskVoid` lambda.
```csharp ```csharp
Action actEvent; Action actEvent;
@ -431,7 +651,7 @@ actEvent += UniTask.Action(async () => { await UniTask.Yield(); });
unityEvent += UniTask.UnityAction(async () => { await UniTask.Yield(); }); unityEvent += UniTask.UnityAction(async () => { await UniTask.Yield(); });
``` ```
`UniTaskVoid` can also use in MonoBehaviour's `Start` method. `UniTaskVoid` can also be used in MonoBehaviour's `Start` method.
```csharp ```csharp
class Sample : MonoBehaviour class Sample : MonoBehaviour
@ -445,7 +665,7 @@ class Sample : MonoBehaviour
UniTaskTracker UniTaskTracker
--- ---
useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTask Tracker`. useful for checking (leaked) UniTasks. You can open tracker window in `Window -> UniTask Tracker`.
![image](https://user-images.githubusercontent.com/46207/83527073-4434bf00-a522-11ea-86e9-3b3975b26266.png) ![image](https://user-images.githubusercontent.com/46207/83527073-4434bf00-a522-11ea-86e9-3b3975b26266.png)
@ -455,15 +675,16 @@ useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTa
* Enable Tracking(Toggle) - Start to track async/await UniTask. Performance impact: low. * Enable Tracking(Toggle) - Start to track async/await UniTask. Performance impact: low.
* Enable StackTrace(Toggle) - Capture StackTrace when task is started. Performance impact: high. * Enable StackTrace(Toggle) - Capture StackTrace when task is started. Performance impact: high.
For debug use, enable tracking and capture stacktrace is useful but it it decline performance. Recommended usage is enable both to find task leak, and when done, finally disable both. UniTaskTracker is intended for debugging use only as enabling tracking and capturing stacktraces is useful but has a heavy performance impact. Recommended usage is to enable both tracking and stacktraces to find task leaks and to disable them both when done.
External Assets External Assets
--- ---
In default, UniTask supports TextMeshPro(`BindTo(TMP_Text)` and `TMP_InputField` event extensions like standard uGUI `InputField`), DOTween(`Tween` as awaitable) and Addressables(`AsyncOperationHandle` and `AsyncOpereationHandle<T>` as awaitable). By default, UniTask supports TextMeshPro(`BindTo(TMP_Text)` and `TMP_InputField` event extensions like standard uGUI `InputField`), DOTween(`Tween` as awaitable) and Addressables(`AsyncOperationHandle` and `AsyncOperationHandle<T>` as awaitable).
There are defined in separated asmdef like `UniTask.TextMeshPro`, `UniTask.DOTween`, `UniTask.Addressables`. There are defined in separated asmdefs like `UniTask.TextMeshPro`, `UniTask.DOTween`, `UniTask.Addressables`.
TextMeshPro and Addressables support are automatically enabled when import there package from package manager. However DOTween support, require to `com.demigiant.dotween` import from [OpenUPM](https://openupm.com/packages/com.demigiant.dotween/) or define `UNITASK_DOTWEEN_SUPPORT` to enable it. TextMeshPro and Addressables support are automatically enabled when importing their packages from package manager.
However for DOTween support, after importing from the [DOTWeen assets](https://assetstore.unity.com/packages/tools/animation/dotween-hotween-v2-27676r) and define the scripting define symbol `UNITASK_DOTWEEN_SUPPORT` to enable it.
```csharp ```csharp
// sequential // sequential
@ -478,13 +699,15 @@ await UniTask.WhenAll(
transform.DOScale(10, 3).WithCancellation(ct)); transform.DOScale(10, 3).WithCancellation(ct));
``` ```
DOTween support's default behaviour(`await`, `WithCancellation`, `ToUniTask`) awaits tween is killed. It works on both Complete(true/false) and Kill(true/false). But if you want to reuse tweens (`SetAutoKill(false)`), it does not work as expected. If you want to await for another timing, the following extension methods exist in Tween, `AwaitForComplete`, `AwaitForPause`, `AwaitForPlay`, `AwaitForRewind`, `AwaitForStepComplete`.
AsyncEnumerable and Async LINQ 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 the async era.
```csharp ```csharp
// Unity 2020.2.0a12, C# 8.0 // Unity 2020.2, C# 8.0
await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate(token)) await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate().WithCancellation(token))
{ {
Debug.Log("Update() " + Time.frameCount); Debug.Log("Update() " + Time.frameCount);
} }
@ -494,10 +717,23 @@ In a C# 7.3 environment, you can use the `ForEachAsync` method to work in almost
```csharp ```csharp
// C# 7.3(Unity 2018.3~) // C# 7.3(Unity 2018.3~)
await UniTaskAsyncEnumerable.EveryUpdate(token).ForEachAsync(_ => await UniTaskAsyncEnumerable.EveryUpdate().ForEachAsync(_ =>
{ {
Debug.Log("Update() " + Time.frameCount); Debug.Log("Update() " + Time.frameCount);
}); }, token);
```
`UniTask.WhenEach` that is similar to .NET 9's `Task.WhenEach` can consume new way for await multiple tasks.
```csharp
await foreach (var result in UniTask.WhenEach(task1, task2, task3))
{
// The result is of type WhenEachResult<T>.
// It contains either `T Result` or `Exception Exception`.
// You can check `IsCompletedSuccessfully` or `IsFaulted` to determine whether to access `.Result` or `.Exception`.
// If you want to throw an exception when `IsFaulted` and retrieve the result when successful, use `GetResult()`.
Debug.Log(result.GetResult());
}
``` ```
UniTaskAsyncEnumerable implements asynchronous LINQ, similar to LINQ in `IEnumerable<T>` or Rx in `IObservable<T>`. All standard LINQ query operators can be applied to asynchronous streams. For example, the following code shows how to apply a Where filter to a button-click asynchronous stream that runs once every two clicks. UniTaskAsyncEnumerable implements asynchronous LINQ, similar to LINQ in `IEnumerable<T>` or Rx in `IObservable<T>`. All standard LINQ query operators can be applied to asynchronous streams. For example, the following code shows how to apply a Where filter to a button-click asynchronous stream that runs once every two clicks.
@ -508,7 +744,7 @@ await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsy
}); });
``` ```
Fire and Forget style(for example, event handling), also you can use `Subscribe`. Fire and Forget style(for example, event handling), you can also use `Subscribe`.
```csharp ```csharp
okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).Subscribe(_ => okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).Subscribe(_ =>
@ -520,7 +756,7 @@ Async LINQ is enabled when `using Cysharp.Threading.Tasks.Linq;`, and `UniTaskAs
It's closer to UniRx (Reactive Extensions), but UniTaskAsyncEnumerable is a pull-based asynchronous stream, whereas Rx was a push-based asynchronous stream. Note that although similar, the characteristics are different and the details behave differently along with them. It's closer to UniRx (Reactive Extensions), but UniTaskAsyncEnumerable is a pull-based asynchronous stream, whereas Rx was a push-based asynchronous stream. Note that although similar, the characteristics are different and the details behave differently along with them.
`UniTaskAsyncEnumerable` is the entry point like `Enumerbale`. In addition to the standard query operators, there are other generators for Unity such as `EveryUpdate`, `Timer`, `TimerFrame`, `Interval`, `IntervalFrame`, and `EveryValueChanged`. And also added additional UniTask original query operators like `Append`, `Prepend`, `DistinctUntilChanged`, `ToHashSet`, `Buffer`, `CombineLatest`, `Do`, `Never`, `ForEachAsync`, `Pairwise`, `Publish`, `Queue`, `Return`, `SkipUntilCanceled`, `TakeUntilCanceled`, `TakeLast`, `Subscribe`. `UniTaskAsyncEnumerable` is the entry point like `Enumerable`. In addition to the standard query operators, there are other generators for Unity such as `EveryUpdate`, `Timer`, `TimerFrame`, `Interval`, `IntervalFrame`, and `EveryValueChanged`. And also added additional UniTask original query operators like `Append`, `Prepend`, `DistinctUntilChanged`, `ToHashSet`, `Buffer`, `CombineLatest`,`Merge` `Do`, `Never`, `ForEachAsync`, `Pairwise`, `Publish`, `Queue`, `Return`, `SkipUntil`, `TakeUntil`, `SkipUntilCanceled`, `TakeUntilCanceled`, `TakeLast`, `Subscribe`.
The method with Func as an argument has three additional overloads, `***Await`, `***AwaitWithCancellation`. The method with Func as an argument has three additional overloads, `***Await`, `***AwaitWithCancellation`.
@ -532,6 +768,38 @@ SelectAwaitWithCancellation(Func<T, CancellationToken, UniTask<TR>> selector)
If you want to use the `async` method inside the func, use the `***Await` or `***AwaitWithCancellation`. If you want to use the `async` method inside the func, use the `***Await` or `***AwaitWithCancellation`.
How to create an async iterator: C# 8.0 supports async iterator(`async yield return`) but it only allows `IAsyncEnumerable<T>` and of course requires C# 8.0. UniTask supports `UniTaskAsyncEnumerable.Create` method to create custom async iterator.
```csharp
// IAsyncEnumerable, C# 8.0 version of async iterator. ( do not use this style, IAsyncEnumerable is not controled in UniTask).
public async IAsyncEnumerable<int> MyEveryUpdate([EnumeratorCancellation]CancellationToken cancelationToken = default)
{
var frameCount = 0;
await UniTask.Yield();
while (!token.IsCancellationRequested)
{
yield return frameCount++;
await UniTask.Yield();
}
}
// UniTaskAsyncEnumerable.Create and use `await writer.YieldAsync` instead of `yield return`.
public IUniTaskAsyncEnumerable<int> MyEveryUpdate()
{
// writer(IAsyncWriter<T>) has `YieldAsync(value)` method.
return UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
{
var frameCount = 0;
await UniTask.Yield();
while (!token.IsCancellationRequested)
{
await writer.YieldAsync(frameCount++); // instead of `yield return`
await UniTask.Yield();
}
});
}
```
Awaitable Events Awaitable Events
--- ---
All uGUI component implements `***AsAsyncEnumerable` to convert asynchronous streams of events. All uGUI component implements `***AsAsyncEnumerable` to convert asynchronous streams of events.
@ -576,32 +844,13 @@ async UniTask TripleClick(CancellationToken token)
} }
``` ```
All MonoBehaviour message events can convert async-streams by `AsyncTriggers` that can enable by `using Cysharp.Threading.Tasks.Triggers;`. All MonoBehaviour message events can convert async-streams by `AsyncTriggers` that can be enabled by `using Cysharp.Threading.Tasks.Triggers;`. AsyncTrigger can be created using `GetAsync***Trigger` and triggers itself as UniTaskAsyncEnumerable.
```csharp ```csharp
using Cysharp.Threading.Tasks.Triggers; var trigger = this.GetOnCollisionEnterAsyncHandler();
await trigger.OnCollisionEnterAsync();
async UniTaskVoid MonitorCollision() await trigger.OnCollisionEnterAsync();
{ await trigger.OnCollisionEnterAsync();
await gameObject.OnCollisionEnterAsync();
Debug.Log("Collision Enter");
/* do anything */
await gameObject.OnCollisionExitAsync();
Debug.Log("Collision Exit");
}
```
Similar as uGUI event, AsyncTrigger can get by `GetAsync***Trigger` and trigger it self is UniTaskAsyncEnumerable.
```csharp
// use await multiple times, get AsyncTriggerHandler is more efficient.
using(var trigger = this.GetOnCollisionEnterAsyncHandler())
{
await OnCollisionEnterAsync();
await OnCollisionEnterAsync();
await OnCollisionEnterAsync();
}
// every moves. // every moves.
await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData => await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
@ -609,7 +858,7 @@ await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
}); });
``` ```
`AsyncReactiveProperty`, `AsyncReadOnlyReactiveProperty` is UniTask version of UniTask's ReactiveProperty. `BindTo` extension method of `IUniTaskAsyncEnumerable<T>` for binding asynchronous stream values to Unity components(Text/Selectable/TMP/Text). `AsyncReactiveProperty`, `AsyncReadOnlyReactiveProperty` is UniTask's version of ReactiveProperty. `BindTo` extension method of `IUniTaskAsyncEnumerable<T>` for binding asynchronous stream values to Unity components(Text/Selectable/TMP/Text).
```csharp ```csharp
var rp = new AsyncReactiveProperty<int>(99); var rp = new AsyncReactiveProperty<int>(99);
@ -629,9 +878,9 @@ rp.WithoutCurrent().BindTo(this.textComponent);
await rp.WaitAsync(); // wait until next value set await rp.WaitAsync(); // wait until next value set
// also exists ToReadOnlyReactiveProperty // also exists ToReadOnlyAsyncReactiveProperty
var rp2 = new AsyncReactiveProperty<int>(99); var rp2 = new AsyncReactiveProperty<int>(99);
var rorp = rp.CombineLatest(rp2, (x, y) => (x, y)).ToReadOnlyReactiveProperty(); var rorp = rp.CombineLatest(rp2, (x, y) => (x, y)).ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
``` ```
A pull-type asynchronous stream does not get the next values until the asynchronous processing in the sequence is complete. This could spill data from push-type events such as buttons. A pull-type asynchronous stream does not get the next values until the asynchronous processing in the sequence is complete. This could spill data from push-type events such as buttons.
@ -644,9 +893,9 @@ await button.OnClickAsAsyncEnumerable().ForEachAwaitAsync(async x =>
}); });
``` ```
It is useful(prevent double-click) but not useful in sometimes. It is useful (prevent double-click) but not useful sometimes.
Using `Queue()` method, which will also queue events during asynchronous processing. Using the `Queue()` method will also queue events during asynchronous processing.
```csharp ```csharp
// queued message in asynchronous processing // queued message in asynchronous processing
@ -667,13 +916,13 @@ button.OnClickAsAsyncEnumerable().Subscribe(async x =>
Channel Channel
--- ---
`Channel` is same as [System.Threading.Tasks.Channels](https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.channels?view=netcore-3.1) that is similar as GoLang Channel. `Channel` is the same as [System.Threading.Tasks.Channels](https://docs.microsoft.com/en-us/dotnet/api/system.threading.channels?view=netcore-3.1) which is similar to a GoLang Channel.
Currently only supports multiple-producer, single-consumer unbounded channel. It can create by `Channel.CreateSingleConsumerUnbounded<T>()`. Currently it only supports multiple-producer, single-consumer unbounded channels. It can create by `Channel.CreateSingleConsumerUnbounded<T>()`.
For producer(`.Writer`), `TryWrite` to push value and `TryComplete` to complete channel. For consumer(`.Reader`), `TryRead`, `WaitToReadAsync`, `ReadAsync`, `Completion` and `ReadAllAsync` to read queued messages. For producer(`.Writer`), use `TryWrite` to push value and `TryComplete` to complete channel. For consumer(`.Reader`), use `TryRead`, `WaitToReadAsync`, `ReadAsync`, `Completion` and `ReadAllAsync` to read queued messages.
`ReadAllAsync` returns `IUniTaskAsyncEnumerable<T>` so query LINQ operators. Reader only allows single-consumer but use `.Publish()` query operator to enable multicast message. For example, make pub/sub utility. `ReadAllAsync` returns `IUniTaskAsyncEnumerable<T>` so query LINQ operators. Reader only allows single-consumer but uses `.Publish()` query operator to enable multicast message. For example, make pub/sub utility.
```csharp ```csharp
public class AsyncMessageBroker<T> : IDisposable public class AsyncMessageBroker<T> : IDisposable
@ -708,9 +957,17 @@ public class AsyncMessageBroker<T> : IDisposable
} }
``` ```
vs Awaitable
---
Unity 6 introduces the awaitable type, [Awaitable](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Awaitable.html). To put it simply, Awaitable can be considered a subset of UniTask, and in fact, Awaitable's design was influenced by UniTask. It should be able to handle PlayerLoop-based awaits, pooled Tasks, and support for cancellation with `CancellationToken` in a similar way. With its inclusion in the standard library, you may wonder whether to continue using UniTask or migrate to Awaitable. Here's a brief guide.
First, the functionality provided by Awaitable is equivalent to what coroutines offer. Instead of `yield return`, you use await; `await NextFrameAsync()` replaces `yield return null`; and there are equivalents for `WaitForSeconds` and `EndOfFrame`. However, that's the extent of it. Being coroutine-based in terms of functionality, it lacks Task-based features. In practical application development using async/await, operations like `WhenAll` are essential. Additionally, UniTask enables many frame-based operations (such as `DelayFrame`) and more flexible PlayerLoopTiming control, which are not available in Awaitable. Of course, there's no Tracker Window either.
Therefore, I recommend using UniTask for application development. UniTask is a superset of Awaitable and includes many essential features. For library development, where you want to avoid external dependencies, using Awaitable as a return type for methods would be appropriate. Awaitable can be converted to UniTask using `AsUniTask`, so there's no issue in handling Awaitable-based functionality within the UniTask library. Of course, if you don't need to worry about dependencies, using UniTask would be the best choice even for library development.
For Unit Testing For Unit Testing
--- ---
Unity's `[UnityTest]` attribute can test coroutine(IEnumerator) but can not test async. `UniTask.ToCoroutine` bridges async/await to coroutine so you can test async method. Unity's `[UnityTest]` attribute can test coroutine(IEnumerator) but can not test async. `UniTask.ToCoroutine` bridges async/await to coroutine so you can test async methods.
```csharp ```csharp
[UnityTest] [UnityTest]
@ -733,11 +990,34 @@ 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. UniTask's own unit tests are written using Unity Test Runner and [Cysharp/RuntimeUnitTestToolkit](https://github.com/Cysharp/RuntimeUnitTestToolkit) to integrate with CI and check if IL2CPP is working.
ThreadPool limitation
---
Most UniTask methods run on a single thread (PlayerLoop), with only `UniTask.Run`(`Task.Run` equivalent) and `UniTask.SwitchToThreadPool` running on a thread pool. If you use a thread pool, it won't work with WebGL and so on.
`UniTask.Run` is now deprecated. You can use `UniTask.RunOnThreadPool` instead. And also consider whether you can use `UniTask.Create` or `UniTask.Void`.
IEnumerator.ToUniTask limitation
---
You can convert coroutine(IEnumerator) to UniTask(or await directly) but it has some limitations.
* `WaitForEndOfFrame`/`WaitForFixedUpdate`/`Coroutine` is not supported.
* Consuming loop timing is not the same as `StartCoroutine`, it uses the specified `PlayerLoopTiming` and the default `PlayerLoopTiming.Update` is run before MonoBehaviour's `Update` and `StartCoroutine`'s loop.
If you want fully compatible conversion from coroutine to async, use the `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 Editor like an Editor Coroutine. However, there are some limitations.
* UniTask.Delay's DelayType.DeltaTime, UnscaledDeltaTime do not work correctly because they can not get deltaTime in editor. Therefore run on EditMode, automatically change DelayType to `DelayType.Realtime` that wait for the right time.
* All PlayerLoopTiming run on the timing `EditorApplication.update`.
* `-batchmode` with `-quit` does not work because Unity does not run `EditorApplication.update` and quit after a single frame. Instead, don't use `-quit` and quit manually with `EditorApplication.Exit(0)`.
Compare with Standard Task API Compare with Standard Task API
--- ---
UniTask has many standard Task-like APIs. This table shows what is the alternative apis. UniTask has many standard Task-like APIs. This table shows what the alternative apis are.
Use standard type. Use standard type.
@ -771,9 +1051,10 @@ Use UniTask type.
| `IAsyncDisposable` | `IUniTaskAsyncDisposable` | | `IAsyncDisposable` | `IUniTaskAsyncDisposable` |
| `Task.Delay` | `UniTask.Delay` | | `Task.Delay` | `UniTask.Delay` |
| `Task.Yield` | `UniTask.Yield` | | `Task.Yield` | `UniTask.Yield` |
| `Task.Run` | `UniTask.Run` | | `Task.Run` | `UniTask.RunOnThreadPool` |
| `Task.WhenAll` | `UniTask.WhenAll` | | `Task.WhenAll` | `UniTask.WhenAll` |
| `Task.WhenAny` | `UniTask.WhenAny` | | `Task.WhenAny` | `UniTask.WhenAny` |
| `Task.WhenEach` | `UniTask.WhenEach` |
| `Task.CompletedTask` | `UniTask.CompletedTask` | | `Task.CompletedTask` | `UniTask.CompletedTask` |
| `Task.FromException` | `UniTask.FromException` | | `Task.FromException` | `UniTask.FromException` |
| `Task.FromResult` | `UniTask.FromResult` | | `Task.FromResult` | `UniTask.FromResult` |
@ -783,7 +1064,7 @@ Use UniTask type.
Pooling Configuration 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 aggressively caches async promise objects to achieve zero allocation (for 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)). By default, it caches all promises but you can configure `TaskPool.SetMaxPoolSize` to your value, the value indicates cache size per type. `TaskPool.GetCacheSizeInfo` returns currently cached objects in pool.
```csharp ```csharp
foreach (var (type, size) in TaskPool.GetCacheSizeInfo()) foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
@ -792,19 +1073,44 @@ 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 the profiler shows allocation of compiler generated AsyncStateMachine but it only occurs in debug(development) build. C# Compiler generates AsyncStateMachine as class on Debug build and as struct on Release build.
Unity supports Code Optimization option starting in 2020.1 (right, footer).
![](https://user-images.githubusercontent.com/46207/89967342-2f944600-dc8c-11ea-99fc-0b74527a16f6.png)
You can change C# compiler optimization to release to remove AsyncStateMachine allocation in development builds. This optimization option can also be set via `Compilation.CompilationPipeline-codeOptimization`, and `Compilation.CodeOptimization`.
UniTaskSynchronizationContext
---
Unity's default SynchronizationContext(`UnitySynchronizationContext`) is a poor implementation for performance. UniTask bypasses `SynchronizationContext`(and `ExecutionContext`) so it does not use it but if exists in `async Task`, still used it. `UniTaskSynchronizationContext` is a replacement of `UnitySynchronizationContext` which 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 performant than `async UniTask` and is not a complete UniTask replacement. It also does not guarantee full behavioral compatibility with the `UnitySynchronizationContext`.
API References API References
--- ---
UniTask's API References is hosted at [cysharp.github.io/UniTask](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.html) by [DocFX](https://dotnet.github.io/docfx/) and [Cysharp/DocfXTemplate](https://github.com/Cysharp/DocfxTemplate). UniTask's API References are hosted at [cysharp.github.io/UniTask](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.html) by [DocFX](https://dotnet.github.io/docfx/) and [Cysharp/DocfXTemplate](https://github.com/Cysharp/DocfxTemplate).
For example, UniTask's factory methods can see at [UniTask#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.UniTask.html#methods-1). UniTaskAsyncEnumerable's factory/extension methods can see at [UniTaskAsyncEnumerable#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.Linq.UniTaskAsyncEnumerable.html#methods-1). For example, UniTask's factory methods can be seen at [UniTask#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.UniTask.html#methods-1). UniTaskAsyncEnumerable's factory/extension methods can be seen at [UniTaskAsyncEnumerable#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.Linq.UniTaskAsyncEnumerable.html#methods-1).
UPM Package UPM Package
--- ---
### Install via git URL ### Install via git URL
After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of git package. You can add `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask` to Package Manager Requires a version of unity that supports path query parameter for git packages (Unity >= 2019.3.4f1, Unity >= 2020.1a21). You can add `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask` to Package Manager
![image](https://user-images.githubusercontent.com/46207/79450714-3aadd100-8020-11ea-8aae-b8d87fc4d7be.png) ![image](https://user-images.githubusercontent.com/46207/79450714-3aadd100-8020-11ea-8aae-b8d87fc4d7be.png)
@ -812,15 +1118,8 @@ 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`. 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.22`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.0.22`. If you want to set a target version, UniTask uses the `*.*.*` 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
The package is available on the [openupm registry](https://openupm.com). It's recommended to install it via [openupm-cli](https://github.com/openupm/openupm-cli).
```
openupm add com.cysharp.unitask
```
.NET Core .NET Core
--- ---
@ -828,11 +1127,11 @@ For .NET Core, use NuGet.
> PM> Install-Package [UniTask](https://www.nuget.org/packages/UniTask) > PM> Install-Package [UniTask](https://www.nuget.org/packages/UniTask)
UniTask of .NET Core version is a subset of Unity UniTask, removed PlayerLoop dependent methods. UniTask of .NET Core version is a subset of Unity UniTask with PlayerLoop dependent methods removed.
It runs at higher performance than the standard Task/ValueTask, but you should be careful to ignore the ExecutionContext/SynchronizationContext when using it. `AysncLocal` also does not work because it ignores ExecutionContext. It runs at higher performance than the standard Task/ValueTask, but you should be careful to ignore the ExecutionContext/SynchronizationContext when using it. `AsyncLocal` also does not work because it ignores ExecutionContext.
If you use UniTask internally, but provide ValueTask as an external API, you can write like the following(Inspired by [PooledAwait](https://github.com/mgravell/PooledAwait)). If you use UniTask internally, but provide ValueTask as an external API, you can write it like the following(Inspired by [PooledAwait](https://github.com/mgravell/PooledAwait)).
```csharp ```csharp
public class ZeroAllocAsyncAwaitInDotNetCore public class ZeroAllocAsyncAwaitInDotNetCore
@ -865,7 +1164,7 @@ public ValueTask TestAsync()
.NET Core version is intended to allow users to use UniTask as an interface when sharing code with Unity (such as [Cysharp/MagicOnion](https://github.com/Cysharp/MagicOnion/)). .NET Core version of UniTask enables smooth code sharing. .NET Core version is intended to allow users to use UniTask as an interface when sharing code with Unity (such as [Cysharp/MagicOnion](https://github.com/Cysharp/MagicOnion/)). .NET Core version of UniTask enables smooth code sharing.
Utility methods such as WhenAll which is equivalent to UniTask are provided as [Cysharp/ValueTaskSupplement](https://github.com/Cysharp/ValueTaskSupplement). Utility methods such as WhenAll which are equivalent to UniTask are provided as [Cysharp/ValueTaskSupplement](https://github.com/Cysharp/ValueTaskSupplement).
License License
--- ---

1170
README_CN.md Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,15 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 17
VisualStudioVersion = 16.0.29613.14 VisualStudioVersion = 17.0.31606.5
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.NetCoreTests", "src\UniTask.NetCoreTests\UniTask.NetCoreTests.csproj", "{B3E311A4-70D8-4131-9965-C073A99D201A}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCoreTests", "src\UniTask.NetCoreTests\UniTask.NetCoreTests.csproj", "{B3E311A4-70D8-4131-9965-C073A99D201A}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.NetCoreSandbox", "src\UniTask.NetCoreSandbox\UniTask.NetCoreSandbox.csproj", "{3915E72E-33E0-4A14-A6D8-872702200E58}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCoreSandbox", "src\UniTask.NetCoreSandbox\UniTask.NetCoreSandbox.csproj", "{3915E72E-33E0-4A14-A6D8-872702200E58}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.Analyzer", "src\UniTask.Analyzer\UniTask.Analyzer.csproj", "{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -27,6 +29,10 @@ Global
{3915E72E-33E0-4A14-A6D8-872702200E58}.Debug|Any CPU.Build.0 = Debug|Any CPU {3915E72E-33E0-4A14-A6D8-872702200E58}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.ActiveCfg = Release|Any CPU {3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.Build.0 = Release|Any CPU {3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.Build.0 = Release|Any CPU
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -0,0 +1,8 @@
{
"profiles": {
"UniTask.Analyzer": {
"commandName": "DebugRoslynComponent",
"targetProject": "..\\UniTask.NetCoreSandbox\\UniTask.NetCoreSandbox.csproj"
}
}
}

View File

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>library</OutputType>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<IsRoslynComponent>true</IsRoslynComponent>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);PackBuildOutputs</TargetsForTfmSpecificContentInPackage>
<IncludeBuildOutput>false</IncludeBuildOutput>
<IncludeSymbols>false</IncludeSymbols>
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
<DevelopmentDependency>true</DevelopmentDependency>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" />
</ItemGroup>
<Target Name="PackBuildOutputs" DependsOnTargets="SatelliteDllsProjectOutputGroup;DebugSymbolsProjectOutputGroup">
<ItemGroup>
<TfmSpecificPackageFile Include="$(TargetDir)\*.dll" PackagePath="analyzers\dotnet\cs" />
<TfmSpecificPackageFile Include="@(SatelliteDllsProjectOutputGroupOutput->'%(FinalOutputPath)')" PackagePath="analyzers\dotnet\cs\%(SatelliteDllsProjectOutputGroupOutput.Culture)\" />
</ItemGroup>
</Target>
</Project>

View File

@ -0,0 +1,54 @@
#pragma warning disable RS2008
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using System.Collections.Immutable;
using System.Threading;
namespace UniTask.Analyzer
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class UniTaskAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
id: "UNITASK001",
title: "UniTaskAnalyzer001: Must pass CancellationToken",
messageFormat: "Must pass CancellationToken",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "Pass CancellationToken or CancellationToken.None.");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterOperationAction(AnalyzeOperation, OperationKind.Invocation);
}
private static void AnalyzeOperation(OperationAnalysisContext context)
{
var token = context.Compilation.GetTypeByMetadataName(typeof(CancellationToken).FullName);
if (token == null) return;
if (context.Operation is IInvocationOperation invocation)
{
foreach (var arg in invocation.Arguments)
{
if (arg.ArgumentKind == ArgumentKind.DefaultValue)
{
if (SymbolEqualityComparer.Default.Equals(arg.Parameter.Type, token))
{
var diagnostic = Diagnostic.Create(Rule, arg.Syntax.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
}
}
}
}
}

View File

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

View File

@ -1,10 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netcoreapp3.1;netstandard2.1;netstandard2.0</TargetFrameworks> <TargetFrameworks>net6.0;net7.0;netstandard2.1;netstandard2.0</TargetFrameworks>
<AssemblyName>UniTask</AssemblyName> <AssemblyName>UniTask</AssemblyName>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace> <RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
<DefineConstants>UNITASK_NETCORE</DefineConstants>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<!-- NuGet Packaging --> <!-- NuGet Packaging -->
<Id>UniTask</Id> <Id>UniTask</Id>
@ -19,6 +22,9 @@
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Icon.png</PackageIcon> <PackageIcon>Icon.png</PackageIcon>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>opensource.snk</AssemblyOriginatorKeyFile>
<IsPackable>true</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -26,28 +32,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\UniTask\Assets\Plugins\UniTask\Runtime\**\*.cs" <Compile Include="..\UniTask\Assets\Plugins\UniTask\Runtime\**\*.cs" Exclude="&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Editor\*.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Triggers\*.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Linq\UnityExtensions\*.cs;&#xD;&#xA; &#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityEqualityComparer.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\DiagnosticsExtensions.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\PlayerLoopRunner.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\ContinuationQueue.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityWebRequestExtensions.cs;&#xD;&#xA; &#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTaskSynchronizationContext.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\CancellationTokenSourceExtensions.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\EnumeratorAsyncExtensions.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\TimeoutController.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopHelper.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopTimer.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Delay.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.*;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;&#xD;&#xA;" />
Exclude=" <Compile Remove="..\UniTask\Assets\Plugins\UniTask\Runtime\_InternalVisibleTo.cs" />
..\UniTask\Assets\Plugins\UniTask\Editor\*.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Triggers\*.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Linq\UnityExtensions\*.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityEqualityComparer.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\DiagnosticsExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\PlayerLoopRunner.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\ContinuationQueue.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityWebRequestExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\CancellationTokenSourceExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\EnumeratorAsyncExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopHelper.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Delay.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.*;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;
" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

Binary file not shown.

View File

@ -1,258 +0,0 @@
using BenchmarkDotNet.Attributes;
using System.Linq;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using Cysharp.Threading.Tasks;
using PooledAwait;
using System;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.CompilerServices;
using Cysharp.Threading.Tasks.CompilerServices;
using System.Collections.Concurrent;
[Config(typeof(BenchmarkConfig))]
public class AllocationCheck
{
// note: all the benchmarks use Task/Task<T> for the public API, because BenchmarkDotNet
// doesn't work reliably with more exotic task-types (even just ValueTask fails); instead,
// we'll obscure the cost of the outer awaitable by doing a relatively large number of
// iterations, so that we're only really measuring the inner loop
private const int InnerOps = 1000;
[Benchmark(OperationsPerInvoke = InnerOps)]
public async Task ViaUniTask()
{
for (int i = 0; i < InnerOps; i++)
{
var a = Core();
var b = Core();
var c = Core();
await a;
await b;
await c;
}
static async UniTask Core()
{
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
}
}
[Benchmark(OperationsPerInvoke = InnerOps)]
public async Task<int> ViaUniTaskT()
{
var sum = 0;
for (int i = 0; i < InnerOps; i++)
{
var a = Core();
var b = Core();
var c = Core();
sum += await a;
sum += await b;
sum += await c;
}
return sum;
static async UniTask<int> Core()
{
var a = await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
var b = await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
var c = await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
return 10;
}
}
//[Benchmark(OperationsPerInvoke = InnerOps)]
//[Benchmark]
public void ViaUniTaskVoid()
{
for (int i = 0; i < InnerOps; i++)
{
Core().Forget();
Core().Forget();
Core().Forget();
}
static async UniTaskVoid Core()
{
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
}
}
struct Foo : IAsyncStateMachine
{
public AsyncUniTaskVoidMethodBuilder builder;
public TestAwaiter awaiter;
public TestAwaiter awaiterawaiter;
public int state;
public void MoveNext()
{
switch (state)
{
case -1:
awaiterawaiter = awaiter.GetAwaiter();
if (awaiterawaiter.IsCompleted)
{
goto case 0;
}
else
{
state = 0;
builder.AwaitUnsafeOnCompleted(ref awaiterawaiter, ref this);
return;
}
case 0:
default:
goto END;
}
END:
builder.SetResult();
}
public void SetStateMachine(IAsyncStateMachine stateMachine)
{
}
}
}
public class TaskTestException : Exception
{
}
public struct TestAwaiter : ICriticalNotifyCompletion
{
readonly UniTaskStatus status;
readonly bool isCompleted;
public TestAwaiter(bool isCompleted, UniTaskStatus status)
{
this.isCompleted = isCompleted;
this.status = status;
}
public TestAwaiter GetAwaiter() => this;
public bool IsCompleted => isCompleted;
public void GetResult()
{
switch (status)
{
case UniTaskStatus.Faulted:
throw new TaskTestException();
case UniTaskStatus.Canceled:
throw new OperationCanceledException();
case UniTaskStatus.Pending:
case UniTaskStatus.Succeeded:
default:
break;
}
}
public void OnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
}
public void UnsafeOnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
}
}
public struct TestAwaiter<T> : ICriticalNotifyCompletion
{
readonly UniTaskStatus status;
readonly bool isCompleted;
readonly T value;
public TestAwaiter(bool isCompleted, UniTaskStatus status, T value)
{
this.isCompleted = isCompleted;
this.status = status;
this.value = value;
}
public TestAwaiter<T> GetAwaiter() => this;
public bool IsCompleted => isCompleted;
public T GetResult()
{
switch (status)
{
case UniTaskStatus.Faulted:
throw new TaskTestException();
case UniTaskStatus.Canceled:
throw new OperationCanceledException();
case UniTaskStatus.Pending:
case UniTaskStatus.Succeeded:
default:
return value;
}
}
public void OnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
}
public void UnsafeOnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
}
}
public sealed class ThreadPoolWorkItem : IThreadPoolWorkItem
{
public static readonly ConcurrentQueue<ThreadPoolWorkItem> pool = new ConcurrentQueue<ThreadPoolWorkItem>();
public static void CreatePoolItems(int count)
{
for (int i = 0; i < count; i++)
{
pool.Enqueue(new ThreadPoolWorkItem());
}
}
Action continuation;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ThreadPoolWorkItem Create(Action continuation)
{
if (!pool.TryDequeue(out var item))
{
item = new ThreadPoolWorkItem();
}
item.continuation = continuation;
return item;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Execute()
{
var call = continuation;
continuation = null;
pool.Enqueue(this);
call.Invoke();
}
}

View File

@ -1,283 +0,0 @@
using BenchmarkDotNet.Attributes;
using System.Linq;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using Cysharp.Threading.Tasks;
using PooledAwait;
using System;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.CompilerServices;
using Cysharp.Threading.Tasks.CompilerServices;
//class Program
//{
// static void Main(string[] args)
// {
// var switcher = new BenchmarkSwitcher(new[]
// {
// typeof(StandardBenchmark)
// });
//#if DEBUG
// var b = new StandardBenchmark();
//#else
// switcher.Run(args);
//#endif
// }
//}
public class BenchmarkConfig : ManualConfig
{
public BenchmarkConfig()
{
AddDiagnoser(MemoryDiagnoser.Default);
AddJob(Job.ShortRun.WithLaunchCount(1).WithIterationCount(1).WithWarmupCount(1)/*.RunOncePerIteration()*/);
}
}
// borrowed from PooledAwait
[Config(typeof(BenchmarkConfig))]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
[CategoriesColumn]
public class ComparisonBenchmarks
{
// note: all the benchmarks use Task/Task<T> for the public API, because BenchmarkDotNet
// doesn't work reliably with more exotic task-types (even just ValueTask fails); instead,
// we'll obscure the cost of the outer awaitable by doing a relatively large number of
// iterations, so that we're only really measuring the inner loop
private const int InnerOps = 1000;
public bool ConfigureAwait { get; set; } = false;
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
[BenchmarkCategory("Task<T>")]
public async Task<int> ViaTaskT()
{
int sum = 0;
for (int i = 0; i < InnerOps; i++)
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
return sum;
static async Task<int> Inner(int x, int y)
{
int i = x;
await Task.Yield();
i *= y;
await Task.Yield();
return 5 * i;
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
[BenchmarkCategory("Task")]
public async Task ViaTask()
{
for (int i = 0; i < InnerOps; i++)
await Inner().ConfigureAwait(ConfigureAwait);
static async Task Inner()
{
await Task.Yield();
await Task.Yield();
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
[BenchmarkCategory("ValueTask<T>")]
public async Task<int> ViaValueTaskT()
{
int sum = 0;
for (int i = 0; i < InnerOps; i++)
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
return sum;
static async ValueTask<int> Inner(int x, int y)
{
int i = x;
await Task.Yield();
i *= y;
await Task.Yield();
return 5 * i;
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
[BenchmarkCategory("ValueTask")]
public async Task ViaValueTask()
{
for (int i = 0; i < InnerOps; i++)
await Inner().ConfigureAwait(ConfigureAwait);
static async ValueTask Inner()
{
await Task.Yield();
await Task.Yield();
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
[BenchmarkCategory("ValueTask<T>")]
public async Task<int> ViaPooledValueTaskT()
{
int sum = 0;
for (int i = 0; i < InnerOps; i++)
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
return sum;
static async PooledValueTask<int> Inner(int x, int y)
{
int i = x;
await Task.Yield();
i *= y;
await Task.Yield();
return 5 * i;
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
[BenchmarkCategory("ValueTask")]
public async Task ViaPooledValueTask()
{
for (int i = 0; i < InnerOps; i++)
await Inner().ConfigureAwait(ConfigureAwait);
static async PooledValueTask Inner()
{
await Task.Yield();
await Task.Yield();
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
[BenchmarkCategory("Task<T>")]
public async Task<int> ViaPooledTaskT()
{
int sum = 0;
for (int i = 0; i < InnerOps; i++)
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
return sum;
static async PooledTask<int> Inner(int x, int y)
{
int i = x;
await Task.Yield();
i *= y;
await Task.Yield();
return 5 * i;
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
[BenchmarkCategory("Task")]
public async Task ViaPooledTask()
{
for (int i = 0; i < InnerOps; i++)
await Inner().ConfigureAwait(ConfigureAwait);
static async PooledTask Inner()
{
await Task.Yield();
await Task.Yield();
}
}
// ---
//[Benchmark(OperationsPerInvoke = InnerOps, Description = "UniTaskVoid")]
//[BenchmarkCategory("UniTask")]
//public async Task ViaUniTaskVoid()
//{
// for (int i = 0; i < InnerOps; i++)
// {
// await Inner();
// }
// static async UniTaskVoid Inner()
// {
// await UniTask.Yield();
// await UniTask.Yield();
// }
//}
[Benchmark(OperationsPerInvoke = InnerOps, Description = "UniTask")]
[BenchmarkCategory("UniTask")]
public async Task ViaUniTask()
{
for (int i = 0; i < InnerOps; i++)
{
await Inner();
}
static async UniTask Inner()
{
await UniTask.Yield();
await UniTask.Yield();
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = "UniTaskT")]
[BenchmarkCategory("UniTask")]
public async Task<int> ViaUniTaskT()
{
var sum = 0;
for (int i = 0; i < InnerOps; i++)
{
sum += await Inner(1, 2);
}
return sum;
static async UniTask<int> Inner(int x, int y)
{
int i = x;
await UniTask.Yield();
i *= y;
await UniTask.Yield();
return 5 * i;
}
}
}
public struct MyAwaiter : ICriticalNotifyCompletion
{
public MyAwaiter GetAwaiter() => this;
public bool IsCompleted => false;
public void GetResult()
{
}
public void OnCompleted(Action continuation)
{
continuation();
}
public void UnsafeOnCompleted(Action continuation)
{
continuation();
}
}
public struct MyTestStateMachine : IAsyncStateMachine
{
public void MoveNext()
{
//throw new NotImplementedException();
}
public void SetStateMachine(IAsyncStateMachine stateMachine)
{
//throw new NotImplementedException();
}
}

View File

@ -1,4 +1,6 @@
using Cysharp.Threading.Tasks; #pragma warning disable CS1998
using Cysharp.Threading.Tasks;
using System.Linq; using System.Linq;
using System; using System;
@ -15,509 +17,34 @@ using System.Reactive.Concurrency;
namespace NetCoreSandbox namespace NetCoreSandbox
{ {
public class MySyncContext : SynchronizationContext public class Program
{ {
public MySyncContext()
{
}
public override void Post(SendOrPostCallback d, object state)
{
Console.WriteLine("Called SyncContext Post!");
base.Post(d, state);
}
}
public class Text
{
public string text { get; set; }
}
public class ZeroAllocAsyncAwaitInDotNetCore
{
public ValueTask<int> NanikaAsync(int x, int y)
{
return Core(this, x, y);
static async UniTask<int> Core(ZeroAllocAsyncAwaitInDotNetCore self, int x, int y)
{
// nanika suru...
await Task.Delay(TimeSpan.FromSeconds(x + y));
return 10;
}
}
}
public class TaskTestException : Exception
{
}
public struct TestAwaiter : ICriticalNotifyCompletion
{
readonly UniTaskStatus status;
readonly bool isCompleted;
public TestAwaiter(bool isCompleted, UniTaskStatus status)
{
this.isCompleted = isCompleted;
this.status = status;
}
public TestAwaiter GetAwaiter() => this;
public bool IsCompleted => isCompleted;
public void GetResult()
{
switch (status)
{
case UniTaskStatus.Faulted:
throw new TaskTestException();
case UniTaskStatus.Canceled:
throw new OperationCanceledException();
case UniTaskStatus.Pending:
case UniTaskStatus.Succeeded:
default:
break;
}
}
public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
}
public void UnsafeOnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
}
}
public struct TestAwaiter<T> : ICriticalNotifyCompletion
{
readonly UniTaskStatus status;
readonly bool isCompleted;
readonly T value;
public TestAwaiter(bool isCompleted, UniTaskStatus status, T value)
{
this.isCompleted = isCompleted;
this.status = status;
this.value = value;
}
public TestAwaiter<T> GetAwaiter() => this;
public bool IsCompleted => isCompleted;
public T GetResult()
{
switch (status)
{
case UniTaskStatus.Faulted:
throw new TaskTestException();
case UniTaskStatus.Canceled:
throw new OperationCanceledException();
case UniTaskStatus.Pending:
case UniTaskStatus.Succeeded:
default:
return value;
}
}
public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
}
public void UnsafeOnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
}
}
public static partial class UnityUIComponentExtensions
{
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text)
{
AAAACORECORE(source, text).Forget();
async UniTaskVoid AAAACORECORE(IUniTaskAsyncEnumerable<string> source2, Text text2)
{
var e = source2.GetAsyncEnumerator();
try
{
while (await e.MoveNextAsync())
{
text2.text = e.Current;
// action(e.Current);
}
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
}
//public static IDisposable SubscribeToText<T>(this IObservable<T> source, Text text)
//{
// return source.SubscribeWithState(text, (x, t) => t.text = x.ToString());
//}
//public static IDisposable SubscribeToText<T>(this IObservable<T> source, Text text, Func<T, string> selector)
//{
// return source.SubscribeWithState2(text, selector, (x, t, s) => t.text = s(x));
//}
//public static IDisposable SubscribeToInteractable(this IObservable<bool> source, Selectable selectable)
//{
// return source.SubscribeWithState(selectable, (x, s) => s.interactable = x);
//}
}
class Program
{
static string FlattenGenArgs(Type type)
{
if (type.IsGenericType)
{
var t = string.Join(", ", type.GetGenericArguments().Select(x => FlattenGenArgs(x)));
return Regex.Replace(type.Name, "`.+", "") + "<" + t + ">";
}
//x.ReturnType.GetGenericArguments()
else
{
return type.Name;
}
}
static async IAsyncEnumerable<int> FooAsync([EnumeratorCancellation]CancellationToken cancellationToken = default)
{
yield return 1;
await Task.Delay(10, cancellationToken);
}
public class MyDisposable : IDisposable
{
public void Dispose()
{
}
}
static void Test()
{
var disp = new MyDisposable();
using var _ = new MyDisposable();
Console.WriteLine("tako");
}
static async UniTask FooBarAsync()
{
await using (UniTask.ReturnToCurrentSynchronizationContext())
{
await UniTask.SwitchToThreadPool();
}
}
static async UniTask Aaa()
{
await FooBarAsync();
Console.WriteLine("FooBarAsync End");
}
static async UniTask WhereSelect()
{
await foreach (var item in UniTaskAsyncEnumerable.Range(1, 10)
.SelectAwait(async x =>
{
await UniTask.Yield();
return x;
})
.Where(x => x % 2 == 0))
{
Console.WriteLine(item);
}
}
static async Task Main(string[] args) static async Task Main(string[] args)
{ {
#if !DEBUG var cts = new CancellationTokenSource();
// OK.
await FooAsync(10, cts.Token);
// NG(Compiler Error)
// await FooAsync(10);
//await new AllocationCheck().ViaUniTaskVoid();
//Console.ReadLine();
BenchmarkDotNet.Running.BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
//await new ComparisonBenchmarks().ViaUniTaskT();
return;
#endif
// await new AllocationCheck().ViaUniTaskVoid();
// AsyncTest().Forge
Console.WriteLine("A?");
var a = await new ZeroAllocAsyncAwaitInDotNetCore().NanikaAsync(1, 2);
Console.WriteLine("RET:" + a);
await WhereSelect();
SynchronizationContext.SetSynchronizationContext(new MySyncContext());
await Aaa();
//AsyncTest().Forget();
// AsyncTest().Forget();
ThreadPool.SetMinThreads(100, 100);
//List<UniTask<int>> list = new List<UniTask<int>>();
for (int i = 0; i < short.MaxValue; i++)
{
//// list.Add(AsyncTest());
await YieldCore();
}
//await UniTask.WhenAll(list);
//Console.WriteLine("TOGO");
//var a = await AsyncTest();
//var b = AsyncTest();
//var c = AsyncTest();
await YieldCore();
//await b;
//await c;
//foreach (var item in Cysharp.Threading.Tasks.Internal.TaskPool.GetCacheSizeInfo())
//{
// Console.WriteLine(item);
//}
Console.ReadLine();
} }
static async UniTask YieldCore() static async UniTask FooAsync(int x, CancellationToken cancellationToken = default)
{ {
await UniTask.Yield(); await UniTask.Yield();
} }
#pragma warning disable CS1998
static async UniTask<int> AsyncTest()
{
// empty
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(true, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
return 10;
} }
#pragma warning restore CS1998
void Foo()
{
// AsyncEnumerable.Range(1,10).Do(
// AsyncEnumerable.t
var sb = new StringBuilder();
sb.AppendLine(@"using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Linq
{
");
var chako = typeof(AsyncEnumerable).GetMethods()
.OrderBy(x => x.Name)
.Select(x =>
{
var ret = FlattenGenArgs(x.ReturnType);
var generics = string.Join(", ", x.GetGenericArguments().Select(x => x.Name));
if (x.GetParameters().Length == 0) return "";
var self = x.GetParameters().First();
if (x.GetCustomAttributes(typeof(ExtensionAttribute), true).Length == 0)
{
return "";
}
var arg1Type = FlattenGenArgs(x.GetParameters().First().ParameterType);
var others = string.Join(", ", x.GetParameters().Skip(1).Select(y => FlattenGenArgs(y.ParameterType) + " " + y.Name));
if (!string.IsNullOrEmpty(others))
{
others = ", " + others;
}
var template = $"public static {ret} {x.Name}<{generics}>(this {arg1Type} {self.Name}{others})";
return template.Replace("ValueTask", "UniTask").Replace("IAsyncEnumerable", "IUniTaskAsyncEnumerable").Replace("<>", "");
})
.Where(x => x != "")
.Select(x => x + "\r\n{\r\n throw new NotImplementedException();\r\n}")
.ToArray();
var huga = string.Join("\r\n\r\n", chako);
foreach (var item in typeof(AsyncEnumerable).GetMethods().Select(x => x.Name).Distinct())
{
if (item.EndsWith("AwaitAsync") || item.EndsWith("AwaitWithCancellationAsync") || item.EndsWith("WithCancellation"))
{
continue;
}
var item2 = item.Replace("Async", "");
item2 = item2.Replace("Await", "");
var format = @"
internal sealed class {0}
{{
}}
";
sb.Append(string.Format(format, item2));
}
sb.Append("}");
Console.WriteLine(sb.ToString());
}
public static async IAsyncEnumerable<int> AsyncGen()
{
await UniTask.SwitchToThreadPool();
yield return 10;
await UniTask.SwitchToThreadPool();
yield return 100;
}
}
class MyEnumerable : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
return new MyEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
class MyEnumerator : IEnumerator<int>
{
public int Current => throw new NotImplementedException();
object IEnumerator.Current => throw new NotImplementedException();
public void Dispose()
{
Console.WriteLine("Called Dispose");
}
public bool MoveNext()
{
throw new NotImplementedException();
}
public void Reset()
{
throw new NotImplementedException();
}
}
public class MyClass<T>
{
public CustomAsyncEnumerator<T> GetAsyncEnumerator()
{
//IAsyncEnumerable
return new CustomAsyncEnumerator<T>();
}
}
public struct CustomAsyncEnumerator<T>
{
int count;
public T Current
{
get
{
return default;
}
}
public UniTask<bool> MoveNextAsync()
{
if (count++ == 3)
{
return UniTask.FromResult(false);
//return false;
}
return UniTask.FromResult(true);
}
public UniTask DisposeAsync()
{
return default;
}
}
} }

View File

@ -1,396 +0,0 @@
using BenchmarkDotNet.Attributes;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
[Config(typeof(BenchmarkConfig))]
public class QueueCheck
{
Node node1 = new Node();
Node node2 = new Node();
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 TaskPoolEqualNull<Node> poolEqualNull;
static TaskPoolClass<Node> poolClass = new TaskPoolClass<Node>();
static TaskPoolWithoutSize<Node> poolWithoutSize;
static TaskPoolWithoutLock<Node> poolWithoutLock;
[Benchmark]
public void Queue()
{
q1.Enqueue(node1);
q1.Enqueue(node1);
q1.TryDequeue(out _);
q1.TryDequeue(out _);
}
[Benchmark]
public void QueueLock()
{
lock (q1) { q1.Enqueue(node1); }
lock (q1) { q1.Enqueue(node1); }
lock (q1) { q1.TryDequeue(out _); }
lock (q1) { q1.TryDequeue(out _); }
}
[Benchmark]
public void Stack()
{
s1.Push(node1);
s1.Push(node2);
s1.TryPop(out _);
s1.TryPop(out _);
}
[Benchmark]
public void StackLock()
{
lock (s1) { s1.Push(node1); }
lock (s1) { s1.Push(node2); }
lock (s1) { s1.TryPop(out _); }
lock (s1) { s1.TryPop(out _); }
}
[Benchmark]
public void ConcurrentQueue()
{
cq.Enqueue(node1);
cq.Enqueue(node1);
cq.TryDequeue(out _);
cq.TryDequeue(out _);
}
[Benchmark]
public void ConcurrentStack()
{
cs.Push(node1);
cs.Push(node2);
cs.TryPop(out _);
cs.TryPop(out _);
}
[Benchmark]
public void TaskPool()
{
pool.TryPush(node1);
pool.TryPush(node2);
pool.TryPop(out _);
pool.TryPop(out _);
}
[Benchmark]
public void TaskPoolEqualNull()
{
poolEqualNull.TryPush(node1);
poolEqualNull.TryPush(node2);
poolEqualNull.TryPop(out _);
poolEqualNull.TryPop(out _);
}
[Benchmark]
public void TaskPoolClass()
{
poolClass.TryPush(node1);
poolClass.TryPush(node2);
poolClass.TryPop(out _);
poolClass.TryPop(out _);
}
[Benchmark]
public void TaskPoolWithoutSize()
{
poolWithoutSize.TryPush(node1);
poolWithoutSize.TryPush(node2);
poolWithoutSize.TryPop(out _);
poolWithoutSize.TryPop(out _);
}
[Benchmark]
public void TaskPoolWithoutLock()
{
poolWithoutLock.TryPush(node1);
poolWithoutLock.TryPush(node2);
poolWithoutLock.TryPop(out _);
poolWithoutLock.TryPop(out _);
}
}
public sealed class Node : ITaskPoolNode<Node>
{
public Node NextNode { get; set; }
}
public interface ITaskPoolNode<T>
{
T NextNode { get; set; }
}
// mutable struct, don't mark readonly.
[StructLayout(LayoutKind.Auto)]
public struct TaskPoolWithoutLock<T>
where T : class, ITaskPoolNode<T>
{
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
root = v.NextNode;
v.NextNode = null;
size--;
result = v;
// Volatile.Write(ref gate, 0);
return true;
}
//Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
// Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
//return false;
}
}
[StructLayout(LayoutKind.Auto)]
public struct TaskPool<T>
where T : class, ITaskPoolNode<T>
{
int gate;
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
root = v.NextNode;
v.NextNode = null;
size--;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
return false;
}
}
[StructLayout(LayoutKind.Auto)]
public struct TaskPoolEqualNull<T>
where T : class, ITaskPoolNode<T>
{
int gate;
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (v != null)
{
root = v.NextNode;
v.NextNode = null;
size--;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
return false;
}
}
public class TaskPoolClass<T>
where T : class, ITaskPoolNode<T>
{
int gate;
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
root = v.NextNode;
v.NextNode = null;
size--;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
return false;
}
}
[StructLayout(LayoutKind.Auto)]
public struct TaskPoolWithoutSize<T>
where T : class, ITaskPoolNode<T>
{
int gate;
T root;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
root = v.NextNode;
v.NextNode = null;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
return false;
}
}

View File

@ -2,8 +2,9 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<RootNamespace>NetCoreSandbox</RootNamespace> <RootNamespace>NetCoreSandbox</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -15,6 +16,12 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" /> <ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
<ProjectReference Include="..\UniTask.Analyzer\UniTask.Analyzer.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<OutputItemType>Analyzer</OutputItemType>
</ProjectReference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -31,7 +31,7 @@ namespace NetCoreTests
var ar = await array; var ar = await array;
ar.Should().BeEquivalentTo(new[] { 99, 100, 100, 100, 131 }); ar.Should().Equal(new[] { 99, 100, 100, 100, 131 });
} }
[Fact] [Fact]
@ -49,7 +49,7 @@ namespace NetCoreTests
var ar = await array; var ar = await array;
ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 }); ar.Should().Equal(new[] { 100, 100, 100, 131, 191 });
} }
//[Fact] //[Fact]
@ -70,7 +70,7 @@ namespace NetCoreTests
// var ar = await array; // var ar = await array;
// ar.Should().BeEquivalentTo(new[] { 99, 100, 100, 100, 131 }); // ar.Should().Equal(new[] { 99, 100, 100, 100, 131 });
//} //}
//[Fact] //[Fact]
@ -88,7 +88,7 @@ namespace NetCoreTests
// var ar = await array; // var ar = await array;
// ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 }); // ar.Should().Equal(new[] { 100, 100, 100, 131, 191 });
//} //}

View File

@ -276,8 +276,8 @@ namespace NetCoreTests
reference.Writer.TryComplete(); reference.Writer.TryComplete();
channel.Writer.TryComplete(); channel.Writer.TryComplete();
(await ta1).Should().BeEquivalentTo(new[] { 10, 20, 30 }); (await ta1).Should().Equal(new[] { 10, 20, 30 });
(await ta2).Should().BeEquivalentTo(new[] { 10, 20, 30 }); (await ta2).Should().Equal(new[] { 10, 20, 30 });
} }
[Fact] [Fact]

View File

@ -481,7 +481,7 @@ namespace NetCoreTests.Linq
list.Add(x); list.Add(x);
}); });
list.Should().BeEquivalentTo(Enumerable.Range(1, 10)); list.Should().Equal(Enumerable.Range(1, 10));
var list2 = new List<(int, int)>(); var list2 = new List<(int, int)>();
await Enumerable.Range(5, 10).ToUniTaskAsyncEnumerable().ForEachAsync((index, x) => await Enumerable.Range(5, 10).ToUniTaskAsyncEnumerable().ForEachAsync((index, x) =>
@ -490,7 +490,7 @@ namespace NetCoreTests.Linq
}); });
var list3 = Enumerable.Range(5, 10).Select((index, x) => (index, x)).ToArray(); var list3 = Enumerable.Range(5, 10).Select((index, x) => (index, x)).ToArray();
list2.Should().BeEquivalentTo(list3); list2.Should().Equal(list3);
} }
} }
} }

View File

@ -24,7 +24,7 @@ namespace NetCoreTests.Linq
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Append(99).ToArrayAsync(); var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Append(99).ToArrayAsync();
var ys = Enumerable.Range(start, count).Append(99).ToArray(); var ys = Enumerable.Range(start, count).Append(99).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
[Fact] [Fact]
@ -50,7 +50,7 @@ namespace NetCoreTests.Linq
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Prepend(99).ToArrayAsync(); var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Prepend(99).ToArrayAsync();
var ys = Enumerable.Range(start, count).Prepend(99).ToArray(); var ys = Enumerable.Range(start, count).Prepend(99).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
[Fact] [Fact]
@ -85,7 +85,7 @@ namespace NetCoreTests.Linq
var xs = await l.ToUniTaskAsyncEnumerable().Concat(r.ToUniTaskAsyncEnumerable()).ToArrayAsync(); var xs = await l.ToUniTaskAsyncEnumerable().Concat(r.ToUniTaskAsyncEnumerable()).ToArrayAsync();
var ys = l.Concat(r).ToArray(); var ys = l.Concat(r).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
[Fact] [Fact]
@ -119,17 +119,17 @@ namespace NetCoreTests.Linq
{ {
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync(); var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
var ys = Enumerable.Range(1, 0).DefaultIfEmpty(99).ToArray(); var ys = Enumerable.Range(1, 0).DefaultIfEmpty(99).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync(); var xs = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
var ys = Enumerable.Range(1, 1).DefaultIfEmpty(99).ToArray(); var ys = Enumerable.Range(1, 1).DefaultIfEmpty(99).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync(); var xs = await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
var ys = Enumerable.Range(1, 10).DefaultIfEmpty(99).ToArray(); var ys = Enumerable.Range(1, 10).DefaultIfEmpty(99).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
// Throw // Throw
{ {

View File

@ -34,11 +34,11 @@ namespace NetCoreTests.Linq
{ {
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, 10).ToObservable().ToArray(); var xs = await UniTaskAsyncEnumerable.Range(1, 10).ToObservable().ToArray();
xs.Should().BeEquivalentTo(Enumerable.Range(1, 10)); xs.Should().Equal(Enumerable.Range(1, 10));
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, 0).ToObservable().ToArray(); var xs = await UniTaskAsyncEnumerable.Range(1, 0).ToObservable().ToArray();
xs.Should().BeEquivalentTo(Enumerable.Range(1, 0)); xs.Should().Equal(Enumerable.Range(1, 0));
} }
} }
@ -70,21 +70,21 @@ namespace NetCoreTests.Linq
var xs = await Observable.Range(1, 100).ToUniTaskAsyncEnumerable().ToArrayAsync(); var xs = await Observable.Range(1, 100).ToUniTaskAsyncEnumerable().ToArrayAsync();
var ys = await Observable.Range(1, 100).ToArray(); var ys = await Observable.Range(1, 100).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await Observable.Range(1, 100, ThreadPoolScheduler.Instance).ToUniTaskAsyncEnumerable().ToArrayAsync(); var xs = await Observable.Range(1, 100, ThreadPoolScheduler.Instance).ToUniTaskAsyncEnumerable().ToArrayAsync();
var ys = await Observable.Range(1, 100, ThreadPoolScheduler.Instance).ToArray(); var ys = await Observable.Range(1, 100, ThreadPoolScheduler.Instance).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await Observable.Empty<int>(ThreadPoolScheduler.Instance).ToUniTaskAsyncEnumerable().ToArrayAsync(); var xs = await Observable.Empty<int>(ThreadPoolScheduler.Instance).ToUniTaskAsyncEnumerable().ToArrayAsync();
var ys = await Observable.Empty<int>(ThreadPoolScheduler.Instance).ToArray(); var ys = await Observable.Empty<int>(ThreadPoolScheduler.Instance).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
} }
@ -95,25 +95,25 @@ namespace NetCoreTests.Linq
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x); var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x);
var ys = Enumerable.Range(1, 100).ToDictionary(x => x); var ys = Enumerable.Range(1, 100).ToDictionary(x => x);
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
} }
{ {
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x); var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x);
var ys = Enumerable.Range(1, 0).ToDictionary(x => x); var ys = Enumerable.Range(1, 0).ToDictionary(x => x);
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
} }
{ {
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x, x => x * 2); var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x, x => x * 2);
var ys = Enumerable.Range(1, 100).ToDictionary(x => x, x => x * 2); var ys = Enumerable.Range(1, 100).ToDictionary(x => x, x => x * 2);
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
} }
{ {
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x, x => x * 2); var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x, x => x * 2);
var ys = Enumerable.Range(1, 0).ToDictionary(x => x, x => x * 2); var ys = Enumerable.Range(1, 0).ToDictionary(x => x, x => x * 2);
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
} }
} }
@ -126,34 +126,34 @@ namespace NetCoreTests.Linq
var ys = arr.ToLookup(x => x); var ys = arr.ToLookup(x => x);
xs.Count.Should().Be(ys.Count); xs.Count.Should().Be(ys.Count);
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); xs.Should().BeEquivalentTo(ys);
foreach (var key in xs.Select(x => x.Key)) foreach (var key in xs.Select(x => x.Key))
{ {
xs[key].Should().BeEquivalentTo(ys[key]); xs[key].Should().Equal(ys[key]);
} }
} }
{ {
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x); var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x);
var ys = Enumerable.Range(1, 0).ToLookup(x => x); var ys = Enumerable.Range(1, 0).ToLookup(x => x);
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); xs.Should().BeEquivalentTo(ys);
} }
{ {
var xs = await arr.ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2); var xs = await arr.ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2);
var ys = arr.ToLookup(x => x, x => x * 2); var ys = arr.ToLookup(x => x, x => x * 2);
xs.Count.Should().Be(ys.Count); xs.Count.Should().Be(ys.Count);
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); xs.Should().BeEquivalentTo(ys);
foreach (var key in xs.Select(x => x.Key)) foreach (var key in xs.Select(x => x.Key))
{ {
xs[key].Should().BeEquivalentTo(ys[key]); xs[key].Should().Equal(ys[key]);
} }
} }
{ {
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2); var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2);
var ys = Enumerable.Range(1, 0).ToLookup(x => x, x => x * 2); var ys = Enumerable.Range(1, 0).ToLookup(x => x, x => x * 2);
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); xs.Should().BeEquivalentTo(ys);
} }
} }
@ -164,13 +164,13 @@ namespace NetCoreTests.Linq
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToListAsync(); var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToListAsync();
var ys = Enumerable.Range(1, 100).ToList(); var ys = Enumerable.Range(1, 100).ToList();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToListAsync(); var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToListAsync();
var ys = Enumerable.Empty<int>().ToList(); var ys = Enumerable.Empty<int>().ToList();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
} }
@ -181,13 +181,13 @@ namespace NetCoreTests.Linq
var xs = await new[] { 1, 20, 4, 5, 20, 4, 6 }.ToUniTaskAsyncEnumerable().ToHashSetAsync(); var xs = await new[] { 1, 20, 4, 5, 20, 4, 6 }.ToUniTaskAsyncEnumerable().ToHashSetAsync();
var ys = new[] { 1, 20, 4, 5, 20, 4, 6 }.ToHashSet(); var ys = new[] { 1, 20, 4, 5, 20, 4, 6 }.ToHashSet();
xs.OrderBy(x => x).Should().BeEquivalentTo(ys.OrderBy(x => x)); xs.OrderBy(x => x).Should().Equal(ys.OrderBy(x => x));
} }
{ {
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToHashSetAsync(); var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToHashSetAsync();
var ys = Enumerable.Empty<int>().ToHashSet(); var ys = Enumerable.Empty<int>().ToHashSet();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
} }
} }

View File

@ -0,0 +1,194 @@
#pragma warning disable CS1998
#pragma warning disable CS0162
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Linq;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests.Linq
{
public class CreateTest
{
[Fact]
public async Task SyncCreation()
{
var from = 10;
var count = 100;
var xs = await UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
{
for (int i = 0; i < count; i++)
{
await writer.YieldAsync(from + i);
}
}).ToArrayAsync();
var ys = await Range(from, count).AsUniTaskAsyncEnumerable().ToArrayAsync();
xs.Should().Equal(ys);
}
[Fact]
public async Task SyncManually()
{
var list = new List<int>();
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
{
list.Add(100);
await writer.YieldAsync(10);
list.Add(200);
await writer.YieldAsync(20);
list.Add(300);
await writer.YieldAsync(30);
list.Add(400);
});
list.Should().BeEmpty();
var e = xs.GetAsyncEnumerator();
list.Should().BeEmpty();
await e.MoveNextAsync();
list.Should().Equal(100);
e.Current.Should().Be(10);
await e.MoveNextAsync();
list.Should().Equal(100, 200);
e.Current.Should().Be(20);
await e.MoveNextAsync();
list.Should().Equal(100, 200, 300);
e.Current.Should().Be(30);
(await e.MoveNextAsync()).Should().BeFalse();
list.Should().Equal(100, 200, 300, 400);
}
[Fact]
public async Task SyncExceptionFirst()
{
var from = 10;
var count = 100;
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
{
for (int i = 0; i < count; i++)
{
throw new UniTaskTestException();
await writer.YieldAsync(from + i);
}
});
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs.ToArrayAsync());
}
[Fact]
public async Task SyncException()
{
var from = 10;
var count = 100;
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
{
for (int i = 0; i < count; i++)
{
await writer.YieldAsync(from + i);
if (i == 15)
{
throw new UniTaskTestException();
}
}
});
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs.ToArrayAsync());
}
[Fact]
public async Task ASyncManually()
{
var list = new List<int>();
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
{
await UniTask.Yield();
list.Add(100);
await writer.YieldAsync(10);
await UniTask.Yield();
list.Add(200);
await writer.YieldAsync(20);
await UniTask.Yield();
list.Add(300);
await UniTask.Yield();
await writer.YieldAsync(30);
await UniTask.Yield();
list.Add(400);
});
list.Should().BeEmpty();
var e = xs.GetAsyncEnumerator();
list.Should().BeEmpty();
await e.MoveNextAsync();
list.Should().Equal(100);
e.Current.Should().Be(10);
await e.MoveNextAsync();
list.Should().Equal(100, 200);
e.Current.Should().Be(20);
await e.MoveNextAsync();
list.Should().Equal(100, 200, 300);
e.Current.Should().Be(30);
(await e.MoveNextAsync()).Should().BeFalse();
list.Should().Equal(100, 200, 300, 400);
}
[Fact]
public async Task AwaitForeachBreak()
{
var finallyCalled = false;
var enumerable = UniTaskAsyncEnumerable.Create<int>(async (writer, _) =>
{
try
{
await writer.YieldAsync(1);
}
finally
{
finallyCalled = true;
}
});
await foreach (var x in enumerable)
{
x.Should().Be(1);
break;
}
finallyCalled.Should().BeTrue();
}
async IAsyncEnumerable<int> Range(int from, int count)
{
for (int i = 0; i < count; i++)
{
yield return from + i;
}
}
}
}

View File

@ -22,7 +22,7 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Range(start, count).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(start, count).ToArrayAsync();
var ys = Enumerable.Range(start, count).ToArray(); var ys = Enumerable.Range(start, count).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
[Theory] [Theory]
@ -36,7 +36,7 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Repeat(value, count).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Repeat(value, count).ToArrayAsync();
var ys = Enumerable.Repeat(value, count).ToArray(); var ys = Enumerable.Repeat(value, count).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
[Fact] [Fact]
@ -45,7 +45,7 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Empty<int>().ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Empty<int>().ToArrayAsync();
var ys = Enumerable.Empty<int>().ToArray(); var ys = Enumerable.Empty<int>().ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
[Theory] [Theory]

View File

@ -23,26 +23,26 @@ namespace NetCoreTests.Linq
{ {
var a = await src.Where(x => x % 2 == 0).ToArrayAsync(); var a = await src.Where(x => x % 2 == 0).ToArrayAsync();
var expected = range.Where(x => x % 2 == 0).ToArray(); var expected = range.Where(x => x % 2 == 0).ToArray();
a.Should().BeEquivalentTo(expected); a.Should().Equal(expected);
} }
{ {
var a = await src.Where((x, i) => (x + i) % 2 == 0).ToArrayAsync(); var a = await src.Where((x, i) => (x + i) % 2 == 0).ToArrayAsync();
var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray(); var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
a.Should().BeEquivalentTo(expected); a.Should().Equal(expected);
} }
{ {
var a = await src.WhereAwait(x => UniTask.Run(() => x % 2 == 0)).ToArrayAsync(); var a = await src.WhereAwait(x => UniTask.Run(() => x % 2 == 0)).ToArrayAsync();
var b = await src.WhereAwait(x => UniTask.FromResult(x % 2 == 0)).ToArrayAsync(); var b = await src.WhereAwait(x => UniTask.FromResult(x % 2 == 0)).ToArrayAsync();
var expected = range.Where(x => x % 2 == 0).ToArray(); var expected = range.Where(x => x % 2 == 0).ToArray();
a.Should().BeEquivalentTo(expected); a.Should().Equal(expected);
b.Should().BeEquivalentTo(expected); b.Should().Equal(expected);
} }
{ {
var a = await src.WhereAwait((x, i) => UniTask.Run(() => (x + i) % 2 == 0)).ToArrayAsync(); var a = await src.WhereAwait((x, i) => UniTask.Run(() => (x + i) % 2 == 0)).ToArrayAsync();
var b = await src.WhereAwait((x, i) => UniTask.FromResult((x + i) % 2 == 0)).ToArrayAsync(); var b = await src.WhereAwait((x, i) => UniTask.FromResult((x + i) % 2 == 0)).ToArrayAsync();
var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray(); var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
a.Should().BeEquivalentTo(expected); a.Should().Equal(expected);
b.Should().BeEquivalentTo(expected); b.Should().Equal(expected);
} }
} }
@ -79,7 +79,7 @@ namespace NetCoreTests.Linq
var a = await data.ToUniTaskAsyncEnumerable().OfType<int>().ToArrayAsync(); var a = await data.ToUniTaskAsyncEnumerable().OfType<int>().ToArrayAsync();
var b = data.OfType<int>().ToArray(); var b = data.OfType<int>().ToArray();
a.Should().BeEquivalentTo(b); a.Should().Equal(b);
} }
@ -101,7 +101,7 @@ namespace NetCoreTests.Linq
var a = await data.ToUniTaskAsyncEnumerable().Cast<int>().ToArrayAsync(); var a = await data.ToUniTaskAsyncEnumerable().Cast<int>().ToArrayAsync();
var b = data.Cast<int>().ToArray(); var b = data.Cast<int>().ToArray();
a.Should().BeEquivalentTo(b); a.Should().Equal(b);
} }

View File

@ -38,7 +38,7 @@ namespace NetCoreTests.Linq
var xs = await outer.ToUniTaskAsyncEnumerable().Join(inner.ToUniTaskAsyncEnumerable(), x => x, x => x, (x, y) => (x, y)).ToArrayAsync(); var xs = await outer.ToUniTaskAsyncEnumerable().Join(inner.ToUniTaskAsyncEnumerable(), x => x, x => x, (x, y) => (x, y)).ToArrayAsync();
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray(); var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
@ -66,7 +66,7 @@ namespace NetCoreTests.Linq
var xs = await outer.ToUniTaskAsyncEnumerable().JoinAwait(inner.ToUniTaskAsyncEnumerable(), x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun((x, y))).ToArrayAsync(); var xs = await outer.ToUniTaskAsyncEnumerable().JoinAwait(inner.ToUniTaskAsyncEnumerable(), x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun((x, y))).ToArrayAsync();
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray(); var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
@ -94,7 +94,7 @@ namespace NetCoreTests.Linq
var xs = await outer.ToUniTaskAsyncEnumerable().JoinAwaitWithCancellation(inner.ToUniTaskAsyncEnumerable(), (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun((x, y))).ToArrayAsync(); var xs = await outer.ToUniTaskAsyncEnumerable().JoinAwaitWithCancellation(inner.ToUniTaskAsyncEnumerable(), (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun((x, y))).ToArrayAsync();
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray(); var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
@ -123,7 +123,7 @@ namespace NetCoreTests.Linq
var ys = arr.GroupBy(x => x).ToArray(); var ys = arr.GroupBy(x => x).ToArray();
xs.Length.Should().Be(ys.Length); xs.Length.Should().Be(ys.Length);
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); xs.Should().BeEquivalentTo(ys);
} }
{ {
@ -131,7 +131,7 @@ namespace NetCoreTests.Linq
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray(); var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
xs.Length.Should().Be(ys.Length); xs.Length.Should().Be(ys.Length);
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().BeEquivalentTo(ys.OrderBy(x => x.key).SelectMany(x => x.Item2)); xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().Equal(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
} }
{ {
@ -139,7 +139,7 @@ namespace NetCoreTests.Linq
var ys = arr.GroupBy(x => x).ToArray(); var ys = arr.GroupBy(x => x).ToArray();
xs.Length.Should().Be(ys.Length); xs.Length.Should().Be(ys.Length);
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); xs.Should().BeEquivalentTo(ys);
} }
{ {
@ -147,7 +147,7 @@ namespace NetCoreTests.Linq
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray(); var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
xs.Length.Should().Be(ys.Length); xs.Length.Should().Be(ys.Length);
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().BeEquivalentTo(ys.OrderBy(x => x.key).SelectMany(x => x.Item2)); xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().Equal(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
} }
{ {
@ -155,7 +155,7 @@ namespace NetCoreTests.Linq
var ys = arr.GroupBy(x => x).ToArray(); var ys = arr.GroupBy(x => x).ToArray();
xs.Length.Should().Be(ys.Length); xs.Length.Should().Be(ys.Length);
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); xs.Should().BeEquivalentTo(ys);
} }
{ {
@ -163,7 +163,7 @@ namespace NetCoreTests.Linq
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray(); var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
xs.Length.Should().Be(ys.Length); xs.Length.Should().Be(ys.Length);
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().BeEquivalentTo(ys.OrderBy(x => x.key).SelectMany(x => x.Item2)); xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().Equal(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
} }
} }
@ -199,21 +199,21 @@ namespace NetCoreTests.Linq
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray(); var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
xs.Length.Should().Be(ys.Length); xs.Length.Should().Be(ys.Length);
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await outer.ToUniTaskAsyncEnumerable().GroupJoinAwait(inner.ToUniTaskAsyncEnumerable(), x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun((x, string.Join(", ", y)))).ToArrayAsync(); var xs = await outer.ToUniTaskAsyncEnumerable().GroupJoinAwait(inner.ToUniTaskAsyncEnumerable(), x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun((x, string.Join(", ", y)))).ToArrayAsync();
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray(); var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
xs.Length.Should().Be(ys.Length); xs.Length.Should().Be(ys.Length);
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await outer.ToUniTaskAsyncEnumerable().GroupJoinAwaitWithCancellation(inner.ToUniTaskAsyncEnumerable(), (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun((x, string.Join(", ", y)))).ToArrayAsync(); var xs = await outer.ToUniTaskAsyncEnumerable().GroupJoinAwaitWithCancellation(inner.ToUniTaskAsyncEnumerable(), (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun((x, string.Join(", ", y)))).ToArrayAsync();
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray(); var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
xs.Length.Should().Be(ys.Length); xs.Length.Should().Be(ys.Length);
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
} }

View File

@ -0,0 +1,139 @@
#pragma warning disable CS1998
using System;
using System.Threading;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Linq;
using FluentAssertions;
using Xunit;
namespace NetCoreTests.Linq
{
public class MergeTest
{
[Fact]
public async Task TwoSource()
{
var semaphore = new SemaphoreSlim(1, 1);
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await UniTask.SwitchToThreadPool();
await semaphore.WaitAsync();
await writer.YieldAsync("A1");
semaphore.Release();
await semaphore.WaitAsync();
await writer.YieldAsync("A2");
semaphore.Release();
});
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await UniTask.SwitchToThreadPool();
await semaphore.WaitAsync();
await writer.YieldAsync("B1");
await writer.YieldAsync("B2");
semaphore.Release();
await semaphore.WaitAsync();
await writer.YieldAsync("B3");
semaphore.Release();
});
var result = await a.Merge(b).ToArrayAsync();
result.Should().Equal("A1", "B1", "B2", "A2", "B3");
}
[Fact]
public async Task ThreeSource()
{
var semaphore = new SemaphoreSlim(0, 1);
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await UniTask.SwitchToThreadPool();
await semaphore.WaitAsync();
await writer.YieldAsync("A1");
semaphore.Release();
await semaphore.WaitAsync();
await writer.YieldAsync("A2");
semaphore.Release();
});
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await UniTask.SwitchToThreadPool();
await semaphore.WaitAsync();
await writer.YieldAsync("B1");
await writer.YieldAsync("B2");
semaphore.Release();
await semaphore.WaitAsync();
await writer.YieldAsync("B3");
semaphore.Release();
});
var c = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await UniTask.SwitchToThreadPool();
await writer.YieldAsync("C1");
semaphore.Release();
});
var result = await a.Merge(b, c).ToArrayAsync();
result.Should().Equal("C1", "A1", "B1", "B2", "A2", "B3");
}
[Fact]
public async Task Throw()
{
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await writer.YieldAsync("A1");
});
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
throw new UniTaskTestException();
});
var enumerator = a.Merge(b).GetAsyncEnumerator();
(await enumerator.MoveNextAsync()).Should().Be(true);
enumerator.Current.Should().Be("A1");
await Assert.ThrowsAsync<UniTaskTestException>(async () => await enumerator.MoveNextAsync());
}
[Fact]
public async Task Cancel()
{
var cts = new CancellationTokenSource();
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await writer.YieldAsync("A1");
});
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await writer.YieldAsync("B1");
});
var enumerator = a.Merge(b).GetAsyncEnumerator(cts.Token);
(await enumerator.MoveNextAsync()).Should().Be(true);
enumerator.Current.Should().Be("A1");
cts.Cancel();
await Assert.ThrowsAsync<OperationCanceledException>(async () => await enumerator.MoveNextAsync());
}
}
}

View File

@ -27,7 +27,7 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Range(1, collection).Skip(skipCount).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).Skip(skipCount).ToArrayAsync();
var ys = Enumerable.Range(1, collection).Skip(skipCount).ToArray(); var ys = Enumerable.Range(1, collection).Skip(skipCount).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
[Fact] [Fact]
@ -52,7 +52,7 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipLast(skipCount).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipLast(skipCount).ToArrayAsync();
var ys = Enumerable.Range(1, collection).SkipLast(skipCount).ToArray(); var ys = Enumerable.Range(1, collection).SkipLast(skipCount).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
[Fact] [Fact]
@ -77,7 +77,7 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeLast(takeCount).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeLast(takeCount).ToArrayAsync();
var ys = Enumerable.Range(1, collection).TakeLast(takeCount).ToArray(); var ys = Enumerable.Range(1, collection).TakeLast(takeCount).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
[Fact] [Fact]
@ -103,7 +103,7 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Range(1, collection).Take(takeCount).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).Take(takeCount).ToArrayAsync();
var ys = Enumerable.Range(1, collection).Take(takeCount).ToArray(); var ys = Enumerable.Range(1, collection).Take(takeCount).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
[Fact] [Fact]
@ -130,37 +130,37 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArrayAsync();
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray(); var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArrayAsync();
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray(); var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwait(x => UniTask.Run(() => x < skipCount)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwait(x => UniTask.Run(() => x < skipCount)).ToArrayAsync();
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray(); var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwait((x, i) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwait((x, i) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray(); var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < skipCount)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < skipCount)).ToArrayAsync();
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray(); var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray(); var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
} }
@ -213,37 +213,37 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArrayAsync();
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray(); var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArrayAsync();
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray(); var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwait(x => UniTask.Run(() => x < skipCount)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwait(x => UniTask.Run(() => x < skipCount)).ToArrayAsync();
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray(); var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwait((x, i) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwait((x, i) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray(); var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < skipCount)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < skipCount)).ToArrayAsync();
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray(); var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray(); var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
} }

View File

@ -24,7 +24,7 @@ namespace NetCoreTests.Linq
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Reverse().ToArrayAsync(); var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Reverse().ToArrayAsync();
var ys = Enumerable.Range(start, count).Reverse().ToArray(); var ys = Enumerable.Range(start, count).Reverse().ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
[Fact] [Fact]
@ -46,18 +46,18 @@ namespace NetCoreTests.Linq
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, count).Select(x => x * x).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, count).Select(x => x * x).ToArrayAsync();
var ys = Enumerable.Range(1, count).Select(x => x * x).ToArray(); var ys = Enumerable.Range(1, count).Select(x => x * x).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
var zs = await UniTaskAsyncEnumerable.Range(1, count).SelectAwait((x) => UniTask.Run(() => x * x)).ToArrayAsync(); var zs = await UniTaskAsyncEnumerable.Range(1, count).SelectAwait((x) => UniTask.Run(() => x * x)).ToArrayAsync();
zs.Should().BeEquivalentTo(ys); zs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, count).Select((x, i) => x * x * i).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, count).Select((x, i) => x * x * i).ToArrayAsync();
var ys = Enumerable.Range(1, count).Select((x, i) => x * x * i).ToArray(); var ys = Enumerable.Range(1, count).Select((x, i) => x * x * i).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
var zs = await UniTaskAsyncEnumerable.Range(1, count).SelectAwait((x, i) => UniTask.Run(() => x * x * i)).ToArrayAsync(); var zs = await UniTaskAsyncEnumerable.Range(1, count).SelectAwait((x, i) => UniTask.Run(() => x * x * i)).ToArrayAsync();
zs.Should().BeEquivalentTo(ys); zs.Should().Equal(ys);
} }
} }
@ -98,22 +98,22 @@ namespace NetCoreTests.Linq
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany(x => UniTaskAsyncEnumerable.Range(99, rightCount * x)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany(x => UniTaskAsyncEnumerable.Range(99, rightCount * x)).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray(); var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany((i, x) => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany((i, x) => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray(); var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany(x => UniTaskAsyncEnumerable.Range(99, rightCount * x), (x, y) => x * y).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany(x => UniTaskAsyncEnumerable.Range(99, rightCount * x), (x, y) => x * y).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray(); var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany((i, x) => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany((i, x) => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray(); var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
// await // await
@ -121,22 +121,22 @@ namespace NetCoreTests.Linq
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x))).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x))).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray(); var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait((i, x) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x))).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait((i, x) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x))).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray(); var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x)), (x, y) => UniTask.Run(() => x * y)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x)), (x, y) => UniTask.Run(() => x * y)).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray(); var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait((i, x) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)), (x, y) => UniTask.Run(() => x * y)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait((i, x) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)), (x, y) => UniTask.Run(() => x * y)).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray(); var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
// with cancel // with cancel
@ -144,22 +144,22 @@ namespace NetCoreTests.Linq
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x))).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x))).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray(); var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((i, x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x))).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((i, x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x))).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray(); var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x)), (x, y, _) => UniTask.Run(() => x * y)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x)), (x, y, _) => UniTask.Run(() => x * y)).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray(); var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((i, x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)), (x, y, _) => UniTask.Run(() => x * y)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((i, x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)), (x, y, _) => UniTask.Run(() => x * y)).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray(); var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
} }
@ -219,17 +219,17 @@ namespace NetCoreTests.Linq
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).Zip(UniTaskAsyncEnumerable.Range(99, rightCount)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).Zip(UniTaskAsyncEnumerable.Range(99, rightCount)).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray(); var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).ZipAwait(UniTaskAsyncEnumerable.Range(99, rightCount), (x, y) => UniTask.Run(() => (x, y))).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).ZipAwait(UniTaskAsyncEnumerable.Range(99, rightCount), (x, y) => UniTask.Run(() => (x, y))).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray(); var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).ZipAwaitWithCancellation(UniTaskAsyncEnumerable.Range(99, rightCount), (x, y, _) => UniTask.Run(() => (x, y))).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).ZipAwaitWithCancellation(UniTaskAsyncEnumerable.Range(99, rightCount), (x, y, _) => UniTask.Run(() => (x, y))).ToArrayAsync();
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray(); var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
} }
[Fact] [Fact]
@ -288,7 +288,7 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount).Select(x => string.Join(",", x)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount).Select(x => string.Join(",", x)).ToArrayAsync();
var ys = await AsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount).Select(x => string.Join(",", x)).ToArrayAsync(); var ys = await AsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount).Select(x => string.Join(",", x)).ToArrayAsync();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
[Theory] [Theory]
@ -305,7 +305,7 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount, skipCount).Select(x => string.Join(",", x)).ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount, skipCount).Select(x => string.Join(",", x)).ToArrayAsync();
var ys = await AsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount, skipCount).Select(x => string.Join(",", x)).ToArrayAsync(); var ys = await AsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount, skipCount).Select(x => string.Join(",", x)).ToArrayAsync();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
[Fact] [Fact]
@ -402,7 +402,7 @@ namespace NetCoreTests.Linq
public async Task PariwiseImmediate() public async Task PariwiseImmediate()
{ {
var xs = await UniTaskAsyncEnumerable.Range(1, 5).Pairwise().ToArrayAsync(); var xs = await UniTaskAsyncEnumerable.Range(1, 5).Pairwise().ToArrayAsync();
xs.Should().BeEquivalentTo((1, 2), (2, 3), (3, 4), (4, 5)); xs.Should().Equal((1, 2), (2, 3), (3, 4), (4, 5));
} }
[Fact] [Fact]
@ -426,7 +426,7 @@ namespace NetCoreTests.Linq
await complete; await complete;
list.Should().BeEquivalentTo((10, 20), (20, 30), (30, 40), (40, 50)); list.Should().Equal((10, 20), (20, 30), (30, 40), (40, 50));
} }
class MyException : Exception class MyException : Exception

View File

@ -26,7 +26,7 @@ namespace NetCoreTests.Linq
rp.Value = 2; rp.Value = 2;
(await b).Should().BeEquivalentTo(1, 2); (await b).Should().Equal(1, 2);
var c = multicast.ToArrayAsync(); var c = multicast.ToArrayAsync();
@ -36,8 +36,8 @@ namespace NetCoreTests.Linq
rp.Dispose(); rp.Dispose();
(await a).Should().BeEquivalentTo(1, 2, 3, 4, 5); (await a).Should().Equal(1, 2, 3, 4, 5);
(await c).Should().BeEquivalentTo(3, 4, 5); (await c).Should().Equal(3, 4, 5);
disp.Dispose(); disp.Dispose();
} }
@ -56,7 +56,7 @@ namespace NetCoreTests.Linq
rp.Value = 2; rp.Value = 2;
(await b).Should().BeEquivalentTo(1, 2); (await b).Should().Equal(1, 2);
var c = multicast.ToArrayAsync(); var c = multicast.ToArrayAsync();

View File

@ -23,7 +23,7 @@ namespace NetCoreTests.Linq
l.Add(x); l.Add(x);
}); });
l.Should().BeEquivalentTo(100, 110, 120, 130, 140, 150, 160, 170, 180, 190); l.Should().Equal(100, 110, 120, 130, 140, 150, 160, 170, 180, 190);
} }
} }
} }

View File

@ -34,10 +34,10 @@ namespace NetCoreTests.Linq
{ {
var ys = array.Distinct().ToArray(); var ys = array.Distinct().ToArray();
{ {
(await array.ToUniTaskAsyncEnumerable().Distinct().ToArrayAsync()).Should().BeEquivalentTo(ys); (await array.ToUniTaskAsyncEnumerable().Distinct().ToArrayAsync()).Should().Equal(ys);
(await array.ToUniTaskAsyncEnumerable().Distinct(x => x).ToArrayAsync()).Should().BeEquivalentTo(ys); (await array.ToUniTaskAsyncEnumerable().Distinct(x => x).ToArrayAsync()).Should().Equal(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys); (await array.ToUniTaskAsyncEnumerable().DistinctAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys); (await array.ToUniTaskAsyncEnumerable().DistinctAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
} }
} }
@ -71,10 +71,10 @@ namespace NetCoreTests.Linq
{ {
var ys = await array.ToAsyncEnumerable().DistinctUntilChanged().ToArrayAsync(); var ys = await array.ToAsyncEnumerable().DistinctUntilChanged().ToArrayAsync();
{ {
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged().ToArrayAsync()).Should().BeEquivalentTo(ys); (await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged().ToArrayAsync()).Should().Equal(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged(x => x).ToArrayAsync()).Should().BeEquivalentTo(ys); (await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged(x => x).ToArrayAsync()).Should().Equal(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys); (await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys); (await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
} }
} }
@ -112,7 +112,7 @@ namespace NetCoreTests.Linq
{ {
var xs = await a1.ToUniTaskAsyncEnumerable().Except(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync(); var xs = await a1.ToUniTaskAsyncEnumerable().Except(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
var ys = a1.Except(a2).ToArray(); var ys = a1.Except(a2).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
} }
} }
@ -141,7 +141,7 @@ namespace NetCoreTests.Linq
{ {
var xs = await a1.ToUniTaskAsyncEnumerable().Intersect(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync(); var xs = await a1.ToUniTaskAsyncEnumerable().Intersect(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
var ys = a1.Intersect(a2).ToArray(); var ys = a1.Intersect(a2).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
} }
} }
@ -170,7 +170,7 @@ namespace NetCoreTests.Linq
{ {
var xs = await a1.ToUniTaskAsyncEnumerable().Union(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync(); var xs = await a1.ToUniTaskAsyncEnumerable().Union(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
var ys = a1.Union(a2).ToArray(); var ys = a1.Union(a2).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
} }
} }

View File

@ -59,32 +59,32 @@ namespace NetCoreTests.Linq
{ {
var xs = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x).ToArrayAsync(); var xs = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x).ToArrayAsync();
var ys = array.OrderBy(x => x).ToArray(); var ys = array.OrderBy(x => x).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x).ToArrayAsync(); var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x).ToArrayAsync();
var ys = array.OrderByDescending(x => x).ToArray(); var ys = array.OrderByDescending(x => x).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwait(RandomRun).ToArrayAsync(); var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwait(RandomRun).ToArrayAsync();
var ys = array.OrderBy(x => x).ToArray(); var ys = array.OrderBy(x => x).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(RandomRun).ToArrayAsync(); var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(RandomRun).ToArrayAsync();
var ys = array.OrderByDescending(x => x).ToArray(); var ys = array.OrderByDescending(x => x).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation(RandomRun).ToArrayAsync(); var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation(RandomRun).ToArrayAsync();
var ys = array.OrderBy(x => x).ToArray(); var ys = array.OrderBy(x => x).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
{ {
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync(); var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync();
var ys = array.OrderByDescending(x => x).ToArray(); var ys = array.OrderByDescending(x => x).ToArray();
xs.Should().BeEquivalentTo(ys); xs.Should().Equal(ys);
} }
} }
@ -125,14 +125,14 @@ namespace NetCoreTests.Linq
var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync(); var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync();
var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync(); var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync();
a.Should().BeEquivalentTo(a2); a.Should().Equal(a2);
b.Should().BeEquivalentTo(b2); b.Should().Equal(b2);
c.Should().BeEquivalentTo(c2); c.Should().Equal(c2);
d.Should().BeEquivalentTo(d2); d.Should().Equal(d2);
e.Should().BeEquivalentTo(e2); e.Should().Equal(e2);
f.Should().BeEquivalentTo(f2); f.Should().Equal(f2);
g.Should().BeEquivalentTo(g2); g.Should().Equal(g2);
h.Should().BeEquivalentTo(h2); h.Should().Equal(h2);
} }
{ {
var a2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync(); var a2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync();
@ -144,14 +144,14 @@ namespace NetCoreTests.Linq
var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync(); var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync();
var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync(); var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync();
a.Should().BeEquivalentTo(a2); a.Should().Equal(a2);
b.Should().BeEquivalentTo(b2); b.Should().Equal(b2);
c.Should().BeEquivalentTo(c2); c.Should().Equal(c2);
d.Should().BeEquivalentTo(d2); d.Should().Equal(d2);
e.Should().BeEquivalentTo(e2); e.Should().Equal(e2);
f.Should().BeEquivalentTo(f2); f.Should().Equal(f2);
g.Should().BeEquivalentTo(g2); g.Should().Equal(g2);
h.Should().BeEquivalentTo(h2); h.Should().Equal(h2);
} }
{ {
var a2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync(); var a2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
@ -163,14 +163,14 @@ namespace NetCoreTests.Linq
var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync(); var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync(); var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
a.Should().BeEquivalentTo(a2); a.Should().Equal(a2);
b.Should().BeEquivalentTo(b2); b.Should().Equal(b2);
c.Should().BeEquivalentTo(c2); c.Should().Equal(c2);
d.Should().BeEquivalentTo(d2); d.Should().Equal(d2);
e.Should().BeEquivalentTo(e2); e.Should().Equal(e2);
f.Should().BeEquivalentTo(f2); f.Should().Equal(f2);
g.Should().BeEquivalentTo(g2); g.Should().Equal(g2);
h.Should().BeEquivalentTo(h2); h.Should().Equal(h2);
} }
} }
} }

View File

@ -24,7 +24,7 @@ namespace NetCoreTests.Linq
rp.Value = 4; rp.Value = 4;
rp.Value = 5; rp.Value = 5;
(await xs).Should().BeEquivalentTo(1, 2, 3, 4, 5); (await xs).Should().Equal(1, 2, 3, 4, 5);
} }
[Fact] [Fact]
@ -39,7 +39,7 @@ namespace NetCoreTests.Linq
rp.Value = 4; rp.Value = 4;
rp.Value = 5; rp.Value = 5;
(await xs).Should().BeEquivalentTo(1, 2, 3, 4); (await xs).Should().Equal(1, 2, 3, 4);
} }
[Fact] [Fact]
@ -56,7 +56,7 @@ namespace NetCoreTests.Linq
await c; await c;
var foo = await xs; var foo = await xs;
foo.Should().BeEquivalentTo(new[] { 1, 10, 20 }); foo.Should().Equal(new[] { 1, 10, 20 });
async Task CancelAsync() async Task CancelAsync()
{ {
@ -85,7 +85,7 @@ namespace NetCoreTests.Linq
await c; await c;
var foo = await xs; var foo = await xs;
foo.Should().BeEquivalentTo(new[] { 20, 30, 40 }); foo.Should().Equal(new[] { 20, 30, 40 });
async Task CancelAsync() async Task CancelAsync()
{ {
@ -116,7 +116,7 @@ namespace NetCoreTests.Linq
await c; await c;
var foo = await xs; var foo = await xs;
foo.Should().BeEquivalentTo(new[] { 1, 10, 20 }); foo.Should().Equal(new[] { 1, 10, 20 });
async Task CancelAsync() async Task CancelAsync()
{ {
@ -145,7 +145,7 @@ namespace NetCoreTests.Linq
await c; await c;
var foo = await xs; var foo = await xs;
foo.Should().BeEquivalentTo(new[] { 20, 30, 40 }); foo.Should().Equal(new[] { 20, 30, 40 });
async Task CancelAsync() async Task CancelAsync()
{ {

View File

@ -0,0 +1,45 @@
#pragma warning disable CS1998
using System;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using Xunit;
namespace NetCoreTests
{
public class TaskExtensionsTest
{
[Fact]
public async Task PropagateException()
{
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
{
await ThrowAsync().AsUniTask();
});
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
{
await ThrowOrValueAsync().AsUniTask();
});
}
[Fact]
public async Task PropagateExceptionWhenAll()
{
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
{
await Task.WhenAll(ThrowAsync(), ThrowAsync()).AsUniTask();
});
}
async Task ThrowAsync()
{
throw new InvalidOperationException();
}
async Task<int> ThrowOrValueAsync()
{
throw new InvalidOperationException();
}
}
}

View File

@ -34,7 +34,7 @@ namespace NetCoreTests
ev.SetResult(20); ev.SetResult(20);
ev.SetResult(30); ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
ev.SetCompleted(); ev.SetCompleted();
@ -46,7 +46,7 @@ namespace NetCoreTests
ev.SetCompleted(); ev.SetCompleted();
ev.SetCanceled(default); ev.SetCanceled(default);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
one.CompletedCalled.Count.Should().Be(1); one.CompletedCalled.Count.Should().Be(1);
} }
// after removed, onemore // after removed, onemore
@ -59,7 +59,7 @@ namespace NetCoreTests
ev.SetResult(20); ev.SetResult(20);
ev.SetResult(30); ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
ev.SetCompleted(); ev.SetCompleted();
@ -71,7 +71,7 @@ namespace NetCoreTests
ev.SetCompleted(); ev.SetCompleted();
ev.SetCanceled(default); ev.SetCanceled(default);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
one.CompletedCalled.Count.Should().Be(1); one.CompletedCalled.Count.Should().Be(1);
} }
} }
@ -102,10 +102,10 @@ namespace NetCoreTests
ev.SetResult(20); ev.SetResult(20);
ev.SetResult(30); ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30); two.NextCalled.Should().Equal(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30); three.NextCalled.Should().Equal(10, 20, 30);
four.NextCalled.Should().BeEquivalentTo(10, 20, 30); four.NextCalled.Should().Equal(10, 20, 30);
ev.SetCompleted(); ev.SetCompleted();
@ -120,11 +120,11 @@ namespace NetCoreTests
ev.SetCompleted(); ev.SetCompleted();
ev.SetCanceled(default); ev.SetCanceled(default);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
one.CompletedCalled.Count.Should().Be(1); one.CompletedCalled.Count.Should().Be(1);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30); two.NextCalled.Should().Equal(10, 20, 30);
three.CompletedCalled.Count.Should().Be(1); three.CompletedCalled.Count.Should().Be(1);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30); two.NextCalled.Should().Equal(10, 20, 30);
three.CompletedCalled.Count.Should().Be(1); three.CompletedCalled.Count.Should().Be(1);
} }
@ -145,10 +145,10 @@ namespace NetCoreTests
ev.SetResult(30); ev.SetResult(30);
ev.Add(four); ev.Add(four);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30); two.NextCalled.Should().Equal(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30); three.NextCalled.Should().Equal(10, 20, 30);
four.NextCalled.Should().BeEquivalentTo(10, 20, 30); four.NextCalled.Should().Equal(10, 20, 30);
ev.SetCompleted(); ev.SetCompleted();
@ -163,11 +163,11 @@ namespace NetCoreTests
ev.SetCompleted(); ev.SetCompleted();
ev.SetCanceled(default); ev.SetCanceled(default);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
one.CompletedCalled.Count.Should().Be(1); one.CompletedCalled.Count.Should().Be(1);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30); two.NextCalled.Should().Equal(10, 20, 30);
three.CompletedCalled.Count.Should().Be(1); three.CompletedCalled.Count.Should().Be(1);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30); two.NextCalled.Should().Equal(10, 20, 30);
three.CompletedCalled.Count.Should().Be(1); three.CompletedCalled.Count.Should().Be(1);
} }
} }
@ -190,9 +190,9 @@ namespace NetCoreTests
ev.SetResult(20); ev.SetResult(20);
ev.SetResult(30); ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30); two.NextCalled.Should().Equal(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30); three.NextCalled.Should().Equal(10, 20, 30);
ev.Remove(one); ev.Remove(one);
@ -200,9 +200,9 @@ namespace NetCoreTests
ev.SetResult(50); ev.SetResult(50);
ev.SetResult(60); ev.SetResult(60);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60); two.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60); three.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
} }
} }
[Fact] [Fact]
@ -222,9 +222,9 @@ namespace NetCoreTests
ev.SetResult(20); ev.SetResult(20);
ev.SetResult(30); ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30); two.NextCalled.Should().Equal(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30); three.NextCalled.Should().Equal(10, 20, 30);
ev.Remove(two); ev.Remove(two);
@ -232,9 +232,9 @@ namespace NetCoreTests
ev.SetResult(50); ev.SetResult(50);
ev.SetResult(60); ev.SetResult(60);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60); one.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30); two.NextCalled.Should().Equal(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60); three.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
} }
} }
[Fact] [Fact]
@ -254,9 +254,9 @@ namespace NetCoreTests
ev.SetResult(20); ev.SetResult(20);
ev.SetResult(30); ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30); two.NextCalled.Should().Equal(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30); three.NextCalled.Should().Equal(10, 20, 30);
ev.Remove(three); ev.Remove(three);
@ -264,9 +264,9 @@ namespace NetCoreTests
ev.SetResult(50); ev.SetResult(50);
ev.SetResult(60); ev.SetResult(60);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60); one.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60); two.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30); three.NextCalled.Should().Equal(10, 20, 30);
} }
} }
@ -321,9 +321,9 @@ namespace NetCoreTests
ev.SetResult(20); ev.SetResult(20);
ev.SetResult(30); ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10); one.NextCalled.Should().Equal(10);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30); two.NextCalled.Should().Equal(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30); three.NextCalled.Should().Equal(10, 20, 30);
} }
public void Run2() public void Run2()
@ -342,9 +342,9 @@ namespace NetCoreTests
ev.SetResult(20); ev.SetResult(20);
ev.SetResult(30); ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10); one.NextCalled.Should().Equal(10);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30); two.NextCalled.Should().Equal(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30); three.NextCalled.Should().Equal(10, 20, 30);
} }
public void Run3() public void Run3()
@ -363,9 +363,9 @@ namespace NetCoreTests
ev.SetResult(20); ev.SetResult(20);
ev.SetResult(30); ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10); one.NextCalled.Should().Equal(10);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30); two.NextCalled.Should().Equal(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30); three.NextCalled.Should().Equal(10, 20, 30);
} }
} }
@ -390,9 +390,9 @@ namespace NetCoreTests
ev.SetResult(20); ev.SetResult(20);
ev.SetResult(30); ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
two.NextCalled.Count.Should().Be(0); two.NextCalled.Count.Should().Be(0);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30); three.NextCalled.Should().Equal(10, 20, 30);
} }
public void Run2() public void Run2()
@ -412,9 +412,9 @@ namespace NetCoreTests
ev.SetResult(20); ev.SetResult(20);
ev.SetResult(30); ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10); two.NextCalled.Should().Equal(10);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30); three.NextCalled.Should().Equal(10, 20, 30);
} }
public void Run3() public void Run3()
@ -434,9 +434,9 @@ namespace NetCoreTests
ev.SetResult(20); ev.SetResult(20);
ev.SetResult(30); ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10); two.NextCalled.Should().Equal(10);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30); three.NextCalled.Should().Equal(10, 20, 30);
} }
} }
@ -464,10 +464,10 @@ namespace NetCoreTests
ev.SetResult(20); ev.SetResult(20);
ev.SetResult(30); ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30); one.NextCalled.Should().Equal(10, 20, 30);
two.NextCalled.Count.Should().Be(0); two.NextCalled.Count.Should().Be(0);
three.NextCalled.Count.Should().Be(0); three.NextCalled.Count.Should().Be(0);
four.NextCalled.Should().BeEquivalentTo(10, 20, 30); four.NextCalled.Should().Equal(10, 20, 30);
} }
public void Run2() public void Run2()
@ -490,10 +490,10 @@ namespace NetCoreTests
ev.SetResult(20); ev.SetResult(20);
ev.SetResult(30); ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10); one.NextCalled.Should().Equal(10);
two.NextCalled.Count.Should().Be(0); two.NextCalled.Count.Should().Be(0);
three.NextCalled.Count.Should().Be(0); three.NextCalled.Count.Should().Be(0);
four.NextCalled.Should().BeEquivalentTo(10, 20, 30); four.NextCalled.Should().Equal(10, 20, 30);
} }
@ -533,10 +533,10 @@ namespace NetCoreTests
ev.SetResult(30); ev.SetResult(30);
ev.SetResult(40); ev.SetResult(40);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40); one.NextCalled.Should().Equal(10, 20, 30, 40);
two.NextCalled.Should().BeEquivalentTo(20, 30, 40); two.NextCalled.Should().Equal(20, 30, 40);
three.NextCalled.Should().BeEquivalentTo(30, 40); three.NextCalled.Should().Equal(30, 40);
four.NextCalled.Should().BeEquivalentTo(40); four.NextCalled.Should().Equal(40);
} }
public void Run2() public void Run2()
@ -573,10 +573,10 @@ namespace NetCoreTests
ev.SetResult(30); ev.SetResult(30);
ev.SetResult(40); ev.SetResult(40);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40); one.NextCalled.Should().Equal(10, 20, 30, 40);
two.NextCalled.Should().BeEquivalentTo(20, 30, 40); two.NextCalled.Should().Equal(20, 30, 40);
three.NextCalled.Should().BeEquivalentTo(30, 40); three.NextCalled.Should().Equal(30, 40);
four.NextCalled.Should().BeEquivalentTo(40); four.NextCalled.Should().Equal(40);
} }
} }
} }

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>

View File

@ -0,0 +1,80 @@
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using FluentAssertions;
using NetCoreTests.Linq;
using Xunit;
namespace NetCoreTests
{
public class AutoResetUniTaskCompletionSourceTest
{
[Fact]
public async Task SetResultAfterReturn()
{
var source1 = AutoResetUniTaskCompletionSource.Create();
source1.TrySetResult();
await source1.Task;
source1.TrySetResult().Should().BeFalse();
var source2 = AutoResetUniTaskCompletionSource.Create();
source2.TrySetResult();
await source2.Task;
source2.TrySetResult().Should().BeFalse();
}
[Fact]
public async Task SetCancelAfterReturn()
{
var source = AutoResetUniTaskCompletionSource.Create();
source.TrySetResult();
await source.Task;
source.TrySetCanceled().Should().BeFalse();
}
[Fact]
public async Task SetExceptionAfterReturn()
{
var source = AutoResetUniTaskCompletionSource.Create();
source.TrySetResult();
await source.Task;
source.TrySetException(new UniTaskTestException()).Should().BeFalse();
}
[Fact]
public async Task SetResultWithValueAfterReturn()
{
var source1 = AutoResetUniTaskCompletionSource<int>.Create();
source1.TrySetResult(100);
(await source1.Task).Should().Be(100);
source1.TrySetResult(100).Should().BeFalse();
var source2 = AutoResetUniTaskCompletionSource.Create();
source2.TrySetResult();
await source2.Task;
source2.TrySetResult().Should().BeFalse();
}
[Fact]
public async Task SetCancelWithValueAfterReturn()
{
var source = AutoResetUniTaskCompletionSource<int>.Create();
source.TrySetResult(100);
(await source.Task).Should().Be(100);
source.TrySetCanceled().Should().BeFalse();
}
[Fact]
public async Task SetExceptionWithValueAfterReturn()
{
var source = AutoResetUniTaskCompletionSource<int>.Create();
source.TrySetResult(100);
(await source.Task).Should().Be(100);
source.TrySetException(new UniTaskTestException()).Should().BeFalse();
}
}
}

View File

@ -0,0 +1,69 @@
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests
{
public class WhenEachTest
{
[Fact]
public async Task Each()
{
var a = Delay(1, 3000);
var b = Delay(2, 1000);
var c = Delay(3, 2000);
var l = new List<int>();
await foreach (var item in UniTask.WhenEach(a, b, c))
{
l.Add(item.Result);
}
l.Should().Equal(2, 3, 1);
}
[Fact]
public async Task Error()
{
var a = Delay2(1, 3000);
var b = Delay2(2, 1000);
var c = Delay2(3, 2000);
var l = new List<WhenEachResult<int>>();
await foreach (var item in UniTask.WhenEach(a, b, c))
{
l.Add(item);
}
l[0].IsCompletedSuccessfully.Should().BeTrue();
l[0].IsFaulted.Should().BeFalse();
l[0].Result.Should().Be(2);
l[1].IsCompletedSuccessfully.Should().BeFalse();
l[1].IsFaulted.Should().BeTrue();
l[1].Exception.Message.Should().Be("ERROR");
l[2].IsCompletedSuccessfully.Should().BeTrue();
l[2].IsFaulted.Should().BeFalse();
l[2].Result.Should().Be(1);
}
async UniTask<int> Delay(int id, int sleep)
{
await Task.Delay(sleep);
return id;
}
async UniTask<int> Delay2(int id, int sleep)
{
await Task.Delay(sleep);
if (id == 3) throw new Exception("ERROR");
return id;
}
}
}

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).AttachExternalCancellation(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;
}).AttachExternalCancellation(cts.Token);
cts.Cancel();
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await t)).CancellationToken.Should().Be(cts.Token);
}
}
}

View File

@ -20,8 +20,11 @@ public static class EditorRunnerChecker
{ {
Debug.Log("Start"); Debug.Log("Start");
var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask(); //var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask();
Debug.Log(r.downloadHandler.text.Substring(0, 100)); //Debug.Log(r.downloadHandler.text.Substring(0, 100));
//await UniTask.Yield();
await UniTask.DelayFrame(30);
Debug.Log("End"); Debug.Log("End");
} }

View File

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

View File

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

View File

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

View File

@ -4,31 +4,29 @@ using System.Threading;
using UnityEngine; using UnityEngine;
using Cysharp.Threading.Tasks.Triggers; using Cysharp.Threading.Tasks.Triggers;
using System; using System;
using Cysharp.Threading.Tasks.Internal;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
{ {
public static class CancellationTokenSourceExtensions
{
public static void CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
{
var delay = UniTask.Delay(millisecondsDelay, delayType, delayTiming, cts.Token);
CancelAfterCore(cts, delay).Forget();
}
public static void CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update) public static partial class CancellationTokenSourceExtensions
{ {
var delay = UniTask.Delay(delayTimeSpan, delayType, delayTiming, cts.Token); readonly static Action<object> CancelCancellationTokenSourceStateDelegate = new Action<object>(CancelCancellationTokenSourceState);
CancelAfterCore(cts, delay).Forget();
}
static async UniTaskVoid CancelAfterCore(CancellationTokenSource cts, UniTask delayTask) static void CancelCancellationTokenSourceState(object state)
{
var alreadyCanceled = await delayTask.SuppressCancellationThrow();
if (!alreadyCanceled)
{ {
var cts = (CancellationTokenSource)state;
cts.Cancel(); cts.Cancel();
cts.Dispose();
} }
public static IDisposable CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
{
return CancelAfterSlim(cts, TimeSpan.FromMilliseconds(millisecondsDelay), delayType, delayTiming);
}
public static IDisposable CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
{
return PlayerLoopTimer.StartNew(delayTimeSpan, false, delayType, delayTiming, cts.Token, CancelCancellationTokenSourceStateDelegate, cts);
} }
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, Component component) public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, Component component)
@ -39,11 +37,7 @@ namespace Cysharp.Threading.Tasks
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, GameObject gameObject) public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, GameObject gameObject)
{ {
var trigger = gameObject.GetAsyncDestroyTrigger(); var trigger = gameObject.GetAsyncDestroyTrigger();
trigger.CancellationToken.RegisterWithoutCaptureExecutionContext(state => trigger.CancellationToken.RegisterWithoutCaptureExecutionContext(CancelCancellationTokenSourceStateDelegate, cts);
{
var cts2 = (CancellationTokenSource)state;
cts2.Cancel();
}, cts);
} }
} }
} }

View File

@ -1,4 +1,4 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591
using Cysharp.Threading.Tasks.Internal; using Cysharp.Threading.Tasks.Internal;
using System; using System;
@ -8,6 +8,10 @@ using System.Runtime.CompilerServices;
namespace Cysharp.Threading.Tasks.CompilerServices namespace Cysharp.Threading.Tasks.CompilerServices
{ {
// #ENABLE_IL2CPP in this file is to avoid bug of IL2CPP VM.
// Issue is tracked on https://issuetracker.unity3d.com/issues/il2cpp-incorrect-results-when-calling-a-method-from-outside-class-in-a-struct
// but currently it is labeled `Won't Fix`.
internal interface IStateMachineRunner internal interface IStateMachineRunner
{ {
Action MoveNext { get; } Action MoveNext { get; }
@ -83,7 +87,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
TaskPool.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size); 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() public void Return()
{ {
@ -153,7 +158,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
result.stateMachine = stateMachine; // copy struct StateMachine(in release build). 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() static AsyncUniTask()
{ {
@ -275,7 +281,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
result.stateMachine = stateMachine; // copy struct StateMachine(in release build). 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() static AsyncUniTask()
{ {

View File

@ -32,10 +32,24 @@ namespace Cysharp.Threading.Tasks
return new UniTask(EnumeratorPromise.Create(enumerator, timing, cancellationToken, out var token), token); 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> sealed class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<EnumeratorPromise>
{ {
static TaskPool<EnumeratorPromise> pool; static TaskPool<EnumeratorPromise> pool;
public EnumeratorPromise NextNode { get; set; } EnumeratorPromise nextNode;
public ref EnumeratorPromise NextNode => ref nextNode;
static EnumeratorPromise() static EnumeratorPromise()
{ {
@ -44,6 +58,9 @@ namespace Cysharp.Threading.Tasks
IEnumerator innerEnumerator; IEnumerator innerEnumerator;
CancellationToken cancellationToken; CancellationToken cancellationToken;
int initialFrame;
bool loopRunning;
bool calledGetResult;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
@ -66,10 +83,18 @@ namespace Cysharp.Threading.Tasks
result.innerEnumerator = ConsumeEnumerator(innerEnumerator); result.innerEnumerator = ConsumeEnumerator(innerEnumerator);
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.loopRunning = true;
PlayerLoopHelper.AddAction(timing, result); result.calledGetResult = false;
result.initialFrame = -1;
token = result.core.Version; token = result.core.Version;
// run immediately.
if (result.MoveNext())
{
PlayerLoopHelper.AddAction(timing, result);
}
return result; return result;
} }
@ -77,13 +102,17 @@ namespace Cysharp.Threading.Tasks
{ {
try try
{ {
calledGetResult = true;
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!loopRunning)
{ {
TryReturn(); TryReturn();
} }
} }
}
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
{ {
@ -102,12 +131,38 @@ namespace Cysharp.Threading.Tasks
public bool MoveNext() public bool MoveNext()
{ {
if (calledGetResult)
{
loopRunning = false;
TryReturn();
return false;
}
if (innerEnumerator == null) // invalid status, returned but loop running?
{
return false;
}
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
loopRunning = false;
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
return false; 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 try
{ {
if (innerEnumerator.MoveNext()) if (innerEnumerator.MoveNext())
@ -117,10 +172,12 @@ namespace Cysharp.Threading.Tasks
} }
catch (Exception ex) catch (Exception ex)
{ {
loopRunning = false;
core.TrySetException(ex); core.TrySetException(ex);
return false; return false;
} }
loopRunning = false;
core.TrySetResult(null); core.TrySetResult(null);
return false; return false;
} }
@ -131,6 +188,7 @@ namespace Cysharp.Threading.Tasks
core.Reset(); core.Reset();
innerEnumerator = default; innerEnumerator = default;
cancellationToken = default; cancellationToken = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
@ -145,11 +203,10 @@ namespace Cysharp.Threading.Tasks
{ {
yield return null; yield return null;
} }
else if (current is CustomYieldInstruction) else if (current is CustomYieldInstruction cyi)
{ {
// WWW, WaitForSecondsRealtime // WWW, WaitForSecondsRealtime
var e2 = UnwrapWaitCustomYieldInstruction((CustomYieldInstruction)current); while (cyi.keepWaiting)
while (e2.MoveNext())
{ {
yield return null; yield return null;
} }
@ -175,7 +232,7 @@ namespace Cysharp.Threading.Tasks
} }
else else
{ {
yield return null; goto WARN;
} }
} }
else if (current is IEnumerator e3) else if (current is IEnumerator e3)
@ -188,17 +245,14 @@ namespace Cysharp.Threading.Tasks
} }
else else
{ {
// WaitForEndOfFrame, WaitForFixedUpdate, others. goto WARN;
yield return null;
}
}
} }
// WWW and others as CustomYieldInstruction. continue;
static IEnumerator UnwrapWaitCustomYieldInstruction(CustomYieldInstruction yieldInstruction)
{ WARN:
while (yieldInstruction.keepWaiting) // 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; yield return null;
} }
} }
@ -208,12 +262,12 @@ namespace Cysharp.Threading.Tasks
static IEnumerator UnwrapWaitForSeconds(WaitForSeconds waitForSeconds) static IEnumerator UnwrapWaitForSeconds(WaitForSeconds waitForSeconds)
{ {
var second = (float)waitForSeconds_Seconds.GetValue(waitForSeconds); var second = (float)waitForSeconds_Seconds.GetValue(waitForSeconds);
var startTime = DateTimeOffset.UtcNow; var elapsed = 0.0f;
while (true) while (true)
{ {
yield return null; yield return null;
var elapsed = (DateTimeOffset.UtcNow - startTime).TotalSeconds; elapsed += Time.deltaTime;
if (elapsed >= second) if (elapsed >= second)
{ {
break; break;
@ -231,4 +285,3 @@ namespace Cysharp.Threading.Tasks
} }
} }
} }

View File

@ -7,6 +7,7 @@ using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
using System.Threading; using System.Threading;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.AsyncOperations;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
@ -15,21 +16,36 @@ namespace Cysharp.Threading.Tasks
{ {
#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) public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken, bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
{ {
if (handle.IsDone) return UniTask.CompletedTask; return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately, autoReleaseWhenCanceled: autoReleaseWhenCanceled);
return new UniTask(AsyncOperationHandleWithCancellationSource.Create(handle, cancellationToken, out var token), token);
} }
public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
{ {
if (handle.IsDone) return UniTask.CompletedTask; if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, out var token), token);
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)
{
return UniTask.FromException(handle.OperationException);
}
return UniTask.CompletedTask;
}
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, cancelImmediately, autoReleaseWhenCanceled, out var token), token);
} }
public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion
@ -77,146 +93,34 @@ namespace Cysharp.Threading.Tasks
} }
} }
sealed class AsyncOperationHandleWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleWithCancellationSource>
{
static TaskPool<AsyncOperationHandleWithCancellationSource> pool;
public AsyncOperationHandleWithCancellationSource NextNode { get; set; }
static AsyncOperationHandleWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperationHandle> continuationAction;
AsyncOperationHandle handle;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<AsyncUnit> core;
AsyncOperationHandleWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource Create(AsyncOperationHandle handle, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleWithCancellationSource();
}
result.handle = handle;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
handle.Completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperationHandle _)
{
handle.Completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (handle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(handle.OperationException);
}
else
{
core.TrySetResult(AsyncUnit.Default);
}
}
}
public void GetResult(short token)
{
core.GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public 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();
handle = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource> sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
{ {
static TaskPool<AsyncOperationHandleConfiguredSource> pool; static TaskPool<AsyncOperationHandleConfiguredSource> pool;
public AsyncOperationHandleConfiguredSource NextNode { get; set; } AsyncOperationHandleConfiguredSource nextNode;
public ref AsyncOperationHandleConfiguredSource NextNode => ref nextNode;
static AsyncOperationHandleConfiguredSource() static AsyncOperationHandleConfiguredSource()
{ {
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size); TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
} }
readonly Action<AsyncOperationHandle> completedCallback;
AsyncOperationHandle handle; AsyncOperationHandle handle;
IProgress<float> progress;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
IProgress<float> progress;
bool autoReleaseWhenCanceled;
bool cancelImmediately;
bool completed;
UniTaskCompletionSourceCore<AsyncUnit> core; UniTaskCompletionSourceCore<AsyncUnit> core;
AsyncOperationHandleConfiguredSource() AsyncOperationHandleConfiguredSource()
{ {
completedCallback = HandleCompleted;
} }
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, bool autoReleaseWhenCanceled, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -231,26 +135,81 @@ namespace Cysharp.Threading.Tasks
result.handle = handle; result.handle = handle;
result.progress = progress; result.progress = progress;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
result.autoReleaseWhenCanceled = autoReleaseWhenCanceled;
result.completed = false;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (AsyncOperationHandleConfiguredSource)state;
if (promise.autoReleaseWhenCanceled && promise.handle.IsValid())
{
Addressables.Release(promise.handle);
}
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result); PlayerLoopHelper.AddAction(timing, result);
handle.Completed += result.completedCallback;
token = result.core.Version; token = result.core.Version;
return result; return result;
} }
void HandleCompleted(AsyncOperationHandle _)
{
if (handle.IsValid())
{
handle.Completed -= completedCallback;
}
if (completed)
{
return;
}
completed = true;
if (cancellationToken.IsCancellationRequested)
{
if (autoReleaseWhenCanceled && handle.IsValid())
{
Addressables.Release(handle);
}
core.TrySetCanceled(cancellationToken);
}
else if (handle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(handle.OperationException);
}
else
{
core.TrySetResult(AsyncUnit.Default);
}
}
public void GetResult(short token) public void GetResult(short token)
{ {
try try
{ {
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@ -270,28 +229,25 @@ namespace Cysharp.Threading.Tasks
public bool MoveNext() public bool MoveNext()
{ {
if (completed)
{
return false;
}
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
completed = true;
if (autoReleaseWhenCanceled && handle.IsValid())
{
Addressables.Release(handle);
}
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
return false; return false;
} }
if (progress != null) if (progress != null && handle.IsValid())
{ {
progress.Report(handle.PercentComplete); progress.Report(handle.GetDownloadStatus().Percent);
}
if (handle.IsDone)
{
if (handle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(handle.OperationException);
}
else
{
core.TrySetResult(AsyncUnit.Default);
}
return false;
} }
return true; return true;
@ -299,11 +255,12 @@ namespace Cysharp.Threading.Tasks
bool TryReturn() bool TryReturn()
{ {
core.Reset();
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset();
handle = default; handle = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@ -312,214 +269,65 @@ namespace Cysharp.Threading.Tasks
#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) public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken, bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
{ {
if (handle.IsDone) return UniTask.FromResult(handle.Result); return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately, autoReleaseWhenCanceled: autoReleaseWhenCanceled);
return new UniTask<T>(AsyncOperationHandleWithCancellationSource<T>.Create(handle, cancellationToken, out var token), token);
} }
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
{ {
if (handle.IsDone) return UniTask.FromResult(handle.Result); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T>(cancellationToken);
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, out var token), token);
if (!handle.IsValid())
{
throw new Exception("Attempting to use an invalid operation handle");
} }
public struct AsyncOperationHandleAwaiter<T> : ICriticalNotifyCompletion if (handle.IsDone)
{ {
AsyncOperationHandle<T> handle;
Action<AsyncOperationHandle> continuationAction;
public AsyncOperationHandleAwaiter(AsyncOperationHandle<T> handle)
{
this.handle = handle;
this.continuationAction = null;
}
public bool IsCompleted => handle.IsDone;
public T GetResult()
{
if (continuationAction != null)
{
handle.CompletedTypeless -= continuationAction;
continuationAction = null;
}
if (handle.Status == AsyncOperationStatus.Failed) if (handle.Status == AsyncOperationStatus.Failed)
{ {
var e = handle.OperationException; return UniTask.FromException<T>(handle.OperationException);
handle = default; }
ExceptionDispatchInfo.Capture(e).Throw(); return UniTask.FromResult(handle.Result);
} }
var result = handle.Result; return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, cancelImmediately, autoReleaseWhenCanceled, out var token), token);
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);
}
readonly Action<AsyncOperationHandle<T>> continuationAction;
AsyncOperationHandle<T> handle;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<T> core;
AsyncOperationHandleWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleWithCancellationSource<T>();
}
result.handle = handle;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
handle.Completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperationHandle<T> _)
{
handle.Completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (handle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(handle.OperationException);
}
else
{
core.TrySetResult(handle.Result);
}
}
}
public T 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();
handle = default;
cancellationToken = default;
return pool.TryPush(this);
}
} }
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>> sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
{ {
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool; static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
public AsyncOperationHandleConfiguredSource<T> NextNode { get; set; } AsyncOperationHandleConfiguredSource<T> nextNode;
public ref AsyncOperationHandleConfiguredSource<T> NextNode => ref nextNode;
static AsyncOperationHandleConfiguredSource() static AsyncOperationHandleConfiguredSource()
{ {
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size); TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
} }
readonly Action<AsyncOperationHandle<T>> completedCallback;
AsyncOperationHandle<T> handle; AsyncOperationHandle<T> handle;
IProgress<float> progress;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
IProgress<float> progress;
bool autoReleaseWhenCanceled;
bool cancelImmediately;
bool completed;
UniTaskCompletionSourceCore<T> core; UniTaskCompletionSourceCore<T> core;
AsyncOperationHandleConfiguredSource() AsyncOperationHandleConfiguredSource()
{ {
completedCallback = HandleCompleted;
} }
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token) public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, bool autoReleaseWhenCanceled, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -532,17 +340,65 @@ namespace Cysharp.Threading.Tasks
} }
result.handle = handle; result.handle = handle;
result.progress = progress;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.completed = false;
result.progress = progress;
result.autoReleaseWhenCanceled = autoReleaseWhenCanceled;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (AsyncOperationHandleConfiguredSource<T>)state;
if (promise.autoReleaseWhenCanceled && promise.handle.IsValid())
{
Addressables.Release(promise.handle);
}
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result); PlayerLoopHelper.AddAction(timing, result);
handle.Completed += result.completedCallback;
token = result.core.Version; token = result.core.Version;
return result; return result;
} }
void HandleCompleted(AsyncOperationHandle<T> argHandle)
{
if (handle.IsValid())
{
handle.Completed -= completedCallback;
}
if (completed)
{
return;
}
completed = true;
if (cancellationToken.IsCancellationRequested)
{
if (autoReleaseWhenCanceled && handle.IsValid())
{
Addressables.Release(handle);
}
core.TrySetCanceled(cancellationToken);
}
else if (argHandle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(argHandle.OperationException);
}
else
{
core.TrySetResult(argHandle.Result);
}
}
public T GetResult(short token) public T GetResult(short token)
{ {
try try
@ -550,9 +406,16 @@ namespace Cysharp.Threading.Tasks
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
@ -577,28 +440,25 @@ namespace Cysharp.Threading.Tasks
public bool MoveNext() public bool MoveNext()
{ {
if (completed)
{
return false;
}
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
completed = true;
if (autoReleaseWhenCanceled && handle.IsValid())
{
Addressables.Release(handle);
}
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
return false; return false;
} }
if (progress != null) if (progress != null && handle.IsValid())
{ {
progress.Report(handle.PercentComplete); progress.Report(handle.GetDownloadStatus().Percent);
}
if (handle.IsDone)
{
if (handle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(handle.OperationException);
}
else
{
core.TrySetResult(handle.Result);
}
return false;
} }
return true; return true;
@ -611,6 +471,7 @@ namespace Cysharp.Threading.Tasks
handle = default; handle = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
return pool.TryPush(this); return pool.TryPush(this);
} }
} }

View File

@ -2,7 +2,8 @@
"name": "UniTask.Addressables", "name": "UniTask.Addressables",
"references": [ "references": [
"UniTask", "UniTask",
"Unity.ResourceManager" "Unity.ResourceManager",
"Unity.Addressables"
], ],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],
@ -16,6 +17,11 @@
"name": "com.unity.addressables", "name": "com.unity.addressables",
"expression": "", "expression": "",
"define": "UNITASK_ADDRESSABLE_SUPPORT" "define": "UNITASK_ADDRESSABLE_SUPPORT"
},
{
"name": "com.unity.addressables.cn",
"expression": "",
"define": "UNITASK_ADDRESSABLE_SUPPORT"
} }
], ],
"noEngineReferences": false "noEngineReferences": false

View File

@ -11,24 +11,33 @@ using System.Threading;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
{ {
// The idea of TweenCancelBehaviour is borrowed from https://www.shibuya24.info/entry/dotween_async_await
public enum TweenCancelBehaviour public enum TweenCancelBehaviour
{ {
Kill, Kill,
KillWithCompleteCallback, KillWithCompleteCallback,
Complete, Complete,
CompleteWithSeqeunceCallback, CompleteWithSequenceCallback,
CancelAwait, CancelAwait,
// AndCancelAwait // AndCancelAwait
KillAndCancelAwait, KillAndCancelAwait,
KillWithCompleteCallbackAndCancelAwait, KillWithCompleteCallbackAndCancelAwait,
CompleteAndCancelAwait, CompleteAndCancelAwait,
CompleteWithSeqeunceCallbackAndCancelAwait CompleteWithSequenceCallbackAndCancelAwait
} }
public static class DOTweenAsyncExtensions public static class DOTweenAsyncExtensions
{ {
enum CallbackType
{
Kill,
Complete,
Pause,
Play,
Rewind,
StepComplete
}
public static TweenAwaiter GetAwaiter(this Tween tween) public static TweenAwaiter GetAwaiter(this Tween tween)
{ {
return new TweenAwaiter(tween); return new TweenAwaiter(tween);
@ -39,7 +48,7 @@ namespace Cysharp.Threading.Tasks
Error.ThrowArgumentNullException(tween, nameof(tween)); Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask; 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) 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)); Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask; 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 public struct TweenAwaiter : ICriticalNotifyCompletion
@ -86,33 +135,32 @@ namespace Cysharp.Threading.Tasks
sealed class TweenConfiguredSource : IUniTaskSource, ITaskPoolNode<TweenConfiguredSource> sealed class TweenConfiguredSource : IUniTaskSource, ITaskPoolNode<TweenConfiguredSource>
{ {
static TaskPool<TweenConfiguredSource> pool; static TaskPool<TweenConfiguredSource> pool;
public TweenConfiguredSource NextNode { get; set; } TweenConfiguredSource nextNode;
public ref TweenConfiguredSource NextNode => ref nextNode;
static TweenConfiguredSource() static TweenConfiguredSource()
{ {
TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size); TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
} }
static readonly TweenCallback EmptyTweenCallback = () => { }; readonly TweenCallback onCompleteCallbackDelegate;
readonly TweenCallback onKillDelegate;
readonly TweenCallback onUpdateDelegate;
Tween tween; Tween tween;
TweenCancelBehaviour cancelBehaviour; TweenCancelBehaviour cancelBehaviour;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationRegistration;
CallbackType callbackType;
bool canceled; bool canceled;
TweenCallback originalUpdateAction; TweenCallback originalCompleteAction;
UniTaskCompletionSourceCore<AsyncUnit> core; UniTaskCompletionSourceCore<AsyncUnit> core;
TweenConfiguredSource() 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) if (cancellationToken.IsCancellationRequested)
{ {
@ -128,17 +176,87 @@ namespace Cysharp.Threading.Tasks
result.tween = tween; result.tween = tween;
result.cancelBehaviour = cancelBehaviour; result.cancelBehaviour = cancelBehaviour;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.callbackType = callbackType;
result.originalUpdateAction = tween.onUpdate;
result.canceled = false; result.canceled = false;
if (result.originalUpdateAction == result.onUpdateDelegate) switch (callbackType)
{ {
result.originalUpdateAction = null; case CallbackType.Kill:
result.originalCompleteAction = tween.onKill;
tween.onKill = result.onCompleteCallbackDelegate;
break;
case CallbackType.Complete:
result.originalCompleteAction = tween.onComplete;
tween.onComplete = result.onCompleteCallbackDelegate;
break;
case CallbackType.Pause:
result.originalCompleteAction = tween.onPause;
tween.onPause = result.onCompleteCallbackDelegate;
break;
case CallbackType.Play:
result.originalCompleteAction = tween.onPlay;
tween.onPlay = result.onCompleteCallbackDelegate;
break;
case CallbackType.Rewind:
result.originalCompleteAction = tween.onRewind;
tween.onRewind = result.onCompleteCallbackDelegate;
break;
case CallbackType.StepComplete:
result.originalCompleteAction = tween.onStepComplete;
tween.onStepComplete = result.onCompleteCallbackDelegate;
break;
default:
break;
} }
tween.onUpdate = result.onUpdateDelegate; if (result.originalCompleteAction == result.onCompleteCallbackDelegate)
tween.onKill = result.onKillDelegate; {
result.originalCompleteAction = null;
}
if (cancellationToken.CanBeCanceled)
{
result.cancellationRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(x =>
{
var source = (TweenConfiguredSource)x;
switch (source.cancelBehaviour)
{
case TweenCancelBehaviour.Kill:
default:
source.tween.Kill(false);
break;
case TweenCancelBehaviour.KillAndCancelAwait:
source.canceled = true;
source.tween.Kill(false);
break;
case TweenCancelBehaviour.KillWithCompleteCallback:
source.tween.Kill(true);
break;
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
source.canceled = true;
source.tween.Kill(true);
break;
case TweenCancelBehaviour.Complete:
source.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteAndCancelAwait:
source.canceled = true;
source.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteWithSequenceCallback:
source.tween.Complete(true);
break;
case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait:
source.canceled = true;
source.tween.Complete(true);
break;
case TweenCancelBehaviour.CancelAwait:
source.RestoreOriginalCallback();
source.core.TrySetCanceled(source.cancellationToken);
break;
}
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@ -146,65 +264,30 @@ namespace Cysharp.Threading.Tasks
return result; 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.CompleteWithSequenceCallbackAndCancelAwait
|| this.cancelBehaviour == TweenCancelBehaviour.CancelAwait)
{
canceled = true;
}
}
if (canceled) if (canceled)
{ {
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
} }
else else
{ {
originalCompleteAction?.Invoke();
core.TrySetResult(AsyncUnit.Default); core.TrySetResult(AsyncUnit.Default);
} }
} }
void OnUpdate()
{
originalUpdateAction?.Invoke();
if (!cancellationToken.IsCancellationRequested)
{
return;
}
switch (this.cancelBehaviour)
{
case TweenCancelBehaviour.Kill:
default:
this.tween.Kill(false);
break;
case TweenCancelBehaviour.KillAndCancelAwait:
this.canceled = true;
this.tween.Kill(false);
break;
case TweenCancelBehaviour.KillWithCompleteCallback:
this.tween.Kill(true);
break;
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
this.canceled = true;
this.tween.Kill(true);
break;
case TweenCancelBehaviour.Complete:
this.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteAndCancelAwait:
this.canceled = true;
this.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
this.tween.Complete(true);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
this.canceled = true;
this.tween.Complete(true);
break;
case TweenCancelBehaviour.CancelAwait:
this.tween.onKill = EmptyTweenCallback; // replace to empty(avoid callback after Canceled(instance is returned to pool.)
this.core.TrySetCanceled(this.cancellationToken);
break;
}
}
static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour) static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour)
{ {
@ -229,10 +312,10 @@ namespace Cysharp.Threading.Tasks
case TweenCancelBehaviour.CompleteAndCancelAwait: case TweenCancelBehaviour.CompleteAndCancelAwait:
tween.Complete(false); tween.Complete(false);
break; break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallback: case TweenCancelBehaviour.CompleteWithSequenceCallback:
tween.Complete(true); tween.Complete(true);
break; break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait: case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait:
tween.Complete(true); tween.Complete(true);
break; break;
case TweenCancelBehaviour.CancelAwait: case TweenCancelBehaviour.CancelAwait:
@ -271,13 +354,42 @@ namespace Cysharp.Threading.Tasks
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
tween.onUpdate = originalUpdateAction; cancellationRegistration.Dispose();
tween.onKill = null;
RestoreOriginalCallback();
tween = default; tween = default;
cancellationToken = default; cancellationToken = default;
originalUpdateAction = default; originalCompleteAction = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
void RestoreOriginalCallback()
{
switch (callbackType)
{
case CallbackType.Kill:
tween.onKill = originalCompleteAction;
break;
case CallbackType.Complete:
tween.onComplete = originalCompleteAction;
break;
case CallbackType.Pause:
tween.onPause = originalCompleteAction;
break;
case CallbackType.Play:
tween.onPlay = originalCompleteAction;
break;
case CallbackType.Rewind:
tween.onRewind = originalCompleteAction;
break;
case CallbackType.StepComplete:
tween.onStepComplete = originalCompleteAction;
break;
default:
break;
}
}
} }
} }

View File

@ -9,6 +9,7 @@ namespace Cysharp.Threading.Tasks
{ {
public static partial class TextMeshProAsyncExtensions public static partial class TextMeshProAsyncExtensions
{ {
// <string> -> Text
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, TMP_Text text, bool rebindOnError = true) public static void BindTo(this IUniTaskAsyncEnumerable<string> source, TMP_Text text, bool rebindOnError = true)
{ {
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); 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

@ -16,6 +16,11 @@
"name": "com.unity.textmeshpro", "name": "com.unity.textmeshpro",
"expression": "", "expression": "",
"define": "UNITASK_TEXTMESHPRO_SUPPORT" "define": "UNITASK_TEXTMESHPRO_SUPPORT"
},
{
"name": "com.unity.ugui",
"expression": "2.0.0",
"define": "UNITASK_TEXTMESHPRO_SUPPORT"
} }
], ],
"noEngineReferences": false "noEngineReferences": false

View File

@ -1,4 +1,9 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
#pragma warning disable CS0108
#if (UNITASK_NETCORE && !NETSTANDARD2_0) || UNITY_2022_3_OR_NEWER
#define SUPPORT_VALUETASK
#endif
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -19,9 +24,8 @@ namespace Cysharp.Threading.Tasks
// similar as IValueTaskSource // similar as IValueTaskSource
public interface IUniTaskSource public interface IUniTaskSource
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0 #if SUPPORT_VALUETASK
: System.Threading.Tasks.Sources.IValueTaskSource : System.Threading.Tasks.Sources.IValueTaskSource
#pragma warning disable CS0108
#endif #endif
{ {
UniTaskStatus GetStatus(short token); UniTaskStatus GetStatus(short token);
@ -30,8 +34,7 @@ namespace Cysharp.Threading.Tasks
UniTaskStatus UnsafeGetStatus(); // only for debug use. UniTaskStatus UnsafeGetStatus(); // only for debug use.
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0 #if SUPPORT_VALUETASK
#pragma warning restore CS0108
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(short token) System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(short token)
{ {
@ -53,13 +56,13 @@ namespace Cysharp.Threading.Tasks
} }
public interface IUniTaskSource<out T> : IUniTaskSource public interface IUniTaskSource<out T> : IUniTaskSource
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0 #if SUPPORT_VALUETASK
, System.Threading.Tasks.Sources.IValueTaskSource<T> , System.Threading.Tasks.Sources.IValueTaskSource<T>
#endif #endif
{ {
new T GetResult(short token); new T GetResult(short token);
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0 #if SUPPORT_VALUETASK
new public UniTaskStatus GetStatus(short token) new public UniTaskStatus GetStatus(short token)
{ {

View File

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

View File

@ -148,7 +148,7 @@ namespace Cysharp.Threading.Tasks.Internal
foreach (var candidateMethod in methods) foreach (var candidateMethod in methods)
{ {
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>(); var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>(false);
if (attributes == null) if (attributes == null)
{ {
continue; continue;

View File

@ -39,7 +39,7 @@ namespace Cysharp.Threading.Tasks.Internal
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowArgumentException<T>(string message) public static void ThrowArgumentException(string message)
{ {
throw new ArgumentException(message); throw new ArgumentException(message);
} }

View File

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

View File

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

View File

@ -7,6 +7,8 @@ using UnityEngine.Networking;
namespace Cysharp.Threading.Tasks.Internal namespace Cysharp.Threading.Tasks.Internal
{ {
#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
internal static class UnityWebRequestResultExtensions internal static class UnityWebRequestResultExtensions
{ {
public static bool IsError(this UnityWebRequest unityWebRequest) public static bool IsError(this UnityWebRequest unityWebRequest)
@ -21,4 +23,6 @@ namespace Cysharp.Threading.Tasks.Internal
#endif #endif
} }
} }
#endif
} }

View File

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

View File

@ -17,7 +17,7 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
Error.ThrowArgumentNullException(source, nameof(source)); Error.ThrowArgumentNullException(source, nameof(source));
return new AppendPrepend<TSource>(source, element, true); return new AppendPrepend<TSource>(source, element, false);
} }
} }
@ -79,7 +79,7 @@ namespace Cysharp.Threading.Tasks.Linq
if (enumerator == null) if (enumerator == null)
{ {
if (state == State.RequireAppend) if (state == State.RequirePrepend)
{ {
Current = element; Current = element;
state = State.None; state = State.None;

View File

@ -0,0 +1,184 @@
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Linq
{
public static partial class UniTaskAsyncEnumerable
{
public static IUniTaskAsyncEnumerable<T> Create<T>(Func<IAsyncWriter<T>, CancellationToken, UniTask> create)
{
Error.ThrowArgumentNullException(create, nameof(create));
return new Create<T>(create);
}
}
public interface IAsyncWriter<T>
{
UniTask YieldAsync(T value);
}
internal sealed class Create<T> : IUniTaskAsyncEnumerable<T>
{
readonly Func<IAsyncWriter<T>, CancellationToken, UniTask> create;
public Create(Func<IAsyncWriter<T>, CancellationToken, UniTask> create)
{
this.create = create;
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new _Create(create, cancellationToken);
}
sealed class _Create : MoveNextSource, IUniTaskAsyncEnumerator<T>
{
readonly Func<IAsyncWriter<T>, CancellationToken, UniTask> create;
readonly CancellationToken cancellationToken;
int state = -1;
AsyncWriter writer;
public _Create(Func<IAsyncWriter<T>, CancellationToken, UniTask> create, CancellationToken cancellationToken)
{
this.create = create;
this.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(this, 3);
}
public T Current { get; private set; }
public UniTask DisposeAsync()
{
TaskTracker.RemoveTracking(this);
writer.Dispose();
return default;
}
public UniTask<bool> MoveNextAsync()
{
if (state == -2) return default;
completionSource.Reset();
MoveNext();
return new UniTask<bool>(this, completionSource.Version);
}
void MoveNext()
{
try
{
switch (state)
{
case -1: // init
{
writer = new AsyncWriter(this);
RunWriterTask(create(writer, cancellationToken)).Forget();
if (Volatile.Read(ref state) == -2)
{
return; // complete synchronously
}
state = 0; // wait YieldAsync, it set TrySetResult(true)
return;
}
case 0:
writer.SignalWriter();
return;
default:
goto DONE;
}
}
catch (Exception ex)
{
state = -2;
completionSource.TrySetException(ex);
return;
}
DONE:
state = -2;
completionSource.TrySetResult(false);
return;
}
async UniTaskVoid RunWriterTask(UniTask task)
{
try
{
await task;
goto DONE;
}
catch (Exception ex)
{
Volatile.Write(ref state, -2);
completionSource.TrySetException(ex);
return;
}
DONE:
Volatile.Write(ref state, -2);
completionSource.TrySetResult(false);
}
public void SetResult(T value)
{
Current = value;
completionSource.TrySetResult(true);
}
}
sealed class AsyncWriter : IUniTaskSource, IAsyncWriter<T>, IDisposable
{
readonly _Create enumerator;
UniTaskCompletionSourceCore<AsyncUnit> core;
public AsyncWriter(_Create enumerator)
{
this.enumerator = enumerator;
}
public void Dispose()
{
var status = core.GetStatus(core.Version);
if (status == UniTaskStatus.Pending)
{
core.TrySetCanceled();
}
}
public void GetResult(short token)
{
core.GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public UniTask YieldAsync(T value)
{
core.Reset();
enumerator.SetResult(value);
return new UniTask(this, core.Version);
}
public void SignalWriter()
{
core.TrySetResult(AsyncUnit.Default);
}
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 8760bbbab905a534eb6fb7b61b736926 guid: 0202f723469f93945afa063bfb440d15
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@ -0,0 +1,234 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Cysharp.Threading.Tasks.Internal;
namespace Cysharp.Threading.Tasks.Linq
{
public static partial class UniTaskAsyncEnumerable
{
public static IUniTaskAsyncEnumerable<T> Merge<T>(this IUniTaskAsyncEnumerable<T> first, IUniTaskAsyncEnumerable<T> second)
{
Error.ThrowArgumentNullException(first, nameof(first));
Error.ThrowArgumentNullException(second, nameof(second));
return new Merge<T>(new [] { first, second });
}
public static IUniTaskAsyncEnumerable<T> Merge<T>(this IUniTaskAsyncEnumerable<T> first, IUniTaskAsyncEnumerable<T> second, IUniTaskAsyncEnumerable<T> third)
{
Error.ThrowArgumentNullException(first, nameof(first));
Error.ThrowArgumentNullException(second, nameof(second));
Error.ThrowArgumentNullException(third, nameof(third));
return new Merge<T>(new[] { first, second, third });
}
public static IUniTaskAsyncEnumerable<T> Merge<T>(this IEnumerable<IUniTaskAsyncEnumerable<T>> sources)
{
return sources is IUniTaskAsyncEnumerable<T>[] array
? new Merge<T>(array)
: new Merge<T>(sources.ToArray());
}
public static IUniTaskAsyncEnumerable<T> Merge<T>(params IUniTaskAsyncEnumerable<T>[] sources)
{
return new Merge<T>(sources);
}
}
internal sealed class Merge<T> : IUniTaskAsyncEnumerable<T>
{
readonly IUniTaskAsyncEnumerable<T>[] sources;
public Merge(IUniTaskAsyncEnumerable<T>[] sources)
{
if (sources.Length <= 0)
{
Error.ThrowArgumentException("No source async enumerable to merge");
}
this.sources = sources;
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
=> new _Merge(sources, cancellationToken);
enum MergeSourceState
{
Pending,
Running,
Completed,
}
sealed class _Merge : MoveNextSource, IUniTaskAsyncEnumerator<T>
{
static readonly Action<object> GetResultAtAction = GetResultAt;
readonly int length;
readonly IUniTaskAsyncEnumerator<T>[] enumerators;
readonly MergeSourceState[] states;
readonly Queue<(T, Exception, bool)> queuedResult = new Queue<(T, Exception, bool)>();
readonly CancellationToken cancellationToken;
int moveNextCompleted;
public T Current { get; private set; }
public _Merge(IUniTaskAsyncEnumerable<T>[] sources, CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
length = sources.Length;
states = ArrayPool<MergeSourceState>.Shared.Rent(length);
enumerators = ArrayPool<IUniTaskAsyncEnumerator<T>>.Shared.Rent(length);
for (var i = 0; i < length; i++)
{
enumerators[i] = sources[i].GetAsyncEnumerator(cancellationToken);
states[i] = (int)MergeSourceState.Pending;;
}
}
public UniTask<bool> MoveNextAsync()
{
cancellationToken.ThrowIfCancellationRequested();
completionSource.Reset();
Interlocked.Exchange(ref moveNextCompleted, 0);
if (HasQueuedResult() && Interlocked.CompareExchange(ref moveNextCompleted, 1, 0) == 0)
{
(T, Exception, bool) value;
lock (states)
{
value = queuedResult.Dequeue();
}
var resultValue = value.Item1;
var exception = value.Item2;
var hasNext = value.Item3;
if (exception != null)
{
completionSource.TrySetException(exception);
}
else
{
Current = resultValue;
completionSource.TrySetResult(hasNext);
}
return new UniTask<bool>(this, completionSource.Version);
}
for (var i = 0; i < length; i++)
{
lock (states)
{
if (states[i] == MergeSourceState.Pending)
{
states[i] = MergeSourceState.Running;
}
else
{
continue;
}
}
var awaiter = enumerators[i].MoveNextAsync().GetAwaiter();
if (awaiter.IsCompleted)
{
GetResultAt(i, awaiter);
}
else
{
awaiter.SourceOnCompleted(GetResultAtAction, StateTuple.Create(this, i, awaiter));
}
}
return new UniTask<bool>(this, completionSource.Version);
}
public async UniTask DisposeAsync()
{
for (var i = 0; i < length; i++)
{
await enumerators[i].DisposeAsync();
}
ArrayPool<MergeSourceState>.Shared.Return(states, true);
ArrayPool<IUniTaskAsyncEnumerator<T>>.Shared.Return(enumerators, true);
}
static void GetResultAt(object state)
{
using (var tuple = (StateTuple<_Merge, int, UniTask<bool>.Awaiter>)state)
{
tuple.Item1.GetResultAt(tuple.Item2, tuple.Item3);
}
}
void GetResultAt(int index, UniTask<bool>.Awaiter awaiter)
{
bool hasNext;
bool completedAll;
try
{
hasNext = awaiter.GetResult();
}
catch (Exception ex)
{
if (Interlocked.CompareExchange(ref moveNextCompleted, 1, 0) == 0)
{
completionSource.TrySetException(ex);
}
else
{
lock (states)
{
queuedResult.Enqueue((default, ex, default));
}
}
return;
}
lock (states)
{
states[index] = hasNext ? MergeSourceState.Pending : MergeSourceState.Completed;
completedAll = !hasNext && IsCompletedAll();
}
if (hasNext || completedAll)
{
if (Interlocked.CompareExchange(ref moveNextCompleted, 1, 0) == 0)
{
Current = enumerators[index].Current;
completionSource.TrySetResult(!completedAll);
}
else
{
lock (states)
{
queuedResult.Enqueue((enumerators[index].Current, null, !completedAll));
}
}
}
}
bool HasQueuedResult()
{
lock (states)
{
return queuedResult.Count > 0;
}
}
bool IsCompletedAll()
{
lock (states)
{
for (var i = 0; i < length; i++)
{
if (states[i] != MergeSourceState.Completed)
{
return false;
}
}
}
return true;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ca56812f160c45d0bacb4339819edf1a
timeCreated: 1694133666

View File

@ -88,6 +88,7 @@ namespace Cysharp.Threading.Tasks.Linq
this.selector = selector; this.selector = selector;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.moveNextAction = MoveNext; this.moveNextAction = MoveNext;
TaskTracker.TrackActiveTask(this, 3);
} }
public TResult Current { get; private set; } public TResult Current { get; private set; }
@ -156,6 +157,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync() public UniTask DisposeAsync()
{ {
TaskTracker.RemoveTracking(this);
return enumerator.DisposeAsync(); return enumerator.DisposeAsync();
} }
} }
@ -195,6 +197,7 @@ namespace Cysharp.Threading.Tasks.Linq
this.selector = selector; this.selector = selector;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.moveNextAction = MoveNext; this.moveNextAction = MoveNext;
TaskTracker.TrackActiveTask(this, 3);
} }
public TResult Current { get; private set; } public TResult Current { get; private set; }
@ -263,6 +266,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync() public UniTask DisposeAsync()
{ {
TaskTracker.RemoveTracking(this);
return enumerator.DisposeAsync(); return enumerator.DisposeAsync();
} }
} }
@ -302,6 +306,7 @@ namespace Cysharp.Threading.Tasks.Linq
this.selector = selector; this.selector = selector;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.moveNextAction = MoveNext; this.moveNextAction = MoveNext;
TaskTracker.TrackActiveTask(this, 3);
} }
public TResult Current { get; private set; } public TResult Current { get; private set; }
@ -382,6 +387,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync() public UniTask DisposeAsync()
{ {
TaskTracker.RemoveTracking(this);
return enumerator.DisposeAsync(); return enumerator.DisposeAsync();
} }
} }
@ -422,6 +428,7 @@ namespace Cysharp.Threading.Tasks.Linq
this.selector = selector; this.selector = selector;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.moveNextAction = MoveNext; this.moveNextAction = MoveNext;
TaskTracker.TrackActiveTask(this, 3);
} }
public TResult Current { get; private set; } public TResult Current { get; private set; }
@ -502,6 +509,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync() public UniTask DisposeAsync()
{ {
TaskTracker.RemoveTracking(this);
return enumerator.DisposeAsync(); return enumerator.DisposeAsync();
} }
} }
@ -541,6 +549,7 @@ namespace Cysharp.Threading.Tasks.Linq
this.selector = selector; this.selector = selector;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.moveNextAction = MoveNext; this.moveNextAction = MoveNext;
TaskTracker.TrackActiveTask(this, 3);
} }
public TResult Current { get; private set; } public TResult Current { get; private set; }
@ -621,6 +630,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync() public UniTask DisposeAsync()
{ {
TaskTracker.RemoveTracking(this);
return enumerator.DisposeAsync(); return enumerator.DisposeAsync();
} }
} }
@ -661,6 +671,7 @@ namespace Cysharp.Threading.Tasks.Linq
this.selector = selector; this.selector = selector;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.moveNextAction = MoveNext; this.moveNextAction = MoveNext;
TaskTracker.TrackActiveTask(this, 3);
} }
public TResult Current { get; private set; } public TResult Current { get; private set; }
@ -741,6 +752,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync() public UniTask DisposeAsync()
{ {
TaskTracker.RemoveTracking(this);
return enumerator.DisposeAsync(); return enumerator.DisposeAsync();
} }
} }

View File

@ -63,6 +63,42 @@ namespace Cysharp.Threading.Tasks.Linq
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget(); 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 // OnNext, OnError
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> 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(); 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 // OnNext, OnCompleted
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action 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(); 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 // IObserver
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, IObserver<TSource> observer) public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, IObserver<TSource> observer)
@ -194,9 +310,16 @@ namespace Cysharp.Threading.Tasks.Linq
try try
{ {
while (await e.MoveNextAsync()) while (await e.MoveNextAsync())
{
try
{ {
onNext(e.Current); onNext(e.Current);
} }
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
onCompleted(); onCompleted();
} }
catch (Exception ex) catch (Exception ex)
@ -226,9 +349,16 @@ namespace Cysharp.Threading.Tasks.Linq
try try
{ {
while (await e.MoveNextAsync()) while (await e.MoveNextAsync())
{
try
{ {
onNext(e.Current).Forget(); onNext(e.Current).Forget();
} }
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
onCompleted(); onCompleted();
} }
catch (Exception ex) catch (Exception ex)
@ -258,9 +388,16 @@ namespace Cysharp.Threading.Tasks.Linq
try try
{ {
while (await e.MoveNextAsync()) while (await e.MoveNextAsync())
{
try
{ {
onNext(e.Current, cancellationToken).Forget(); onNext(e.Current, cancellationToken).Forget();
} }
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
onCompleted(); onCompleted();
} }
catch (Exception ex) catch (Exception ex)
@ -290,9 +427,16 @@ namespace Cysharp.Threading.Tasks.Linq
try try
{ {
while (await e.MoveNextAsync()) while (await e.MoveNextAsync())
{
try
{ {
observer.OnNext(e.Current); observer.OnNext(e.Current);
} }
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
observer.OnCompleted(); observer.OnCompleted();
} }
catch (Exception ex) catch (Exception ex)
@ -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) internal static async UniTask<TSource[]> ToArrayAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
{ {
// UnityEngine.Debug.Log("Called ToArray");
var pool = ArrayPool<TSource>.Shared; var pool = ArrayPool<TSource>.Shared;
var array = pool.Rent(16); var array = pool.Rent(16);

View File

@ -4,38 +4,50 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
public static partial class UniTaskAsyncEnumerable public static partial class UniTaskAsyncEnumerable
{ {
public static IUniTaskAsyncEnumerable<AsyncUnit> EveryUpdate(PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) public static IUniTaskAsyncEnumerable<AsyncUnit> EveryUpdate(PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool cancelImmediately = false)
{ {
return new EveryUpdate(updateTiming); return new EveryUpdate(updateTiming, cancelImmediately);
} }
} }
internal class EveryUpdate : IUniTaskAsyncEnumerable<AsyncUnit> internal class EveryUpdate : IUniTaskAsyncEnumerable<AsyncUnit>
{ {
readonly PlayerLoopTiming updateTiming; readonly PlayerLoopTiming updateTiming;
readonly bool cancelImmediately;
public EveryUpdate(PlayerLoopTiming updateTiming) public EveryUpdate(PlayerLoopTiming updateTiming, bool cancelImmediately)
{ {
this.updateTiming = updateTiming; this.updateTiming = updateTiming;
this.cancelImmediately = cancelImmediately;
} }
public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default) public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{ {
return new _EveryUpdate(updateTiming, cancellationToken); return new _EveryUpdate(updateTiming, cancellationToken, cancelImmediately);
} }
class _EveryUpdate : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem class _EveryUpdate : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem
{ {
readonly PlayerLoopTiming updateTiming; readonly PlayerLoopTiming updateTiming;
CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
bool disposed; bool disposed;
public _EveryUpdate(PlayerLoopTiming updateTiming, CancellationToken cancellationToken) public _EveryUpdate(PlayerLoopTiming updateTiming, CancellationToken cancellationToken, bool cancelImmediately)
{ {
this.updateTiming = updateTiming; this.updateTiming = updateTiming;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (_EveryUpdate)state;
source.completionSource.TrySetCanceled(source.cancellationToken);
}, this);
}
TaskTracker.TrackActiveTask(this, 2); TaskTracker.TrackActiveTask(this, 2);
PlayerLoopHelper.AddAction(updateTiming, this); PlayerLoopHelper.AddAction(updateTiming, this);
} }
@ -44,10 +56,14 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {
// return false instead of throw if (disposed) return CompletedTasks.False;
if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False;
completionSource.Reset(); completionSource.Reset();
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
}
return new UniTask<bool>(this, completionSource.Version); return new UniTask<bool>(this, completionSource.Version);
} }
@ -55,6 +71,7 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
if (!disposed) if (!disposed)
{ {
cancellationTokenRegistration.Dispose();
disposed = true; disposed = true;
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
} }
@ -63,7 +80,13 @@ namespace Cysharp.Threading.Tasks.Linq
public bool MoveNext() public bool MoveNext()
{ {
if (disposed || cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
return false;
}
if (disposed)
{ {
completionSource.TrySetResult(false); completionSource.TrySetResult(false);
return false; return false;

View File

@ -7,7 +7,7 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
public static partial class UniTaskAsyncEnumerable public static partial class UniTaskAsyncEnumerable
{ {
public static IUniTaskAsyncEnumerable<TProperty> EveryValueChanged<TTarget, TProperty>(TTarget target, Func<TTarget, TProperty> propertySelector, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<TProperty> equalityComparer = null) public static IUniTaskAsyncEnumerable<TProperty> EveryValueChanged<TTarget, TProperty>(TTarget target, Func<TTarget, TProperty> propertySelector, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<TProperty> equalityComparer = null, bool cancelImmediately = false)
where TTarget : class where TTarget : class
{ {
var unityObject = target as UnityEngine.Object; var unityObject = target as UnityEngine.Object;
@ -15,11 +15,11 @@ namespace Cysharp.Threading.Tasks.Linq
if (isUnityObject) if (isUnityObject)
{ {
return new EveryValueChangedUnityObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming); return new EveryValueChangedUnityObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming, cancelImmediately);
} }
else else
{ {
return new EveryValueChangedStandardObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming); return new EveryValueChangedStandardObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming, cancelImmediately);
} }
} }
} }
@ -30,18 +30,20 @@ namespace Cysharp.Threading.Tasks.Linq
readonly Func<TTarget, TProperty> propertySelector; readonly Func<TTarget, TProperty> propertySelector;
readonly IEqualityComparer<TProperty> equalityComparer; readonly IEqualityComparer<TProperty> equalityComparer;
readonly PlayerLoopTiming monitorTiming; readonly PlayerLoopTiming monitorTiming;
readonly bool cancelImmediately;
public EveryValueChangedUnityObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming) public EveryValueChangedUnityObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, bool cancelImmediately)
{ {
this.target = target; this.target = target;
this.propertySelector = propertySelector; this.propertySelector = propertySelector;
this.equalityComparer = equalityComparer; this.equalityComparer = equalityComparer;
this.monitorTiming = monitorTiming; this.monitorTiming = monitorTiming;
this.cancelImmediately = cancelImmediately;
} }
public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default) public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{ {
return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken); return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken, cancelImmediately);
} }
sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem
@ -50,13 +52,14 @@ namespace Cysharp.Threading.Tasks.Linq
readonly UnityEngine.Object targetAsUnityObject; readonly UnityEngine.Object targetAsUnityObject;
readonly IEqualityComparer<TProperty> equalityComparer; readonly IEqualityComparer<TProperty> equalityComparer;
readonly Func<TTarget, TProperty> propertySelector; readonly Func<TTarget, TProperty> propertySelector;
CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
bool first; bool first;
TProperty currentValue; TProperty currentValue;
bool disposed; bool disposed;
public _EveryValueChanged(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken) public _EveryValueChanged(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken, bool cancelImmediately)
{ {
this.target = target; this.target = target;
this.targetAsUnityObject = target as UnityEngine.Object; this.targetAsUnityObject = target as UnityEngine.Object;
@ -64,6 +67,16 @@ namespace Cysharp.Threading.Tasks.Linq
this.equalityComparer = equalityComparer; this.equalityComparer = equalityComparer;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.first = true; this.first = true;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (_EveryValueChanged)state;
source.completionSource.TrySetCanceled(source.cancellationToken);
}, this);
}
TaskTracker.TrackActiveTask(this, 2); TaskTracker.TrackActiveTask(this, 2);
PlayerLoopHelper.AddAction(monitorTiming, this); PlayerLoopHelper.AddAction(monitorTiming, this);
} }
@ -72,8 +85,15 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {
// return false instead of throw if (disposed) return CompletedTasks.False;
if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False;
completionSource.Reset();
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
return new UniTask<bool>(this, completionSource.Version);
}
if (first) if (first)
{ {
@ -86,7 +106,6 @@ namespace Cysharp.Threading.Tasks.Linq
return CompletedTasks.True; return CompletedTasks.True;
} }
completionSource.Reset();
return new UniTask<bool>(this, completionSource.Version); return new UniTask<bool>(this, completionSource.Version);
} }
@ -94,6 +113,7 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
if (!disposed) if (!disposed)
{ {
cancellationTokenRegistration.Dispose();
disposed = true; disposed = true;
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
} }
@ -102,13 +122,18 @@ namespace Cysharp.Threading.Tasks.Linq
public bool MoveNext() public bool MoveNext()
{ {
if (disposed || cancellationToken.IsCancellationRequested || targetAsUnityObject == null) // destroyed = cancel. if (disposed || targetAsUnityObject == null)
{ {
completionSource.TrySetResult(false); completionSource.TrySetResult(false);
DisposeAsync().Forget(); DisposeAsync().Forget();
return false; return false;
} }
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
return false;
}
TProperty nextValue = default(TProperty); TProperty nextValue = default(TProperty);
try try
{ {
@ -139,18 +164,20 @@ namespace Cysharp.Threading.Tasks.Linq
readonly Func<TTarget, TProperty> propertySelector; readonly Func<TTarget, TProperty> propertySelector;
readonly IEqualityComparer<TProperty> equalityComparer; readonly IEqualityComparer<TProperty> equalityComparer;
readonly PlayerLoopTiming monitorTiming; readonly PlayerLoopTiming monitorTiming;
readonly bool cancelImmediately;
public EveryValueChangedStandardObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming) public EveryValueChangedStandardObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, bool cancelImmediately)
{ {
this.target = new WeakReference<TTarget>(target, false); this.target = new WeakReference<TTarget>(target, false);
this.propertySelector = propertySelector; this.propertySelector = propertySelector;
this.equalityComparer = equalityComparer; this.equalityComparer = equalityComparer;
this.monitorTiming = monitorTiming; this.monitorTiming = monitorTiming;
this.cancelImmediately = cancelImmediately;
} }
public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default) public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{ {
return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken); return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken, cancelImmediately);
} }
sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem
@ -158,19 +185,30 @@ namespace Cysharp.Threading.Tasks.Linq
readonly WeakReference<TTarget> target; readonly WeakReference<TTarget> target;
readonly IEqualityComparer<TProperty> equalityComparer; readonly IEqualityComparer<TProperty> equalityComparer;
readonly Func<TTarget, TProperty> propertySelector; readonly Func<TTarget, TProperty> propertySelector;
CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
bool first; bool first;
TProperty currentValue; TProperty currentValue;
bool disposed; bool disposed;
public _EveryValueChanged(WeakReference<TTarget> target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken) public _EveryValueChanged(WeakReference<TTarget> target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken, bool cancelImmediately)
{ {
this.target = target; this.target = target;
this.propertySelector = propertySelector; this.propertySelector = propertySelector;
this.equalityComparer = equalityComparer; this.equalityComparer = equalityComparer;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.first = true; this.first = true;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (_EveryValueChanged)state;
source.completionSource.TrySetCanceled(source.cancellationToken);
}, this);
}
TaskTracker.TrackActiveTask(this, 2); TaskTracker.TrackActiveTask(this, 2);
PlayerLoopHelper.AddAction(monitorTiming, this); PlayerLoopHelper.AddAction(monitorTiming, this);
} }
@ -179,7 +217,15 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {
if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False; if (disposed) return CompletedTasks.False;
completionSource.Reset();
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
return new UniTask<bool>(this, completionSource.Version);
}
if (first) if (first)
{ {
@ -192,7 +238,6 @@ namespace Cysharp.Threading.Tasks.Linq
return CompletedTasks.True; return CompletedTasks.True;
} }
completionSource.Reset();
return new UniTask<bool>(this, completionSource.Version); return new UniTask<bool>(this, completionSource.Version);
} }
@ -200,6 +245,7 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
if (!disposed) if (!disposed)
{ {
cancellationTokenRegistration.Dispose();
disposed = true; disposed = true;
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
} }
@ -208,14 +254,19 @@ namespace Cysharp.Threading.Tasks.Linq
public bool MoveNext() public bool MoveNext()
{ {
UnityEngine.Debug.Log("TRY_RESULT:" + target.TryGetTarget(out var _)); if (disposed || !target.TryGetTarget(out var t))
if (disposed || cancellationToken.IsCancellationRequested || !target.TryGetTarget(out var t))
{ {
completionSource.TrySetResult(false); completionSource.TrySetResult(false);
DisposeAsync().Forget(); DisposeAsync().Forget();
return false; return false;
} }
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
return false;
}
TProperty nextValue = default(TProperty); TProperty nextValue = default(TProperty);
try try
{ {

View File

@ -6,32 +6,32 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
public static partial class UniTaskAsyncEnumerable public static partial class UniTaskAsyncEnumerable
{ {
public static IUniTaskAsyncEnumerable<AsyncUnit> Timer(TimeSpan dueTime, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false) public static IUniTaskAsyncEnumerable<AsyncUnit> Timer(TimeSpan dueTime, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false, bool cancelImmediately = false)
{ {
return new Timer(dueTime, null, updateTiming, ignoreTimeScale); return new Timer(dueTime, null, updateTiming, ignoreTimeScale, cancelImmediately);
} }
public static IUniTaskAsyncEnumerable<AsyncUnit> Timer(TimeSpan dueTime, TimeSpan period, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false) public static IUniTaskAsyncEnumerable<AsyncUnit> Timer(TimeSpan dueTime, TimeSpan period, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false, bool cancelImmediately = false)
{ {
return new Timer(dueTime, period, updateTiming, ignoreTimeScale); return new Timer(dueTime, period, updateTiming, ignoreTimeScale, cancelImmediately);
} }
public static IUniTaskAsyncEnumerable<AsyncUnit> Interval(TimeSpan period, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false) public static IUniTaskAsyncEnumerable<AsyncUnit> Interval(TimeSpan period, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false, bool cancelImmediately = false)
{ {
return new Timer(period, period, updateTiming, ignoreTimeScale); return new Timer(period, period, updateTiming, ignoreTimeScale, cancelImmediately);
} }
public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool cancelImmediately = false)
{ {
if (dueTimeFrameCount < 0) if (dueTimeFrameCount < 0)
{ {
throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. dueTimeFrameCount:" + dueTimeFrameCount); throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. dueTimeFrameCount:" + dueTimeFrameCount);
} }
return new TimerFrame(dueTimeFrameCount, null, updateTiming); return new TimerFrame(dueTimeFrameCount, null, updateTiming, cancelImmediately);
} }
public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, int periodFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, int periodFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool cancelImmediately = false)
{ {
if (dueTimeFrameCount < 0) if (dueTimeFrameCount < 0)
{ {
@ -42,16 +42,16 @@ namespace Cysharp.Threading.Tasks.Linq
throw new ArgumentOutOfRangeException("Delay does not allow minus periodFrameCount. periodFrameCount:" + dueTimeFrameCount); throw new ArgumentOutOfRangeException("Delay does not allow minus periodFrameCount. periodFrameCount:" + dueTimeFrameCount);
} }
return new TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming); return new TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming, cancelImmediately);
} }
public static IUniTaskAsyncEnumerable<AsyncUnit> IntervalFrame(int intervalFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) public static IUniTaskAsyncEnumerable<AsyncUnit> IntervalFrame(int intervalFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool cancelImmediately = false)
{ {
if (intervalFrameCount < 0) if (intervalFrameCount < 0)
{ {
throw new ArgumentOutOfRangeException("Delay does not allow minus intervalFrameCount. intervalFrameCount:" + intervalFrameCount); throw new ArgumentOutOfRangeException("Delay does not allow minus intervalFrameCount. intervalFrameCount:" + intervalFrameCount);
} }
return new TimerFrame(intervalFrameCount, intervalFrameCount, updateTiming); return new TimerFrame(intervalFrameCount, intervalFrameCount, updateTiming, cancelImmediately);
} }
} }
@ -61,18 +61,20 @@ namespace Cysharp.Threading.Tasks.Linq
readonly TimeSpan dueTime; readonly TimeSpan dueTime;
readonly TimeSpan? period; readonly TimeSpan? period;
readonly bool ignoreTimeScale; readonly bool ignoreTimeScale;
readonly bool cancelImmediately;
public Timer(TimeSpan dueTime, TimeSpan? period, PlayerLoopTiming updateTiming, bool ignoreTimeScale) public Timer(TimeSpan dueTime, TimeSpan? period, PlayerLoopTiming updateTiming, bool ignoreTimeScale, bool cancelImmediately)
{ {
this.updateTiming = updateTiming; this.updateTiming = updateTiming;
this.dueTime = dueTime; this.dueTime = dueTime;
this.period = period; this.period = period;
this.ignoreTimeScale = ignoreTimeScale; this.ignoreTimeScale = ignoreTimeScale;
this.cancelImmediately = cancelImmediately;
} }
public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default) public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{ {
return new _Timer(dueTime, period, updateTiming, ignoreTimeScale, cancellationToken); return new _Timer(dueTime, period, updateTiming, ignoreTimeScale, cancellationToken, cancelImmediately);
} }
class _Timer : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem class _Timer : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem
@ -81,7 +83,8 @@ namespace Cysharp.Threading.Tasks.Linq
readonly float? period; readonly float? period;
readonly PlayerLoopTiming updateTiming; readonly PlayerLoopTiming updateTiming;
readonly bool ignoreTimeScale; readonly bool ignoreTimeScale;
CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
int initialFrame; int initialFrame;
float elapsed; float elapsed;
@ -89,7 +92,7 @@ namespace Cysharp.Threading.Tasks.Linq
bool completed; bool completed;
bool disposed; bool disposed;
public _Timer(TimeSpan dueTime, TimeSpan? period, PlayerLoopTiming updateTiming, bool ignoreTimeScale, CancellationToken cancellationToken) public _Timer(TimeSpan dueTime, TimeSpan? period, PlayerLoopTiming updateTiming, bool ignoreTimeScale, CancellationToken cancellationToken, bool cancelImmediately)
{ {
this.dueTime = (float)dueTime.TotalSeconds; this.dueTime = (float)dueTime.TotalSeconds;
this.period = (period == null) ? null : (float?)period.Value.TotalSeconds; this.period = (period == null) ? null : (float?)period.Value.TotalSeconds;
@ -105,6 +108,16 @@ namespace Cysharp.Threading.Tasks.Linq
this.updateTiming = updateTiming; this.updateTiming = updateTiming;
this.ignoreTimeScale = ignoreTimeScale; this.ignoreTimeScale = ignoreTimeScale;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (_Timer)state;
source.completionSource.TrySetCanceled(source.cancellationToken);
}, this);
}
TaskTracker.TrackActiveTask(this, 2); TaskTracker.TrackActiveTask(this, 2);
PlayerLoopHelper.AddAction(updateTiming, this); PlayerLoopHelper.AddAction(updateTiming, this);
} }
@ -114,12 +127,16 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {
// return false instead of throw // return false instead of throw
if (disposed || cancellationToken.IsCancellationRequested || completed) return CompletedTasks.False; if (disposed || completed) return CompletedTasks.False;
// reset value here. // reset value here.
this.elapsed = 0; this.elapsed = 0;
completionSource.Reset(); completionSource.Reset();
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
}
return new UniTask<bool>(this, completionSource.Version); return new UniTask<bool>(this, completionSource.Version);
} }
@ -127,6 +144,7 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
if (!disposed) if (!disposed)
{ {
cancellationTokenRegistration.Dispose();
disposed = true; disposed = true;
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
} }
@ -135,11 +153,16 @@ namespace Cysharp.Threading.Tasks.Linq
public bool MoveNext() public bool MoveNext()
{ {
if (disposed || cancellationToken.IsCancellationRequested) if (disposed)
{ {
completionSource.TrySetResult(false); completionSource.TrySetResult(false);
return false; return false;
} }
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
return false;
}
if (dueTimePhase) if (dueTimePhase)
{ {
@ -187,24 +210,27 @@ namespace Cysharp.Threading.Tasks.Linq
readonly PlayerLoopTiming updateTiming; readonly PlayerLoopTiming updateTiming;
readonly int dueTimeFrameCount; readonly int dueTimeFrameCount;
readonly int? periodFrameCount; readonly int? periodFrameCount;
readonly bool cancelImmediately;
public TimerFrame(int dueTimeFrameCount, int? periodFrameCount, PlayerLoopTiming updateTiming) public TimerFrame(int dueTimeFrameCount, int? periodFrameCount, PlayerLoopTiming updateTiming, bool cancelImmediately)
{ {
this.updateTiming = updateTiming; this.updateTiming = updateTiming;
this.dueTimeFrameCount = dueTimeFrameCount; this.dueTimeFrameCount = dueTimeFrameCount;
this.periodFrameCount = periodFrameCount; this.periodFrameCount = periodFrameCount;
this.cancelImmediately = cancelImmediately;
} }
public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default) public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{ {
return new _TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming, cancellationToken); return new _TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming, cancellationToken, cancelImmediately);
} }
class _TimerFrame : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem class _TimerFrame : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem
{ {
readonly int dueTimeFrameCount; readonly int dueTimeFrameCount;
readonly int? periodFrameCount; readonly int? periodFrameCount;
CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
int initialFrame; int initialFrame;
int currentFrame; int currentFrame;
@ -212,7 +238,7 @@ namespace Cysharp.Threading.Tasks.Linq
bool completed; bool completed;
bool disposed; bool disposed;
public _TimerFrame(int dueTimeFrameCount, int? periodFrameCount, PlayerLoopTiming updateTiming, CancellationToken cancellationToken) public _TimerFrame(int dueTimeFrameCount, int? periodFrameCount, PlayerLoopTiming updateTiming, CancellationToken cancellationToken, bool cancelImmediately)
{ {
if (dueTimeFrameCount <= 0) dueTimeFrameCount = 0; if (dueTimeFrameCount <= 0) dueTimeFrameCount = 0;
if (periodFrameCount != null) if (periodFrameCount != null)
@ -226,6 +252,15 @@ namespace Cysharp.Threading.Tasks.Linq
this.periodFrameCount = periodFrameCount; this.periodFrameCount = periodFrameCount;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (_TimerFrame)state;
source.completionSource.TrySetCanceled(source.cancellationToken);
}, this);
}
TaskTracker.TrackActiveTask(this, 2); TaskTracker.TrackActiveTask(this, 2);
PlayerLoopHelper.AddAction(updateTiming, this); PlayerLoopHelper.AddAction(updateTiming, this);
} }
@ -234,13 +269,15 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {
// return false instead of throw if (disposed || completed) return CompletedTasks.False;
if (disposed || cancellationToken.IsCancellationRequested || completed) return CompletedTasks.False;
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
}
// reset value here. // reset value here.
this.currentFrame = 0; this.currentFrame = 0;
completionSource.Reset(); completionSource.Reset();
return new UniTask<bool>(this, completionSource.Version); return new UniTask<bool>(this, completionSource.Version);
} }
@ -249,6 +286,7 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
if (!disposed) if (!disposed)
{ {
cancellationTokenRegistration.Dispose();
disposed = true; disposed = true;
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
} }
@ -257,7 +295,12 @@ namespace Cysharp.Threading.Tasks.Linq
public bool MoveNext() public bool MoveNext()
{ {
if (disposed || cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
return false;
}
if (disposed)
{ {
completionSource.TrySetResult(false); completionSource.TrySetResult(false);
return false; return false;

View File

@ -88,6 +88,7 @@ namespace Cysharp.Threading.Tasks.Linq
this.predicate = predicate; this.predicate = predicate;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.moveNextAction = MoveNext; this.moveNextAction = MoveNext;
TaskTracker.TrackActiveTask(this, 3);
} }
public TSource Current { get; private set; } public TSource Current { get; private set; }
@ -165,6 +166,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync() public UniTask DisposeAsync()
{ {
TaskTracker.RemoveTracking(this);
return enumerator.DisposeAsync(); return enumerator.DisposeAsync();
} }
} }
@ -204,6 +206,7 @@ namespace Cysharp.Threading.Tasks.Linq
this.predicate = predicate; this.predicate = predicate;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.moveNextAction = MoveNext; this.moveNextAction = MoveNext;
TaskTracker.TrackActiveTask(this, 3);
} }
public TSource Current { get; private set; } public TSource Current { get; private set; }
@ -281,6 +284,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync() public UniTask DisposeAsync()
{ {
TaskTracker.RemoveTracking(this);
return enumerator.DisposeAsync(); return enumerator.DisposeAsync();
} }
} }
@ -320,6 +324,7 @@ namespace Cysharp.Threading.Tasks.Linq
this.predicate = predicate; this.predicate = predicate;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.moveNextAction = MoveNext; this.moveNextAction = MoveNext;
TaskTracker.TrackActiveTask(this, 3);
} }
public TSource Current { get; private set; } public TSource Current { get; private set; }
@ -410,6 +415,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync() public UniTask DisposeAsync()
{ {
TaskTracker.RemoveTracking(this);
return enumerator.DisposeAsync(); return enumerator.DisposeAsync();
} }
} }
@ -450,6 +456,7 @@ namespace Cysharp.Threading.Tasks.Linq
this.predicate = predicate; this.predicate = predicate;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.moveNextAction = MoveNext; this.moveNextAction = MoveNext;
TaskTracker.TrackActiveTask(this, 3);
} }
public TSource Current { get; private set; } public TSource Current { get; private set; }
@ -540,6 +547,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync() public UniTask DisposeAsync()
{ {
TaskTracker.RemoveTracking(this);
return enumerator.DisposeAsync(); return enumerator.DisposeAsync();
} }
} }
@ -579,6 +587,7 @@ namespace Cysharp.Threading.Tasks.Linq
this.predicate = predicate; this.predicate = predicate;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.moveNextAction = MoveNext; this.moveNextAction = MoveNext;
TaskTracker.TrackActiveTask(this, 3);
} }
public TSource Current { get; private set; } public TSource Current { get; private set; }
@ -669,6 +678,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync() public UniTask DisposeAsync()
{ {
TaskTracker.RemoveTracking(this);
return enumerator.DisposeAsync(); return enumerator.DisposeAsync();
} }
} }
@ -709,6 +719,7 @@ namespace Cysharp.Threading.Tasks.Linq
this.predicate = predicate; this.predicate = predicate;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.moveNextAction = MoveNext; this.moveNextAction = MoveNext;
TaskTracker.TrackActiveTask(this, 3);
} }
public TSource Current { get; private set; } public TSource Current { get; private set; }
@ -799,6 +810,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync() public UniTask DisposeAsync()
{ {
TaskTracker.RemoveTracking(this);
return enumerator.DisposeAsync(); return enumerator.DisposeAsync();
} }
} }

View File

@ -8,8 +8,10 @@ using System.Threading;
#if UNITY_2019_3_OR_NEWER #if UNITY_2019_3_OR_NEWER
using UnityEngine.LowLevel; using UnityEngine.LowLevel;
using PlayerLoopType = UnityEngine.PlayerLoop;
#else #else
using UnityEngine.Experimental.LowLevel; using UnityEngine.Experimental.LowLevel;
using PlayerLoopType = UnityEngine.Experimental.PlayerLoop;
#endif #endif
#if UNITY_EDITOR #if UNITY_EDITOR
@ -57,6 +59,13 @@ namespace Cysharp.Threading.Tasks
public struct UniTaskLoopRunnerLastYieldUpdate { }; public struct UniTaskLoopRunnerLastYieldUpdate { };
public struct UniTaskLoopRunnerLastYieldPreLateUpdate { }; public struct UniTaskLoopRunnerLastYieldPreLateUpdate { };
public struct UniTaskLoopRunnerLastYieldPostLateUpdate { }; 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 public enum PlayerLoopTiming
@ -80,7 +89,86 @@ namespace Cysharp.Threading.Tasks
LastPreLateUpdate = 11, LastPreLateUpdate = 11,
PostLateUpdate = 12, 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
}
[Flags]
public enum InjectPlayerLoopTimings
{
/// <summary>
/// Preset: All loops(default).
/// </summary>
All =
Initialization | LastInitialization |
EarlyUpdate | LastEarlyUpdate |
FixedUpdate | LastFixedUpdate |
PreUpdate | LastPreUpdate |
Update | LastUpdate |
PreLateUpdate | LastPreLateUpdate |
PostLateUpdate | LastPostLateUpdate
#if UNITY_2020_2_OR_NEWER
| TimeUpdate | LastTimeUpdate,
#else
,
#endif
/// <summary>
/// Preset: All without last except LastPostLateUpdate.
/// </summary>
Standard =
Initialization |
EarlyUpdate |
FixedUpdate |
PreUpdate |
Update |
PreLateUpdate |
PostLateUpdate | LastPostLateUpdate
#if UNITY_2020_2_OR_NEWER
| TimeUpdate
#endif
,
/// <summary>
/// Preset: Minimum pattern, Update | FixedUpdate | LastPostLateUpdate
/// </summary>
Minimum =
Update | FixedUpdate | LastPostLateUpdate,
// PlayerLoopTiming
Initialization = 1,
LastInitialization = 2,
EarlyUpdate = 4,
LastEarlyUpdate = 8,
FixedUpdate = 16,
LastFixedUpdate = 32,
PreUpdate = 64,
LastPreUpdate = 128,
Update = 256,
LastUpdate = 512,
PreLateUpdate = 1024,
LastPreLateUpdate = 2048,
PostLateUpdate = 4096,
LastPostLateUpdate = 8192
#if UNITY_2020_2_OR_NEWER
,
// Unity 2020.2 added TimeUpdate https://docs.unity3d.com/2020.2/Documentation/ScriptReference/PlayerLoop.TimeUpdate.html
TimeUpdate = 16384,
LastTimeUpdate = 32768
#endif
} }
public interface IPlayerLoopItem public interface IPlayerLoopItem
@ -90,7 +178,10 @@ namespace Cysharp.Threading.Tasks
public static class PlayerLoopHelper public static class PlayerLoopHelper
{ {
public static SynchronizationContext UnitySynchronizationContext => unitySynchronizationContetext; static readonly ContinuationQueue ThrowMarkerContinuationQueue = new ContinuationQueue(PlayerLoopTiming.Initialization);
static readonly PlayerLoopRunner ThrowMarkerPlayerLoopRunner = new PlayerLoopRunner(PlayerLoopTiming.Initialization);
public static SynchronizationContext UnitySynchronizationContext => unitySynchronizationContext;
public static int MainThreadId => mainThreadId; public static int MainThreadId => mainThreadId;
internal static string ApplicationDataPath => applicationDataPath; internal static string ApplicationDataPath => applicationDataPath;
@ -98,39 +189,34 @@ namespace Cysharp.Threading.Tasks
static int mainThreadId; static int mainThreadId;
static string applicationDataPath; static string applicationDataPath;
static SynchronizationContext unitySynchronizationContetext; static SynchronizationContext unitySynchronizationContext;
static ContinuationQueue[] yielders; static ContinuationQueue[] yielders;
static PlayerLoopRunner[] runners; static PlayerLoopRunner[] runners;
internal static bool IsEditorApplicationQuitting { get; private set; }
static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem, static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem,
Type loopRunnerYieldType, ContinuationQueue cq, Type lastLoopRunnerYieldType, ContinuationQueue lastCq, bool injectOnFirst,
Type loopRunnerType, PlayerLoopRunner runner, Type lastLoopRunnerType, PlayerLoopRunner lastRunner) Type loopRunnerYieldType, ContinuationQueue cq,
Type loopRunnerType, PlayerLoopRunner runner)
{ {
#if UNITY_EDITOR #if UNITY_EDITOR
EditorApplication.playModeStateChanged += (state) => 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) if (runner != null)
{ {
runner.Run();
runner.Clear(); runner.Clear();
} }
if (lastRunner != null)
{
lastRunner.Clear();
}
if (cq != null) if (cq != null)
{ {
cq.Run();
cq.Clear(); cq.Clear();
} }
if (lastCq != null) IsEditorApplicationQuitting = false;
{
lastCq.Clear();
} }
}; };
#endif #endif
@ -141,45 +227,73 @@ namespace Cysharp.Threading.Tasks
updateDelegate = cq.Run updateDelegate = cq.Run
}; };
var lastYieldLoop = new PlayerLoopSystem
{
type = lastLoopRunnerYieldType,
updateDelegate = lastCq.Run
};
var runnerLoop = new PlayerLoopSystem var runnerLoop = new PlayerLoopSystem
{ {
type = loopRunnerType, type = loopRunnerType,
updateDelegate = runner.Run updateDelegate = runner.Run
}; };
var lastRunnerLoop = new PlayerLoopSystem
{
type = lastLoopRunnerType,
updateDelegate = lastRunner.Run
};
// Remove items from previous initializations. // Remove items from previous initializations.
var source = loopSystem.subSystemList var source = RemoveRunner(loopSystem, loopRunnerYieldType, loopRunnerType);
.Where(ls => ls.type != loopRunnerYieldType && ls.type != loopRunnerType && ls.type != lastLoopRunnerYieldType && ls.type != lastLoopRunnerType) var dest = new PlayerLoopSystem[source.Length + 2];
.ToArray();
var dest = new PlayerLoopSystem[source.Length + 4]; Array.Copy(source, 0, dest, injectOnFirst ? 2 : 0, source.Length);
if (injectOnFirst)
Array.Copy(source, 0, dest, 2, source.Length); {
dest[0] = yieldLoop; dest[0] = yieldLoop;
dest[1] = runnerLoop; dest[1] = runnerLoop;
dest[dest.Length - 2] = lastYieldLoop; }
dest[dest.Length - 1] = lastRunnerLoop; else
{
dest[dest.Length - 2] = yieldLoop;
dest[dest.Length - 1] = runnerLoop;
}
return dest; return dest;
} }
static PlayerLoopSystem[] RemoveRunner(PlayerLoopSystem loopSystem, Type loopRunnerYieldType, Type loopRunnerType)
{
return loopSystem.subSystemList
.Where(ls => ls.type != loopRunnerYieldType && ls.type != loopRunnerType)
.ToArray();
}
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();
}
#if UNITY_2020_1_OR_NEWER
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
#else
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
#endif
static void Init() static void Init()
{ {
// capture default(unity) sync-context. // capture default(unity) sync-context.
unitySynchronizationContetext = SynchronizationContext.Current; unitySynchronizationContext = SynchronizationContext.Current;
mainThreadId = Thread.CurrentThread.ManagedThreadId; mainThreadId = Thread.CurrentThread.ManagedThreadId;
try try
{ {
@ -246,52 +360,130 @@ namespace Cysharp.Threading.Tasks
if (item != null) item.Run(); if (item != null) item.Run();
} }
} }
UniTaskSynchronizationContext.Run();
} }
#endif #endif
public static void Initialize(ref PlayerLoopSystem playerLoop) 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);
}
static void InsertLoop(PlayerLoopSystem[] copyList, InjectPlayerLoopTimings injectTimings, Type loopType, InjectPlayerLoopTimings targetTimings,
int index, bool injectOnFirst, Type loopRunnerYieldType, Type loopRunnerType, PlayerLoopTiming playerLoopTiming)
{
var i = FindLoopSystemIndex(copyList, loopType);
if ((injectTimings & targetTimings) == targetTimings)
{
copyList[i].subSystemList = InsertRunner(copyList[i], injectOnFirst,
loopRunnerYieldType, yielders[index] = new ContinuationQueue(playerLoopTiming),
loopRunnerType, runners[index] = new PlayerLoopRunner(playerLoopTiming));
}
else
{
copyList[i].subSystemList = RemoveRunner(copyList[i], loopRunnerYieldType, loopRunnerType);
}
}
public static void Initialize(ref PlayerLoopSystem playerLoop, InjectPlayerLoopTimings injectTimings = InjectPlayerLoopTimings.All)
{
#if UNITY_2020_2_OR_NEWER
yielders = new ContinuationQueue[16];
runners = new PlayerLoopRunner[16];
#else
yielders = new ContinuationQueue[14]; yielders = new ContinuationQueue[14];
runners = new PlayerLoopRunner[14]; runners = new PlayerLoopRunner[14];
#endif
var copyList = playerLoop.subSystemList.ToArray(); var copyList = playerLoop.subSystemList.ToArray();
// Initialization // Initialization
copyList[0].subSystemList = InsertRunner(copyList[0], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), yielders[0] = new ContinuationQueue(PlayerLoopTiming.Initialization), InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Initialization),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldInitialization), yielders[1] = new ContinuationQueue(PlayerLoopTiming.LastInitialization), InjectPlayerLoopTimings.Initialization, 0, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), runners[1] = new PlayerLoopRunner(PlayerLoopTiming.Initialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), PlayerLoopTiming.Initialization);
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastInitialization), runners[1] = new PlayerLoopRunner(PlayerLoopTiming.LastInitialization));
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Initialization),
InjectPlayerLoopTimings.LastInitialization, 1, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastInitialization), PlayerLoopTiming.LastInitialization);
// EarlyUpdate // EarlyUpdate
copyList[1].subSystemList = InsertRunner(copyList[1], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), yielders[2] = new ContinuationQueue(PlayerLoopTiming.EarlyUpdate), InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.EarlyUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldEarlyUpdate), yielders[3] = new ContinuationQueue(PlayerLoopTiming.LastEarlyUpdate), InjectPlayerLoopTimings.EarlyUpdate, 2, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerEarlyUpdate), runners[2] = new PlayerLoopRunner(PlayerLoopTiming.EarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerEarlyUpdate), PlayerLoopTiming.EarlyUpdate);
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastEarlyUpdate), runners[3] = new PlayerLoopRunner(PlayerLoopTiming.LastEarlyUpdate));
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.EarlyUpdate),
InjectPlayerLoopTimings.LastEarlyUpdate, 3, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldEarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastEarlyUpdate), PlayerLoopTiming.LastEarlyUpdate);
// FixedUpdate // FixedUpdate
copyList[2].subSystemList = InsertRunner(copyList[2], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), yielders[4] = new ContinuationQueue(PlayerLoopTiming.FixedUpdate), InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.FixedUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldFixedUpdate), yielders[5] = new ContinuationQueue(PlayerLoopTiming.LastFixedUpdate), InjectPlayerLoopTimings.FixedUpdate, 4, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerFixedUpdate), runners[4] = new PlayerLoopRunner(PlayerLoopTiming.FixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerFixedUpdate), PlayerLoopTiming.FixedUpdate);
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastFixedUpdate), runners[5] = new PlayerLoopRunner(PlayerLoopTiming.LastFixedUpdate));
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.FixedUpdate),
InjectPlayerLoopTimings.LastFixedUpdate, 5, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldFixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastFixedUpdate), PlayerLoopTiming.LastFixedUpdate);
// PreUpdate // PreUpdate
copyList[3].subSystemList = InsertRunner(copyList[3], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), yielders[6] = new ContinuationQueue(PlayerLoopTiming.PreUpdate), InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreUpdate), yielders[7] = new ContinuationQueue(PlayerLoopTiming.LastPreUpdate), InjectPlayerLoopTimings.PreUpdate, 6, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreUpdate), runners[6] = new PlayerLoopRunner(PlayerLoopTiming.PreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreUpdate), PlayerLoopTiming.PreUpdate);
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreUpdate), runners[7] = new PlayerLoopRunner(PlayerLoopTiming.LastPreUpdate));
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreUpdate),
InjectPlayerLoopTimings.LastPreUpdate, 7, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreUpdate), PlayerLoopTiming.LastPreUpdate);
// Update // Update
copyList[4].subSystemList = InsertRunner(copyList[4], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), yielders[8] = new ContinuationQueue(PlayerLoopTiming.Update), InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Update),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldUpdate), yielders[9] = new ContinuationQueue(PlayerLoopTiming.LastUpdate), InjectPlayerLoopTimings.Update, 8, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerUpdate), runners[8] = new PlayerLoopRunner(PlayerLoopTiming.Update), typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerUpdate), PlayerLoopTiming.Update);
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastUpdate), runners[9] = new PlayerLoopRunner(PlayerLoopTiming.LastUpdate));
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Update),
InjectPlayerLoopTimings.LastUpdate, 9, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastUpdate), PlayerLoopTiming.LastUpdate);
// PreLateUpdate // PreLateUpdate
copyList[5].subSystemList = InsertRunner(copyList[5], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), yielders[10] = new ContinuationQueue(PlayerLoopTiming.PreLateUpdate), InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreLateUpdate), yielders[11] = new ContinuationQueue(PlayerLoopTiming.LastPreLateUpdate), InjectPlayerLoopTimings.PreLateUpdate, 10, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreLateUpdate), runners[10] = new PlayerLoopRunner(PlayerLoopTiming.PreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreLateUpdate), PlayerLoopTiming.PreLateUpdate);
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreLateUpdate), runners[11] = new PlayerLoopRunner(PlayerLoopTiming.LastPreLateUpdate));
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreLateUpdate),
InjectPlayerLoopTimings.LastPreLateUpdate, 11, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreLateUpdate), PlayerLoopTiming.LastPreLateUpdate);
// PostLateUpdate // PostLateUpdate
copyList[6].subSystemList = InsertRunner(copyList[6], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), yielders[12] = new ContinuationQueue(PlayerLoopTiming.PostLateUpdate), InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PostLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPostLateUpdate), yielders[13] = new ContinuationQueue(PlayerLoopTiming.LastPostLateUpdate), InjectPlayerLoopTimings.PostLateUpdate, 12, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), runners[12] = new PlayerLoopRunner(PlayerLoopTiming.PostLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), PlayerLoopTiming.PostLateUpdate);
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPostLateUpdate), runners[13] = new PlayerLoopRunner(PlayerLoopTiming.LastPostLateUpdate));
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PostLateUpdate),
InjectPlayerLoopTimings.LastPostLateUpdate, 13, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPostLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPostLateUpdate), PlayerLoopTiming.LastPostLateUpdate);
#if UNITY_2020_2_OR_NEWER
// TimeUpdate
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.TimeUpdate),
InjectPlayerLoopTimings.TimeUpdate, 14, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldTimeUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerTimeUpdate), PlayerLoopTiming.TimeUpdate);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.TimeUpdate),
InjectPlayerLoopTimings.LastTimeUpdate, 15, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldTimeUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastTimeUpdate), PlayerLoopTiming.LastTimeUpdate);
#endif
// Insert UniTaskSynchronizationContext to Update loop
var i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Update));
copyList[i].subSystemList = InsertUniTaskSynchronizationContext(copyList[i]);
playerLoop.subSystemList = copyList; playerLoop.subSystemList = copyList;
PlayerLoop.SetPlayerLoop(playerLoop); PlayerLoop.SetPlayerLoop(playerLoop);
@ -299,13 +491,91 @@ namespace Cysharp.Threading.Tasks
public static void AddAction(PlayerLoopTiming timing, IPlayerLoopItem action) public static void AddAction(PlayerLoopTiming timing, IPlayerLoopItem action)
{ {
runners[(int)timing].AddAction(action); var runner = runners[(int)timing];
if (runner == null)
{
ThrowInvalidLoopTiming(timing);
}
runner.AddAction(action);
}
static void ThrowInvalidLoopTiming(PlayerLoopTiming playerLoopTiming)
{
throw new InvalidOperationException("Target playerLoopTiming is not injected. Please check PlayerLoopHelper.Initialize. PlayerLoopTiming:" + playerLoopTiming);
} }
public static void AddContinuation(PlayerLoopTiming timing, Action continuation) public static void AddContinuation(PlayerLoopTiming timing, Action continuation)
{ {
yielders[(int)timing].Enqueue(continuation); var q = yielders[(int)timing];
if (q == null)
{
ThrowInvalidLoopTiming(timing);
}
q.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();
if (header.subSystemList is null)
{
sb.AppendFormat("{0} has no subsystems!", header.ToString());
sb.AppendLine();
continue;
}
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)
{
if (header.subSystemList is null)
{
continue;
}
foreach (var subSystem in header.subSystemList)
{
if (subSystem.type == typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization))
{
return true;
}
}
}
return false;
}
#endif
}
}

View File

@ -0,0 +1,262 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System.Threading;
using System;
using Cysharp.Threading.Tasks.Internal;
using UnityEngine;
namespace Cysharp.Threading.Tasks
{
public abstract class PlayerLoopTimer : IDisposable, IPlayerLoopItem
{
readonly CancellationToken cancellationToken;
readonly Action<object> timerCallback;
readonly object state;
readonly PlayerLoopTiming playerLoopTiming;
readonly bool periodic;
bool isRunning;
bool tryStop;
bool isDisposed;
protected PlayerLoopTimer(bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
{
this.periodic = periodic;
this.playerLoopTiming = playerLoopTiming;
this.cancellationToken = cancellationToken;
this.timerCallback = timerCallback;
this.state = state;
}
public static PlayerLoopTimer Create(TimeSpan interval, bool periodic, DelayType delayType, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
{
#if UNITY_EDITOR
// force use Realtime.
if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
{
delayType = DelayType.Realtime;
}
#endif
switch (delayType)
{
case DelayType.UnscaledDeltaTime:
return new IgnoreTimeScalePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state);
case DelayType.Realtime:
return new RealtimePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state);
case DelayType.DeltaTime:
default:
return new DeltaTimePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state);
}
}
public static PlayerLoopTimer StartNew(TimeSpan interval, bool periodic, DelayType delayType, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
{
var timer = Create(interval, periodic, delayType, playerLoopTiming, cancellationToken, timerCallback, state);
timer.Restart();
return timer;
}
/// <summary>
/// Restart(Reset and Start) timer.
/// </summary>
public void Restart()
{
if (isDisposed) throw new ObjectDisposedException(null);
ResetCore(null); // init state
if (!isRunning)
{
isRunning = true;
PlayerLoopHelper.AddAction(playerLoopTiming, this);
}
tryStop = false;
}
/// <summary>
/// Restart(Reset and Start) and change interval.
/// </summary>
public void Restart(TimeSpan interval)
{
if (isDisposed) throw new ObjectDisposedException(null);
ResetCore(interval); // init state
if (!isRunning)
{
isRunning = true;
PlayerLoopHelper.AddAction(playerLoopTiming, this);
}
tryStop = false;
}
/// <summary>
/// Stop timer.
/// </summary>
public void Stop()
{
tryStop = true;
}
protected abstract void ResetCore(TimeSpan? newInterval);
public void Dispose()
{
isDisposed = true;
}
bool IPlayerLoopItem.MoveNext()
{
if (isDisposed)
{
isRunning = false;
return false;
}
if (tryStop)
{
isRunning = false;
return false;
}
if (cancellationToken.IsCancellationRequested)
{
isRunning = false;
return false;
}
if (!MoveNextCore())
{
timerCallback(state);
if (periodic)
{
ResetCore(null);
return true;
}
else
{
isRunning = false;
return false;
}
}
return true;
}
protected abstract bool MoveNextCore();
}
sealed class DeltaTimePlayerLoopTimer : PlayerLoopTimer
{
int initialFrame;
float elapsed;
float interval;
public DeltaTimePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
: base(periodic, playerLoopTiming, cancellationToken, timerCallback, state)
{
ResetCore(interval);
}
protected override bool MoveNextCore()
{
if (elapsed == 0.0f)
{
if (initialFrame == Time.frameCount)
{
return true;
}
}
elapsed += Time.deltaTime;
if (elapsed >= interval)
{
return false;
}
return true;
}
protected override void ResetCore(TimeSpan? interval)
{
this.elapsed = 0.0f;
this.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
if (interval != null)
{
this.interval = (float)interval.Value.TotalSeconds;
}
}
}
sealed class IgnoreTimeScalePlayerLoopTimer : PlayerLoopTimer
{
int initialFrame;
float elapsed;
float interval;
public IgnoreTimeScalePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
: base(periodic, playerLoopTiming, cancellationToken, timerCallback, state)
{
ResetCore(interval);
}
protected override bool MoveNextCore()
{
if (elapsed == 0.0f)
{
if (initialFrame == Time.frameCount)
{
return true;
}
}
elapsed += Time.unscaledDeltaTime;
if (elapsed >= interval)
{
return false;
}
return true;
}
protected override void ResetCore(TimeSpan? interval)
{
this.elapsed = 0.0f;
this.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
if (interval != null)
{
this.interval = (float)interval.Value.TotalSeconds;
}
}
}
sealed class RealtimePlayerLoopTimer : PlayerLoopTimer
{
ValueStopwatch stopwatch;
long intervalTicks;
public RealtimePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
: base(periodic, playerLoopTiming, cancellationToken, timerCallback, state)
{
ResetCore(interval);
}
protected override bool MoveNextCore()
{
if (stopwatch.ElapsedTicks >= intervalTicks)
{
return false;
}
return true;
}
protected override void ResetCore(TimeSpan? interval)
{
this.stopwatch = ValueStopwatch.StartNew();
if (interval != null)
{
this.intervalTicks = interval.Value.Ticks;
}
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 12bdad0556e999f4aa82da29415d361f guid: 57095a17fdca7ee4380450910afc7f26
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@ -7,12 +7,14 @@ using System.Threading;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
{ {
// internaly used but public, allow to user create custom operator with pooling. // internally used but public, allow to user create custom operator with pooling.
public static class TaskPool public static class TaskPool
{ {
internal static int MaxPoolSize; 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() static TaskPool()
{ {
@ -39,23 +41,28 @@ namespace Cysharp.Threading.Tasks
} }
public static IEnumerable<(Type, int)> GetCacheSizeInfo() public static IEnumerable<(Type, int)> GetCacheSizeInfo()
{
lock (sizes)
{ {
foreach (var item in sizes) foreach (var item in sizes)
{ {
yield return (item.Key, item.Value()); yield return (item.Key, item.Value());
} }
} }
}
public static void RegisterSizeGetter(Type type, Func<int> getSize) public static void RegisterSizeGetter(Type type, Func<int> getSize)
{
lock (sizes)
{ {
sizes[type] = getSize; sizes[type] = getSize;
} }
} }
}
public interface ITaskPoolNode<T> public interface ITaskPoolNode<T>
{ {
T NextNode { get; set; } ref T NextNode { get; }
} }
// mutable struct, don't mark readonly. // mutable struct, don't mark readonly.
@ -77,8 +84,9 @@ namespace Cysharp.Threading.Tasks
var v = root; var v = root;
if (!(v is null)) if (!(v is null))
{ {
root = v.NextNode; ref var nextNode = ref v.NextNode;
v.NextNode = null; root = nextNode;
nextNode = null;
size--; size--;
result = v; result = v;
Volatile.Write(ref gate, 0); Volatile.Write(ref gate, 0);

View File

@ -0,0 +1,129 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
// CancellationTokenSource itself can not reuse but CancelAfter(Timeout.InfiniteTimeSpan) allows reuse if did not reach timeout.
// Similar discussion:
// https://github.com/dotnet/runtime/issues/4694
// https://github.com/dotnet/runtime/issues/48492
// This TimeoutController emulate similar implementation, using CancelAfterSlim; to achieve zero allocation timeout.
public sealed class TimeoutController : IDisposable
{
readonly static Action<object> CancelCancellationTokenSourceStateDelegate = new Action<object>(CancelCancellationTokenSourceState);
static void CancelCancellationTokenSourceState(object state)
{
var cts = (CancellationTokenSource)state;
cts.Cancel();
}
CancellationTokenSource timeoutSource;
CancellationTokenSource linkedSource;
PlayerLoopTimer timer;
bool isDisposed;
readonly DelayType delayType;
readonly PlayerLoopTiming delayTiming;
readonly CancellationTokenSource originalLinkCancellationTokenSource;
public TimeoutController(DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
{
this.timeoutSource = new CancellationTokenSource();
this.originalLinkCancellationTokenSource = null;
this.linkedSource = null;
this.delayType = delayType;
this.delayTiming = delayTiming;
}
public TimeoutController(CancellationTokenSource linkCancellationTokenSource, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
{
this.timeoutSource = new CancellationTokenSource();
this.originalLinkCancellationTokenSource = linkCancellationTokenSource;
this.linkedSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutSource.Token, linkCancellationTokenSource.Token);
this.delayType = delayType;
this.delayTiming = delayTiming;
}
public CancellationToken Timeout(int millisecondsTimeout)
{
return Timeout(TimeSpan.FromMilliseconds(millisecondsTimeout));
}
public CancellationToken Timeout(TimeSpan timeout)
{
if (originalLinkCancellationTokenSource != null && originalLinkCancellationTokenSource.IsCancellationRequested)
{
return originalLinkCancellationTokenSource.Token;
}
// Timeouted, create new source and timer.
if (timeoutSource.IsCancellationRequested)
{
timeoutSource.Dispose();
timeoutSource = new CancellationTokenSource();
if (linkedSource != null)
{
this.linkedSource.Cancel();
this.linkedSource.Dispose();
this.linkedSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutSource.Token, originalLinkCancellationTokenSource.Token);
}
timer?.Dispose();
timer = null;
}
var useSource = (linkedSource != null) ? linkedSource : timeoutSource;
var token = useSource.Token;
if (timer == null)
{
// Timer complete => timeoutSource.Cancel() -> linkedSource will be canceled.
// (linked)token is canceled => stop timer
timer = PlayerLoopTimer.StartNew(timeout, false, delayType, delayTiming, token, CancelCancellationTokenSourceStateDelegate, timeoutSource);
}
else
{
timer.Restart(timeout);
}
return token;
}
public bool IsTimeout()
{
return timeoutSource.IsCancellationRequested;
}
public void Reset()
{
timer?.Stop();
}
public void Dispose()
{
if (isDisposed) return;
try
{
// stop timer.
timer?.Dispose();
// cancel and dispose.
timeoutSource.Cancel();
timeoutSource.Dispose();
if (linkedSource != null)
{
linkedSource.Cancel();
linkedSource.Dispose();
}
}
finally
{
isDisposed = true;
}
}
}
}

View File

@ -1,8 +1,7 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 3518da33b6245d341a0ef3670ee9268b guid: 6347ab34d2db6d744a654e8d62d96b96
timeCreated: 1488689723
licenseType: Pro
MonoImporter: MonoImporter:
externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: 0 executionOrder: 0

View File

@ -20,8 +20,6 @@ namespace Cysharp.Threading.Tasks
{ {
ITriggerHandler<T> head; // head.prev is last ITriggerHandler<T> head; // head.prev is last
ITriggerHandler<T> iteratingHead; ITriggerHandler<T> iteratingHead;
bool preserveRemoveSelf;
ITriggerHandler<T> iteratingNode; ITriggerHandler<T> iteratingNode;
void LogError(Exception ex) void LogError(Exception ex)
@ -55,18 +53,9 @@ namespace Cysharp.Threading.Tasks
Remove(h); Remove(h);
} }
if (preserveRemoveSelf) // If `h` itself is removed by OnNext, h.Next is null.
{ // Therefore, instead of looking at h.Next, the `iteratingNode` reference itself is replaced.
preserveRemoveSelf = false; h = h == iteratingNode ? h.Next : iteratingNode;
iteratingNode = null;
var next = h.Next;
Remove(h);
h = next;
}
else
{
h = h.Next;
}
} }
iteratingNode = null; iteratingNode = null;
@ -97,9 +86,8 @@ namespace Cysharp.Threading.Tasks
LogError(ex); LogError(ex);
} }
preserveRemoveSelf = false; var next = h == iteratingNode ? h.Next : iteratingNode;
iteratingNode = null; iteratingNode = null;
var next = h.Next;
Remove(h); Remove(h);
h = next; h = next;
} }
@ -132,9 +120,8 @@ namespace Cysharp.Threading.Tasks
LogError(ex); LogError(ex);
} }
preserveRemoveSelf = false; var next = h == iteratingNode ? h.Next : iteratingNode;
iteratingNode = null; iteratingNode = null;
var next = h.Next;
Remove(h); Remove(h);
h = next; h = next;
} }
@ -167,9 +154,8 @@ namespace Cysharp.Threading.Tasks
LogError(ex); LogError(ex);
} }
preserveRemoveSelf = false; var next = h == iteratingNode ? h.Next : iteratingNode;
iteratingNode = null; iteratingNode = null;
var next = h.Next;
Remove(h); Remove(h);
h = next; h = next;
} }
@ -241,13 +227,6 @@ namespace Cysharp.Threading.Tasks
{ {
if (handler == null) throw new ArgumentNullException(nameof(handler)); if (handler == null) throw new ArgumentNullException(nameof(handler));
if (iteratingNode != null && iteratingNode == handler)
{
// if remove self, reserve remove self after invoke completed.
preserveRemoveSelf = true;
}
else
{
var prev = handler.Prev; var prev = handler.Prev;
var next = handler.Next; var next = handler.Next;
@ -260,17 +239,19 @@ namespace Cysharp.Threading.Tasks
{ {
head = next; head = next;
} }
else if (handler == iteratingHead)
{
iteratingHead = next;
}
else
{
// when handler is head, prev indicate last so don't use it. // when handler is head, prev indicate last so don't use it.
if (prev != null) else if (prev != null)
{ {
prev.Next = next; prev.Next = next;
} }
if (handler == iteratingNode)
{
iteratingNode = next;
}
if (handler == iteratingHead)
{
iteratingHead = next;
} }
if (head != null) if (head != null)
@ -308,4 +289,3 @@ namespace Cysharp.Threading.Tasks
} }
} }
} }
}

View File

@ -32,13 +32,11 @@ namespace Cysharp.Threading.Tasks.Triggers
if (cancellationTokenSource == null) if (cancellationTokenSource == null)
{ {
cancellationTokenSource = new CancellationTokenSource(); cancellationTokenSource = new CancellationTokenSource();
}
if (!awakeCalled) if (!awakeCalled)
{ {
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this)); PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this));
} }
}
return cancellationTokenSource.Token; return cancellationTokenSource.Token;
} }
} }
@ -83,7 +81,7 @@ namespace Cysharp.Threading.Tasks.Triggers
public bool MoveNext() public bool MoveNext()
{ {
if (trigger.called) return false; if (trigger.called || trigger.awakeCalled) return false;
if (trigger == null) if (trigger == null)
{ {
trigger.OnDestroy(); trigger.OnDestroy();

View File

@ -8,6 +8,16 @@ namespace Cysharp.Threading.Tasks
{ {
public static class UniTaskCancellationExtensions public static class UniTaskCancellationExtensions
{ {
#if UNITY_2022_2_OR_NEWER
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
public static CancellationToken GetCancellationTokenOnDestroy(this MonoBehaviour monoBehaviour)
{
return monoBehaviour.destroyCancellationToken;
}
#endif
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary> /// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
public static CancellationToken GetCancellationTokenOnDestroy(this GameObject gameObject) public static CancellationToken GetCancellationTokenOnDestroy(this GameObject gameObject)
{ {
@ -17,6 +27,13 @@ namespace Cysharp.Threading.Tasks
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary> /// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
public static CancellationToken GetCancellationTokenOnDestroy(this Component component) public static CancellationToken GetCancellationTokenOnDestroy(this Component component)
{ {
#if UNITY_2022_2_OR_NEWER
if (component is MonoBehaviour mb)
{
return mb.destroyCancellationToken;
}
#endif
return component.GetAsyncDestroyTrigger().CancellationToken; return component.GetAsyncDestroyTrigger().CancellationToken;
} }
} }

View File

@ -2,7 +2,9 @@
using System.Threading; using System.Threading;
using UnityEngine; using UnityEngine;
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
#endif
namespace Cysharp.Threading.Tasks.Triggers namespace Cysharp.Threading.Tasks.Triggers
{ {
@ -715,6 +717,7 @@ namespace Cysharp.Threading.Tasks.Triggers
#endregion #endregion
#region CollisionEnter #region CollisionEnter
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnCollisionEnterHandler public interface IAsyncOnCollisionEnterHandler
{ {
@ -771,9 +774,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnCollisionEnterHandler)new AsyncTriggerHandler<Collision>(this, cancellationToken, true)).OnCollisionEnterAsync(); return ((IAsyncOnCollisionEnterHandler)new AsyncTriggerHandler<Collision>(this, cancellationToken, true)).OnCollisionEnterAsync();
} }
} }
#endif
#endregion #endregion
#region CollisionEnter2D #region CollisionEnter2D
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
public interface IAsyncOnCollisionEnter2DHandler public interface IAsyncOnCollisionEnter2DHandler
{ {
@ -830,9 +835,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnCollisionEnter2DHandler)new AsyncTriggerHandler<Collision2D>(this, cancellationToken, true)).OnCollisionEnter2DAsync(); return ((IAsyncOnCollisionEnter2DHandler)new AsyncTriggerHandler<Collision2D>(this, cancellationToken, true)).OnCollisionEnter2DAsync();
} }
} }
#endif
#endregion #endregion
#region CollisionExit #region CollisionExit
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnCollisionExitHandler public interface IAsyncOnCollisionExitHandler
{ {
@ -889,9 +896,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnCollisionExitHandler)new AsyncTriggerHandler<Collision>(this, cancellationToken, true)).OnCollisionExitAsync(); return ((IAsyncOnCollisionExitHandler)new AsyncTriggerHandler<Collision>(this, cancellationToken, true)).OnCollisionExitAsync();
} }
} }
#endif
#endregion #endregion
#region CollisionExit2D #region CollisionExit2D
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
public interface IAsyncOnCollisionExit2DHandler public interface IAsyncOnCollisionExit2DHandler
{ {
@ -948,9 +957,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnCollisionExit2DHandler)new AsyncTriggerHandler<Collision2D>(this, cancellationToken, true)).OnCollisionExit2DAsync(); return ((IAsyncOnCollisionExit2DHandler)new AsyncTriggerHandler<Collision2D>(this, cancellationToken, true)).OnCollisionExit2DAsync();
} }
} }
#endif
#endregion #endregion
#region CollisionStay #region CollisionStay
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnCollisionStayHandler public interface IAsyncOnCollisionStayHandler
{ {
@ -1007,9 +1018,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnCollisionStayHandler)new AsyncTriggerHandler<Collision>(this, cancellationToken, true)).OnCollisionStayAsync(); return ((IAsyncOnCollisionStayHandler)new AsyncTriggerHandler<Collision>(this, cancellationToken, true)).OnCollisionStayAsync();
} }
} }
#endif
#endregion #endregion
#region CollisionStay2D #region CollisionStay2D
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
public interface IAsyncOnCollisionStay2DHandler public interface IAsyncOnCollisionStay2DHandler
{ {
@ -1066,9 +1079,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnCollisionStay2DHandler)new AsyncTriggerHandler<Collision2D>(this, cancellationToken, true)).OnCollisionStay2DAsync(); return ((IAsyncOnCollisionStay2DHandler)new AsyncTriggerHandler<Collision2D>(this, cancellationToken, true)).OnCollisionStay2DAsync();
} }
} }
#endif
#endregion #endregion
#region ControllerColliderHit #region ControllerColliderHit
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnControllerColliderHitHandler public interface IAsyncOnControllerColliderHitHandler
{ {
@ -1125,6 +1140,7 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnControllerColliderHitHandler)new AsyncTriggerHandler<ControllerColliderHit>(this, cancellationToken, true)).OnControllerColliderHitAsync(); return ((IAsyncOnControllerColliderHitHandler)new AsyncTriggerHandler<ControllerColliderHit>(this, cancellationToken, true)).OnControllerColliderHitAsync();
} }
} }
#endif
#endregion #endregion
#region Disable #region Disable
@ -1423,6 +1439,7 @@ namespace Cysharp.Threading.Tasks.Triggers
#endregion #endregion
#region JointBreak #region JointBreak
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnJointBreakHandler public interface IAsyncOnJointBreakHandler
{ {
@ -1479,9 +1496,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnJointBreakHandler)new AsyncTriggerHandler<float>(this, cancellationToken, true)).OnJointBreakAsync(); return ((IAsyncOnJointBreakHandler)new AsyncTriggerHandler<float>(this, cancellationToken, true)).OnJointBreakAsync();
} }
} }
#endif
#endregion #endregion
#region JointBreak2D #region JointBreak2D
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
public interface IAsyncOnJointBreak2DHandler public interface IAsyncOnJointBreak2DHandler
{ {
@ -1538,9 +1557,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnJointBreak2DHandler)new AsyncTriggerHandler<Joint2D>(this, cancellationToken, true)).OnJointBreak2DAsync(); return ((IAsyncOnJointBreak2DHandler)new AsyncTriggerHandler<Joint2D>(this, cancellationToken, true)).OnJointBreak2DAsync();
} }
} }
#endif
#endregion #endregion
#region MouseDown #region MouseDown
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
public interface IAsyncOnMouseDownHandler public interface IAsyncOnMouseDownHandler
{ {
@ -1597,9 +1618,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMouseDownHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseDownAsync(); return ((IAsyncOnMouseDownHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseDownAsync();
} }
} }
#endif
#endregion #endregion
#region MouseDrag #region MouseDrag
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
public interface IAsyncOnMouseDragHandler public interface IAsyncOnMouseDragHandler
{ {
@ -1656,9 +1679,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMouseDragHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseDragAsync(); return ((IAsyncOnMouseDragHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseDragAsync();
} }
} }
#endif
#endregion #endregion
#region MouseEnter #region MouseEnter
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
public interface IAsyncOnMouseEnterHandler public interface IAsyncOnMouseEnterHandler
{ {
@ -1715,9 +1740,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMouseEnterHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseEnterAsync(); return ((IAsyncOnMouseEnterHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseEnterAsync();
} }
} }
#endif
#endregion #endregion
#region MouseExit #region MouseExit
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
public interface IAsyncOnMouseExitHandler public interface IAsyncOnMouseExitHandler
{ {
@ -1774,9 +1801,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMouseExitHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseExitAsync(); return ((IAsyncOnMouseExitHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseExitAsync();
} }
} }
#endif
#endregion #endregion
#region MouseOver #region MouseOver
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
public interface IAsyncOnMouseOverHandler public interface IAsyncOnMouseOverHandler
{ {
@ -1833,9 +1862,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMouseOverHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseOverAsync(); return ((IAsyncOnMouseOverHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseOverAsync();
} }
} }
#endif
#endregion #endregion
#region MouseUp #region MouseUp
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
public interface IAsyncOnMouseUpHandler public interface IAsyncOnMouseUpHandler
{ {
@ -1892,9 +1923,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMouseUpHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseUpAsync(); return ((IAsyncOnMouseUpHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseUpAsync();
} }
} }
#endif
#endregion #endregion
#region MouseUpAsButton #region MouseUpAsButton
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
public interface IAsyncOnMouseUpAsButtonHandler public interface IAsyncOnMouseUpAsButtonHandler
{ {
@ -1951,6 +1984,7 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMouseUpAsButtonHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseUpAsButtonAsync(); return ((IAsyncOnMouseUpAsButtonHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseUpAsButtonAsync();
} }
} }
#endif
#endregion #endregion
#region ParticleCollision #region ParticleCollision
@ -2131,7 +2165,7 @@ namespace Cysharp.Threading.Tasks.Triggers
#endregion #endregion
#region ParticleUpdateJobScheduled #region ParticleUpdateJobScheduled
#if UNITY_2019_3_OR_NEWER #if UNITY_2019_3_OR_NEWER && (!UNITY_2019_1_OR_NEWER || UNITASK_PARTICLESYSTEM_SUPPORT)
public interface IAsyncOnParticleUpdateJobScheduledHandler public interface IAsyncOnParticleUpdateJobScheduledHandler
{ {
@ -2782,6 +2816,7 @@ namespace Cysharp.Threading.Tasks.Triggers
#endregion #endregion
#region TriggerEnter #region TriggerEnter
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnTriggerEnterHandler public interface IAsyncOnTriggerEnterHandler
{ {
@ -2838,9 +2873,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnTriggerEnterHandler)new AsyncTriggerHandler<Collider>(this, cancellationToken, true)).OnTriggerEnterAsync(); return ((IAsyncOnTriggerEnterHandler)new AsyncTriggerHandler<Collider>(this, cancellationToken, true)).OnTriggerEnterAsync();
} }
} }
#endif
#endregion #endregion
#region TriggerEnter2D #region TriggerEnter2D
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
public interface IAsyncOnTriggerEnter2DHandler public interface IAsyncOnTriggerEnter2DHandler
{ {
@ -2897,9 +2934,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnTriggerEnter2DHandler)new AsyncTriggerHandler<Collider2D>(this, cancellationToken, true)).OnTriggerEnter2DAsync(); return ((IAsyncOnTriggerEnter2DHandler)new AsyncTriggerHandler<Collider2D>(this, cancellationToken, true)).OnTriggerEnter2DAsync();
} }
} }
#endif
#endregion #endregion
#region TriggerExit #region TriggerExit
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnTriggerExitHandler public interface IAsyncOnTriggerExitHandler
{ {
@ -2956,9 +2995,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnTriggerExitHandler)new AsyncTriggerHandler<Collider>(this, cancellationToken, true)).OnTriggerExitAsync(); return ((IAsyncOnTriggerExitHandler)new AsyncTriggerHandler<Collider>(this, cancellationToken, true)).OnTriggerExitAsync();
} }
} }
#endif
#endregion #endregion
#region TriggerExit2D #region TriggerExit2D
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
public interface IAsyncOnTriggerExit2DHandler public interface IAsyncOnTriggerExit2DHandler
{ {
@ -3015,9 +3056,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnTriggerExit2DHandler)new AsyncTriggerHandler<Collider2D>(this, cancellationToken, true)).OnTriggerExit2DAsync(); return ((IAsyncOnTriggerExit2DHandler)new AsyncTriggerHandler<Collider2D>(this, cancellationToken, true)).OnTriggerExit2DAsync();
} }
} }
#endif
#endregion #endregion
#region TriggerStay #region TriggerStay
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnTriggerStayHandler public interface IAsyncOnTriggerStayHandler
{ {
@ -3074,9 +3117,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnTriggerStayHandler)new AsyncTriggerHandler<Collider>(this, cancellationToken, true)).OnTriggerStayAsync(); return ((IAsyncOnTriggerStayHandler)new AsyncTriggerHandler<Collider>(this, cancellationToken, true)).OnTriggerStayAsync();
} }
} }
#endif
#endregion #endregion
#region TriggerStay2D #region TriggerStay2D
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
public interface IAsyncOnTriggerStay2DHandler public interface IAsyncOnTriggerStay2DHandler
{ {
@ -3133,6 +3178,7 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnTriggerStay2DHandler)new AsyncTriggerHandler<Collider2D>(this, cancellationToken, true)).OnTriggerStay2DAsync(); return ((IAsyncOnTriggerStay2DHandler)new AsyncTriggerHandler<Collider2D>(this, cancellationToken, true)).OnTriggerStay2DAsync();
} }
} }
#endif
#endregion #endregion
#region Validate #region Validate
@ -3372,6 +3418,7 @@ namespace Cysharp.Threading.Tasks.Triggers
#endregion #endregion
#region BeginDrag #region BeginDrag
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnBeginDragHandler public interface IAsyncOnBeginDragHandler
{ {
@ -3428,9 +3475,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnBeginDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnBeginDragAsync(); return ((IAsyncOnBeginDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnBeginDragAsync();
} }
} }
#endif
#endregion #endregion
#region Cancel #region Cancel
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnCancelHandler public interface IAsyncOnCancelHandler
{ {
@ -3487,9 +3536,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnCancelHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnCancelAsync(); return ((IAsyncOnCancelHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnCancelAsync();
} }
} }
#endif
#endregion #endregion
#region Deselect #region Deselect
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnDeselectHandler public interface IAsyncOnDeselectHandler
{ {
@ -3546,9 +3597,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnDeselectHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnDeselectAsync(); return ((IAsyncOnDeselectHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnDeselectAsync();
} }
} }
#endif
#endregion #endregion
#region Drag #region Drag
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnDragHandler public interface IAsyncOnDragHandler
{ {
@ -3605,9 +3658,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnDragAsync(); return ((IAsyncOnDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnDragAsync();
} }
} }
#endif
#endregion #endregion
#region Drop #region Drop
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnDropHandler public interface IAsyncOnDropHandler
{ {
@ -3664,9 +3719,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnDropHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnDropAsync(); return ((IAsyncOnDropHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnDropAsync();
} }
} }
#endif
#endregion #endregion
#region EndDrag #region EndDrag
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnEndDragHandler public interface IAsyncOnEndDragHandler
{ {
@ -3723,9 +3780,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnEndDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnEndDragAsync(); return ((IAsyncOnEndDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnEndDragAsync();
} }
} }
#endif
#endregion #endregion
#region InitializePotentialDrag #region InitializePotentialDrag
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnInitializePotentialDragHandler public interface IAsyncOnInitializePotentialDragHandler
{ {
@ -3782,9 +3841,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnInitializePotentialDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnInitializePotentialDragAsync(); return ((IAsyncOnInitializePotentialDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnInitializePotentialDragAsync();
} }
} }
#endif
#endregion #endregion
#region Move #region Move
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnMoveHandler public interface IAsyncOnMoveHandler
{ {
@ -3841,9 +3902,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMoveHandler)new AsyncTriggerHandler<AxisEventData>(this, cancellationToken, true)).OnMoveAsync(); return ((IAsyncOnMoveHandler)new AsyncTriggerHandler<AxisEventData>(this, cancellationToken, true)).OnMoveAsync();
} }
} }
#endif
#endregion #endregion
#region PointerClick #region PointerClick
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnPointerClickHandler public interface IAsyncOnPointerClickHandler
{ {
@ -3900,9 +3963,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnPointerClickHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerClickAsync(); return ((IAsyncOnPointerClickHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerClickAsync();
} }
} }
#endif
#endregion #endregion
#region PointerDown #region PointerDown
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnPointerDownHandler public interface IAsyncOnPointerDownHandler
{ {
@ -3959,9 +4024,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnPointerDownHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerDownAsync(); return ((IAsyncOnPointerDownHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerDownAsync();
} }
} }
#endif
#endregion #endregion
#region PointerEnter #region PointerEnter
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnPointerEnterHandler public interface IAsyncOnPointerEnterHandler
{ {
@ -4018,9 +4085,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnPointerEnterHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerEnterAsync(); return ((IAsyncOnPointerEnterHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerEnterAsync();
} }
} }
#endif
#endregion #endregion
#region PointerExit #region PointerExit
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnPointerExitHandler public interface IAsyncOnPointerExitHandler
{ {
@ -4077,9 +4146,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnPointerExitHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerExitAsync(); return ((IAsyncOnPointerExitHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerExitAsync();
} }
} }
#endif
#endregion #endregion
#region PointerUp #region PointerUp
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnPointerUpHandler public interface IAsyncOnPointerUpHandler
{ {
@ -4136,9 +4207,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnPointerUpHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerUpAsync(); return ((IAsyncOnPointerUpHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerUpAsync();
} }
} }
#endif
#endregion #endregion
#region Scroll #region Scroll
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnScrollHandler public interface IAsyncOnScrollHandler
{ {
@ -4195,9 +4268,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnScrollHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnScrollAsync(); return ((IAsyncOnScrollHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnScrollAsync();
} }
} }
#endif
#endregion #endregion
#region Select #region Select
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnSelectHandler public interface IAsyncOnSelectHandler
{ {
@ -4254,9 +4329,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnSelectHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnSelectAsync(); return ((IAsyncOnSelectHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnSelectAsync();
} }
} }
#endif
#endregion #endregion
#region Submit #region Submit
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnSubmitHandler public interface IAsyncOnSubmitHandler
{ {
@ -4313,9 +4390,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnSubmitHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnSubmitAsync(); return ((IAsyncOnSubmitHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnSubmitAsync();
} }
} }
#endif
#endregion #endregion
#region UpdateSelected #region UpdateSelected
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnUpdateSelectedHandler public interface IAsyncOnUpdateSelectedHandler
{ {
@ -4372,6 +4451,7 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnUpdateSelectedHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnUpdateSelectedAsync(); return ((IAsyncOnUpdateSelectedHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnUpdateSelectedAsync();
} }
} }
#endif
#endregion #endregion
} }

View File

@ -25,13 +25,7 @@
("Update", "Update", "AsyncUnit", null, empty), ("Update", "Update", "AsyncUnit", null, empty),
("FixedUpdate", "FixedUpdate", "AsyncUnit", null, empty), ("FixedUpdate", "FixedUpdate", "AsyncUnit", null, empty),
("LateUpdate", "LateUpdate", "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") }), ("ParticleCollision", "OnParticleCollision", "GameObject", null, new []{ ("GameObject", "other") }),
("RectTransformDimensionsChange", "OnRectTransformDimensionsChange", "AsyncUnit", null, empty), ("RectTransformDimensionsChange", "OnRectTransformDimensionsChange", "AsyncUnit", null, empty),
("RectTransformRemoved", "OnRectTransformRemoved", "AsyncUnit", null, empty), ("RectTransformRemoved", "OnRectTransformRemoved", "AsyncUnit", null, empty),
@ -47,6 +41,15 @@
("BecameInvisible", "OnBecameInvisible", "AsyncUnit", null, empty), ("BecameInvisible", "OnBecameInvisible", "AsyncUnit", null, empty),
("BecameVisible", "OnBecameVisible", "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 // new in v2
("ApplicationFocus", "OnApplicationFocus", "bool", null, new []{("bool", "hasFocus") }), ("ApplicationFocus", "OnApplicationFocus", "bool", null, new []{("bool", "hasFocus") }),
("ApplicationPause", "OnApplicationPause", "bool", null, new []{("bool", "pauseStatus") }), ("ApplicationPause", "OnApplicationPause", "bool", null, new []{("bool", "pauseStatus") }),
@ -103,20 +106,36 @@
Func<(string argType, string argName)[], string> BuildMethodArgument = x => string.Join(", ", x.Select(y => y.argType + " " + y.argName)); Func<(string argType, string argName)[], string> BuildMethodArgument = x => string.Join(", ", x.Select(y => y.argType + " " + y.argName));
Func<(string argType, string argName)[], string> BuildResultParameter = x => x.Length == 0 ? "AsyncUnit.Default" : "(" + string.Join(", ", x.Select(y => y.argName)) + ")"; 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> IsParticleSystem = x => x == "ParticleUpdateJobScheduled";
Func<string, bool> IsMouseTrigger = x => x.StartsWith("Mouse");
Func<string, string> RequirePhysicsModule = x => (x.StartsWith("Collision") || x.StartsWith("Collider") || x.StartsWith("ControllerCollider") || x.StartsWith("Joint") || x.StartsWith("Trigger"))
? (x.Contains("2D") ? "UNITASK_PHYSICS2D_SUPPORT" : "UNITASK_PHYSICS_SUPPORT")
: null;
Func<string, bool> IsUguiSystem = x => x != null;
#> #>
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System.Threading; using System.Threading;
using UnityEngine; using UnityEngine;
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
#endif
namespace Cysharp.Threading.Tasks.Triggers namespace Cysharp.Threading.Tasks.Triggers
{ {
<# foreach(var t in triggers) { #> <# foreach(var t in triggers) { #>
#region <#= t.triggerName #> #region <#= t.triggerName #>
<# if(Is2019_3(t.triggerName)) { #> <# if(IsUguiSystem(t.handlerInterface)) { #>
#if UNITY_2019_3_OR_NEWER #if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
<# } #>
<# if(IsParticleSystem(t.triggerName)) { #>
#if UNITY_2019_3_OR_NEWER && (!UNITY_2019_1_OR_NEWER || UNITASK_PARTICLESYSTEM_SUPPORT)
<# } #>
<# if(IsMouseTrigger(t.triggerName)) { #>
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
<# } #>
<# if(RequirePhysicsModule(t.triggerName) != null) { #>
#if !UNITY_2019_1_OR_NEWER || <#= RequirePhysicsModule(t.triggerName) #>
<# } #> <# } #>
public interface <#= ToInterfaceName(t.methodName) #> public interface <#= ToInterfaceName(t.methodName) #>
@ -174,7 +193,13 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((<#= ToInterfaceName(t.methodName) #>)new AsyncTriggerHandler<<#= t.returnType #>>(this, cancellationToken, true)).<#= t.methodName #>Async(); return ((<#= ToInterfaceName(t.methodName) #>)new AsyncTriggerHandler<<#= t.returnType #>>(this, cancellationToken, true)).<#= t.methodName #>Async();
} }
} }
<# if(Is2019_3(t.triggerName)) { #> <# if(IsUguiSystem(t.handlerInterface)) { #>
#endif
<# } #>
<# if(RequirePhysicsModule(t.triggerName) != null) { #>
#endif
<# } #>
<# if(IsParticleSystem(t.triggerName) || IsMouseTrigger(t.triggerName)) { #>
#endif #endif
<# } #> <# } #>
#endregion #endregion

View File

@ -1,5 +1,11 @@
#pragma warning disable 0649 #pragma warning disable 0649
#if UNITASK_NETCORE || UNITY_2022_3_OR_NEWER
#define SUPPORT_VALUETASK
#endif
#if SUPPORT_VALUETASK
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading.Tasks.Sources; using System.Threading.Tasks.Sources;
@ -10,7 +16,7 @@ namespace Cysharp.Threading.Tasks
{ {
public static ValueTask AsValueTask(this in UniTask task) public static ValueTask AsValueTask(this in UniTask task)
{ {
#if NETSTANDARD2_0 #if (UNITASK_NETCORE && NETSTANDARD2_0)
return new ValueTask(new UniTaskValueTaskSource(task), 0); return new ValueTask(new UniTaskValueTaskSource(task), 0);
#else #else
return task; return task;
@ -19,7 +25,7 @@ namespace Cysharp.Threading.Tasks
public static ValueTask<T> AsValueTask<T>(this in UniTask<T> task) public static ValueTask<T> AsValueTask<T>(this in UniTask<T> task)
{ {
#if NETSTANDARD2_0 #if (UNITASK_NETCORE && NETSTANDARD2_0)
return new ValueTask<T>(new UniTaskValueTaskSource<T>(task), 0); return new ValueTask<T>(new UniTaskValueTaskSource<T>(task), 0);
#else #else
return task; return task;
@ -36,7 +42,7 @@ namespace Cysharp.Threading.Tasks
await task; await task;
} }
#if NETSTANDARD2_0 #if (UNITASK_NETCORE && NETSTANDARD2_0)
class UniTaskValueTaskSource : IValueTaskSource class UniTaskValueTaskSource : IValueTaskSource
{ {
@ -95,3 +101,4 @@ namespace Cysharp.Threading.Tasks
#endif #endif
} }
} }
#endif

View File

@ -1,8 +1,7 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 660baed073888b8438569f57e42679b2 guid: d38f0478933be42d895c37b862540a1c
timeCreated: 1476793308
licenseType: Pro
MonoImporter: MonoImporter:
externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: 0 executionOrder: 0

View File

@ -2,6 +2,7 @@
using Cysharp.Threading.Tasks.Internal; using Cysharp.Threading.Tasks.Internal;
using System; using System;
using System.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using UnityEngine; using UnityEngine;
@ -20,106 +21,176 @@ namespace Cysharp.Threading.Tasks
public partial struct UniTask public partial struct UniTask
{ {
public static YieldAwaitable Yield(PlayerLoopTiming timing = PlayerLoopTiming.Update) public static YieldAwaitable Yield()
{
// optimized for single continuation
return new YieldAwaitable(PlayerLoopTiming.Update);
}
public static YieldAwaitable Yield(PlayerLoopTiming timing)
{ {
// optimized for single continuation // optimized for single continuation
return new YieldAwaitable(timing); return new YieldAwaitable(timing);
} }
public static UniTask Yield(PlayerLoopTiming timing, CancellationToken cancellationToken) public static UniTask Yield(CancellationToken cancellationToken, bool cancelImmediately = false)
{ {
return new UniTask(YieldPromise.Create(timing, cancellationToken, out var token), token); return new UniTask(YieldPromise.Create(PlayerLoopTiming.Update, cancellationToken, cancelImmediately, out var token), token);
}
public static UniTask Yield(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately = false)
{
return new UniTask(YieldPromise.Create(timing, cancellationToken, cancelImmediately, out var token), token);
} }
/// <summary> /// <summary>
/// Similar as UniTask.Yield but guaranteed run on next frame. /// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary> /// </summary>
public static UniTask NextFrame(PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default) public static UniTask NextFrame()
{ {
return new UniTask(NextFramePromise.Create(timing, cancellationToken, out var token), token); return new UniTask(NextFramePromise.Create(PlayerLoopTiming.Update, CancellationToken.None, false, out var token), token);
} }
/// <summary> /// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). /// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary> /// </summary>
public static UniTask NextFrame(PlayerLoopTiming timing)
{
return new UniTask(NextFramePromise.Create(timing, CancellationToken.None, false, out var token), token);
}
/// <summary>
/// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary>
public static UniTask NextFrame(CancellationToken cancellationToken, bool cancelImmediately = false)
{
return new UniTask(NextFramePromise.Create(PlayerLoopTiming.Update, cancellationToken, cancelImmediately, out var token), token);
}
/// <summary>
/// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary>
public static UniTask NextFrame(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately = false)
{
return new UniTask(NextFramePromise.Create(timing, cancellationToken, cancelImmediately, out var token), token);
}
#if UNITY_2023_1_OR_NEWER
public static async UniTask WaitForEndOfFrame(CancellationToken cancellationToken = default)
{
await Awaitable.EndOfFrameAsync(cancellationToken);
}
#else
[Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")]
public static YieldAwaitable WaitForEndOfFrame() public static YieldAwaitable WaitForEndOfFrame()
{ {
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate); return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
} }
/// <summary> [Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")]
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken). public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken, bool cancelImmediately = false)
/// </summary>
public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken)
{ {
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken); return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken, cancelImmediately);
}
#endif
public static UniTask WaitForEndOfFrame(MonoBehaviour coroutineRunner)
{
var source = WaitForEndOfFramePromise.Create(coroutineRunner, CancellationToken.None, false, out var token);
return new UniTask(source, token);
}
public static UniTask WaitForEndOfFrame(MonoBehaviour coroutineRunner, CancellationToken cancellationToken, bool cancelImmediately = false)
{
var source = WaitForEndOfFramePromise.Create(coroutineRunner, cancellationToken, cancelImmediately, out var token);
return new UniTask(source, token);
} }
/// <summary> /// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate). /// Same as UniTask.Yield(PlayerLoopTiming.LastFixedUpdate).
/// </summary> /// </summary>
public static YieldAwaitable WaitForFixedUpdate() public static YieldAwaitable WaitForFixedUpdate()
{ {
return UniTask.Yield(PlayerLoopTiming.FixedUpdate); // use LastFixedUpdate instead of FixedUpdate
// https://github.com/Cysharp/UniTask/issues/377
return UniTask.Yield(PlayerLoopTiming.LastFixedUpdate);
} }
/// <summary> /// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken). /// Same as UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken).
/// </summary> /// </summary>
public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken) public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken, bool cancelImmediately = false)
{ {
return UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken); return UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken, cancelImmediately);
} }
public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask WaitForSeconds(float duration, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{
return Delay(Mathf.RoundToInt(1000 * duration), ignoreTimeScale, delayTiming, cancellationToken, cancelImmediately);
}
public static UniTask WaitForSeconds(int duration, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{
return Delay(1000 * duration, ignoreTimeScale, delayTiming, cancellationToken, cancelImmediately);
}
public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
if (delayFrameCount < 0) if (delayFrameCount < 0)
{ {
throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. delayFrameCount:" + delayFrameCount); throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. delayFrameCount:" + delayFrameCount);
} }
return new UniTask(DelayFramePromise.Create(delayFrameCount, delayTiming, cancellationToken, out var token), token); return new UniTask(DelayFramePromise.Create(delayFrameCount, delayTiming, cancellationToken, cancelImmediately, out var token), token);
} }
public static UniTask Delay(int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask Delay(int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay); var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
return Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cancellationToken); return Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cancellationToken, cancelImmediately);
} }
public static UniTask Delay(TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask Delay(TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
var delayType = ignoreTimeScale ? DelayType.UnscaledDeltaTime : DelayType.DeltaTime; var delayType = ignoreTimeScale ? DelayType.UnscaledDeltaTime : DelayType.DeltaTime;
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken); return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken, cancelImmediately);
} }
public static UniTask Delay(int millisecondsDelay, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask Delay(int millisecondsDelay, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay); var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken); return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken, cancelImmediately);
} }
public static UniTask Delay(TimeSpan delayTimeSpan, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask Delay(TimeSpan delayTimeSpan, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
if (delayTimeSpan < TimeSpan.Zero) if (delayTimeSpan < TimeSpan.Zero)
{ {
throw new ArgumentOutOfRangeException("Delay does not allow minus delayTimeSpan. delayTimeSpan:" + delayTimeSpan); throw new ArgumentOutOfRangeException("Delay does not allow minus delayTimeSpan. delayTimeSpan:" + delayTimeSpan);
} }
#if UNITY_EDITOR
// force use Realtime.
if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
{
delayType = DelayType.Realtime;
}
#endif
switch (delayType) switch (delayType)
{ {
case DelayType.UnscaledDeltaTime: case DelayType.UnscaledDeltaTime:
{ {
return new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token); return new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
} }
case DelayType.Realtime: case DelayType.Realtime:
{ {
return new UniTask(DelayRealtimePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token); return new UniTask(DelayRealtimePromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
} }
case DelayType.DeltaTime: case DelayType.DeltaTime:
default: default:
{ {
return new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token); return new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
} }
} }
} }
@ -127,7 +198,8 @@ namespace Cysharp.Threading.Tasks
sealed class YieldPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<YieldPromise> sealed class YieldPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<YieldPromise>
{ {
static TaskPool<YieldPromise> pool; static TaskPool<YieldPromise> pool;
public YieldPromise NextNode { get; set; } YieldPromise nextNode;
public ref YieldPromise NextNode => ref nextNode;
static YieldPromise() static YieldPromise()
{ {
@ -135,13 +207,15 @@ namespace Cysharp.Threading.Tasks
} }
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
YieldPromise() YieldPromise()
{ {
} }
public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -153,8 +227,17 @@ namespace Cysharp.Threading.Tasks
result = new YieldPromise(); result = new YieldPromise();
} }
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (YieldPromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@ -171,9 +254,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@ -208,6 +298,8 @@ namespace Cysharp.Threading.Tasks
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@ -215,7 +307,8 @@ namespace Cysharp.Threading.Tasks
sealed class NextFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<NextFramePromise> sealed class NextFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<NextFramePromise>
{ {
static TaskPool<NextFramePromise> pool; static TaskPool<NextFramePromise> pool;
public NextFramePromise NextNode { get; set; } NextFramePromise nextNode;
public ref NextFramePromise NextNode => ref nextNode;
static NextFramePromise() static NextFramePromise()
{ {
@ -223,14 +316,16 @@ namespace Cysharp.Threading.Tasks
} }
int frameCount; int frameCount;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<AsyncUnit> core; UniTaskCompletionSourceCore<AsyncUnit> core;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
NextFramePromise() NextFramePromise()
{ {
} }
public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -244,6 +339,16 @@ namespace Cysharp.Threading.Tasks
result.frameCount = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1; result.frameCount = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (NextFramePromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@ -260,9 +365,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@ -302,14 +414,143 @@ namespace Cysharp.Threading.Tasks
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
sealed class WaitForEndOfFramePromise : IUniTaskSource, ITaskPoolNode<WaitForEndOfFramePromise>, System.Collections.IEnumerator
{
static TaskPool<WaitForEndOfFramePromise> pool;
WaitForEndOfFramePromise nextNode;
public ref WaitForEndOfFramePromise NextNode => ref nextNode;
static WaitForEndOfFramePromise()
{
TaskPool.RegisterSizeGetter(typeof(WaitForEndOfFramePromise), () => pool.Size);
}
UniTaskCompletionSourceCore<object> core;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
WaitForEndOfFramePromise()
{
}
public static IUniTaskSource Create(MonoBehaviour coroutineRunner, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new WaitForEndOfFramePromise();
}
result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitForEndOfFramePromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3);
coroutineRunner.StartCoroutine(result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{
TryReturn();
}
else
{
TaskTracker.RemoveTracking(this);
}
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
Reset(); // Reset Enumerator
cancellationToken = default;
cancellationTokenRegistration.Dispose();
return pool.TryPush(this);
}
// Coroutine Runner implementation
static readonly WaitForEndOfFrame waitForEndOfFrameYieldInstruction = new WaitForEndOfFrame();
bool isFirst = true;
object IEnumerator.Current => waitForEndOfFrameYieldInstruction;
bool IEnumerator.MoveNext()
{
if (isFirst)
{
isFirst = false;
return true; // start WaitForEndOfFrame
}
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
core.TrySetResult(null);
return false;
}
public void Reset()
{
isFirst = true;
}
}
sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise> sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise>
{ {
static TaskPool<DelayFramePromise> pool; static TaskPool<DelayFramePromise> pool;
public DelayFramePromise NextNode { get; set; } DelayFramePromise nextNode;
public ref DelayFramePromise NextNode => ref nextNode;
static DelayFramePromise() static DelayFramePromise()
{ {
@ -319,6 +560,8 @@ namespace Cysharp.Threading.Tasks
int initialFrame; int initialFrame;
int delayFrameCount; int delayFrameCount;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
int currentFrameCount; int currentFrameCount;
UniTaskCompletionSourceCore<AsyncUnit> core; UniTaskCompletionSourceCore<AsyncUnit> core;
@ -327,7 +570,7 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public static IUniTaskSource Create(int delayFrameCount, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(int delayFrameCount, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -342,6 +585,16 @@ namespace Cysharp.Threading.Tasks
result.delayFrameCount = delayFrameCount; result.delayFrameCount = delayFrameCount;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1; result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (DelayFramePromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@ -358,9 +611,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@ -397,8 +657,20 @@ namespace Cysharp.Threading.Tasks
// skip in initial frame. // skip in initial frame.
if (initialFrame == Time.frameCount) if (initialFrame == Time.frameCount)
{ {
#if UNITY_EDITOR
// force use Realtime.
if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
{
//goto ++currentFrameCount
}
else
{
return true; return true;
} }
#else
return true;
#endif
}
} }
if (++currentFrameCount >= delayFrameCount) if (++currentFrameCount >= delayFrameCount)
@ -417,6 +689,8 @@ namespace Cysharp.Threading.Tasks
currentFrameCount = default; currentFrameCount = default;
delayFrameCount = default; delayFrameCount = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@ -424,7 +698,8 @@ namespace Cysharp.Threading.Tasks
sealed class DelayPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayPromise> sealed class DelayPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayPromise>
{ {
static TaskPool<DelayPromise> pool; static TaskPool<DelayPromise> pool;
public DelayPromise NextNode { get; set; } DelayPromise nextNode;
public ref DelayPromise NextNode => ref nextNode;
static DelayPromise() static DelayPromise()
{ {
@ -435,6 +710,8 @@ namespace Cysharp.Threading.Tasks
float delayTimeSpan; float delayTimeSpan;
float elapsed; float elapsed;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
@ -442,7 +719,7 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -458,6 +735,16 @@ namespace Cysharp.Threading.Tasks
result.delayTimeSpan = (float)delayTimeSpan.TotalSeconds; result.delayTimeSpan = (float)delayTimeSpan.TotalSeconds;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1; result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (DelayPromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@ -474,9 +761,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@ -527,6 +821,8 @@ namespace Cysharp.Threading.Tasks
delayTimeSpan = default; delayTimeSpan = default;
elapsed = default; elapsed = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@ -534,7 +830,8 @@ namespace Cysharp.Threading.Tasks
sealed class DelayIgnoreTimeScalePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayIgnoreTimeScalePromise> sealed class DelayIgnoreTimeScalePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayIgnoreTimeScalePromise>
{ {
static TaskPool<DelayIgnoreTimeScalePromise> pool; static TaskPool<DelayIgnoreTimeScalePromise> pool;
public DelayIgnoreTimeScalePromise NextNode { get; set; } DelayIgnoreTimeScalePromise nextNode;
public ref DelayIgnoreTimeScalePromise NextNode => ref nextNode;
static DelayIgnoreTimeScalePromise() static DelayIgnoreTimeScalePromise()
{ {
@ -545,6 +842,8 @@ namespace Cysharp.Threading.Tasks
float elapsed; float elapsed;
int initialFrame; int initialFrame;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
@ -552,7 +851,7 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public static IUniTaskSource Create(TimeSpan delayFrameTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(TimeSpan delayFrameTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -568,6 +867,16 @@ namespace Cysharp.Threading.Tasks
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds; result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1; result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (DelayIgnoreTimeScalePromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@ -584,9 +893,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@ -637,6 +953,8 @@ namespace Cysharp.Threading.Tasks
delayFrameTimeSpan = default; delayFrameTimeSpan = default;
elapsed = default; elapsed = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@ -644,7 +962,8 @@ namespace Cysharp.Threading.Tasks
sealed class DelayRealtimePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayRealtimePromise> sealed class DelayRealtimePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayRealtimePromise>
{ {
static TaskPool<DelayRealtimePromise> pool; static TaskPool<DelayRealtimePromise> pool;
public DelayRealtimePromise NextNode { get; set; } DelayRealtimePromise nextNode;
public ref DelayRealtimePromise NextNode => ref nextNode;
static DelayRealtimePromise() static DelayRealtimePromise()
{ {
@ -654,6 +973,8 @@ namespace Cysharp.Threading.Tasks
long delayTimeSpanTicks; long delayTimeSpanTicks;
ValueStopwatch stopwatch; ValueStopwatch stopwatch;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<AsyncUnit> core; UniTaskCompletionSourceCore<AsyncUnit> core;
@ -661,7 +982,7 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -676,6 +997,16 @@ namespace Cysharp.Threading.Tasks
result.stopwatch = ValueStopwatch.StartNew(); result.stopwatch = ValueStopwatch.StartNew();
result.delayTimeSpanTicks = delayTimeSpan.Ticks; result.delayTimeSpanTicks = delayTimeSpan.Ticks;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (DelayRealtimePromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@ -692,9 +1023,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@ -720,6 +1058,12 @@ namespace Cysharp.Threading.Tasks
return false; return false;
} }
if (stopwatch.IsInvalid)
{
core.TrySetResult(AsyncUnit.Default);
return false;
}
if (stopwatch.ElapsedTicks >= delayTimeSpanTicks) if (stopwatch.ElapsedTicks >= delayTimeSpanTicks)
{ {
core.TrySetResult(AsyncUnit.Default); core.TrySetResult(AsyncUnit.Default);
@ -735,6 +1079,8 @@ namespace Cysharp.Threading.Tasks
core.Reset(); core.Reset();
stopwatch = default; stopwatch = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }

View File

@ -3,6 +3,7 @@
using Cysharp.Threading.Tasks.Internal; using Cysharp.Threading.Tasks.Internal;
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Threading; using System.Threading;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
@ -80,6 +81,16 @@ namespace Cysharp.Threading.Tasks
return factory(); return factory();
} }
public static UniTask Create(Func<CancellationToken, UniTask> factory, CancellationToken cancellationToken)
{
return factory(cancellationToken);
}
public static UniTask Create<T>(T state, Func<T, UniTask> factory)
{
return factory(state);
}
public static UniTask<T> Create<T>(Func<UniTask<T>> factory) public static UniTask<T> Create<T>(Func<UniTask<T>> factory)
{ {
return factory(); return factory();
@ -136,11 +147,19 @@ namespace Cysharp.Threading.Tasks
return () => asyncAction(cancellationToken).Forget(); return () => asyncAction(cancellationToken).Forget();
} }
/// <summary>
/// helper of create add UniTaskVoid to delegate.
/// </summary>
public static Action Action<T>(T state, Func<T, UniTaskVoid> asyncAction)
{
return () => asyncAction(state).Forget();
}
#if UNITY_2018_3_OR_NEWER #if UNITY_2018_3_OR_NEWER
/// <summary> /// <summary>
/// Create async void(UniTaskVoid) UnityAction. /// Create async void(UniTaskVoid) UnityAction.
/// For exampe: onClick.AddListener(UniTask.UnityAction(async () => { /* */ } )) /// For example: onClick.AddListener(UniTask.UnityAction(async () => { /* */ } ))
/// </summary> /// </summary>
public static UnityEngine.Events.UnityAction UnityAction(Func<UniTaskVoid> asyncAction) public static UnityEngine.Events.UnityAction UnityAction(Func<UniTaskVoid> asyncAction)
{ {
@ -149,13 +168,94 @@ namespace Cysharp.Threading.Tasks
/// <summary> /// <summary>
/// Create async void(UniTaskVoid) UnityAction. /// Create async void(UniTaskVoid) UnityAction.
/// For exampe: onClick.AddListener(UniTask.UnityAction(FooAsync, this.GetCancellationTokenOnDestroy())) /// For example: onClick.AddListener(UniTask.UnityAction(FooAsync, this.GetCancellationTokenOnDestroy()))
/// </summary> /// </summary>
public static UnityEngine.Events.UnityAction UnityAction(Func<CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken) public static UnityEngine.Events.UnityAction UnityAction(Func<CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
{ {
return () => asyncAction(cancellationToken).Forget(); return () => asyncAction(cancellationToken).Forget();
} }
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(FooAsync, Argument))
/// </summary>
public static UnityEngine.Events.UnityAction UnityAction<T>(T state, Func<T, UniTaskVoid> asyncAction)
{
return () => asyncAction(state).Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T arg) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T> UnityAction<T>(Func<T, UniTaskVoid> asyncAction)
{
return (arg) => asyncAction(arg).Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T0, T1> UnityAction<T0, T1>(Func<T0, T1, UniTaskVoid> asyncAction)
{
return (arg0, arg1) => asyncAction(arg0, arg1).Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T0, T1, T2> UnityAction<T0, T1, T2>(Func<T0, T1, T2, UniTaskVoid> asyncAction)
{
return (arg0, arg1, arg2) => asyncAction(arg0, arg1, arg2).Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2, T3 arg3) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T0, T1, T2, T3> UnityAction<T0, T1, T2, T3>(Func<T0, T1, T2, T3, UniTaskVoid> asyncAction)
{
return (arg0, arg1, arg2, arg3) => asyncAction(arg0, arg1, arg2, arg3).Forget();
}
// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T arg, CancellationToken cancellationToken) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T> UnityAction<T>(Func<T, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
{
return (arg) => asyncAction(arg, cancellationToken).Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, CancellationToken cancellationToken) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T0, T1> UnityAction<T0, T1>(Func<T0, T1, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
{
return (arg0, arg1) => asyncAction(arg0, arg1, cancellationToken).Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2, CancellationToken cancellationToken) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T0, T1, T2> UnityAction<T0, T1, T2>(Func<T0, T1, T2, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
{
return (arg0, arg1, arg2) => asyncAction(arg0, arg1, arg2, cancellationToken).Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2, T3 arg3, CancellationToken cancellationToken) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T0, T1, T2, T3> UnityAction<T0, T1, T2, T3>(Func<T0, T1, T2, T3, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
{
return (arg0, arg1, arg2, arg3) => asyncAction(arg0, arg1, arg2, arg3, cancellationToken).Forget();
}
#endif #endif
/// <summary> /// <summary>
@ -174,18 +274,56 @@ namespace Cysharp.Threading.Tasks
return new UniTask<T>(new DeferPromise<T>(factory), 0); return new UniTask<T>(new DeferPromise<T>(factory), 0);
} }
/// <summary>
/// Defer the task creation just before call await.
/// </summary>
public static UniTask Defer<TState>(TState state, Func<TState, UniTask> factory)
{
return new UniTask(new DeferPromiseWithState<TState>(state, factory), 0);
}
/// <summary>
/// Defer the task creation just before call await.
/// </summary>
public static UniTask<TResult> Defer<TState, TResult>(TState state, Func<TState, UniTask<TResult>> factory)
{
return new UniTask<TResult>(new DeferPromiseWithState<TState, TResult>(state, factory), 0);
}
/// <summary>
/// Never complete.
/// </summary>
public static UniTask Never(CancellationToken cancellationToken)
{
return new UniTask<AsyncUnit>(new NeverPromise<AsyncUnit>(cancellationToken), 0);
}
/// <summary>
/// Never complete.
/// </summary>
public static UniTask<T> Never<T>(CancellationToken cancellationToken)
{
return new UniTask<T>(new NeverPromise<T>(cancellationToken), 0);
}
sealed class ExceptionResultSource : IUniTaskSource sealed class ExceptionResultSource : IUniTaskSource
{ {
readonly Exception exception; readonly ExceptionDispatchInfo exception;
bool calledGet;
public ExceptionResultSource(Exception exception) public ExceptionResultSource(Exception exception)
{ {
this.exception = exception; this.exception = ExceptionDispatchInfo.Capture(exception);
} }
public void GetResult(short token) public void GetResult(short token)
{ {
throw exception; if (!calledGet)
{
calledGet = true;
GC.SuppressFinalize(this);
}
exception.Throw();
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@ -202,25 +340,45 @@ namespace Cysharp.Threading.Tasks
{ {
continuation(state); continuation(state);
} }
~ExceptionResultSource()
{
if (!calledGet)
{
UniTaskScheduler.PublishUnobservedTaskException(exception.SourceException);
}
}
} }
sealed class ExceptionResultSource<T> : IUniTaskSource<T> sealed class ExceptionResultSource<T> : IUniTaskSource<T>
{ {
readonly Exception exception; readonly ExceptionDispatchInfo exception;
bool calledGet;
public ExceptionResultSource(Exception exception) public ExceptionResultSource(Exception exception)
{ {
this.exception = exception; this.exception = ExceptionDispatchInfo.Capture(exception);
} }
public T GetResult(short token) public T GetResult(short token)
{ {
throw exception; if (!calledGet)
{
calledGet = true;
GC.SuppressFinalize(this);
}
exception.Throw();
return default;
} }
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
{ {
throw exception; if (!calledGet)
{
calledGet = true;
GC.SuppressFinalize(this);
}
exception.Throw();
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@ -237,6 +395,14 @@ namespace Cysharp.Threading.Tasks
{ {
continuation(state); continuation(state);
} }
~ExceptionResultSource()
{
if (!calledGet)
{
UniTaskScheduler.PublishUnobservedTaskException(exception.SourceException);
}
}
} }
sealed class CanceledResultSource : IUniTaskSource sealed class CanceledResultSource : IUniTaskSource
@ -323,10 +489,12 @@ namespace Cysharp.Threading.Tasks
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
{ {
var f = Interlocked.Exchange(ref factory, null); var f = Interlocked.Exchange(ref factory, null);
if (f == null) throw new InvalidOperationException("Can't call twice."); if (f != null)
{
task = f(); task = f();
awaiter = task.GetAwaiter(); awaiter = task.GetAwaiter();
}
return task.Status; return task.Status;
} }
@ -365,10 +533,12 @@ namespace Cysharp.Threading.Tasks
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
{ {
var f = Interlocked.Exchange(ref factory, null); var f = Interlocked.Exchange(ref factory, null);
if (f == null) throw new InvalidOperationException("Can't call twice."); if (f != null)
{
task = f(); task = f();
awaiter = task.GetAwaiter(); awaiter = task.GetAwaiter();
}
return task.Status; return task.Status;
} }
@ -382,6 +552,141 @@ namespace Cysharp.Threading.Tasks
return task.Status; return task.Status;
} }
} }
sealed class DeferPromiseWithState<TState> : IUniTaskSource
{
Func<TState, UniTask> factory;
TState argument;
UniTask task;
UniTask.Awaiter awaiter;
public DeferPromiseWithState(TState argument, Func<TState, UniTask> factory)
{
this.argument = argument;
this.factory = factory;
}
public void GetResult(short token)
{
awaiter.GetResult();
}
public UniTaskStatus GetStatus(short token)
{
var f = Interlocked.Exchange(ref factory, null);
if (f != null)
{
task = f(argument);
awaiter = task.GetAwaiter();
}
return task.Status;
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
awaiter.SourceOnCompleted(continuation, state);
}
public UniTaskStatus UnsafeGetStatus()
{
return task.Status;
}
}
sealed class DeferPromiseWithState<TState, TResult> : IUniTaskSource<TResult>
{
Func<TState, UniTask<TResult>> factory;
TState argument;
UniTask<TResult> task;
UniTask<TResult>.Awaiter awaiter;
public DeferPromiseWithState(TState argument, Func<TState, UniTask<TResult>> factory)
{
this.argument = argument;
this.factory = factory;
}
public TResult GetResult(short token)
{
return awaiter.GetResult();
}
void IUniTaskSource.GetResult(short token)
{
awaiter.GetResult();
}
public UniTaskStatus GetStatus(short token)
{
var f = Interlocked.Exchange(ref factory, null);
if (f != null)
{
task = f(argument);
awaiter = task.GetAwaiter();
}
return task.Status;
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
awaiter.SourceOnCompleted(continuation, state);
}
public UniTaskStatus UnsafeGetStatus()
{
return task.Status;
}
}
sealed class NeverPromise<T> : IUniTaskSource<T>
{
static readonly Action<object> cancellationCallback = CancellationCallback;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<T> core;
public NeverPromise(CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
if (this.cancellationToken.CanBeCanceled)
{
this.cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
}
}
static void CancellationCallback(object state)
{
var self = (NeverPromise<T>)state;
self.core.TrySetCanceled(self.cancellationToken);
}
public T GetResult(short token)
{
return core.GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
void IUniTaskSource.GetResult(short token)
{
core.GetResult(token);
}
}
} }
internal static class CompletedTasks internal static class CompletedTasks

View File

@ -1,16 +1,73 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System; using System;
using System.Threading;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
{ {
public partial struct UniTask public partial struct UniTask
{ {
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary> #region OBSOLETE_RUN
public static async UniTask Run(Action action, bool configureAwait = true)
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
public static UniTask Run(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
return RunOnThreadPool(action, configureAwait, cancellationToken);
}
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
public static UniTask Run(Action<object> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
return RunOnThreadPool(action, state, configureAwait, cancellationToken);
}
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
public static UniTask Run(Func<UniTask> action, bool configureAwait = true, CancellationToken cancellationToken = default)
{
return RunOnThreadPool(action, configureAwait, cancellationToken);
}
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
public static UniTask Run(Func<object, UniTask> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
return RunOnThreadPool(action, state, configureAwait, cancellationToken);
}
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
public static UniTask<T> Run<T>(Func<T> func, bool configureAwait = true, CancellationToken cancellationToken = default)
{
return RunOnThreadPool(func, configureAwait, cancellationToken);
}
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
public static UniTask<T> Run<T>(Func<UniTask<T>> func, bool configureAwait = true, CancellationToken cancellationToken = default)
{
return RunOnThreadPool(func, configureAwait, cancellationToken);
}
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
public static UniTask<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
return RunOnThreadPool(func, state, configureAwait, cancellationToken);
}
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
public static UniTask<T> Run<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
return RunOnThreadPool(func, state, configureAwait, cancellationToken);
}
#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(); await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait) if (configureAwait)
{ {
try try
@ -26,13 +83,19 @@ namespace Cysharp.Threading.Tasks
{ {
action(); action();
} }
cancellationToken.ThrowIfCancellationRequested();
} }
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary> /// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask Run(Action<object> action, object state, bool configureAwait = true) public static async UniTask RunOnThreadPool(Action<object> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool(); await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait) if (configureAwait)
{ {
try try
@ -48,13 +111,19 @@ namespace Cysharp.Threading.Tasks
{ {
action(state); action(state);
} }
cancellationToken.ThrowIfCancellationRequested();
} }
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary> /// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask Run(Func<UniTask> action, bool configureAwait = true) public static async UniTask RunOnThreadPool(Func<UniTask> action, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool(); await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait) if (configureAwait)
{ {
try try
@ -70,13 +139,19 @@ namespace Cysharp.Threading.Tasks
{ {
await action(); await action();
} }
cancellationToken.ThrowIfCancellationRequested();
} }
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary> /// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask Run(Func<object, UniTask> action, object state, bool configureAwait = true) public static async UniTask RunOnThreadPool(Func<object, UniTask> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool(); await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait) if (configureAwait)
{ {
try try
@ -92,12 +167,19 @@ namespace Cysharp.Threading.Tasks
{ {
await action(state); await action(state);
} }
cancellationToken.ThrowIfCancellationRequested();
} }
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary> /// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> Run<T>(Func<T> func, bool configureAwait = true) public static async UniTask<T> RunOnThreadPool<T>(Func<T> func, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool(); await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait) if (configureAwait)
{ {
try try
@ -107,6 +189,7 @@ namespace Cysharp.Threading.Tasks
finally finally
{ {
await UniTask.Yield(); await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
} }
} }
else else
@ -116,9 +199,14 @@ namespace Cysharp.Threading.Tasks
} }
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary> /// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> Run<T>(Func<UniTask<T>> func, bool configureAwait = true) public static async UniTask<T> RunOnThreadPool<T>(Func<UniTask<T>> func, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool(); await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait) if (configureAwait)
{ {
try try
@ -127,20 +215,28 @@ namespace Cysharp.Threading.Tasks
} }
finally finally
{ {
cancellationToken.ThrowIfCancellationRequested();
await UniTask.Yield(); await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
} }
} }
else else
{ {
return await func(); var result = await func();
cancellationToken.ThrowIfCancellationRequested();
return result;
} }
} }
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary> /// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true) public static async UniTask<T> RunOnThreadPool<T>(Func<object, T> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool(); await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait) if (configureAwait)
{ {
try try
@ -150,6 +246,7 @@ namespace Cysharp.Threading.Tasks
finally finally
{ {
await UniTask.Yield(); await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
} }
} }
else else
@ -159,10 +256,14 @@ namespace Cysharp.Threading.Tasks
} }
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary> /// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> Run<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true) 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(); await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait) if (configureAwait)
{ {
try try
@ -171,12 +272,16 @@ namespace Cysharp.Threading.Tasks
} }
finally finally
{ {
cancellationToken.ThrowIfCancellationRequested();
await UniTask.Yield(); await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
} }
} }
else else
{ {
return await func(state); 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> sealed class ThreadPoolWorkItem : IThreadPoolWorkItem, ITaskPoolNode<ThreadPoolWorkItem>
{ {
static TaskPool<ThreadPoolWorkItem> pool; static TaskPool<ThreadPoolWorkItem> pool;
public ThreadPoolWorkItem NextNode { get; set; } ThreadPoolWorkItem nextNode;
public ref ThreadPoolWorkItem NextNode => ref nextNode;
static ThreadPoolWorkItem() static ThreadPoolWorkItem()
{ {

View File

@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Threading; using System.Threading;
using Cysharp.Threading.Tasks.Internal; using Cysharp.Threading.Tasks.Internal;
@ -9,36 +10,47 @@ namespace Cysharp.Threading.Tasks
{ {
public partial struct UniTask public partial struct UniTask
{ {
public static UniTask WaitUntil(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask WaitUntil(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
return new UniTask(WaitUntilPromise.Create(predicate, timing, cancellationToken, out var token), token); return new UniTask(WaitUntilPromise.Create(predicate, timing, cancellationToken, cancelImmediately, out var token), token);
} }
public static UniTask WaitWhile(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask WaitUntil<T>(T state, Func<T, bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
return new UniTask(WaitWhilePromise.Create(predicate, timing, cancellationToken, out var token), token); return new UniTask(WaitUntilPromise<T>.Create(state, predicate, timing, cancellationToken, cancelImmediately, out var token), token);
} }
public static UniTask WaitUntilCanceled(CancellationToken cancellationToken, PlayerLoopTiming timing = PlayerLoopTiming.Update) public static UniTask WaitWhile(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
return new UniTask(WaitUntilCanceledPromise.Create(cancellationToken, timing, out var token), token); return new UniTask(WaitWhilePromise.Create(predicate, timing, cancellationToken, cancelImmediately, out var token), token);
} }
public static UniTask<U> WaitUntilValueChanged<T, U>(T target, Func<T, U> monitorFunction, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<U> equalityComparer = null, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask WaitWhile<T>(T state, Func<T, bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{
return new UniTask(WaitWhilePromise<T>.Create(state, predicate, timing, cancellationToken, cancelImmediately, out var token), token);
}
public static UniTask WaitUntilCanceled(CancellationToken cancellationToken, PlayerLoopTiming timing = PlayerLoopTiming.Update, bool completeImmediately = false)
{
return new UniTask(WaitUntilCanceledPromise.Create(cancellationToken, timing, completeImmediately, out var token), token);
}
public static UniTask<U> WaitUntilValueChanged<T, U>(T target, Func<T, U> monitorFunction, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<U> equalityComparer = null, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
where T : class where T : class
{ {
var unityObject = target as UnityEngine.Object; var unityObject = target as UnityEngine.Object;
var isUnityObject = target is UnityEngine.Object; // don't use (unityObject == null) var isUnityObject = target is UnityEngine.Object; // don't use (unityObject == null)
return new UniTask<U>(isUnityObject return new UniTask<U>(isUnityObject
? WaitUntilValueChangedUnityObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, out var token) ? WaitUntilValueChangedUnityObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, cancelImmediately, out var token)
: WaitUntilValueChangedStandardObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, out token), token); : WaitUntilValueChangedStandardObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, cancelImmediately, out token), token);
} }
sealed class WaitUntilPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilPromise> sealed class WaitUntilPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilPromise>
{ {
static TaskPool<WaitUntilPromise> pool; static TaskPool<WaitUntilPromise> pool;
public WaitUntilPromise NextNode { get; set; } WaitUntilPromise nextNode;
public ref WaitUntilPromise NextNode => ref nextNode;
static WaitUntilPromise() static WaitUntilPromise()
{ {
@ -47,6 +59,8 @@ namespace Cysharp.Threading.Tasks
Func<bool> predicate; Func<bool> predicate;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
@ -54,7 +68,7 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public static IUniTaskSource Create(Func<bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(Func<bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -68,6 +82,16 @@ namespace Cysharp.Threading.Tasks
result.predicate = predicate; result.predicate = predicate;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitUntilPromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@ -84,9 +108,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@ -135,30 +166,36 @@ namespace Cysharp.Threading.Tasks
core.Reset(); core.Reset();
predicate = default; predicate = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
sealed class WaitWhilePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise> sealed class WaitUntilPromise<T> : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilPromise<T>>
{ {
static TaskPool<WaitWhilePromise> pool; static TaskPool<WaitUntilPromise<T>> pool;
public WaitWhilePromise NextNode { get; set; } WaitUntilPromise<T> nextNode;
public ref WaitUntilPromise<T> NextNode => ref nextNode;
static WaitWhilePromise() static WaitUntilPromise()
{ {
TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise), () => pool.Size); TaskPool.RegisterSizeGetter(typeof(WaitUntilPromise<T>), () => pool.Size);
} }
Func<bool> predicate; Func<T, bool> predicate;
T argument;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
WaitWhilePromise() WaitUntilPromise()
{ {
} }
public static IUniTaskSource Create(Func<bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(T argument, Func<T, bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -167,11 +204,22 @@ namespace Cysharp.Threading.Tasks
if (!pool.TryPop(out var result)) if (!pool.TryPop(out var result))
{ {
result = new WaitWhilePromise(); result = new WaitUntilPromise<T>();
} }
result.predicate = predicate; result.predicate = predicate;
result.argument = argument;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitUntilPromise<T>)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@ -188,9 +236,143 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
try
{
if (!predicate(argument))
{
return true;
}
}
catch (Exception ex)
{
core.TrySetException(ex);
return false;
}
core.TrySetResult(null);
return false;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
predicate = default;
argument = default;
cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this);
}
}
sealed class WaitWhilePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise>
{
static TaskPool<WaitWhilePromise> pool;
WaitWhilePromise nextNode;
public ref WaitWhilePromise NextNode => ref nextNode;
static WaitWhilePromise()
{
TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise), () => pool.Size);
}
Func<bool> predicate;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core;
WaitWhilePromise()
{
}
public static IUniTaskSource Create(Func<bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new WaitWhilePromise();
}
result.predicate = predicate;
result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitWhilePromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{
TryReturn();
}
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@ -239,29 +421,36 @@ namespace Cysharp.Threading.Tasks
core.Reset(); core.Reset();
predicate = default; predicate = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
sealed class WaitUntilCanceledPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilCanceledPromise> sealed class WaitWhilePromise<T> : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise<T>>
{ {
static TaskPool<WaitUntilCanceledPromise> pool; static TaskPool<WaitWhilePromise<T>> pool;
public WaitUntilCanceledPromise NextNode { get; set; } WaitWhilePromise<T> nextNode;
public ref WaitWhilePromise<T> NextNode => ref nextNode;
static WaitUntilCanceledPromise() static WaitWhilePromise()
{ {
TaskPool.RegisterSizeGetter(typeof(WaitUntilCanceledPromise), () => pool.Size); TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise<T>), () => pool.Size);
} }
Func<T, bool> predicate;
T argument;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
WaitUntilCanceledPromise() WaitWhilePromise()
{ {
} }
public static IUniTaskSource Create(CancellationToken cancellationToken, PlayerLoopTiming timing, out short token) public static IUniTaskSource Create(T argument, Func<T, bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -270,10 +459,22 @@ namespace Cysharp.Threading.Tasks
if (!pool.TryPop(out var result)) if (!pool.TryPop(out var result))
{ {
result = new WaitUntilCanceledPromise(); result = new WaitWhilePromise<T>();
} }
result.predicate = predicate;
result.argument = argument;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitWhilePromise<T>)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@ -290,9 +491,141 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
try
{
if (predicate(argument))
{
return true;
}
}
catch (Exception ex)
{
core.TrySetException(ex);
return false;
}
core.TrySetResult(null);
return false;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
predicate = default;
argument = default;
cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this);
}
}
sealed class WaitUntilCanceledPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilCanceledPromise>
{
static TaskPool<WaitUntilCanceledPromise> pool;
WaitUntilCanceledPromise nextNode;
public ref WaitUntilCanceledPromise NextNode => ref nextNode;
static WaitUntilCanceledPromise()
{
TaskPool.RegisterSizeGetter(typeof(WaitUntilCanceledPromise), () => pool.Size);
}
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core;
WaitUntilCanceledPromise()
{
}
public static IUniTaskSource Create(CancellationToken cancellationToken, PlayerLoopTiming timing, bool cancelImmediately, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new WaitUntilCanceledPromise();
}
result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitUntilCanceledPromise)state;
promise.core.TrySetResult(null);
}, result);
}
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{
TryReturn();
}
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@ -326,6 +659,8 @@ namespace Cysharp.Threading.Tasks
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@ -334,7 +669,8 @@ namespace Cysharp.Threading.Tasks
sealed class WaitUntilValueChangedUnityObjectPromise<T, U> : IUniTaskSource<U>, IPlayerLoopItem, ITaskPoolNode<WaitUntilValueChangedUnityObjectPromise<T, U>> sealed class WaitUntilValueChangedUnityObjectPromise<T, U> : IUniTaskSource<U>, IPlayerLoopItem, ITaskPoolNode<WaitUntilValueChangedUnityObjectPromise<T, U>>
{ {
static TaskPool<WaitUntilValueChangedUnityObjectPromise<T, U>> pool; 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() static WaitUntilValueChangedUnityObjectPromise()
{ {
@ -347,6 +683,8 @@ namespace Cysharp.Threading.Tasks
Func<T, U> monitorFunction; Func<T, U> monitorFunction;
IEqualityComparer<U> equalityComparer; IEqualityComparer<U> equalityComparer;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<U> core; UniTaskCompletionSourceCore<U> core;
@ -354,7 +692,7 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -372,6 +710,16 @@ namespace Cysharp.Threading.Tasks
result.currentValue = monitorFunction(target); result.currentValue = monitorFunction(target);
result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault<U>(); result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault<U>();
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitUntilValueChangedUnityObjectPromise<T, U>)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@ -388,9 +736,16 @@ namespace Cysharp.Threading.Tasks
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
@ -449,6 +804,8 @@ namespace Cysharp.Threading.Tasks
monitorFunction = default; monitorFunction = default;
equalityComparer = default; equalityComparer = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@ -457,7 +814,8 @@ namespace Cysharp.Threading.Tasks
where T : class where T : class
{ {
static TaskPool<WaitUntilValueChangedStandardObjectPromise<T, U>> pool; 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() static WaitUntilValueChangedStandardObjectPromise()
{ {
@ -469,6 +827,8 @@ namespace Cysharp.Threading.Tasks
Func<T, U> monitorFunction; Func<T, U> monitorFunction;
IEqualityComparer<U> equalityComparer; IEqualityComparer<U> equalityComparer;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<U> core; UniTaskCompletionSourceCore<U> core;
@ -476,7 +836,7 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -493,6 +853,16 @@ namespace Cysharp.Threading.Tasks
result.currentValue = monitorFunction(target); result.currentValue = monitorFunction(target);
result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault<U>(); result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault<U>();
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitUntilValueChangedStandardObjectPromise<T, U>)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@ -509,9 +879,16 @@ namespace Cysharp.Threading.Tasks
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
@ -570,6 +947,8 @@ namespace Cysharp.Threading.Tasks
monitorFunction = default; monitorFunction = default;
equalityComparer = default; equalityComparer = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }

View File

@ -0,0 +1,183 @@
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Collections.Generic;
using System.Runtime.ExceptionServices;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public partial struct UniTask
{
public static IUniTaskAsyncEnumerable<WhenEachResult<T>> WhenEach<T>(IEnumerable<UniTask<T>> tasks)
{
return new WhenEachEnumerable<T>(tasks);
}
public static IUniTaskAsyncEnumerable<WhenEachResult<T>> WhenEach<T>(params UniTask<T>[] tasks)
{
return new WhenEachEnumerable<T>(tasks);
}
}
public readonly struct WhenEachResult<T>
{
public T Result { get; }
public Exception Exception { get; }
//[MemberNotNullWhen(false, nameof(Exception))]
public bool IsCompletedSuccessfully => Exception == null;
//[MemberNotNullWhen(true, nameof(Exception))]
public bool IsFaulted => Exception != null;
public WhenEachResult(T result)
{
this.Result = result;
this.Exception = null;
}
public WhenEachResult(Exception exception)
{
if (exception == null) throw new ArgumentNullException(nameof(exception));
this.Result = default;
this.Exception = exception;
}
public void TryThrow()
{
if (IsFaulted)
{
ExceptionDispatchInfo.Capture(Exception).Throw();
}
}
public T GetResult()
{
if (IsFaulted)
{
ExceptionDispatchInfo.Capture(Exception).Throw();
}
return Result;
}
public override string ToString()
{
if (IsCompletedSuccessfully)
{
return Result?.ToString() ?? "";
}
else
{
return $"Exception{{{Exception.Message}}}";
}
}
}
internal enum WhenEachState : byte
{
NotRunning,
Running,
Completed
}
internal sealed class WhenEachEnumerable<T> : IUniTaskAsyncEnumerable<WhenEachResult<T>>
{
IEnumerable<UniTask<T>> source;
public WhenEachEnumerable(IEnumerable<UniTask<T>> source)
{
this.source = source;
}
public IUniTaskAsyncEnumerator<WhenEachResult<T>> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, cancellationToken);
}
sealed class Enumerator : IUniTaskAsyncEnumerator<WhenEachResult<T>>
{
readonly IEnumerable<UniTask<T>> source;
CancellationToken cancellationToken;
Channel<WhenEachResult<T>> channel;
IUniTaskAsyncEnumerator<WhenEachResult<T>> channelEnumerator;
int completeCount;
WhenEachState state;
public Enumerator(IEnumerable<UniTask<T>> source, CancellationToken cancellationToken)
{
this.source = source;
this.cancellationToken = cancellationToken;
}
public WhenEachResult<T> Current => channelEnumerator.Current;
public UniTask<bool> MoveNextAsync()
{
cancellationToken.ThrowIfCancellationRequested();
if (state == WhenEachState.NotRunning)
{
state = WhenEachState.Running;
channel = Channel.CreateSingleConsumerUnbounded<WhenEachResult<T>>();
channelEnumerator = channel.Reader.ReadAllAsync().GetAsyncEnumerator(cancellationToken);
if (source is UniTask<T>[] array)
{
ConsumeAll(this, array, array.Length);
}
else
{
using (var rentArray = ArrayPoolUtil.Materialize(source))
{
ConsumeAll(this, rentArray.Array, rentArray.Length);
}
}
}
return channelEnumerator.MoveNextAsync();
}
static void ConsumeAll(Enumerator self, UniTask<T>[] array, int length)
{
for (int i = 0; i < length; i++)
{
RunWhenEachTask(self, array[i], length).Forget();
}
}
static async UniTaskVoid RunWhenEachTask(Enumerator self, UniTask<T> task, int length)
{
try
{
var result = await task;
self.channel.Writer.TryWrite(new WhenEachResult<T>(result));
}
catch (Exception ex)
{
self.channel.Writer.TryWrite(new WhenEachResult<T>(ex));
}
if (Interlocked.Increment(ref self.completeCount) == length)
{
self.state = WhenEachState.Completed;
self.channel.Writer.TryComplete();
}
}
public async UniTask DisposeAsync()
{
if (channelEnumerator != null)
{
await channelEnumerator.DisposeAsync();
}
if (state != WhenEachState.Completed)
{
state = WhenEachState.Completed;
channel.Writer.TryComplete(new OperationCanceledException());
}
}
}
}
}

View File

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

View File

@ -1,7 +1,7 @@
{ {
"name": "UniTask", "name": "UniTask",
"references": [ "rootNamespace": "",
], "references": [],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],
"allowUnsafeCode": false, "allowUnsafeCode": false,
@ -10,6 +10,36 @@
"autoReferenced": true, "autoReferenced": true,
"defineConstraints": [], "defineConstraints": [],
"versionDefines": [ "versionDefines": [
{
"name": "com.unity.modules.assetbundle",
"expression": "",
"define": "UNITASK_ASSETBUNDLE_SUPPORT"
},
{
"name": "com.unity.modules.physics",
"expression": "",
"define": "UNITASK_PHYSICS_SUPPORT"
},
{
"name": "com.unity.modules.physics2d",
"expression": "",
"define": "UNITASK_PHYSICS2D_SUPPORT"
},
{
"name": "com.unity.modules.particlesystem",
"expression": "",
"define": "UNITASK_PARTICLESYSTEM_SUPPORT"
},
{
"name": "com.unity.ugui",
"expression": "",
"define": "UNITASK_UGUI_SUPPORT"
},
{
"name": "com.unity.modules.unitywebrequest",
"expression": "",
"define": "UNITASK_WEBREQUEST_SUPPORT"
}
], ],
"noEngineReferences": false "noEngineReferences": false
} }

View File

@ -1,6 +1,10 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS0436 #pragma warning disable CS0436
#if UNITASK_NETCORE || UNITY_2022_3_OR_NEWER
#define SUPPORT_VALUETASK
#endif
using Cysharp.Threading.Tasks.CompilerServices; using Cysharp.Threading.Tasks.CompilerServices;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
@ -69,7 +73,7 @@ namespace Cysharp.Threading.Tasks
return new UniTask<bool>(new IsCanceledSource(source), token); return new UniTask<bool>(new IsCanceledSource(source), token);
} }
#if !UNITY_2018_3_OR_NEWER #if SUPPORT_VALUETASK
public static implicit operator System.Threading.Tasks.ValueTask(in UniTask self) public static implicit operator System.Threading.Tasks.ValueTask(in UniTask self)
{ {
@ -78,7 +82,7 @@ namespace Cysharp.Threading.Tasks
return default; return default;
} }
#if NETSTANDARD2_0 #if (UNITASK_NETCORE && NETSTANDARD2_0)
return self.AsValueTask(); return self.AsValueTask();
#else #else
return new System.Threading.Tasks.ValueTask(self.source, self.token); return new System.Threading.Tasks.ValueTask(self.source, self.token);
@ -115,8 +119,13 @@ namespace Cysharp.Threading.Tasks
var status = this.source.GetStatus(this.token); var status = this.source.GetStatus(this.token);
if (status.IsCompletedSuccessfully()) if (status.IsCompletedSuccessfully())
{ {
this.source.GetResult(this.token);
return CompletedTasks.AsyncUnit; 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); return new UniTask<AsyncUnit>(new AsyncUnitSource(this.source), this.token);
} }
@ -422,6 +431,7 @@ namespace Cysharp.Threading.Tasks
var status = this.source.GetStatus(this.token); var status = this.source.GetStatus(this.token);
if (status.IsCompletedSuccessfully()) if (status.IsCompletedSuccessfully())
{ {
this.source.GetResult(this.token);
return UniTask.CompletedTask; return UniTask.CompletedTask;
} }
@ -434,7 +444,7 @@ namespace Cysharp.Threading.Tasks
return self.AsUniTask(); return self.AsUniTask();
} }
#if !UNITY_2018_3_OR_NEWER #if SUPPORT_VALUETASK
public static implicit operator System.Threading.Tasks.ValueTask<T>(in UniTask<T> self) public static implicit operator System.Threading.Tasks.ValueTask<T>(in UniTask<T> self)
{ {
@ -443,7 +453,7 @@ namespace Cysharp.Threading.Tasks
return new System.Threading.Tasks.ValueTask<T>(self.result); return new System.Threading.Tasks.ValueTask<T>(self.result);
} }
#if NETSTANDARD2_0 #if (UNITASK_NETCORE && NETSTANDARD2_0)
return self.AsValueTask(); return self.AsValueTask();
#else #else
return new System.Threading.Tasks.ValueTask<T>(self.source, self.token); return new System.Threading.Tasks.ValueTask<T>(self.source, self.token);

Some files were not shown because too many files have changed in this diff Show More