Compare commits

...

219 Commits
2.1.0 ... 2.4.1

Author SHA1 Message Date
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
88 changed files with 4634 additions and 4299 deletions

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

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

View File

@@ -4,64 +4,70 @@ on:
push:
branches:
- "master"
tags:
- "!*" # not a tag push
pull_request:
types:
- opened
- synchronize
branches:
- "master"
jobs:
build-dotnet:
runs-on: ubuntu-latest
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
NUGET_XMLDOC_MODE: skip
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1
- uses: actions/checkout@v3
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
with:
dotnet-version: 3.1.x
- run: dotnet test -c Debug ./src/UniTask.NetCoreTests/UniTask.NetCoreTests.csproj
dotnet-version: |
6.0.x
7.0.x
- run: dotnet build -c Debug
- run: dotnet test -c Debug
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:'))"
strategy:
matrix:
unity: ["2019.3.9f1", "2020.1.0b5"]
include:
- unity: 2019.3.9f1
license: UNITY_2019_3
- unity: 2020.1.0b5
license: UNITY_2020_1
unity: ["2019.3.9f1", "2019.4.13f1", "2020.1.12f1"]
runs-on: ubuntu-latest
container:
# with linux-il2cpp. image from https://hub.docker.com/r/gableroux/unity3d/tags
image: gableroux/unity3d:${{ matrix.unity }}-linux-il2cpp
timeout-minutes: 15
steps:
- run: apt update && apt install git -y
- uses: actions/checkout@v2
- run: echo -n "$UNITY_LICENSE" >> .Unity.ulf
env:
UNITY_LICENSE: ${{ secrets[matrix.license] }}
- 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.
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -manualLicenseFile .Unity.ulf || exit 0
- uses: actions/checkout@v3
# Execute scripts: RuntimeUnitTestToolkit
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend mono /BuildTarget StandaloneLinux64
- 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
uses: Cysharp/Actions/.github/actions/unity-builder@main
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
with:
projectPath: src/UniTask
unityVersion: ${{ matrix.unity }}
targetPlatform: StandaloneLinux64
buildMethod: UnitTestBuilder.BuildUnitTest
customParameters: /headless /ScriptBackend mono
- name: Execute UnitTest
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_Mono2x/test
# Execute scripts: Export Package
- name: Export unitypackage
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
working-directory: src/UniTask
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
- name: Build Unity (.unitypacakge)
uses: Cysharp/Actions/.github/actions/unity-builder@main
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.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.
- uses: actions/upload-artifact@v2
with:
name: UniTask.unitypackage.zip
name: UniTask.unitypackage-${{ matrix.unity }}.zip
path: ./src/UniTask/*.unitypackage

View File

@@ -8,11 +8,11 @@ on:
jobs:
run-docfx:
if: "!(contains(github.event.head_commit.message, '[skip ci]') || contains(github.event.head_commit.message, '[ci skip]'))"
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: actions/checkout@v3
with:
repository: Cysharp/DocfxTemplate
path: docs/_DocfxTemplate
@@ -28,4 +28,4 @@ jobs:
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/_site
publish_dir: docs/_site

View File

@@ -1,62 +1,82 @@
name: Build-Release
name: build-release
on:
push:
tags:
- "[0-9]+.[0-9]+.[0-9]+*"
workflow_dispatch:
inputs:
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
env:
GIT_TAG: ${{ github.event.inputs.tag }}
DRY_RUN: ${{ github.event.inputs.dry-run }}
jobs:
build-dotnet:
runs-on: ubuntu-latest
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:
dotnet-version: 3.1.x
# set release tag(*.*.*) to env.GIT_TAG
- run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
update-packagejson:
uses: Cysharp/Actions/.github/workflows/update-packagejson.yaml@main
with:
file-path: ./src/UniTask/Assets/Plugins/UniTask/package.json
tag: ${{ github.event.inputs.tag }}
dry-run: ${{ fromJson(github.event.inputs.dry-run) }}
build-dotnet:
needs: [update-packagejson]
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- run: echo ${{ needs.update-packagejson.outputs.sha }}
- uses: actions/checkout@v3
with:
ref: ${{ needs.update-packagejson.outputs.sha }}
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
with:
dotnet-version: |
3.1.x
6.0.x
# build and pack
- run: dotnet build -c Release -p:Version=${{ env.GIT_TAG }}
- 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=${{ env.GIT_TAG }} -o ./publish
# Store artifacts.
- uses: actions/upload-artifact@v1
- uses: actions/upload-artifact@v2
with:
name: nuget
path: ./src/UniTask.NetCore/bin/Release/UniTask.${{ env.GIT_TAG }}.nupkg
path: ./publish/
build-unity:
needs: [update-packagejson]
strategy:
matrix:
unity: ["2019.3.9f1"]
include:
- unity: 2019.3.9f1
license: UNITY_2019_3
runs-on: ubuntu-latest
container:
# with linux-il2cpp. image from https://hub.docker.com/r/gableroux/unity3d/tags
image: gableroux/unity3d:${{ matrix.unity }}-linux-il2cpp
timeout-minutes: 15
steps:
- run: apt update && apt install git -y
- uses: actions/checkout@v2
- run: echo -n "$UNITY_LICENSE" >> .Unity.ulf
env:
UNITY_LICENSE: ${{ secrets[matrix.license] }}
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -manualLicenseFile .Unity.ulf || exit 0
# set release tag(*.*.*) to env.GIT_TAG
- run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
- run: echo ${{ needs.update-packagejson.outputs.sha }}
- uses: actions/checkout@v3
with:
ref: ${{ needs.update-packagejson.outputs.sha }}
# Execute scripts: Export Package
- name: Export unitypackage
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
working-directory: src/UniTask
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
- name: Build Unity (.unitypacakge)
uses: Cysharp/Actions/.github/actions/unity-builder@main
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.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.
- uses: actions/upload-artifact@v2
@@ -65,35 +85,27 @@ jobs:
path: ./src/UniTask/UniTask.${{ env.GIT_TAG }}.unitypackage
create-release:
needs: [build-dotnet, build-unity]
if: github.event.inputs.dry-run == 'false'
needs: [update-packagejson, build-dotnet, build-unity]
runs-on: ubuntu-latest
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
NUGET_XMLDOC_MODE: skip
timeout-minutes: 10
steps:
# setup dotnet for nuget push
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.x
# set release tag(*.*.*) to env.GIT_TAG
- run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
# Create Releases
- uses: actions/create-release@v1
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Ver.${{ github.ref }}
tag_name: ${{ env.GIT_TAG }}
release_name: Ver.${{ env.GIT_TAG }}
commitish: ${{ needs.update-packagejson.outputs.sha }}
draft: true
prerelease: false
# Download(All) Artifacts to current directory
- uses: actions/download-artifact@v2-preview
- uses: actions/download-artifact@v2
# Upload to NuGet
- run: dotnet nuget push "./nuget/*.nupkg" -s https://www.nuget.org/api/v2/package -k ${{ secrets.NUGET_KEY }}
- run: dotnet nuget push "./nuget/*.nupkg" --skip-duplicate -s https://www.nuget.org/api/v2/package -k ${{ secrets.NUGET_KEY }}
# Upload to Releases(unitypackage)
- uses: actions/upload-release-asset@v1
env:
@@ -103,3 +115,10 @@ jobs:
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
cleanup:
if: needs.update-packagejson.outputs.is-branch-created == 'true'
needs: [update-packagejson, build-dotnet, build-unity]
uses: Cysharp/Actions/.github/workflows/clean-packagejson-branch.yaml@main
with:
branch: ${{ needs.update-packagejson.outputs.branch-name }}

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

View File

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

2
.gitignore vendored
View File

@@ -259,3 +259,5 @@ src/UniTask/Unity.EditorCoroutines.Editor.csproj
src/UniTask/.vsconfig
src/UniTask/Logs/ApiUpdaterCheck.txt
src/UniTask/Assembly-CSharp-firstpass.csproj

419
README.md
View File

@@ -1,21 +1,21 @@
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
* All Unity AsyncOperations and Coroutine to awaitable
* PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc..) that enable to replace all coroutine operation
* Struct based `UniTask<T>` and custom AsyncMethodBuilder to achieve zero allocation
* Makes all Unity AsyncOperations and Coroutines awaitable
* 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
* 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
* TaskTracker window to prevent memory leak
* TaskTracker window to prevent memory leaks
* 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)
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)
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 -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
@@ -24,6 +24,7 @@ Advanced tips, see blog post: [Extends UnityWebRequest via async decorator patte
- [Getting started](#getting-started)
- [Basics of UniTask and AsyncOperation](#basics-of-unitask-and-asyncoperation)
- [Cancellation and Exception handling](#cancellation-and-exception-handling)
- [Timeout handling](#timeout-handling)
- [Progress](#progress)
- [PlayerLoop](#playerloop)
- [async void vs async UniTaskVoid](#async-void-vs-async-unitaskvoid)
@@ -43,7 +44,6 @@ Advanced tips, see blog post: [Extends UnityWebRequest via async decorator patte
- [API References](#api-references)
- [UPM Package](#upm-package)
- [Install via git URL](#install-via-git-url)
- [Install via OpenUPM](#install-via-openupm)
- [.NET Core](#net-core)
- [License](#license)
@@ -72,7 +72,7 @@ async UniTask<string> DemoAsync()
// .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x)));
// await frame-based operation like coroutine
// await frame-based operation like a coroutine
await UniTask.DelayFrame(100);
// replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
@@ -85,8 +85,13 @@ async UniTask<string> DemoAsync()
await UniTask.Yield();
await UniTask.NextFrame();
// replacement of WaitForEndOfFrame(same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate))
// replacement of WaitForEndOfFrame
#if UNITY_2023_1_OR_NEWER
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))
await UniTask.WaitForFixedUpdate();
@@ -97,10 +102,10 @@ async UniTask<string> DemoAsync()
// special helper of WaitUntil
await UniTask.WaitUntilValueChanged(this, x => x.isActive);
// You can await IEnumerator coroutine
// You can await IEnumerator coroutines
await FooCoroutineEnumerator();
// You can await standard task
// You can await a standard task
await Task.Run(() => 100);
// Multithreading, run on ThreadPool under this code
@@ -122,14 +127,11 @@ async UniTask<string> DemoAsync()
var task2 = GetTextAsync(UnityWebRequest.Get("http://bing.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);
// shorthand of WhenAll, tuple can await directly
var (google2, bing2, yahoo2) = await (task1, task2, task3);
// You can handle timeout easily
await GetTextAsync(UnityWebRequest.Get("http://unity.com")).Timeout(TimeSpan.FromMilliseconds(300));
// 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");
@@ -138,9 +140,9 @@ async UniTask<string> DemoAsync()
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;`.
@@ -152,13 +154,13 @@ UniTask provides three pattern of extension methods.
* .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.
> Note: AssetBundleRequest has `asset` and `allAssets`, in default await returns `asset`. If you want to get `allAssets`, you can use `AwaitForAllAssets()` method.
> 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 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.
The type of `UniTask` can use utilities like `UniTask.WhenAll`, `UniTask.WhenAny`. 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
public async UniTaskVoid LoadManyAsync()
@@ -177,7 +179,7 @@ async UniTask<Sprite> LoadAsSprite(string path)
}
```
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
public UniTask<int> WrapByUniTaskCompletionSource()
@@ -194,7 +196,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.
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.
@@ -213,13 +215,13 @@ await task;
await task; // NG, throws Exception
```
Store to the class field, you can use `UniTask.Lazy` that gurantee call multipletimes. `.Preserve()` allows for multiple calls (internally cached results). This is useful when multiple calls in a function scope.
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 multipletimes and await from many caller.
Also `UniTaskCompletionSource` can await multiple times and await from many callers.
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).
@@ -236,18 +238,66 @@ await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().WithCancellati
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
// this CancellationToken lifecycle is same as GameObject.
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();
}
}
```
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.`
Andalso `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
public async UniTask<int> FooAsync()
@@ -257,7 +307,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
public async UniTask<int> BarAsync()
@@ -267,14 +317,14 @@ public async UniTask<int> BarAsync()
var x = await FooAsync();
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;
}
}
```
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
var (isCanceled, _) = await UniTask.DelayFrame(10, cancellationToken: cts.Token).SuppressCancellationThrow();
@@ -284,11 +334,105 @@ 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.
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
---
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
var progress = Progress.Create<float>(x => Debug.Log(x));
@@ -298,9 +442,9 @@ var request = await UnityWebRequest.Get("http://google.co.jp")
.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
public class Foo : MonoBehaviour, IProgress<float>
@@ -321,7 +465,7 @@ public class Foo : MonoBehaviour, IProgress<float>
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
public enum PlayerLoopTiming
@@ -356,25 +500,27 @@ public enum PlayerLoopTiming
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`.
> `await UniTask.WaitForEndOfFrame()` is not equilavelnt to coroutine's `yield return new WaitForEndOfFrame()`. Coroutine's WaitForEndOfFrame seems to run after the PlayerLoop is done. Some methods that require coroutine's end of frame(`ScreenCapture.CaptureScreenshotAsTexture`, `CommandBuffer`, etc) does not work correctly when replace to async/await. In that case, use a coroutine.
> `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`.
`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`.
`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`.
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and run on YieldRunner. It is most lightweight and faster.
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and runs on YieldRunner. It is the most lightweight and fastest.
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`.
`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 UniTask, await directly and `WithCancellation` use native timing, `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, causes different order of Start and continuation after await. so recommend not to use `LoadSceneAsync.ToUniTask`.
In 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`.
In stacktrace, you can check where is running in playerloop.
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)
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
// AfterAssembliesLoaded is called before BeforeSceneLoad
@@ -386,9 +532,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
// Get ECS Loop.
@@ -398,7 +544,7 @@ var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
PlayerLoopHelper.Initialize(ref playerLoop);
```
You can diagnostic UniTask's player loop is ready by `PlayerLoopHelper.IsInjectedUniTaskPlayerLoop()`. And also `PlayerLoopHelper.DumpCurrentPlayerLoop` shows current all playerloop to console.
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()
@@ -408,9 +554,40 @@ void Start()
}
```
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` 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
public async UniTaskVoid FireAndForgetMethod()
@@ -425,7 +602,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
public async UniTask DoAsync()
@@ -440,7 +617,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
Action actEvent;
@@ -455,7 +632,7 @@ actEvent += UniTask.Action(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
class Sample : MonoBehaviour
@@ -469,7 +646,7 @@ class Sample : MonoBehaviour
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)
@@ -479,15 +656,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 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
---
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
// sequential
@@ -502,11 +680,11 @@ await UniTask.WhenAll(
transform.DOScale(10, 3).WithCancellation(ct));
```
DOTween support's default behaviour(`await`, `WithCancellation`, `ToUniTask`) awaits tween is killed. It works both Complete(true/false) and Kill(true/false). But if you want to tween reuse(`SetAutoKill(false)`), it does not work you expected. Or, if you want to await for another timing, the following extension methods exist in Tween, `AwaitForComplete`, `AwaitForPause`, `AwaitForPlay`, `AwaitForRewind`, `AwaitForStepComplete`.
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
---
Unity 2020.2 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
// Unity 2020.2, C# 8.0
@@ -534,7 +712,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
okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).Subscribe(_ =>
@@ -546,7 +724,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.
`UniTaskAsyncEnumerable` is the entry point like `Enumerbale`. In addition to the standard query operators, there are other generators for Unity such as `EveryUpdate`, `Timer`, `TimerFrame`, `Interval`, `IntervalFrame`, and `EveryValueChanged`. And also added additional UniTask original query operators like `Append`, `Prepend`, `DistinctUntilChanged`, `ToHashSet`, `Buffer`, `CombineLatest`, `Do`, `Never`, `ForEachAsync`, `Pairwise`, `Publish`, `Queue`, `Return`, `SkipUntil`, `TakeUntil`, `SkipUntilCanceled`, `TakeUntilCanceled`, `TakeLast`, `Subscribe`.
`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`.
@@ -558,7 +736,7 @@ SelectAwaitWithCancellation(Func<T, CancellationToken, UniTask<TR>> selector)
If you want to use the `async` method inside the func, use the `***Await` or `***AwaitWithCancellation`.
How to create async iterator, C# 8.0 supports async iterator(`async yield return`) but it only allows `IAsyncEnumerable<T>` and of course requires C# 8.0. UniTask supports `UniTaskAsyncEnumerable.Create` method to create custom async iterator.
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).
@@ -634,32 +812,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
using Cysharp.Threading.Tasks.Triggers;
async UniTaskVoid MonitorCollision()
{
await gameObject.OnCollisionEnterAsync();
Debug.Log("Collision Enter");
/* do anything */
await gameObject.OnCollisionExitAsync();
Debug.Log("Collision Exit");
}
```
Similar as uGUI event, AsyncTrigger can get by `GetAsync***Trigger` and trigger it self is UniTaskAsyncEnumerable.
```csharp
// use await multiple times, get AsyncTriggerHandler is more efficient.
using(var trigger = this.GetOnCollisionEnterAsyncHandler())
{
await OnCollisionEnterAsync();
await OnCollisionEnterAsync();
await OnCollisionEnterAsync();
}
var trigger = this.GetOnCollisionEnterAsyncHandler();
await trigger.OnCollisionEnterAsync();
await trigger.OnCollisionEnterAsync();
await trigger.OnCollisionEnterAsync();
// every moves.
await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
@@ -667,7 +826,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
var rp = new AsyncReactiveProperty<int>(99);
@@ -687,9 +846,9 @@ rp.WithoutCurrent().BindTo(this.textComponent);
await rp.WaitAsync(); // wait until next value set
// also exists ToReadOnlyReactiveProperty
// also exists ToReadOnlyAsyncReactiveProperty
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.
@@ -702,9 +861,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
// queued message in asynchronous processing
@@ -725,13 +884,13 @@ button.OnClickAsAsyncEnumerable().Subscribe(async x =>
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
public class AsyncMessageBroker<T> : IDisposable
@@ -768,7 +927,7 @@ public class AsyncMessageBroker<T> : IDisposable
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
[UnityTest]
@@ -791,33 +950,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 in a single thread (PlayerLoop), but only `UniTask.Run` and `UniTask.SwitchToThreadPool` run on a thread pool. If you use a thread pool, it won't work with WebGL and so on.
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` will be deprecated in the future (marked with an Obsolete) and only `RunOnThreadPool` will be used. Also, if you use `UniTask.Run`, consider whether you can use `UniTask.Create` or `UniTask.Void`.
`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 has some limitations.
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 same as StartCoroutine, it is used specified PlayerLoopTiming, and default's `PlayerLoopTiming.Update` is run before MonoBehaviour's Update and StartCoroutine's loop.
* 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 to convert fully compatible from coroutine to async, use `IEnumerator.ToUniTask(MonoBehaviour coroutineRunner)` overload. It executes StartCoroutine on an instance of the argument MonoBehaviour and waits for it to complete in UniTask.
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 Edtitor like Editor Coroutine. However, there are some limitations.
UniTask can run on Unity Editor like an Editor Coroutine. However, there are some limitations.
* Delay, DelayFrame is not work correctly because can not get deltaTime in editor. Return the result of the await immediately; you can use `DelayType.Realtime` to wait for the right time.
* All PlayerLoopTiming run on timing, `EditorApplication.update`.
* 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
---
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.
@@ -851,7 +1011,7 @@ Use UniTask type.
| `IAsyncDisposable` | `IUniTaskAsyncDisposable` |
| `Task.Delay` | `UniTask.Delay` |
| `Task.Yield` | `UniTask.Yield` |
| `Task.Run` | `UniTask.Run` |
| `Task.Run` | `UniTask.RunOnThreadPool` |
| `Task.WhenAll` | `UniTask.WhenAll` |
| `Task.WhenAny` | `UniTask.WhenAny` |
| `Task.CompletedTask` | `UniTask.CompletedTask` |
@@ -863,7 +1023,7 @@ Use UniTask type.
Pooling Configuration
---
UniTask is aggressively caching async promise object to achive zero allocation(technical details, see blog post [UniTask v2 — Zero Allocation async/await for Unity, with Asynchronous LINQ](https://medium.com/@neuecc/unitask-v2-zero-allocation-async-await-for-unity-with-asynchronous-linq-1aa9c96aa7dd)). In default, cache all promises but you can configure `TaskPool.SetMaxPoolSize` to your value, the value indicates cache size per type. `TaskPool.GetCacheSizeInfo` returns current cached object in pool.
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
foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
@@ -874,17 +1034,17 @@ foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
Allocation on Profiler
---
In UnityEditor profiler shows allocation of compiler generated AsyncStateMachine but it only occurs in debug(development) build. C# Compiler generate AsyncStateMachine as class on Debug build and as struct on Release build.
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.
After Unity 2020.1 supports Code Optimization option on UnityEditor(right, footer).
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, it removes AsyncStateMachine allocation. Andalso optimization option can set via `Compilation.CompilationPipeline-codeOptimization`, and `Compilation.CodeOptimization`.
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 poor implementation for performance. UniTask itself is bypass `SynchronizationContext`(and `ExecutionContext`) so does not use it but if exists in `async Task`, still used it. `UniTaskSynchronizationContext` is replacement of `UnitySynchronizationContext`, it is better for performance.
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
@@ -897,19 +1057,19 @@ public class SyncContextInjecter
}
```
This is an optional choice and is not always recommended; `UniTaskSynchronizationContext` is less performance than `async UniTask` and is not a complete UniTask replacement. It also does not guarantee full behavioral compatibility with the `UnitySynchronizationContext`.
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
---
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
---
### 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)
@@ -917,15 +1077,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`.
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#2.1.0`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0`.
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
---
@@ -933,11 +1086,11 @@ For .NET Core, use NuGet.
> PM> Install-Package [UniTask](https://www.nuget.org/packages/UniTask)
UniTask of .NET Core version is a subset of Unity UniTask, removed PlayerLoop dependent methods.
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
public class ZeroAllocAsyncAwaitInDotNetCore
@@ -970,7 +1123,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.
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
---

1134
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
# Visual Studio Version 16
VisualStudioVersion = 16.0.29613.14
# Visual Studio Version 17
VisualStudioVersion = 17.0.31606.5
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
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}"
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
Global
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}.Release|Any CPU.ActiveCfg = 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
GlobalSection(SolutionProperties) = preSolution
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

@@ -1,10 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;netstandard2.1;netstandard2.0</TargetFrameworks>
<TargetFrameworks>net6.0;net7.0;netstandard2.1;netstandard2.0</TargetFrameworks>
<AssemblyName>UniTask</AssemblyName>
<LangVersion>8.0</LangVersion>
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<!-- NuGet Packaging -->
<Id>UniTask</Id>
@@ -19,6 +21,9 @@
<RepositoryType>git</RepositoryType>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Icon.png</PackageIcon>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>opensource.snk</AssemblyOriginatorKeyFile>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup>
@@ -26,29 +31,8 @@
</ItemGroup>
<ItemGroup>
<Compile Include="..\UniTask\Assets\Plugins\UniTask\Runtime\**\*.cs"
Exclude="
..\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\UniTaskSynchronizationContext.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;
" />
<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;" />
<Compile Remove="..\UniTask\Assets\Plugins\UniTask\Runtime\_InternalVisibleTo.cs" />
</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

@@ -17,499 +17,34 @@ using System.Reactive.Concurrency;
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
{
}
class Foo
{
public async UniTask MethodFooAsync()
{
await MethodBarAsync();
}
private async UniTask MethodBarAsync()
{
Throw();
}
private void Throw()
{
throw new 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)
{
#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
var e = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Start {i}");
await writer.YieldAsync(i);
Console.WriteLine($"End {i}");
}
});
var ee = e.GetAsyncEnumerator();
while (await ee.MoveNextAsync())
{
Console.WriteLine("ForEach " + ee.Current);
}
}
static async UniTask YieldCore()
static async UniTask FooAsync(int x, CancellationToken cancellationToken = default)
{
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,473 +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();
RefNode refNode1 = new RefNode();
RefNode refNode2 = new RefNode();
Queue<Node> q1 = new Queue<Node>();
Stack<Node> s1 = new Stack<Node>();
ConcurrentQueue<Node> cq = new ConcurrentQueue<Node>();
ConcurrentStack<Node> cs = new ConcurrentStack<Node>();
static TaskPool<Node> pool;
static TaskPoolRefNode<RefNode> poolRefNode;
static TaskPoolEqualNull<Node> poolEqualNull;
static TaskPoolClass<Node> poolClass = new TaskPoolClass<Node>();
static TaskPoolWithoutSize<Node> poolWithoutSize;
static TaskPoolWithoutLock<Node> poolWithoutLock;
[Benchmark]
public void Queue()
{
q1.Enqueue(node1);
q1.Enqueue(node1);
q1.TryDequeue(out _);
q1.TryDequeue(out _);
}
[Benchmark]
public void QueueLock()
{
lock (q1) { q1.Enqueue(node1); }
lock (q1) { q1.Enqueue(node1); }
lock (q1) { q1.TryDequeue(out _); }
lock (q1) { q1.TryDequeue(out _); }
}
[Benchmark]
public void Stack()
{
s1.Push(node1);
s1.Push(node2);
s1.TryPop(out _);
s1.TryPop(out _);
}
[Benchmark]
public void StackLock()
{
lock (s1) { s1.Push(node1); }
lock (s1) { s1.Push(node2); }
lock (s1) { s1.TryPop(out _); }
lock (s1) { s1.TryPop(out _); }
}
[Benchmark]
public void ConcurrentQueue()
{
cq.Enqueue(node1);
cq.Enqueue(node1);
cq.TryDequeue(out _);
cq.TryDequeue(out _);
}
[Benchmark]
public void ConcurrentStack()
{
cs.Push(node1);
cs.Push(node2);
cs.TryPop(out _);
cs.TryPop(out _);
}
[Benchmark]
public void TaskPool()
{
pool.TryPush(node1);
pool.TryPush(node2);
pool.TryPop(out _);
pool.TryPop(out _);
}
[Benchmark]
public void TaskPoolRefNode()
{
poolRefNode.TryPush(refNode1);
poolRefNode.TryPush(refNode2);
poolRefNode.TryPop(out _);
poolRefNode.TryPop(out _);
}
[Benchmark]
public void TaskPoolEqualNull()
{
poolEqualNull.TryPush(node1);
poolEqualNull.TryPush(node2);
poolEqualNull.TryPop(out _);
poolEqualNull.TryPop(out _);
}
[Benchmark]
public void TaskPoolClass()
{
poolClass.TryPush(node1);
poolClass.TryPush(node2);
poolClass.TryPop(out _);
poolClass.TryPop(out _);
}
[Benchmark]
public void TaskPoolWithoutSize()
{
poolWithoutSize.TryPush(node1);
poolWithoutSize.TryPush(node2);
poolWithoutSize.TryPop(out _);
poolWithoutSize.TryPop(out _);
}
[Benchmark]
public void TaskPoolWithoutLock()
{
poolWithoutLock.TryPush(node1);
poolWithoutLock.TryPush(node2);
poolWithoutLock.TryPop(out _);
poolWithoutLock.TryPop(out _);
}
}
public sealed class Node : ITaskPoolNode<Node>
{
public Node NextNode { get; set; }
}
public interface ITaskPoolNode<T>
{
T NextNode { get; set; }
}
public sealed class RefNode :ITaskPoolRefNode<RefNode>
{
RefNode nextNode;
public ref RefNode NextNode => ref nextNode;
}
public interface ITaskPoolRefNode<T>
{
ref T NextNode { get; }
}
// mutable struct, don't mark readonly.
[StructLayout(LayoutKind.Auto)]
public struct TaskPoolWithoutLock<T>
where T : class, ITaskPoolNode<T>
{
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
root = v.NextNode;
v.NextNode = null;
size--;
result = v;
// Volatile.Write(ref gate, 0);
return true;
}
//Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
// Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
//return false;
}
}
[StructLayout(LayoutKind.Auto)]
public struct TaskPool<T>
where T : class, ITaskPoolNode<T>
{
int gate;
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
root = v.NextNode;
v.NextNode = null;
size--;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
return false;
}
}
[StructLayout(LayoutKind.Auto)]
public struct TaskPoolRefNode<T>
where T : class, ITaskPoolRefNode<T>
{
int gate;
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
ref var nextNode = ref v.NextNode;
root = nextNode;
nextNode = null;
size--;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
return false;
}
}
[StructLayout(LayoutKind.Auto)]
public struct TaskPoolEqualNull<T>
where T : class, ITaskPoolNode<T>
{
int gate;
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (v != null)
{
root = v.NextNode;
v.NextNode = null;
size--;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
return false;
}
}
public class TaskPoolClass<T>
where T : class, ITaskPoolNode<T>
{
int gate;
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
root = v.NextNode;
v.NextNode = null;
size--;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
return false;
}
}
[StructLayout(LayoutKind.Auto)]
public struct TaskPoolWithoutSize<T>
where T : class, ITaskPoolNode<T>
{
int gate;
T root;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
root = v.NextNode;
v.NextNode = null;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
return false;
}
}

View File

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

View File

@@ -31,7 +31,7 @@ namespace NetCoreTests
var ar = await array;
ar.Should().BeEquivalentTo(new[] { 99, 100, 100, 100, 131 });
ar.Should().Equal(new[] { 99, 100, 100, 100, 131 });
}
[Fact]
@@ -49,7 +49,7 @@ namespace NetCoreTests
var ar = await array;
ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 });
ar.Should().Equal(new[] { 100, 100, 100, 131, 191 });
}
//[Fact]
@@ -70,7 +70,7 @@ namespace NetCoreTests
// var ar = await array;
// ar.Should().BeEquivalentTo(new[] { 99, 100, 100, 100, 131 });
// ar.Should().Equal(new[] { 99, 100, 100, 100, 131 });
//}
//[Fact]
@@ -88,7 +88,7 @@ namespace NetCoreTests
// 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();
channel.Writer.TryComplete();
(await ta1).Should().BeEquivalentTo(new[] { 10, 20, 30 });
(await ta2).Should().BeEquivalentTo(new[] { 10, 20, 30 });
(await ta1).Should().Equal(new[] { 10, 20, 30 });
(await ta2).Should().Equal(new[] { 10, 20, 30 });
}
[Fact]

View File

@@ -481,7 +481,7 @@ namespace NetCoreTests.Linq
list.Add(x);
});
list.Should().BeEquivalentTo(Enumerable.Range(1, 10));
list.Should().Equal(Enumerable.Range(1, 10));
var list2 = new List<(int, int)>();
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();
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 ys = Enumerable.Range(start, count).Append(99).ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
[Fact]
@@ -50,7 +50,7 @@ namespace NetCoreTests.Linq
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Prepend(99).ToArrayAsync();
var ys = Enumerable.Range(start, count).Prepend(99).ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
[Fact]
@@ -85,7 +85,7 @@ namespace NetCoreTests.Linq
var xs = await l.ToUniTaskAsyncEnumerable().Concat(r.ToUniTaskAsyncEnumerable()).ToArrayAsync();
var ys = l.Concat(r).ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
[Fact]
@@ -119,17 +119,17 @@ namespace NetCoreTests.Linq
{
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
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 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 ys = Enumerable.Range(1, 10).DefaultIfEmpty(99).ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
// Throw
{

View File

@@ -34,11 +34,11 @@ namespace NetCoreTests.Linq
{
{
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();
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 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 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 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 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 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 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 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);
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))
{
xs[key].Should().BeEquivalentTo(ys[key]);
xs[key].Should().Equal(ys[key]);
}
}
{
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(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 ys = arr.ToLookup(x => x, x => x * 2);
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))
{
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 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 ys = Enumerable.Range(1, 100).ToList();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
{
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToListAsync();
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 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 ys = Enumerable.Empty<int>().ToHashSet();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
}
}

View File

@@ -30,7 +30,7 @@ namespace NetCoreTests.Linq
var ys = await Range(from, count).AsUniTaskAsyncEnumerable().ToArrayAsync();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
[Fact]
@@ -57,19 +57,19 @@ namespace NetCoreTests.Linq
list.Should().BeEmpty();
await e.MoveNextAsync();
list.Should().BeEquivalentTo(100);
list.Should().Equal(100);
e.Current.Should().Be(10);
await e.MoveNextAsync();
list.Should().BeEquivalentTo(100, 200);
list.Should().Equal(100, 200);
e.Current.Should().Be(20);
await e.MoveNextAsync();
list.Should().BeEquivalentTo(100, 200, 300);
list.Should().Equal(100, 200, 300);
e.Current.Should().Be(30);
(await e.MoveNextAsync()).Should().BeFalse();
list.Should().BeEquivalentTo(100, 200, 300, 400);
list.Should().Equal(100, 200, 300, 400);
}
[Fact]
@@ -144,19 +144,43 @@ namespace NetCoreTests.Linq
list.Should().BeEmpty();
await e.MoveNextAsync();
list.Should().BeEquivalentTo(100);
list.Should().Equal(100);
e.Current.Should().Be(10);
await e.MoveNextAsync();
list.Should().BeEquivalentTo(100, 200);
list.Should().Equal(100, 200);
e.Current.Should().Be(20);
await e.MoveNextAsync();
list.Should().BeEquivalentTo(100, 200, 300);
list.Should().Equal(100, 200, 300);
e.Current.Should().Be(30);
(await e.MoveNextAsync()).Should().BeFalse();
list.Should().BeEquivalentTo(100, 200, 300, 400);
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)

View File

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

View File

@@ -23,26 +23,26 @@ namespace NetCoreTests.Linq
{
var a = await src.Where(x => x % 2 == 0).ToArrayAsync();
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 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 b = await src.WhereAwait(x => UniTask.FromResult(x % 2 == 0)).ToArrayAsync();
var expected = range.Where(x => x % 2 == 0).ToArray();
a.Should().BeEquivalentTo(expected);
b.Should().BeEquivalentTo(expected);
a.Should().Equal(expected);
b.Should().Equal(expected);
}
{
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 expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
a.Should().BeEquivalentTo(expected);
b.Should().BeEquivalentTo(expected);
a.Should().Equal(expected);
b.Should().Equal(expected);
}
}
@@ -79,7 +79,7 @@ namespace NetCoreTests.Linq
var a = await data.ToUniTaskAsyncEnumerable().OfType<int>().ToArrayAsync();
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 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 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 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 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();
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();
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();
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();
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();
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();
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();
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 ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
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 ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
xs.Length.Should().Be(ys.Length);
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
}

View File

@@ -0,0 +1,137 @@
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 ys = Enumerable.Range(1, collection).Skip(skipCount).ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
[Fact]
@@ -52,7 +52,7 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipLast(skipCount).ToArrayAsync();
var ys = Enumerable.Range(1, collection).SkipLast(skipCount).ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
[Fact]
@@ -77,7 +77,7 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeLast(takeCount).ToArrayAsync();
var ys = Enumerable.Range(1, collection).TakeLast(takeCount).ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
[Fact]
@@ -103,7 +103,7 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Range(1, collection).Take(takeCount).ToArrayAsync();
var ys = Enumerable.Range(1, collection).Take(takeCount).ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
[Fact]
@@ -130,37 +130,37 @@ namespace NetCoreTests.Linq
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArrayAsync();
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 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 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 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 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 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 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 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 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 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 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 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 ys = Enumerable.Range(start, count).Reverse().ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
[Fact]
@@ -46,18 +46,18 @@ namespace NetCoreTests.Linq
{
var xs = await UniTaskAsyncEnumerable.Range(1, count).Select(x => x * x).ToArrayAsync();
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();
zs.Should().BeEquivalentTo(ys);
zs.Should().Equal(ys);
}
{
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();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
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 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 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 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 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
@@ -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 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 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 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 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
@@ -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 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 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 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 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 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 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 ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
}
[Fact]
@@ -288,7 +288,7 @@ namespace NetCoreTests.Linq
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();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
[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 ys = await AsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount, skipCount).Select(x => string.Join(",", x)).ToArrayAsync();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
[Fact]
@@ -402,7 +402,7 @@ namespace NetCoreTests.Linq
public async Task PariwiseImmediate()
{
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]
@@ -426,7 +426,7 @@ namespace NetCoreTests.Linq
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

View File

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

View File

@@ -23,7 +23,7 @@ namespace NetCoreTests.Linq
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();
{
(await array.ToUniTaskAsyncEnumerable().Distinct().ToArrayAsync()).Should().BeEquivalentTo(ys);
(await array.ToUniTaskAsyncEnumerable().Distinct(x => x).ToArrayAsync()).Should().BeEquivalentTo(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
(await array.ToUniTaskAsyncEnumerable().Distinct().ToArrayAsync()).Should().Equal(ys);
(await array.ToUniTaskAsyncEnumerable().Distinct(x => x).ToArrayAsync()).Should().Equal(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(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();
{
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged().ToArrayAsync()).Should().BeEquivalentTo(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged(x => x).ToArrayAsync()).Should().BeEquivalentTo(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged().ToArrayAsync()).Should().Equal(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged(x => x).ToArrayAsync()).Should().Equal(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(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 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 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 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 ys = array.OrderBy(x => x).ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
{
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x).ToArrayAsync();
var ys = array.OrderByDescending(x => x).ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
{
var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwait(RandomRun).ToArrayAsync();
var ys = array.OrderBy(x => x).ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
{
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(RandomRun).ToArrayAsync();
var ys = array.OrderByDescending(x => x).ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
{
var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation(RandomRun).ToArrayAsync();
var ys = array.OrderBy(x => x).ToArray();
xs.Should().BeEquivalentTo(ys);
xs.Should().Equal(ys);
}
{
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync();
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 h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync();
a.Should().BeEquivalentTo(a2);
b.Should().BeEquivalentTo(b2);
c.Should().BeEquivalentTo(c2);
d.Should().BeEquivalentTo(d2);
e.Should().BeEquivalentTo(e2);
f.Should().BeEquivalentTo(f2);
g.Should().BeEquivalentTo(g2);
h.Should().BeEquivalentTo(h2);
a.Should().Equal(a2);
b.Should().Equal(b2);
c.Should().Equal(c2);
d.Should().Equal(d2);
e.Should().Equal(e2);
f.Should().Equal(f2);
g.Should().Equal(g2);
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();
@@ -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 h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync();
a.Should().BeEquivalentTo(a2);
b.Should().BeEquivalentTo(b2);
c.Should().BeEquivalentTo(c2);
d.Should().BeEquivalentTo(d2);
e.Should().BeEquivalentTo(e2);
f.Should().BeEquivalentTo(f2);
g.Should().BeEquivalentTo(g2);
h.Should().BeEquivalentTo(h2);
a.Should().Equal(a2);
b.Should().Equal(b2);
c.Should().Equal(c2);
d.Should().Equal(d2);
e.Should().Equal(e2);
f.Should().Equal(f2);
g.Should().Equal(g2);
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();
@@ -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 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);
b.Should().BeEquivalentTo(b2);
c.Should().BeEquivalentTo(c2);
d.Should().BeEquivalentTo(d2);
e.Should().BeEquivalentTo(e2);
f.Should().BeEquivalentTo(f2);
g.Should().BeEquivalentTo(g2);
h.Should().BeEquivalentTo(h2);
a.Should().Equal(a2);
b.Should().Equal(b2);
c.Should().Equal(c2);
d.Should().Equal(d2);
e.Should().Equal(e2);
f.Should().Equal(f2);
g.Should().Equal(g2);
h.Should().Equal(h2);
}
}
}

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<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

@@ -16,7 +16,7 @@ namespace NetCoreTests
{
CancellationTokenSource cts = new CancellationTokenSource();
var v = await UniTask.Run(() => 10).WithCancellation(cts.Token);
var v = await UniTask.Run(() => 10).AttachExternalCancellation(cts.Token);
v.Should().Be(10);
}
@@ -30,7 +30,7 @@ namespace NetCoreTests
{
await Task.Delay(TimeSpan.FromSeconds(1));
return 10;
}).WithCancellation(cts.Token);
}).AttachExternalCancellation(cts.Token);
cts.Cancel();

View File

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

View File

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

View File

@@ -87,11 +87,14 @@ namespace Cysharp.Threading.Tasks
result.calledGetResult = false;
result.initialFrame = -1;
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
result.MoveNext(); // run immediately.
// run immediately.
if (result.MoveNext())
{
PlayerLoopHelper.AddAction(timing, result);
}
return result;
}
@@ -185,6 +188,7 @@ namespace Cysharp.Threading.Tasks
core.Reset();
innerEnumerator = default;
cancellationToken = default;
return pool.TryPush(this);
}
@@ -243,7 +247,7 @@ namespace Cysharp.Threading.Tasks
{
goto WARN;
}
continue;
WARN:

View File

@@ -16,7 +16,12 @@
"name": "com.unity.addressables",
"expression": "",
"define": "UNITASK_ADDRESSABLE_SUPPORT"
},
{
"name": "com.unity.addressables.cn",
"expression": "",
"define": "UNITASK_ADDRESSABLE_SUPPORT"
}
],
"noEngineReferences": false
}
}

View File

@@ -16,14 +16,14 @@ namespace Cysharp.Threading.Tasks
Kill,
KillWithCompleteCallback,
Complete,
CompleteWithSeqeunceCallback,
CompleteWithSequenceCallback,
CancelAwait,
// AndCancelAwait
KillAndCancelAwait,
KillWithCompleteCallbackAndCancelAwait,
CompleteAndCancelAwait,
CompleteWithSeqeunceCallbackAndCancelAwait
CompleteWithSequenceCallbackAndCancelAwait
}
public static class DOTweenAsyncExtensions
@@ -143,24 +143,21 @@ namespace Cysharp.Threading.Tasks
TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
}
static readonly TweenCallback EmptyTweenCallback = () => { };
readonly TweenCallback onCompleteCallbackDelegate;
readonly TweenCallback onUpdateDelegate;
Tween tween;
TweenCancelBehaviour cancelBehaviour;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationRegistration;
CallbackType callbackType;
bool canceled;
TweenCallback originalUpdateAction;
TweenCallback originalCompleteAction;
UniTaskCompletionSourceCore<AsyncUnit> core;
TweenConfiguredSource()
{
onCompleteCallbackDelegate = OnCompleteCallbackDelegate;
onUpdateDelegate = OnUpdate;
}
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, CallbackType callbackType, out short token)
@@ -180,41 +177,87 @@ namespace Cysharp.Threading.Tasks
result.cancelBehaviour = cancelBehaviour;
result.cancellationToken = cancellationToken;
result.callbackType = callbackType;
result.originalUpdateAction = tween.onUpdate;
result.canceled = false;
if (result.originalUpdateAction == result.onUpdateDelegate)
{
result.originalUpdateAction = null;
}
tween.onUpdate = result.onUpdateDelegate;
switch (callbackType)
{
case CallbackType.Kill:
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;
}
if (result.originalCompleteAction == result.onCompleteCallbackDelegate)
{
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);
token = result.core.Version;
@@ -228,7 +271,7 @@ namespace Cysharp.Threading.Tasks
if (this.cancelBehaviour == TweenCancelBehaviour.KillAndCancelAwait
|| this.cancelBehaviour == TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteAndCancelAwait
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait
|| this.cancelBehaviour == TweenCancelBehaviour.CancelAwait)
{
canceled = true;
@@ -240,81 +283,11 @@ namespace Cysharp.Threading.Tasks
}
else
{
originalCompleteAction?.Invoke();
core.TrySetResult(AsyncUnit.Default);
}
}
void OnUpdate()
{
originalUpdateAction?.Invoke();
if (!cancellationToken.IsCancellationRequested)
{
return;
}
switch (this.cancelBehaviour)
{
case TweenCancelBehaviour.Kill:
default:
this.tween.Kill(false);
break;
case TweenCancelBehaviour.KillAndCancelAwait:
this.canceled = true;
this.tween.Kill(false);
break;
case TweenCancelBehaviour.KillWithCompleteCallback:
this.tween.Kill(true);
break;
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
this.canceled = true;
this.tween.Kill(true);
break;
case TweenCancelBehaviour.Complete:
this.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteAndCancelAwait:
this.canceled = true;
this.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
this.tween.Complete(true);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
this.canceled = true;
this.tween.Complete(true);
break;
case TweenCancelBehaviour.CancelAwait:
// replace to empty(avoid callback after Canceled(instance is returned to pool.)
switch (callbackType)
{
case CallbackType.Kill:
tween.onKill = EmptyTweenCallback;
break;
case CallbackType.Complete:
tween.onComplete = EmptyTweenCallback;
break;
case CallbackType.Pause:
tween.onPause = EmptyTweenCallback;
break;
case CallbackType.Play:
tween.onPlay = EmptyTweenCallback;
break;
case CallbackType.Rewind:
tween.onRewind = EmptyTweenCallback;
break;
case CallbackType.StepComplete:
tween.onStepComplete = EmptyTweenCallback;
break;
default:
break;
}
this.core.TrySetCanceled(this.cancellationToken);
break;
}
}
static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour)
{
@@ -339,10 +312,10 @@ namespace Cysharp.Threading.Tasks
case TweenCancelBehaviour.CompleteAndCancelAwait:
tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
case TweenCancelBehaviour.CompleteWithSequenceCallback:
tween.Complete(true);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait:
tween.Complete(true);
break;
case TweenCancelBehaviour.CancelAwait:
@@ -381,36 +354,41 @@ namespace Cysharp.Threading.Tasks
{
TaskTracker.RemoveTracking(this);
core.Reset();
tween.onUpdate = originalUpdateAction;
cancellationRegistration.Dispose();
RestoreOriginalCallback();
tween = default;
cancellationToken = default;
originalCompleteAction = default;
return pool.TryPush(this);
}
void RestoreOriginalCallback()
{
switch (callbackType)
{
case CallbackType.Kill:
tween.onKill = null;
tween.onKill = originalCompleteAction;
break;
case CallbackType.Complete:
tween.onComplete = null;
tween.onComplete = originalCompleteAction;
break;
case CallbackType.Pause:
tween.onPause = null;
tween.onPause = originalCompleteAction;
break;
case CallbackType.Play:
tween.onPlay = null;
tween.onPlay = originalCompleteAction;
break;
case CallbackType.Rewind:
tween.onRewind = null;
tween.onRewind = originalCompleteAction;
break;
case CallbackType.StepComplete:
tween.onStepComplete = null;
tween.onStepComplete = originalCompleteAction;
break;
default:
break;
}
tween = default;
cancellationToken = default;
originalUpdateAction = default;
return pool.TryPush(this);
}
}
}

View File

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

View File

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

View File

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

View File

@@ -17,7 +17,7 @@ namespace Cysharp.Threading.Tasks.Linq
{
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 (state == State.RequireAppend)
if (state == State.RequirePrepend)
{
Current = element;
state = State.None;

View File

@@ -52,6 +52,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync()
{
TaskTracker.RemoveTracking(this);
writer.Dispose();
return default;
}
@@ -127,7 +128,7 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
sealed class AsyncWriter : IUniTaskSource, IAsyncWriter<T>
sealed class AsyncWriter : IUniTaskSource, IAsyncWriter<T>, IDisposable
{
readonly _Create enumerator;
@@ -137,6 +138,15 @@ namespace Cysharp.Threading.Tasks.Linq
{
this.enumerator = enumerator;
}
public void Dispose()
{
var status = core.GetStatus(core.Version);
if (status == UniTaskStatus.Pending)
{
core.TrySetCanceled();
}
}
public void GetResult(short token)
{

View File

@@ -0,0 +1,232 @@
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 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

@@ -98,6 +98,79 @@ namespace Cysharp.Threading.Tasks
#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
{
bool MoveNext();
@@ -105,7 +178,10 @@ namespace Cysharp.Threading.Tasks
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;
internal static string ApplicationDataPath => applicationDataPath;
@@ -113,13 +189,14 @@ namespace Cysharp.Threading.Tasks
static int mainThreadId;
static string applicationDataPath;
static SynchronizationContext unitySynchronizationContetext;
static SynchronizationContext unitySynchronizationContext;
static ContinuationQueue[] yielders;
static PlayerLoopRunner[] runners;
internal static bool IsEditorApplicationQuitting { get; private set; }
static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem,
Type loopRunnerYieldType, ContinuationQueue cq, Type lastLoopRunnerYieldType, ContinuationQueue lastCq,
Type loopRunnerType, PlayerLoopRunner runner, Type lastLoopRunnerType, PlayerLoopRunner lastRunner)
bool injectOnFirst,
Type loopRunnerYieldType, ContinuationQueue cq,
Type loopRunnerType, PlayerLoopRunner runner)
{
#if UNITY_EDITOR
@@ -134,22 +211,11 @@ namespace Cysharp.Threading.Tasks
runner.Run();
runner.Clear();
}
if (lastRunner != null)
{
lastRunner.Run();
lastRunner.Clear();
}
if (cq != null)
{
cq.Run();
cq.Clear();
}
if (lastCq != null)
{
lastCq.Run();
lastCq.Clear();
}
IsEditorApplicationQuitting = false;
}
};
@@ -161,40 +227,38 @@ namespace Cysharp.Threading.Tasks
updateDelegate = cq.Run
};
var lastYieldLoop = new PlayerLoopSystem
{
type = lastLoopRunnerYieldType,
updateDelegate = lastCq.Run
};
var runnerLoop = new PlayerLoopSystem
{
type = loopRunnerType,
updateDelegate = runner.Run
};
var lastRunnerLoop = new PlayerLoopSystem
{
type = lastLoopRunnerType,
updateDelegate = lastRunner.Run
};
// Remove items from previous initializations.
var source = loopSystem.subSystemList
.Where(ls => ls.type != loopRunnerYieldType && ls.type != loopRunnerType && ls.type != lastLoopRunnerYieldType && ls.type != lastLoopRunnerType)
.ToArray();
var source = RemoveRunner(loopSystem, loopRunnerYieldType, loopRunnerType);
var dest = new PlayerLoopSystem[source.Length + 2];
var dest = new PlayerLoopSystem[source.Length + 4];
Array.Copy(source, 0, dest, 2, source.Length);
dest[0] = yieldLoop;
dest[1] = runnerLoop;
dest[dest.Length - 2] = lastYieldLoop;
dest[dest.Length - 1] = lastRunnerLoop;
Array.Copy(source, 0, dest, injectOnFirst ? 2 : 0, source.Length);
if (injectOnFirst)
{
dest[0] = yieldLoop;
dest[1] = runnerLoop;
}
else
{
dest[dest.Length - 2] = yieldLoop;
dest[dest.Length - 1] = runnerLoop;
}
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
@@ -221,11 +285,15 @@ namespace Cysharp.Threading.Tasks
return dest.ToArray();
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
#if UNITY_2020_1_OR_NEWER
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
#else
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
#endif
static void Init()
{
// capture default(unity) sync-context.
unitySynchronizationContetext = SynchronizationContext.Current;
unitySynchronizationContext = SynchronizationContext.Current;
mainThreadId = Thread.CurrentThread.ManagedThreadId;
try
{
@@ -311,7 +379,23 @@ namespace Cysharp.Threading.Tasks
throw new Exception("Target PlayerLoopSystem does not found. Type:" + systemType.FullName);
}
public static void Initialize(ref PlayerLoopSystem playerLoop)
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];
@@ -323,58 +407,82 @@ namespace Cysharp.Threading.Tasks
var copyList = playerLoop.subSystemList.ToArray();
var i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Initialization));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), yielders[0] = new ContinuationQueue(PlayerLoopTiming.Initialization),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldInitialization), yielders[1] = new ContinuationQueue(PlayerLoopTiming.LastInitialization),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), runners[0] = new PlayerLoopRunner(PlayerLoopTiming.Initialization),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastInitialization), runners[1] = new PlayerLoopRunner(PlayerLoopTiming.LastInitialization));
// Initialization
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Initialization),
InjectPlayerLoopTimings.Initialization, 0, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), PlayerLoopTiming.Initialization);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Initialization),
InjectPlayerLoopTimings.LastInitialization, 1, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastInitialization), PlayerLoopTiming.LastInitialization);
// EarlyUpdate
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.EarlyUpdate));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), yielders[2] = new ContinuationQueue(PlayerLoopTiming.EarlyUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldEarlyUpdate), yielders[3] = new ContinuationQueue(PlayerLoopTiming.LastEarlyUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerEarlyUpdate), runners[2] = new PlayerLoopRunner(PlayerLoopTiming.EarlyUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastEarlyUpdate), runners[3] = new PlayerLoopRunner(PlayerLoopTiming.LastEarlyUpdate));
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.EarlyUpdate),
InjectPlayerLoopTimings.EarlyUpdate, 2, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerEarlyUpdate), PlayerLoopTiming.EarlyUpdate);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.EarlyUpdate),
InjectPlayerLoopTimings.LastEarlyUpdate, 3, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldEarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastEarlyUpdate), PlayerLoopTiming.LastEarlyUpdate);
// FixedUpdate
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.FixedUpdate));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), yielders[4] = new ContinuationQueue(PlayerLoopTiming.FixedUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldFixedUpdate), yielders[5] = new ContinuationQueue(PlayerLoopTiming.LastFixedUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerFixedUpdate), runners[4] = new PlayerLoopRunner(PlayerLoopTiming.FixedUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastFixedUpdate), runners[5] = new PlayerLoopRunner(PlayerLoopTiming.LastFixedUpdate));
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.FixedUpdate),
InjectPlayerLoopTimings.FixedUpdate, 4, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerFixedUpdate), PlayerLoopTiming.FixedUpdate);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.FixedUpdate),
InjectPlayerLoopTimings.LastFixedUpdate, 5, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldFixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastFixedUpdate), PlayerLoopTiming.LastFixedUpdate);
// PreUpdate
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.PreUpdate));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), yielders[6] = new ContinuationQueue(PlayerLoopTiming.PreUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreUpdate), yielders[7] = new ContinuationQueue(PlayerLoopTiming.LastPreUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreUpdate), runners[6] = new PlayerLoopRunner(PlayerLoopTiming.PreUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreUpdate), runners[7] = new PlayerLoopRunner(PlayerLoopTiming.LastPreUpdate));
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreUpdate),
InjectPlayerLoopTimings.PreUpdate, 6, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreUpdate), PlayerLoopTiming.PreUpdate);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreUpdate),
InjectPlayerLoopTimings.LastPreUpdate, 7, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreUpdate), PlayerLoopTiming.LastPreUpdate);
// Update
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Update));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), yielders[8] = new ContinuationQueue(PlayerLoopTiming.Update),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldUpdate), yielders[9] = new ContinuationQueue(PlayerLoopTiming.LastUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerUpdate), runners[8] = new PlayerLoopRunner(PlayerLoopTiming.Update),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastUpdate), runners[9] = new PlayerLoopRunner(PlayerLoopTiming.LastUpdate));
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Update),
InjectPlayerLoopTimings.Update, 8, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerUpdate), PlayerLoopTiming.Update);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Update),
InjectPlayerLoopTimings.LastUpdate, 9, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastUpdate), PlayerLoopTiming.LastUpdate);
// PreLateUpdate
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.PreLateUpdate));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), yielders[10] = new ContinuationQueue(PlayerLoopTiming.PreLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreLateUpdate), yielders[11] = new ContinuationQueue(PlayerLoopTiming.LastPreLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreLateUpdate), runners[10] = new PlayerLoopRunner(PlayerLoopTiming.PreLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreLateUpdate), runners[11] = new PlayerLoopRunner(PlayerLoopTiming.LastPreLateUpdate));
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreLateUpdate),
InjectPlayerLoopTimings.PreLateUpdate, 10, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreLateUpdate), PlayerLoopTiming.PreLateUpdate);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreLateUpdate),
InjectPlayerLoopTimings.LastPreLateUpdate, 11, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreLateUpdate), PlayerLoopTiming.LastPreLateUpdate);
// PostLateUpdate
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.PostLateUpdate));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), yielders[12] = new ContinuationQueue(PlayerLoopTiming.PostLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPostLateUpdate), yielders[13] = new ContinuationQueue(PlayerLoopTiming.LastPostLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), runners[12] = new PlayerLoopRunner(PlayerLoopTiming.PostLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPostLateUpdate), runners[13] = new PlayerLoopRunner(PlayerLoopTiming.LastPostLateUpdate));
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PostLateUpdate),
InjectPlayerLoopTimings.PostLateUpdate, 12, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), PlayerLoopTiming.PostLateUpdate);
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
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.TimeUpdate));
copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldTimeUpdate), yielders[14] = new ContinuationQueue(PlayerLoopTiming.TimeUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldTimeUpdate), yielders[15] = new ContinuationQueue(PlayerLoopTiming.LastTimeUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerTimeUpdate), runners[14] = new PlayerLoopRunner(PlayerLoopTiming.TimeUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastTimeUpdate), runners[15] = new PlayerLoopRunner(PlayerLoopTiming.LastTimeUpdate));
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
i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Update));
var i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Update));
copyList[i].subSystemList = InsertUniTaskSynchronizationContext(copyList[i]);
playerLoop.subSystemList = copyList;
@@ -383,12 +491,27 @@ namespace Cysharp.Threading.Tasks
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)
{
yielders[(int)timing].Enqueue(continuation);
var q = yielders[(int)timing];
if (q == null)
{
ThrowInvalidLoopTiming(timing);
}
q.Enqueue(continuation);
}
// Diagnostics helper
@@ -405,6 +528,14 @@ namespace Cysharp.Threading.Tasks
{
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);
@@ -426,6 +557,11 @@ namespace Cysharp.Threading.Tasks
foreach (var header in playerLoop.subSystemList)
{
if (header.subSystemList is null)
{
continue;
}
foreach (var subSystem in header.subSystemList)
{
if (subSystem.type == typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization))

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

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

View File

@@ -7,7 +7,7 @@ using System.Threading;
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
{

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

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

View File

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

View File

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

View File

@@ -8,6 +8,16 @@ namespace Cysharp.Threading.Tasks
{
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>
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>
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;
}
}

View File

@@ -2,7 +2,9 @@
using System.Threading;
using UnityEngine;
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
using UnityEngine.EventSystems;
#endif
namespace Cysharp.Threading.Tasks.Triggers
{
@@ -715,6 +717,7 @@ namespace Cysharp.Threading.Tasks.Triggers
#endregion
#region CollisionEnter
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnCollisionEnterHandler
{
@@ -771,9 +774,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnCollisionEnterHandler)new AsyncTriggerHandler<Collision>(this, cancellationToken, true)).OnCollisionEnterAsync();
}
}
#endif
#endregion
#region CollisionEnter2D
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
public interface IAsyncOnCollisionEnter2DHandler
{
@@ -830,9 +835,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnCollisionEnter2DHandler)new AsyncTriggerHandler<Collision2D>(this, cancellationToken, true)).OnCollisionEnter2DAsync();
}
}
#endif
#endregion
#region CollisionExit
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnCollisionExitHandler
{
@@ -889,9 +896,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnCollisionExitHandler)new AsyncTriggerHandler<Collision>(this, cancellationToken, true)).OnCollisionExitAsync();
}
}
#endif
#endregion
#region CollisionExit2D
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
public interface IAsyncOnCollisionExit2DHandler
{
@@ -948,9 +957,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnCollisionExit2DHandler)new AsyncTriggerHandler<Collision2D>(this, cancellationToken, true)).OnCollisionExit2DAsync();
}
}
#endif
#endregion
#region CollisionStay
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnCollisionStayHandler
{
@@ -1007,9 +1018,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnCollisionStayHandler)new AsyncTriggerHandler<Collision>(this, cancellationToken, true)).OnCollisionStayAsync();
}
}
#endif
#endregion
#region CollisionStay2D
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
public interface IAsyncOnCollisionStay2DHandler
{
@@ -1066,9 +1079,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnCollisionStay2DHandler)new AsyncTriggerHandler<Collision2D>(this, cancellationToken, true)).OnCollisionStay2DAsync();
}
}
#endif
#endregion
#region ControllerColliderHit
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnControllerColliderHitHandler
{
@@ -1125,6 +1140,7 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnControllerColliderHitHandler)new AsyncTriggerHandler<ControllerColliderHit>(this, cancellationToken, true)).OnControllerColliderHitAsync();
}
}
#endif
#endregion
#region Disable
@@ -1423,6 +1439,7 @@ namespace Cysharp.Threading.Tasks.Triggers
#endregion
#region JointBreak
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnJointBreakHandler
{
@@ -1479,9 +1496,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnJointBreakHandler)new AsyncTriggerHandler<float>(this, cancellationToken, true)).OnJointBreakAsync();
}
}
#endif
#endregion
#region JointBreak2D
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
public interface IAsyncOnJointBreak2DHandler
{
@@ -1538,6 +1557,7 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnJointBreak2DHandler)new AsyncTriggerHandler<Joint2D>(this, cancellationToken, true)).OnJointBreak2DAsync();
}
}
#endif
#endregion
#region MouseDown
@@ -2145,7 +2165,7 @@ namespace Cysharp.Threading.Tasks.Triggers
#endregion
#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
{
@@ -2796,6 +2816,7 @@ namespace Cysharp.Threading.Tasks.Triggers
#endregion
#region TriggerEnter
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnTriggerEnterHandler
{
@@ -2852,9 +2873,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnTriggerEnterHandler)new AsyncTriggerHandler<Collider>(this, cancellationToken, true)).OnTriggerEnterAsync();
}
}
#endif
#endregion
#region TriggerEnter2D
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
public interface IAsyncOnTriggerEnter2DHandler
{
@@ -2911,9 +2934,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnTriggerEnter2DHandler)new AsyncTriggerHandler<Collider2D>(this, cancellationToken, true)).OnTriggerEnter2DAsync();
}
}
#endif
#endregion
#region TriggerExit
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnTriggerExitHandler
{
@@ -2970,9 +2995,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnTriggerExitHandler)new AsyncTriggerHandler<Collider>(this, cancellationToken, true)).OnTriggerExitAsync();
}
}
#endif
#endregion
#region TriggerExit2D
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
public interface IAsyncOnTriggerExit2DHandler
{
@@ -3029,9 +3056,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnTriggerExit2DHandler)new AsyncTriggerHandler<Collider2D>(this, cancellationToken, true)).OnTriggerExit2DAsync();
}
}
#endif
#endregion
#region TriggerStay
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
public interface IAsyncOnTriggerStayHandler
{
@@ -3088,9 +3117,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnTriggerStayHandler)new AsyncTriggerHandler<Collider>(this, cancellationToken, true)).OnTriggerStayAsync();
}
}
#endif
#endregion
#region TriggerStay2D
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
public interface IAsyncOnTriggerStay2DHandler
{
@@ -3147,6 +3178,7 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnTriggerStay2DHandler)new AsyncTriggerHandler<Collider2D>(this, cancellationToken, true)).OnTriggerStay2DAsync();
}
}
#endif
#endregion
#region Validate
@@ -3386,6 +3418,7 @@ namespace Cysharp.Threading.Tasks.Triggers
#endregion
#region BeginDrag
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnBeginDragHandler
{
@@ -3442,9 +3475,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnBeginDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnBeginDragAsync();
}
}
#endif
#endregion
#region Cancel
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnCancelHandler
{
@@ -3501,9 +3536,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnCancelHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnCancelAsync();
}
}
#endif
#endregion
#region Deselect
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnDeselectHandler
{
@@ -3560,9 +3597,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnDeselectHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnDeselectAsync();
}
}
#endif
#endregion
#region Drag
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnDragHandler
{
@@ -3619,9 +3658,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnDragAsync();
}
}
#endif
#endregion
#region Drop
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnDropHandler
{
@@ -3678,9 +3719,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnDropHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnDropAsync();
}
}
#endif
#endregion
#region EndDrag
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnEndDragHandler
{
@@ -3737,9 +3780,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnEndDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnEndDragAsync();
}
}
#endif
#endregion
#region InitializePotentialDrag
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnInitializePotentialDragHandler
{
@@ -3796,9 +3841,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnInitializePotentialDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnInitializePotentialDragAsync();
}
}
#endif
#endregion
#region Move
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnMoveHandler
{
@@ -3855,9 +3902,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnMoveHandler)new AsyncTriggerHandler<AxisEventData>(this, cancellationToken, true)).OnMoveAsync();
}
}
#endif
#endregion
#region PointerClick
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnPointerClickHandler
{
@@ -3914,9 +3963,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnPointerClickHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerClickAsync();
}
}
#endif
#endregion
#region PointerDown
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnPointerDownHandler
{
@@ -3973,9 +4024,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnPointerDownHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerDownAsync();
}
}
#endif
#endregion
#region PointerEnter
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnPointerEnterHandler
{
@@ -4032,9 +4085,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnPointerEnterHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerEnterAsync();
}
}
#endif
#endregion
#region PointerExit
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnPointerExitHandler
{
@@ -4091,9 +4146,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnPointerExitHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerExitAsync();
}
}
#endif
#endregion
#region PointerUp
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnPointerUpHandler
{
@@ -4150,9 +4207,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnPointerUpHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerUpAsync();
}
}
#endif
#endregion
#region Scroll
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnScrollHandler
{
@@ -4209,9 +4268,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnScrollHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnScrollAsync();
}
}
#endif
#endregion
#region Select
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnSelectHandler
{
@@ -4268,9 +4329,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnSelectHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnSelectAsync();
}
}
#endif
#endregion
#region Submit
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnSubmitHandler
{
@@ -4327,9 +4390,11 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnSubmitHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnSubmitAsync();
}
}
#endif
#endregion
#region UpdateSelected
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
public interface IAsyncOnUpdateSelectedHandler
{
@@ -4386,6 +4451,7 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((IAsyncOnUpdateSelectedHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnUpdateSelectedAsync();
}
}
#endif
#endregion
}

View File

@@ -106,24 +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> 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
using System.Threading;
using UnityEngine;
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
using UnityEngine.EventSystems;
#endif
namespace Cysharp.Threading.Tasks.Triggers
{
<# foreach(var t in triggers) { #>
#region <#= t.triggerName #>
<# if(Is2019_3(t.triggerName)) { #>
#if UNITY_2019_3_OR_NEWER
<# if(IsUguiSystem(t.handlerInterface)) { #>
#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) #>
@@ -181,7 +193,13 @@ namespace Cysharp.Threading.Tasks.Triggers
return ((<#= ToInterfaceName(t.methodName) #>)new AsyncTriggerHandler<<#= t.returnType #>>(this, cancellationToken, true)).<#= t.methodName #>Async();
}
}
<# if(Is2019_3(t.triggerName) || IsMouseTrigger(t.triggerName)) { #>
<# if(IsUguiSystem(t.handlerInterface)) { #>
#endif
<# } #>
<# if(RequirePhysicsModule(t.triggerName) != null) { #>
#endif
<# } #>
<# if(IsParticleSystem(t.triggerName) || IsMouseTrigger(t.triggerName)) { #>
#endif
<# } #>
#endregion

View File

@@ -2,6 +2,7 @@
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Collections;
using System.Runtime.CompilerServices;
using System.Threading;
using UnityEngine;
@@ -20,12 +21,23 @@ namespace Cysharp.Threading.Tasks
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
return new YieldAwaitable(timing);
}
public static UniTask Yield(CancellationToken cancellationToken)
{
return new UniTask(YieldPromise.Create(PlayerLoopTiming.Update, cancellationToken, out var token), token);
}
public static UniTask Yield(PlayerLoopTiming timing, CancellationToken cancellationToken)
{
return new UniTask(YieldPromise.Create(timing, cancellationToken, out var token), token);
@@ -34,44 +46,89 @@ namespace Cysharp.Threading.Tasks
/// <summary>
/// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary>
public static UniTask NextFrame(PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default)
public static UniTask NextFrame()
{
return new UniTask(NextFramePromise.Create(PlayerLoopTiming.Update, CancellationToken.None, out var token), token);
}
/// <summary>
/// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary>
public static UniTask NextFrame(PlayerLoopTiming timing)
{
return new UniTask(NextFramePromise.Create(timing, CancellationToken.None, out var token), token);
}
/// <summary>
/// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary>
public static UniTask NextFrame(CancellationToken cancellationToken)
{
return new UniTask(NextFramePromise.Create(PlayerLoopTiming.Update, cancellationToken, out var token), token);
}
/// <summary>
/// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary>
public static UniTask NextFrame(PlayerLoopTiming timing, CancellationToken cancellationToken)
{
return new UniTask(NextFramePromise.Create(timing, cancellationToken, out var token), token);
}
/// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate).
/// </summary>
#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()
{
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
}
/// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken).
/// </summary>
[Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")]
public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken)
{
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken);
}
#endif
public static UniTask WaitForEndOfFrame(MonoBehaviour coroutineRunner, CancellationToken cancellationToken = default)
{
var source = WaitForEndOfFramePromise.Create(coroutineRunner, cancellationToken, out var token);
return new UniTask(source, token);
}
/// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate).
/// Same as UniTask.Yield(PlayerLoopTiming.LastFixedUpdate).
/// </summary>
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>
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken).
/// Same as UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken).
/// </summary>
public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken)
{
return UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken);
return UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken);
}
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))
{
return Delay(Mathf.RoundToInt(1000 * duration), ignoreTimeScale, delayTiming, cancellationToken);
}
public static UniTask WaitForSeconds(int duration, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
return Delay(1000 * duration, ignoreTimeScale, delayTiming, cancellationToken);
}
public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
if (delayFrameCount < 0)
{
@@ -106,6 +163,14 @@ namespace Cysharp.Threading.Tasks
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)
{
case DelayType.UnscaledDeltaTime:
@@ -308,6 +373,113 @@ namespace Cysharp.Threading.Tasks
}
}
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);
}
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<object> core;
WaitForEndOfFramePromise()
{
}
public static IUniTaskSource Create(MonoBehaviour coroutineRunner, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new WaitForEndOfFramePromise();
}
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
coroutineRunner.StartCoroutine(result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
TryReturn();
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
Reset(); // Reset Enumerator
cancellationToken = default;
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>
{
static TaskPool<DelayFramePromise> pool;
@@ -400,7 +572,19 @@ namespace Cysharp.Threading.Tasks
// skip in initial frame.
if (initialFrame == Time.frameCount)
{
#if UNITY_EDITOR
// force use Realtime.
if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
{
//goto ++currentFrameCount
}
else
{
return true;
}
#else
return true;
#endif
}
}

View File

@@ -194,6 +194,7 @@ namespace Cysharp.Threading.Tasks
sealed class ExceptionResultSource : IUniTaskSource
{
readonly ExceptionDispatchInfo exception;
bool calledGet;
public ExceptionResultSource(Exception exception)
{
@@ -202,6 +203,11 @@ namespace Cysharp.Threading.Tasks
public void GetResult(short token)
{
if (!calledGet)
{
calledGet = true;
GC.SuppressFinalize(this);
}
exception.Throw();
}
@@ -219,11 +225,20 @@ namespace Cysharp.Threading.Tasks
{
continuation(state);
}
~ExceptionResultSource()
{
if (!calledGet)
{
UniTaskScheduler.PublishUnobservedTaskException(exception.SourceException);
}
}
}
sealed class ExceptionResultSource<T> : IUniTaskSource<T>
{
readonly ExceptionDispatchInfo exception;
bool calledGet;
public ExceptionResultSource(Exception exception)
{
@@ -232,12 +247,22 @@ namespace Cysharp.Threading.Tasks
public T GetResult(short token)
{
if (!calledGet)
{
calledGet = true;
GC.SuppressFinalize(this);
}
exception.Throw();
return default;
}
void IUniTaskSource.GetResult(short token)
{
if (!calledGet)
{
calledGet = true;
GC.SuppressFinalize(this);
}
exception.Throw();
}
@@ -255,6 +280,14 @@ namespace Cysharp.Threading.Tasks
{
continuation(state);
}
~ExceptionResultSource()
{
if (!calledGet)
{
UniTaskScheduler.PublishUnobservedTaskException(exception.SourceException);
}
}
}
sealed class CanceledResultSource : IUniTaskSource

View File

@@ -9,237 +9,56 @@ namespace Cysharp.Threading.Tasks
{
#region OBSOLETE_RUN
// Run is a confusing name, use only RunOnThreadPool in the future.
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask Run(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
[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)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
action();
}
finally
{
await UniTask.Yield();
}
}
else
{
action();
}
cancellationToken.ThrowIfCancellationRequested();
return RunOnThreadPool(action, configureAwait, cancellationToken);
}
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask Run(Action<object> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
[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)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
action(state);
}
finally
{
await UniTask.Yield();
}
}
else
{
action(state);
}
cancellationToken.ThrowIfCancellationRequested();
return RunOnThreadPool(action, state, configureAwait, cancellationToken);
}
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask Run(Func<UniTask> action, bool configureAwait = true, CancellationToken cancellationToken = default)
[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)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
await action();
}
finally
{
await UniTask.Yield();
}
}
else
{
await action();
}
cancellationToken.ThrowIfCancellationRequested();
return RunOnThreadPool(action, configureAwait, cancellationToken);
}
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask Run(Func<object, UniTask> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
[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)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
await action(state);
}
finally
{
await UniTask.Yield();
}
}
else
{
await action(state);
}
cancellationToken.ThrowIfCancellationRequested();
return RunOnThreadPool(action, state, configureAwait, cancellationToken);
}
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask<T> Run<T>(Func<T> func, bool configureAwait = true, CancellationToken cancellationToken = default)
[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)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return func();
}
finally
{
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
return func();
}
return RunOnThreadPool(func, configureAwait, cancellationToken);
}
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask<T> Run<T>(Func<UniTask<T>> func, bool configureAwait = true, CancellationToken cancellationToken = default)
[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)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return await func();
}
finally
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
var result = await func();
cancellationToken.ThrowIfCancellationRequested();
return result;
}
return RunOnThreadPool(func, configureAwait, cancellationToken);
}
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
[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)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return func(state);
}
finally
{
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
return func(state);
}
return RunOnThreadPool(func, state, configureAwait, cancellationToken);
}
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask<T> Run<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
[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)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return await func(state);
}
finally
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
var result = await func(state);
cancellationToken.ThrowIfCancellationRequested();
return result;
}
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)
{

View File

@@ -1,7 +1,7 @@
{
"name": "UniTask",
"references": [
],
"rootNamespace": "",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
@@ -14,6 +14,31 @@
"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

View File

@@ -137,8 +137,8 @@ namespace Cysharp.Threading.Tasks
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
{
continuation(continuationState);
return true;
}
return true;
}
return false;
@@ -165,8 +165,8 @@ namespace Cysharp.Threading.Tasks
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
{
continuation(continuationState);
return true;
}
return true;
}
return false;
@@ -184,8 +184,8 @@ namespace Cysharp.Threading.Tasks
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
{
continuation(continuationState);
return true;
}
return true;
}
return false;
@@ -328,6 +328,7 @@ namespace Cysharp.Threading.Tasks
}
UniTaskCompletionSourceCore<AsyncUnit> core;
short version;
AutoResetUniTaskCompletionSource()
{
@@ -340,6 +341,7 @@ namespace Cysharp.Threading.Tasks
{
result = new AutoResetUniTaskCompletionSource();
}
result.version = result.core.Version;
TaskTracker.TrackActiveTask(result, 2);
return result;
}
@@ -383,19 +385,19 @@ namespace Cysharp.Threading.Tasks
[DebuggerHidden]
public bool TrySetResult()
{
return core.TrySetResult(AsyncUnit.Default);
return version == core.Version && core.TrySetResult(AsyncUnit.Default);
}
[DebuggerHidden]
public bool TrySetCanceled(CancellationToken cancellationToken = default)
{
return core.TrySetCanceled(cancellationToken);
return version == core.Version && core.TrySetCanceled(cancellationToken);
}
[DebuggerHidden]
public bool TrySetException(Exception exception)
{
return core.TrySetException(exception);
return version == core.Version && core.TrySetException(exception);
}
[DebuggerHidden]
@@ -409,7 +411,6 @@ namespace Cysharp.Threading.Tasks
{
TryReturn();
}
}
[DebuggerHidden]
@@ -451,6 +452,7 @@ namespace Cysharp.Threading.Tasks
}
UniTaskCompletionSourceCore<T> core;
short version;
AutoResetUniTaskCompletionSource()
{
@@ -463,6 +465,7 @@ namespace Cysharp.Threading.Tasks
{
result = new AutoResetUniTaskCompletionSource<T>();
}
result.version = result.core.Version;
TaskTracker.TrackActiveTask(result, 2);
return result;
}
@@ -506,19 +509,19 @@ namespace Cysharp.Threading.Tasks
[DebuggerHidden]
public bool TrySetResult(T result)
{
return core.TrySetResult(result);
return version == core.Version && core.TrySetResult(result);
}
[DebuggerHidden]
public bool TrySetCanceled(CancellationToken cancellationToken = default)
{
return core.TrySetCanceled(cancellationToken);
return version == core.Version && core.TrySetCanceled(cancellationToken);
}
[DebuggerHidden]
public bool TrySetException(Exception exception)
{
return core.TrySetException(exception);
return version == core.Version && core.TrySetException(exception);
}
[DebuggerHidden]
@@ -937,5 +940,5 @@ namespace Cysharp.Threading.Tasks
}
return false;
}
}
}
}

View File

@@ -28,7 +28,7 @@ namespace Cysharp.Threading.Tasks
p.TrySetCanceled();
break;
case TaskStatus.Faulted:
p.TrySetException(x.Exception);
p.TrySetException(x.Exception.InnerException ?? x.Exception);
break;
case TaskStatus.RanToCompletion:
p.TrySetResult(x.Result);
@@ -58,7 +58,7 @@ namespace Cysharp.Threading.Tasks
p.TrySetCanceled();
break;
case TaskStatus.Faulted:
p.TrySetException(x.Exception);
p.TrySetException(x.Exception.InnerException ?? x.Exception);
break;
case TaskStatus.RanToCompletion:
p.TrySetResult();
@@ -192,7 +192,7 @@ namespace Cysharp.Threading.Tasks
/// <summary>
/// Ignore task result when cancel raised first.
/// </summary>
public static UniTask WithCancellation(this UniTask task, CancellationToken cancellationToken)
public static UniTask AttachExternalCancellation(this UniTask task, CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled)
{
@@ -209,13 +209,13 @@ namespace Cysharp.Threading.Tasks
return task;
}
return new UniTask(new WithCancellationSource(task, cancellationToken), 0);
return new UniTask(new AttachExternalCancellationSource(task, cancellationToken), 0);
}
/// <summary>
/// Ignore task result when cancel raised first.
/// </summary>
public static UniTask<T> WithCancellation<T>(this UniTask<T> task, CancellationToken cancellationToken)
public static UniTask<T> AttachExternalCancellation<T>(this UniTask<T> task, CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled)
{
@@ -232,10 +232,10 @@ namespace Cysharp.Threading.Tasks
return task;
}
return new UniTask<T>(new WithCancellationSource<T>(task, cancellationToken), 0);
return new UniTask<T>(new AttachExternalCancellationSource<T>(task, cancellationToken), 0);
}
sealed class WithCancellationSource : IUniTaskSource
sealed class AttachExternalCancellationSource : IUniTaskSource
{
static readonly Action<object> cancellationCallbackDelegate = CancellationCallback;
@@ -243,7 +243,7 @@ namespace Cysharp.Threading.Tasks
CancellationTokenRegistration tokenRegistration;
UniTaskCompletionSourceCore<AsyncUnit> core;
public WithCancellationSource(UniTask task, CancellationToken cancellationToken)
public AttachExternalCancellationSource(UniTask task, CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
this.tokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallbackDelegate, this);
@@ -269,7 +269,7 @@ namespace Cysharp.Threading.Tasks
static void CancellationCallback(object state)
{
var self = (WithCancellationSource)state;
var self = (AttachExternalCancellationSource)state;
self.core.TrySetCanceled(self.cancellationToken);
}
@@ -294,7 +294,7 @@ namespace Cysharp.Threading.Tasks
}
}
sealed class WithCancellationSource<T> : IUniTaskSource<T>
sealed class AttachExternalCancellationSource<T> : IUniTaskSource<T>
{
static readonly Action<object> cancellationCallbackDelegate = CancellationCallback;
@@ -302,7 +302,7 @@ namespace Cysharp.Threading.Tasks
CancellationTokenRegistration tokenRegistration;
UniTaskCompletionSourceCore<T> core;
public WithCancellationSource(UniTask<T> task, CancellationToken cancellationToken)
public AttachExternalCancellationSource(UniTask<T> task, CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
this.tokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallbackDelegate, this);
@@ -327,7 +327,7 @@ namespace Cysharp.Threading.Tasks
static void CancellationCallback(object state)
{
var self = (WithCancellationSource<T>)state;
var self = (AttachExternalCancellationSource<T>)state;
self.core.TrySetCanceled(self.cancellationToken);
}

View File

@@ -58,6 +58,7 @@ namespace Cysharp.Threading.Tasks
{
try
{
task.GetAwaiter().GetResult();
return new ReturnObservable<AsyncUnit>(AsyncUnit.Default);
}
catch (Exception ex)
@@ -73,16 +74,19 @@ namespace Cysharp.Threading.Tasks
static async UniTaskVoid Fire<T>(AsyncSubject<T> subject, UniTask<T> task)
{
T value;
try
{
var value = await task;
subject.OnNext(value);
subject.OnCompleted();
value = await task;
}
catch (Exception ex)
{
subject.OnError(ex);
return;
}
subject.OnNext(value);
subject.OnCompleted();
}
static async UniTaskVoid Fire(AsyncSubject<AsyncUnit> subject, UniTask task)
@@ -90,13 +94,15 @@ namespace Cysharp.Threading.Tasks
try
{
await task;
subject.OnNext(AsyncUnit.Default);
subject.OnCompleted();
}
catch (Exception ex)
{
subject.OnError(ex);
return;
}
subject.OnNext(AsyncUnit.Default);
subject.OnCompleted();
}
class ToUniTaskObserver<T> : IObserver<T>
@@ -127,7 +133,7 @@ namespace Cysharp.Threading.Tasks
{
var self = (ToUniTaskObserver<T>)state;
self.disposable.Dispose();
self.promise.TrySetCanceled();
self.promise.TrySetCanceled(self.cancellationToken);
}
public void OnNext(T value)
@@ -197,7 +203,7 @@ namespace Cysharp.Threading.Tasks
{
var self = (FirstValueToUniTaskObserver<T>)state;
self.disposable.Dispose();
self.promise.TrySetCanceled();
self.promise.TrySetCanceled(self.cancellationToken);
}
public void OnNext(T value)

View File

@@ -21,10 +21,7 @@ namespace Cysharp.Threading.Tasks
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object[]>(cancellationToken);
if (asyncOperation.isDone) return UniTask.FromResult<UnityEngine.Object[]>(asyncOperation.allAssets);
return new UniTask<UnityEngine.Object[]>(AssetBundleRequestAllAssetsWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
return AwaitForAllAssets(asyncOperation, null, PlayerLoopTiming.Update, cancellationToken: cancellationToken);
}
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -84,129 +81,6 @@ namespace Cysharp.Threading.Tasks
}
}
sealed class AssetBundleRequestAllAssetsWithCancellationSource : IUniTaskSource<UnityEngine.Object[]>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestAllAssetsWithCancellationSource>
{
static TaskPool<AssetBundleRequestAllAssetsWithCancellationSource> pool;
AssetBundleRequestAllAssetsWithCancellationSource nextNode;
public ref AssetBundleRequestAllAssetsWithCancellationSource NextNode => ref nextNode;
static AssetBundleRequestAllAssetsWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(AssetBundleRequestAllAssetsWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
AssetBundleRequest asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<UnityEngine.Object[]> core;
AssetBundleRequestAllAssetsWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<UnityEngine.Object[]> Create(AssetBundleRequest asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<UnityEngine.Object[]>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AssetBundleRequestAllAssetsWithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return;
}
core.TrySetResult(asyncOperation.allAssets);
}
}
public UnityEngine.Object[] GetResult(short token)
{
return core.GetResult(token);
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AssetBundleRequestAllAssetsConfiguredSource : IUniTaskSource<UnityEngine.Object[]>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestAllAssetsConfiguredSource>
{
static TaskPool<AssetBundleRequestAllAssetsConfiguredSource> pool;

View File

@@ -1,4 +1,4 @@
#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.Threading;

View File

@@ -5,7 +5,7 @@ using System.Runtime.CompilerServices;
using System.Threading;
using UnityEngine;
using Cysharp.Threading.Tasks.Internal;
#if ENABLE_UNITYWEBREQUEST
#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
using UnityEngine.Networking;
#endif
@@ -15,18 +15,19 @@ namespace Cysharp.Threading.Tasks
{
#region AsyncOperation
#if !UNITY_2023_1_OR_NEWER
// from Unity2023.1.0a15, AsyncOperationAwaitableExtensions.GetAwaiter is defined in UnityEngine.
public static AsyncOperationAwaiter GetAwaiter(this AsyncOperation asyncOperation)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new AsyncOperationAwaiter(asyncOperation);
}
#endif
public static UniTask WithCancellation(this AsyncOperation asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);
if (asyncOperation.isDone) return UniTask.CompletedTask;
return new UniTask(AsyncOperationWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
}
public static UniTask ToUniTask(this AsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -77,125 +78,6 @@ namespace Cysharp.Threading.Tasks
}
}
sealed class AsyncOperationWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationWithCancellationSource>
{
static TaskPool<AsyncOperationWithCancellationSource> pool;
AsyncOperationWithCancellationSource nextNode;
public ref AsyncOperationWithCancellationSource NextNode => ref nextNode;
static AsyncOperationWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
AsyncOperation asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<AsyncUnit> core;
AsyncOperationWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource Create(AsyncOperation asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AsyncOperationWithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return;
}
core.TrySetResult(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();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AsyncOperationConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationConfiguredSource>
{
static TaskPool<AsyncOperationConfiguredSource> pool;
@@ -315,10 +197,7 @@ namespace Cysharp.Threading.Tasks
public static UniTask<UnityEngine.Object> WithCancellation(this ResourceRequest asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object>(cancellationToken);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset);
return new UniTask<UnityEngine.Object>(ResourceRequestWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
}
public static UniTask<UnityEngine.Object> ToUniTask(this ResourceRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -373,129 +252,6 @@ namespace Cysharp.Threading.Tasks
}
}
sealed class ResourceRequestWithCancellationSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<ResourceRequestWithCancellationSource>
{
static TaskPool<ResourceRequestWithCancellationSource> pool;
ResourceRequestWithCancellationSource nextNode;
public ref ResourceRequestWithCancellationSource NextNode => ref nextNode;
static ResourceRequestWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(ResourceRequestWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
ResourceRequest asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<UnityEngine.Object> core;
ResourceRequestWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<UnityEngine.Object> Create(ResourceRequest asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<UnityEngine.Object>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new ResourceRequestWithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return;
}
core.TrySetResult(asyncOperation.asset);
}
}
public UnityEngine.Object GetResult(short token)
{
return core.GetResult(token);
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class ResourceRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<ResourceRequestConfiguredSource>
{
static TaskPool<ResourceRequestConfiguredSource> pool;
@@ -620,10 +376,7 @@ namespace Cysharp.Threading.Tasks
public static UniTask<UnityEngine.Object> WithCancellation(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object>(cancellationToken);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset);
return new UniTask<UnityEngine.Object>(AssetBundleRequestWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
}
public static UniTask<UnityEngine.Object> ToUniTask(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -678,129 +431,6 @@ namespace Cysharp.Threading.Tasks
}
}
sealed class AssetBundleRequestWithCancellationSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestWithCancellationSource>
{
static TaskPool<AssetBundleRequestWithCancellationSource> pool;
AssetBundleRequestWithCancellationSource nextNode;
public ref AssetBundleRequestWithCancellationSource NextNode => ref nextNode;
static AssetBundleRequestWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(AssetBundleRequestWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
AssetBundleRequest asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<UnityEngine.Object> core;
AssetBundleRequestWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<UnityEngine.Object> Create(AssetBundleRequest asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<UnityEngine.Object>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AssetBundleRequestWithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return;
}
core.TrySetResult(asyncOperation.asset);
}
}
public UnityEngine.Object GetResult(short token)
{
return core.GetResult(token);
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AssetBundleRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestConfiguredSource>
{
static TaskPool<AssetBundleRequestConfiguredSource> pool;
@@ -926,10 +556,7 @@ namespace Cysharp.Threading.Tasks
public static UniTask<AssetBundle> WithCancellation(this AssetBundleCreateRequest asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<AssetBundle>(cancellationToken);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.assetBundle);
return new UniTask<AssetBundle>(AssetBundleCreateRequestWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
}
public static UniTask<AssetBundle> ToUniTask(this AssetBundleCreateRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -984,129 +611,6 @@ namespace Cysharp.Threading.Tasks
}
}
sealed class AssetBundleCreateRequestWithCancellationSource : IUniTaskSource<AssetBundle>, IPlayerLoopItem, ITaskPoolNode<AssetBundleCreateRequestWithCancellationSource>
{
static TaskPool<AssetBundleCreateRequestWithCancellationSource> pool;
AssetBundleCreateRequestWithCancellationSource nextNode;
public ref AssetBundleCreateRequestWithCancellationSource NextNode => ref nextNode;
static AssetBundleCreateRequestWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(AssetBundleCreateRequestWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
AssetBundleCreateRequest asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<AssetBundle> core;
AssetBundleCreateRequestWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<AssetBundle> Create(AssetBundleCreateRequest asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<AssetBundle>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AssetBundleCreateRequestWithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return;
}
core.TrySetResult(asyncOperation.assetBundle);
}
}
public AssetBundle GetResult(short token)
{
return core.GetResult(token);
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AssetBundleCreateRequestConfiguredSource : IUniTaskSource<AssetBundle>, IPlayerLoopItem, ITaskPoolNode<AssetBundleCreateRequestConfiguredSource>
{
static TaskPool<AssetBundleCreateRequestConfiguredSource> pool;
@@ -1221,7 +725,7 @@ namespace Cysharp.Threading.Tasks
#endregion
#endif
#if ENABLE_UNITYWEBREQUEST
#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
#region UnityWebRequestAsyncOperation
public static UnityWebRequestAsyncOperationAwaiter GetAwaiter(this UnityWebRequestAsyncOperation asyncOperation)
@@ -1232,17 +736,7 @@ namespace Cysharp.Threading.Tasks
public static UniTask<UnityWebRequest> WithCancellation(this UnityWebRequestAsyncOperation asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityWebRequest>(cancellationToken);
if (asyncOperation.isDone)
{
if (asyncOperation.webRequest.IsError())
{
return UniTask.FromException<UnityWebRequest>(new UnityWebRequestException(asyncOperation.webRequest));
}
return UniTask.FromResult(asyncOperation.webRequest);
}
return new UniTask<UnityWebRequest>(UnityWebRequestAsyncOperationWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
}
public static UniTask<UnityWebRequest> ToUniTask(this UnityWebRequestAsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -1312,138 +806,6 @@ namespace Cysharp.Threading.Tasks
}
}
sealed class UnityWebRequestAsyncOperationWithCancellationSource : IUniTaskSource<UnityWebRequest>, IPlayerLoopItem, ITaskPoolNode<UnityWebRequestAsyncOperationWithCancellationSource>
{
static TaskPool<UnityWebRequestAsyncOperationWithCancellationSource> pool;
UnityWebRequestAsyncOperationWithCancellationSource nextNode;
public ref UnityWebRequestAsyncOperationWithCancellationSource NextNode => ref nextNode;
static UnityWebRequestAsyncOperationWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(UnityWebRequestAsyncOperationWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
UnityWebRequestAsyncOperation asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<UnityWebRequest> core;
UnityWebRequestAsyncOperationWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<UnityWebRequest> Create(UnityWebRequestAsyncOperation asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<UnityWebRequest>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new UnityWebRequestAsyncOperationWithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return;
}
var result = asyncOperation.webRequest;
if (result.IsError())
{
core.TrySetException(new UnityWebRequestException(result));
}
else
{
core.TrySetResult(result);
}
}
}
public UnityWebRequest 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;
asyncOperation.webRequest.Abort();
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class UnityWebRequestAsyncOperationConfiguredSource : IUniTaskSource<UnityWebRequest>, IPlayerLoopItem, ITaskPoolNode<UnityWebRequestAsyncOperationConfiguredSource>
{
static TaskPool<UnityWebRequestAsyncOperationConfiguredSource> pool;
@@ -1538,7 +900,11 @@ namespace Cysharp.Threading.Tasks
if (asyncOperation.isDone)
{
if (asyncOperation.webRequest.IsError())
if (asyncOperation.webRequest == null)
{
core.TrySetException(new ObjectDisposedException("The webRequest has been destroyed."));
}
else if (asyncOperation.webRequest.IsError())
{
core.TrySetException(new UnityWebRequestException(asyncOperation.webRequest));
}

View File

@@ -11,7 +11,7 @@
("ResourceRequest", "UnityEngine.Object", "asset"),
("AssetBundleRequest", "UnityEngine.Object", "asset"), // allAssets?
("AssetBundleCreateRequest", "AssetBundle", "assetBundle"),
("UnityWebRequestAsyncOperation", "UnityWebRequest", "webRequest") // -> #if ENABLE_UNITYWEBREQUEST
("UnityWebRequestAsyncOperation", "UnityWebRequest", "webRequest") // -> #if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
};
Func<string, string> ToUniTaskReturnType = x => (x == "void") ? "UniTask" : $"UniTask<{x}>";
@@ -27,7 +27,7 @@ using System.Runtime.CompilerServices;
using System.Threading;
using UnityEngine;
using Cysharp.Threading.Tasks.Internal;
#if ENABLE_UNITYWEBREQUEST
#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
using UnityEngine.Networking;
#endif
@@ -37,7 +37,7 @@ namespace Cysharp.Threading.Tasks
{
<# foreach(var t in types) { #>
<# if(IsUnityWebRequest(t)) { #>
#if ENABLE_UNITYWEBREQUEST
#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
<# } else if(IsAssetBundleModule(t)) { #>
#if UNITASK_ASSETBUNDLE_SUPPORT
<# } #>
@@ -51,21 +51,7 @@ namespace Cysharp.Threading.Tasks
public static <#= ToUniTaskReturnType(t.returnType) #> WithCancellation(this <#= t.typeName #> asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<#= IsVoid(t) ? "" : "<" + t.returnType + ">" #>(cancellationToken);
<# if(IsUnityWebRequest(t)) { #>
if (asyncOperation.isDone)
{
if (asyncOperation.webRequest.IsError())
{
return UniTask.FromException<UnityWebRequest>(new UnityWebRequestException(asyncOperation.webRequest));
}
return UniTask.FromResult(asyncOperation.webRequest);
}
<# } else { #>
if (asyncOperation.isDone) return <#= IsVoid(t) ? "UniTask.CompletedTask" : $"UniTask.FromResult(asyncOperation.{t.returnField})" #>;
<# } #>
return new <#= ToUniTaskReturnType(t.returnType) #>(<#= t.typeName #>WithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
}
public static <#= ToUniTaskReturnType(t.returnType) #> ToUniTask(this <#= t.typeName #> asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -151,150 +137,6 @@ namespace Cysharp.Threading.Tasks
}
}
sealed class <#= t.typeName #>WithCancellationSource : <#= ToIUniTaskSourceReturnType(t.returnType) #>, IPlayerLoopItem, ITaskPoolNode<<#= t.typeName #>WithCancellationSource>
{
static TaskPool<<#= t.typeName #>WithCancellationSource> pool;
<#= t.typeName #>WithCancellationSource nextNode;
public ref <#= t.typeName #>WithCancellationSource NextNode => ref nextNode;
static <#= t.typeName #>WithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(<#= t.typeName #>WithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
<#= t.typeName #> asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<<#= IsVoid(t) ? "AsyncUnit" : t.returnType #>> core;
<#= t.typeName #>WithCancellationSource()
{
continuationAction = Continuation;
}
public static <#= ToIUniTaskSourceReturnType(t.returnType) #> Create(<#= t.typeName #> asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<#= IsVoid(t) ? "" : $"<{t.returnType}>" #>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new <#= t.typeName #>WithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return;
}
<# if(IsUnityWebRequest(t)) { #>
var result = asyncOperation.webRequest;
if (result.IsError())
{
core.TrySetException(new UnityWebRequestException(result));
}
else
{
core.TrySetResult(result);
}
<# } else { #>
core.TrySetResult(<#= IsVoid(t) ? "AsyncUnit.Default" : $"asyncOperation.{t.returnField}" #>);
<# } #>
}
}
public <#= t.returnType #> GetResult(short token)
{
<# if (!IsVoid(t)) { #>
return core.GetResult(token);
<# } else { #>
core.GetResult(token);
<# } #>
}
<# if (!IsVoid(t)) { #>
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;
<# if(IsUnityWebRequest(t)) { #>
asyncOperation.webRequest.Abort();
<# } #>
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class <#= t.typeName #>ConfiguredSource : <#= ToIUniTaskSourceReturnType(t.returnType) #>, IPlayerLoopItem, ITaskPoolNode<<#= t.typeName #>ConfiguredSource>
{
static TaskPool<<#= t.typeName #>ConfiguredSource> pool;

View File

@@ -1,5 +1,5 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
using System;
using System.Threading;
using UnityEngine;
@@ -364,6 +364,7 @@ namespace Cysharp.Threading.Tasks
public AsyncUnityEventHandler(UnityEvent unityEvent, CancellationToken cancellationToken, bool callOnce)
{
this.cancellationToken = cancellationToken;
if (cancellationToken.IsCancellationRequested)
{
isDisposed = true;
@@ -372,7 +373,6 @@ namespace Cysharp.Threading.Tasks
this.action = Invoke;
this.unityEvent = unityEvent;
this.cancellationToken = cancellationToken;
this.callOnce = callOnce;
unityEvent.AddListener(action);
@@ -388,6 +388,10 @@ namespace Cysharp.Threading.Tasks
public UniTask OnInvokeAsync()
{
core.Reset();
if (isDisposed)
{
core.TrySetCanceled(this.cancellationToken);
}
return new UniTask(this, core.Version);
}
@@ -470,6 +474,7 @@ namespace Cysharp.Threading.Tasks
public AsyncUnityEventHandler(UnityEvent<T> unityEvent, CancellationToken cancellationToken, bool callOnce)
{
this.cancellationToken = cancellationToken;
if (cancellationToken.IsCancellationRequested)
{
isDisposed = true;
@@ -478,7 +483,6 @@ namespace Cysharp.Threading.Tasks
this.action = Invoke;
this.unityEvent = unityEvent;
this.cancellationToken = cancellationToken;
this.callOnce = callOnce;
unityEvent.AddListener(action);
@@ -494,6 +498,10 @@ namespace Cysharp.Threading.Tasks
public UniTask<T> OnInvokeAsync()
{
core.Reset();
if (isDisposed)
{
core.TrySetCanceled(this.cancellationToken);
}
return new UniTask<T>(this, core.Version);
}
@@ -665,7 +673,7 @@ namespace Cysharp.Threading.Tasks
}
if (cancellationToken2.CanBeCanceled)
{
registration2 = cancellationToken1.RegisterWithoutCaptureExecutionContext(cancel2, this);
registration2 = cancellationToken2.RegisterWithoutCaptureExecutionContext(cancel2, this);
}
}
@@ -680,13 +688,27 @@ namespace Cysharp.Threading.Tasks
static void OnCanceled1(object state)
{
var self = (UnityEventHandlerAsyncEnumerator)state;
self.DisposeAsync().Forget();
try
{
self.completionSource.TrySetCanceled(self.cancellationToken1);
}
finally
{
self.DisposeAsync().Forget();
}
}
static void OnCanceled2(object state)
{
var self = (UnityEventHandlerAsyncEnumerator)state;
self.DisposeAsync().Forget();
try
{
self.completionSource.TrySetCanceled(self.cancellationToken2);
}
finally
{
self.DisposeAsync().Forget();
}
}
public UniTask DisposeAsync()
@@ -698,6 +720,8 @@ namespace Cysharp.Threading.Tasks
registration1.Dispose();
registration2.Dispose();
unityEvent.RemoveListener(unityAction);
completionSource.TrySetCanceled();
}
return default;
@@ -769,7 +793,7 @@ namespace Cysharp.Threading.Tasks
}
if (cancellationToken2.CanBeCanceled)
{
registration2 = cancellationToken1.RegisterWithoutCaptureExecutionContext(cancel2, this);
registration2 = cancellationToken2.RegisterWithoutCaptureExecutionContext(cancel2, this);
}
}
@@ -785,13 +809,27 @@ namespace Cysharp.Threading.Tasks
static void OnCanceled1(object state)
{
var self = (UnityEventHandlerAsyncEnumerator)state;
self.DisposeAsync().Forget();
try
{
self.completionSource.TrySetCanceled(self.cancellationToken1);
}
finally
{
self.DisposeAsync().Forget();
}
}
static void OnCanceled2(object state)
{
var self = (UnityEventHandlerAsyncEnumerator)state;
self.DisposeAsync().Forget();
try
{
self.completionSource.TrySetCanceled(self.cancellationToken2);
}
finally
{
self.DisposeAsync().Forget();
}
}
public UniTask DisposeAsync()
@@ -807,10 +845,14 @@ namespace Cysharp.Threading.Tasks
disp.Dispose();
}
unityEvent.RemoveListener(unityAction);
completionSource.TrySetCanceled();
}
return default;
}
}
}
}
}
#endif

View File

@@ -1,25 +1,28 @@
using System;
using System.Threading;
using UnityEngine;
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
using UnityEngine.UI;
#endif
namespace Cysharp.Threading.Tasks
{
public static class UnityBindingExtensions
{
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
// <string> -> Text
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text, bool rebindOnError = true)
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, UnityEngine.UI.Text text, bool rebindOnError = true)
{
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
}
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text, CancellationToken cancellationToken, bool rebindOnError = true)
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, UnityEngine.UI.Text text, CancellationToken cancellationToken, bool rebindOnError = true)
{
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
}
static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<string> source, Text text, CancellationToken cancellationToken, bool rebindOnError)
static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<string> source, UnityEngine.UI.Text text, CancellationToken cancellationToken, bool rebindOnError)
{
var repeat = false;
BIND_AGAIN:
@@ -65,22 +68,22 @@ namespace Cysharp.Threading.Tasks
// <T> -> Text
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, Text text, bool rebindOnError = true)
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, UnityEngine.UI.Text text, bool rebindOnError = true)
{
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
}
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, Text text, CancellationToken cancellationToken, bool rebindOnError = true)
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, UnityEngine.UI.Text text, CancellationToken cancellationToken, bool rebindOnError = true)
{
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
}
public static void BindTo<T>(this AsyncReactiveProperty<T> source, Text text, bool rebindOnError = true)
public static void BindTo<T>(this AsyncReactiveProperty<T> source, UnityEngine.UI.Text text, bool rebindOnError = true)
{
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
}
static async UniTaskVoid BindToCore<T>(IUniTaskAsyncEnumerable<T> source, Text text, CancellationToken cancellationToken, bool rebindOnError)
static async UniTaskVoid BindToCore<T>(IUniTaskAsyncEnumerable<T> source, UnityEngine.UI.Text text, CancellationToken cancellationToken, bool rebindOnError)
{
var repeat = false;
BIND_AGAIN:
@@ -180,6 +183,7 @@ namespace Cysharp.Threading.Tasks
}
}
}
#endif
// <T> -> Action

View File

@@ -1,4 +1,4 @@
#if ENABLE_UNITYWEBREQUEST
#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
using System;
using System.Collections.Generic;
@@ -49,7 +49,7 @@ namespace Cysharp.Threading.Tasks
{
if (msg == null)
{
if (Text != null)
if(!string.IsNullOrWhiteSpace(Text))
{
msg = Error + Environment.NewLine + Text;
}

View File

@@ -1,11 +1,12 @@
{
"name": "com.cysharp.unitask",
"displayName": "UniTask",
"version": "2.1.0",
"author": { "name": "Cysharp, Inc.", "url": "https://cysharp.co.jp/en/" },
"version": "2.4.1",
"unity": "2018.4",
"description": "Provides an efficient async/await integration to Unity.",
"keywords": [ "async/await", "async", "Task", "UniTask" ],
"license": "MIT",
"category": "Task",
"dependencies": {}
}
}

View File

@@ -11,30 +11,33 @@ public class Test1
public static async UniTaskVoid TestFunc()
{
await DoSomeThing();
string[] scenes = new string[]
{
"Assets/Scenes/SandboxMain.unity",
};
//string[] scenes = new string[]
//{
// "Assets/Scenes/SandboxMain.unity",
//};
try
{
Debug.Log("Build Begin");
BuildPipeline.BuildPlayer(scenes, Application.dataPath + "../target", BuildTarget.StandaloneWindows, BuildOptions.CompressWithLz4);
Debug.Log("Build After");
}
catch (Exception e)
{
Debug.LogError(e.Message);
}
//try
//{
// Debug.Log("Build Begin");
// BuildPipeline.BuildPlayer(scenes, Application.dataPath + "../target", BuildTarget.StandaloneWindows, BuildOptions.CompressWithLz4);
// Debug.Log("Build After");
//}
//catch (Exception e)
//{
// Debug.LogError(e.Message);
//}
}
public static async UniTask DoSomeThing()
{
Debug.Log("Dosomething");
await UniTask.Delay(1500, DelayType.Realtime);
await UniTask.Delay(1500, DelayType.DeltaTime);
Debug.Log("Dosomething 2");
await UniTask.Delay(1000, DelayType.Realtime);
await UniTask.Delay(1000, DelayType.DeltaTime);
Debug.Log("Dosomething 3");
Debug.Log("and Quit.");
Environment.Exit(0);
}
}

View File

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

View File

@@ -18,7 +18,6 @@ using UnityEngine.SceneManagement;
using UnityEngine.Rendering;
using System.IO;
using System.Linq.Expressions;
using Cysharp.Threading.Tasks.Sample;
// using DG.Tweening;
@@ -269,32 +268,33 @@ public class SandboxMain : MonoBehaviour
async Task Test1()
{
var r = await TcsAsync("https://bing.com/");
// var r = await TcsAsync("https://bing.com/");
await Task.Yield();
Debug.Log("TASKASYNC");
}
async UniTaskVoid Test2()
{
try
{
//var cts = new CancellationTokenSource();
//var r = UniAsync("https://bing.com/", cts.Token);
//cts.Cancel();
//await r;
Debug.Log("SendWebRequestDone:" + PlayerLoopInfo.CurrentLoopType);
//async UniTaskVoid Test2()
//{
// try
// {
// //var cts = new CancellationTokenSource();
// //var r = UniAsync("https://bing.com/", cts.Token);
// //cts.Cancel();
// //await r;
// Debug.Log("SendWebRequestDone:" + PlayerLoopInfo.CurrentLoopType);
// var foo = await UnityWebRequest.Get("https://bing.com/").SendWebRequest();
// foo.downloadHandler.text;
//
_ = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().WithCancellation(CancellationToken.None);
Debug.Log("SendWebRequestWithCancellationDone:" + PlayerLoopInfo.CurrentLoopType);
}
catch
{
Debug.Log("Canceled");
}
}
// // var foo = await UnityWebRequest.Get("https://bing.com/").SendWebRequest();
// // foo.downloadHandler.text;
// //
// _ = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().WithCancellation(CancellationToken.None);
// Debug.Log("SendWebRequestWithCancellationDone:" + PlayerLoopInfo.CurrentLoopType);
// }
// catch
// {
// Debug.Log("Canceled");
// }
//}
IEnumerator Test3(string url)
{
@@ -303,17 +303,17 @@ public class SandboxMain : MonoBehaviour
Debug.Log("COROUTINE");
}
static async Task<UnityWebRequest> TcsAsync(string url)
{
var req = await UnityWebRequest.Get(url).SendWebRequest();
return req;
}
//static async Task<UnityWebRequest> TcsAsync(string url)
//{
// var req = await UnityWebRequest.Get(url).SendWebRequest();
// return req;
//}
static async UniTask<UnityWebRequest> UniAsync(string url, CancellationToken cancellationToken)
{
var req = await UnityWebRequest.Get(url).SendWebRequest().WithCancellation(cancellationToken);
return req;
}
//static async UniTask<UnityWebRequest> UniAsync(string url, CancellationToken cancellationToken)
//{
// var req = await UnityWebRequest.Get(url).SendWebRequest().WithCancellation(cancellationToken);
// return req;
//}
async Task<int> Test()
{
@@ -523,257 +523,107 @@ public class SandboxMain : MonoBehaviour
Debug.Log("YieldEnd:" + Time.frameCount);
}
private static async UniTask TestAsync(CancellationToken ct)
{
Debug.Log("TestAsync Start.");
var count = 0;
while (!ct.IsCancellationRequested)
{
try
{
Debug.Log($"TestAsync try count:{++count}");
var task1 = new WaitUntil(() => UnityEngine.Random.Range(0, 10) == 0).ToUniTask();
var task2 = new WaitUntil(() => UnityEngine.Random.Range(0, 10) == 0).ToUniTask();
var task3 = new WaitUntil(() => UnityEngine.Random.Range(0, 10) == 0).ToUniTask();
await UniTask.WhenAny(task1, task2, task3);
}
catch (Exception e)
{
Debug.LogError(e);
return;
}
}
Debug.Log("TestAsync Finished.");
}
async UniTaskVoid Start()
{
await TestCor().ToUniTask(this);
Debug.Log("App Start");
Application.quitting += () =>
{
Debug.Log("called quitting");
quitSource.Cancel();
};
QuitCheck().Forget();
//Expression.Lambda<Func<int>>(null).Compile(true);
//RunStandardTaskAsync();
//UnityEngine.Debug.Log("UniTaskPlayerLoop ready? " + PlayerLoopHelper.IsInjectedUniTaskPlayerLoop());
//var url = "http://google.com/404";
//var webRequestAsyncOperation = UnityWebRequest.Get(url).SendWebRequest();
//await webRequestAsyncOperation.ToUniTask();
//PlayerLoopInfo.Inject();
//_ = AsyncFixedUpdate();
//StartCoroutine(CoroutineFixedUpdate());
//StartCoroutine(TestCoroutine().ToCoroutine());
// Application.logMessageReceived += Application_logMessageReceived;
// var rp = new AsyncReactiveProperty<int>();
// rp.AddTo(this.GetCancellationTokenOnDestroy());
//var cts = new CancellationTokenSource();
// UniTask.Delay(TimeSpan.FromSeconds(1)).TimeoutWithoutException
// UniTask.Post(
// CancellationToken.
//UniTask.Delay(TimeSpan.FromSeconds(3)).
var currentLoop = PlayerLoop.GetDefaultPlayerLoop();
PlayerLoopHelper.Initialize(ref currentLoop, InjectPlayerLoopTimings.Minimum); // minimum is Update | FixedUpdate | LastPostLateUpdate
// TestAsync(cts.Token).Forget();
okButton.onClick.AddListener(UniTask.UnityAction(async () =>
{
StartCoroutine(LastYieldCore());
await UniTask.WaitForEndOfFrame(this);
var texture = new Texture2D(Screen.width, Screen.height);
texture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
texture.Apply();
Debug.Log("BEFORE:" + Time.frameCount);
var jpg = texture.EncodeToJPG();
File.WriteAllBytes("testscreencapture.jpg", jpg);
Debug.Log("ok?");
await UniTask.Yield();
// await UniTask.Yield(PlayerLoopTiming.LastTimeUpdate);
Debug.Log("AFTER:" + Time.frameCount);
//var texture = ScreenCapture.CaptureScreenshotAsTexture();
//if (texture == null)
//{
// Debug.Log("fail");
//}
//else
//{
// var jpg = texture.EncodeToJPG();
// File.WriteAllBytes("testscreencapture.jpg", jpg);
// Debug.Log("ok?");
//}
}));
// _ = ExecuteAsync();
// await UniTask.Yield();
// //await DelayCheck();
// /*
// UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType);
// StartCoroutine(CoroutineRun());
// StartCoroutine(CoroutineRun2());
// _ = AsyncRun();
// _ = AsyncLastUpdate();
// _ = AsyncLastLast();
// */
// //await UniTask.Yield();
// //_ = Test2();
// // EarlyUpdate.ExecuteMainThreadJobs
// // _ = Test2();
// //var t = await Resources.LoadAsync<TextAsset>(Application.streamingAssetsPath + "test.txt");
// //Debug.Log("LoadEnd" + PlayerLoopInfo.CurrentLoopType + ", " + (t != null));
// //Debug.Log("LoadEnd" + PlayerLoopInfo.CurrentLoopType + ", " + ((TextAsset)t).text);
// //await UniTask.Yield(PlayerLoopTiming.LastUpdate);
// //UnityEngine.Debug.Log("after update:" + Time.frameCount);
// ////await UniTask.NextFrame();
// ////await UniTask.Yield();
// ////UnityEngine.Debug.Log("after update nextframe:" + Time.frameCount);
// //StartCoroutine(CoroutineRun2());
// ////StartCoroutine(CoroutineRun());
// //UnityEngine.Debug.Log("FOO?");
// //_ = DelayFrame3_Pre();
// //await UniTask.Yield();
//}));
//cancelButton.onClick.AddListener(UniTask.UnityAction(async () =>
//{
// _ = DelayFrame3_Post();
// await UniTask.Yield();
// //await UniTask.Yield(PlayerLoopTiming.LastPreUpdate);
// //UnityEngine.Debug.Log("before update:" + Time.frameCount);
// //await UniTask.NextFrame();
// //await UniTask.Yield();
// //UnityEngine.Debug.Log("before update nextframe:" + Time.frameCount);
// //StartCoroutine(CoroutineRun());
// //UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType);
// //_ = Yieldding();
// //var cts = new CancellationTokenSource();
// //UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType + ":" + Time.frameCount);
// //var la = SceneManager.LoadSceneAsync("Scenes/ExceptionExamples").WithCancellation(cts.Token);
// ////cts.Cancel();
// //await la;
// //UnityEngine.Debug.Log("End LoadSceneAsync" + PlayerLoopInfo.CurrentLoopType + ":" + Time.frameCount);
//}));
//return;
//await UniTask.SwitchToMainThread();
//UniTaskAsyncEnumerable.EveryValueChanged(mcc, x => x.MyProperty)
// .Do(_ => { }, () => Debug.Log("COMPLETED"))
// .ForEachAsync(x =>
// {
// Debug.Log("VALUE_CHANGED:" + x);
// })
// .Forget();
//_ = Test1();
//Test2().Forget();
//StartCoroutine(Test3("https://bing.com/"));
//bool flip = false;
//var rect = cancelButton.GetComponent<RectTransform>();
//var cts = new CancellationTokenSource();
//var ct = cts.Token;
//okButton.onClick.AddListener(UniTask.UnityAction(async () =>
//{
// await rect.DOMoveX(10f * (flip ? -1 : 1), 3).OnUpdate(() => { Debug.Log("UPDATE YEAH"); }).WithCancellation(ct);
// flip = !flip;
// // ok.
//}));
//cancelButton.onClick.AddListener(() =>
//{
// cts.Cancel();
//});
// DG.Tweening.Core.TweenerCore<int>
//Debug.Log("GO MOVEX");
//await okButton.GetComponent<RectTransform>().DOMoveX(-10.2f, 3).WithCancellation(CancellationToken.None);
//Debug.Log("END MOVEX");
//Debug.Log("AGAIN MOVE");
//await okButton.GetComponent<RectTransform>().DOMoveY(10.2f, 3).WithCancellation(CancellationToken.None);
//Debug.Log("AGAIN END MOVE");
//Debug.Log(Test().GetType().FullName);
// check stacktrace
// await UniTaskAsyncEnumerable.EveryUpdate().Where((x, i) => i % 2 == 0).Select(x => x).DistinctUntilChanged().ForEachAsync(x =>
//{
// Debug.Log("test");
//});
//// DOTween.To(
//var cts = new CancellationTokenSource();
////var tween = okButton.GetComponent<RectTransform>().DOLocalMoveX(100, 5.0f);
//cancelButton.OnClickAsAsyncEnumerable().ForEachAsync(_ =>
//{
// cts.Cancel();
//}).Forget();
//// await tween.ToUniTask(TweenCancelBehaviour.KillAndCancelAwait, cts.Token);
////tween.SetRecyclable(true);
//Debug.Log("END");
//// tween.Play();
//// DOTween.
//// DOVirtual.Float(0, 1, 1, x => { }).ToUniTask();
//await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate())
//{
// Debug.Log("Update() " + Time.frameCount);
//}
//await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsync(_ =>
//{
//});
//okButton.OnClickAsAsyncEnumerable().ForEachAsync(_ =>
//{
//foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
//{
// Debug.Log(type + ":" + size);
//}
//}).Forget();
//CloseAsync(this.GetCancellationTokenOnDestroy()).Forget();
//okButton.onClick.AddListener(UniTask.UnityAction(async () => await UniTask.Yield()));
//UpdateUniTask().Forget();
//StartCoroutine(Coroutine());
// PlayerLoopInfo.Inject();
await UniTask.Delay(TimeSpan.FromSeconds(1));
PlayerLoopInfo.DumpPlayerLoop("current", PlayerLoop.GetCurrentPlayerLoop());
// _ = ReturnToMainThreadTest();
//GameObject.Destroy(this.gameObject);
cancelButton.onClick.AddListener(UniTask.UnityAction(async () =>
{
//clickCancelSource.Cancel();
//RunCheck(PlayerLoopTiming.Initialization).Forget();
//RunCheck(PlayerLoopTiming.LastInitialization).Forget();
//RunCheck(PlayerLoopTiming.EarlyUpdate).Forget();
//RunCheck(PlayerLoopTiming.LastEarlyUpdate).Forget();
//RunCheck(PlayerLoopTiming.FixedUpdate).Forget();
//RunCheck(PlayerLoopTiming.LastFixedUpdate).Forget();
//RunCheck(PlayerLoopTiming.PreUpdate).Forget();
//RunCheck(PlayerLoopTiming.LastPreUpdate).Forget();
//RunCheck(PlayerLoopTiming.Update).Forget();
//RunCheck(PlayerLoopTiming.LastUpdate).Forget();
//RunCheck(PlayerLoopTiming.PreLateUpdate).Forget();
//RunCheck(PlayerLoopTiming.LastPreLateUpdate).Forget();
//RunCheck(PlayerLoopTiming.PostLateUpdate).Forget();
//RunCheck(PlayerLoopTiming.LastPostLateUpdate).Forget();
await UniTask.Yield();
}));
await UniTask.Yield();
}
async UniTaskVoid RunCheck(PlayerLoopTiming timing)
{
//await UniTask.Yield(timing);
//UnityEngine.Debug.Log("Yield:" + timing);
await UniTask.DelayFrame(1, timing);
UnityEngine.Debug.Log("Delay:" + timing);
}
private void Application_logMessageReceived2(string condition, string stackTrace, LogType type)

View File

@@ -12,16 +12,16 @@ public class FooMonoBehaviour : MonoBehaviour
}
private async UniTask Download(UnityWebRequest req, string filePath)
{
var foo = req.SendWebRequest();
//private async UniTask Download(UnityWebRequest req, string filePath)
//{
// _ = req.SendWebRequest();
var aaa = await foo;
Debug.Log(aaa);
//File.WriteAllText(filePath, req.downloadHandler.text ?? string.Empty);
}
// // var aaa = await foo;
// // Debug.Log(aaa);
// await UniTask.Yield();
// //File.WriteAllText(filePath, req.downloadHandler.text ?? string.Empty);
//}
}

View File

@@ -310,33 +310,33 @@ namespace Cysharp.Threading.TasksTests
yield return null;
}
[UnityTest]
public IEnumerator ExceptionUnobserved1() => UniTask.ToCoroutine(async () =>
{
bool calledEx = false;
Action<Exception> action = exx =>
{
calledEx = true;
exx.Message.Should().Be("MyException");
};
//[UnityTest]
//public IEnumerator ExceptionUnobserved1() => UniTask.ToCoroutine(async () =>
//{
// bool calledEx = false;
// Action<Exception> action = exx =>
// {
// calledEx = true;
// exx.Message.Should().Be("MyException");
// };
UniTaskScheduler.UnobservedTaskException += action;
// UniTaskScheduler.UnobservedTaskException += action;
var ex = InException1();
ex = default(UniTask);
// var ex = InException1();
// ex = default(UniTask);
await UniTask.DelayFrame(3);
// await UniTask.DelayFrame(3);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
// GC.Collect();
// GC.WaitForPendingFinalizers();
// GC.Collect();
await UniTask.DelayFrame(1);
// await UniTask.DelayFrame(1);
calledEx.Should().BeTrue();
// calledEx.Should().BeTrue();
UniTaskScheduler.UnobservedTaskException -= action;
});
// UniTaskScheduler.UnobservedTaskException -= action;
//});
[UnityTest]
public IEnumerator ExceptionUnobserved2() => UniTask.ToCoroutine(async () =>
@@ -366,6 +366,29 @@ namespace Cysharp.Threading.TasksTests
UniTaskScheduler.UnobservedTaskException -= action;
});
// can not run on RuntimeUnitTestToolkit so ignore...
// [UnityTest]
// public IEnumerator ThrowExceptionUnawaited() => UniTask.ToCoroutine(async () =>
// {
// LogAssert.Expect(LogType.Exception, "Exception: MyException");
//#pragma warning disable 1998
// async UniTask Throw() => throw new Exception("MyException");
//#pragma warning restore 1998
//#pragma warning disable 4014
// Throw();
//#pragma warning restore 4014
// await UniTask.DelayFrame(3);
// GC.Collect();
// GC.WaitForPendingFinalizers();
// GC.Collect();
// await UniTask.DelayFrame(1);
// });
async UniTask InException1()
{
await UniTask.Yield();

View File

@@ -0,0 +1,179 @@
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine.TestTools;
namespace Cysharp.Threading.TasksTests
{
public class PlayerLoopTimerTest
{
void Between(TimeSpan l, TimeSpan data, TimeSpan r)
{
NUnit.Framework.Assert.AreEqual(l < data, true, "{0} < {1} failed.", l, data);
NUnit.Framework.Assert.AreEqual(data < r, true, "{0} < {1} failed.", data, r);
}
[UnityTest]
public IEnumerator StandardTicks() => UniTask.ToCoroutine(async () =>
{
foreach (var delay in new[] { DelayType.DeltaTime, DelayType.Realtime, DelayType.UnscaledDeltaTime })
{
var raisedTimeout = new UniTaskCompletionSource();
PlayerLoopTimer.StartNew(TimeSpan.FromSeconds(1), false, delay, PlayerLoopTiming.Update, CancellationToken.None, _ =>
{
raisedTimeout.TrySetResult();
}, null);
var sw = Stopwatch.StartNew();
await raisedTimeout.Task;
sw.Stop();
Between(TimeSpan.FromSeconds(0.9), sw.Elapsed, TimeSpan.FromSeconds(1.1));
}
});
[UnityTest]
public IEnumerator Periodic() => UniTask.ToCoroutine(async () =>
{
var raisedTime = new List<DateTime>();
var count = 0;
var complete = new UniTaskCompletionSource();
PlayerLoopTimer timer = null;
timer = PlayerLoopTimer.StartNew(TimeSpan.FromSeconds(1), true, DelayType.DeltaTime, PlayerLoopTiming.Update, CancellationToken.None, _ =>
{
raisedTime.Add(DateTime.UtcNow);
count++;
if (count == 3)
{
complete.TrySetResult();
timer.Dispose();
}
}, null);
var start = DateTime.UtcNow;
await complete.Task;
Between(TimeSpan.FromSeconds(0.9), raisedTime[0] - start, TimeSpan.FromSeconds(1.1));
Between(TimeSpan.FromSeconds(1.9), raisedTime[1] - start, TimeSpan.FromSeconds(2.1));
Between(TimeSpan.FromSeconds(2.9), raisedTime[2] - start, TimeSpan.FromSeconds(3.1));
});
[UnityTest]
public IEnumerator CancelAfterSlimTest() => UniTask.ToCoroutine(async () =>
{
var cts = new CancellationTokenSource();
var complete = new UniTaskCompletionSource();
cts.Token.RegisterWithoutCaptureExecutionContext(() =>
{
complete.TrySetResult();
});
cts.CancelAfterSlim(TimeSpan.FromSeconds(1));
var sw = Stopwatch.StartNew();
await complete.Task;
Between(TimeSpan.FromSeconds(0.9), sw.Elapsed, TimeSpan.FromSeconds(1.1));
});
[UnityTest]
public IEnumerator CancelAfterSlimCancelTest() => UniTask.ToCoroutine(async () =>
{
var cts = new CancellationTokenSource();
var complete = new UniTaskCompletionSource();
cts.Token.RegisterWithoutCaptureExecutionContext(() =>
{
complete.TrySetResult();
});
var d = cts.CancelAfterSlim(TimeSpan.FromSeconds(1));
var sw = Stopwatch.StartNew();
await UniTask.Delay(TimeSpan.FromMilliseconds(100));
d.Dispose();
await UniTask.Delay(TimeSpan.FromSeconds(2));
complete.Task.Status.Should().Be(UniTaskStatus.Pending);
});
[UnityTest]
public IEnumerator TimeoutController() => UniTask.ToCoroutine(async () =>
{
var controller = new TimeoutController();
var token = controller.Timeout(TimeSpan.FromSeconds(1));
var complete = new UniTaskCompletionSource();
token.RegisterWithoutCaptureExecutionContext(() =>
{
complete.TrySetResult();
});
var sw = Stopwatch.StartNew();
await complete.Task;
Between(TimeSpan.FromSeconds(0.9), sw.Elapsed, TimeSpan.FromSeconds(1.1));
controller.IsTimeout().Should().BeTrue();
});
[UnityTest]
public IEnumerator TimeoutReuse() => UniTask.ToCoroutine(async () =>
{
var controller = new TimeoutController(DelayType.DeltaTime);
var token = controller.Timeout(TimeSpan.FromSeconds(2));
var complete = new UniTaskCompletionSource();
token.RegisterWithoutCaptureExecutionContext(() =>
{
complete.TrySetResult(); // reuse, used same token?
});
await UniTask.Delay(TimeSpan.FromMilliseconds(100));
controller.Reset();
controller.IsTimeout().Should().BeFalse();
var sw = Stopwatch.StartNew();
controller.Timeout(TimeSpan.FromSeconds(5));
await complete.Task;
UnityEngine.Debug.Log(UnityEngine.Time.timeScale);
Between(TimeSpan.FromSeconds(4.9), sw.Elapsed, TimeSpan.FromSeconds(5.1));
controller.IsTimeout().Should().BeTrue();
});
[UnityTest]
public IEnumerator LinkedTokenTest() => UniTask.ToCoroutine(async () =>
{
var cts = new CancellationTokenSource();
var controller = new TimeoutController(cts);
var token = controller.Timeout(TimeSpan.FromSeconds(2));
await UniTask.DelayFrame(3);
cts.Cancel();
token.IsCancellationRequested.Should().BeTrue();
controller.Dispose();
});
}
}

View File

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

View File

@@ -5,6 +5,9 @@ EditorUserSettings:
m_ObjectHideFlags: 0
serializedVersion: 4
m_ConfigSettings:
RecentlyUsedScenePath-0:
value: 22424703114646680e0b0227036c6c1118131a25340527392367083debf42d
flags: 0
vcSharedLogLevel:
value: 0d5e400f0650
flags: 0
@@ -15,5 +18,7 @@ EditorUserSettings:
m_SemanticMergeMode: 2
m_VCShowFailedCheckout: 1
m_VCOverwriteFailedCheckoutAssets: 1
m_VCOverlayIcons: 1
m_VCProjectOverlayIcons: 1
m_VCHierarchyOverlayIcons: 1
m_VCOtherOverlayIcons: 1
m_VCAllowAsyncUpdate: 0