Compare commits

..

279 Commits

Author SHA1 Message Date
neuecc
85fb08552e Merge remote-tracking branch 'origin/master' 2020-09-07 18:52:22 +09:00
neuecc
7bd4b6faf7 2.0.32 2020-09-07 18:52:11 +09:00
neuecc
0b1ae7e295 docs: update TOC 2020-09-07 08:56:35 +00:00
neuecc
35a893ad9e Add UniTask.RunOnThreadPool 2020-09-07 17:56:02 +09:00
Yoshifumi Kawai
4955ed18f1 Merge pull request #157 from RamType-0/OptimizeConsumeEnumerator
Optimize EnumeratorPromise.ConsumeEnumerator while consuming CustomYieldInstruction
2020-09-07 17:23:08 +09:00
Yoshifumi Kawai
fe462328ab Merge pull request #148 from RamType-0/TaskPoolNodeByRef
Use ref T for ITaskPoolNode<T>.NextNode
2020-09-07 15:33:22 +09:00
Yoshifumi Kawai
5136d92efa Merge pull request #147 from RamType-0/OptimizeInternalContinuationIteration
Optimize ContinuationQueue and PlayerLoopRunner iteration
2020-09-07 15:32:50 +09:00
RamType0
227f7872cb Remove comments 2020-09-02 13:19:11 +09:00
Ram.Type-0
725b2fdc35 Format comment 2020-09-01 21:25:43 +09:00
Ram.Type-0
75abc8059f Optimize EnumeratorPromise.ConsumeEnumerator while consuming CustomYieldInstruction 2020-09-01 21:18:13 +09:00
RamType0
f1193743c8 Remove deprecated comment 2020-09-01 11:53:10 +09:00
Ram.Type-0
109730eacd Add missing migration from previous one 2020-08-30 14:18:55 +09:00
Ram.Type-0
1f736afe86 Revert change for ContinuationQueue.RunCore 2020-08-30 13:53:29 +09:00
Yoshifumi Kawai
4d554a6718 Merge pull request #149 from RamType-0/PlayerLoopRunnerInitializationFix
Fix PlayerLoopRunner at PlayerLoopTiming.Initialization is not assigned to PlayerLoopHelper.runners[0]
2020-08-30 09:49:19 +09:00
Ram.Type-0
69c0c362e9 Fix PlayerLoopRunner at PlayerLoopTiming.Initialization is not assigned to PlayerLoopHelper.runners[0] 2020-08-28 21:15:45 +09:00
RamType0
3bb446556a Use ref T for ITaskPoolNode<T>.NextNode 2020-08-28 18:03:22 +09:00
RamType0
ea950d8cec Optimize ContinuationQueue and PlayerLoopRunner iteration
- Fix failing to eliminate array bounds check in PlayerLoopRunner.RunCore
- Reduce number of array bounds check of continuation iteration in ContinuationQueue.RunCore
2020-08-28 17:12:03 +09:00
Yoshifumi Kawai
a65f4da7a2 Merge pull request #145 from RamType-0/TaskPoolNodeBenchmark
Add TaskPoolRefNode benchmark
2020-08-28 11:15:57 +09:00
Ram.Type-0
0bdc933c20 Add TaskPoolRefNode benchmark 2020-08-28 10:54:33 +09:00
Yoshifumi Kawai
0c0f79c6db Merge pull request #142 from hikarin522/patch-2
Add UniTaskExtensions.Unwrap()
2020-08-28 04:51:16 +09:00
hikari
32f9b9d4ac add Unwrap 2020-08-26 15:59:40 +09:00
neuecc
53907a3719 Merge remote-tracking branch 'origin/master' 2020-08-25 07:16:16 +09:00
neuecc
4937aeee3f 2.0.31 2020-08-25 07:16:08 +09:00
Yoshifumi Kawai
5e5b8aff89 Merge pull request #140 from hikarin522/patch-1
Fix ToCancellationToken
2020-08-25 07:13:56 +09:00
Yoshifumi Kawai
a2cbbd82d0 Merge pull request #139 from Cysharp/feature/SubscribeAwait
Add UniTaskAsyncEnumerable.SubscribeAwait
2020-08-25 07:12:00 +09:00
Yoshifumi Kawai
7eac5d8ba8 Merge pull request #138 from Cysharp/hotfix/ContinueOnErrorCallingOnNext
Continue to subscribe if an exception is raised when calling onNext
2020-08-25 07:11:43 +09:00
Yoshifumi Kawai
e2b1ed55ae Merge pull request #134 from SeungHuLee/Add-TMP-Generic-BindTo
Add TMP Generic BindTo Support
2020-08-25 07:11:26 +09:00
neuecc
727c7102d3 Add more DoTween support - AwaitForComplete, AwaitForPause, AwaitForPlay, AwaitForRewind, AwaitForStepComplete. 2020-08-25 07:10:39 +09:00
neuecc
1494ea6717 removelog spam on UniTaskAsyncEnumerable.EveryValueChanged #141 2020-08-25 06:43:31 +09:00
neuecc
f1ce64dbd3 middleware sample 2020-08-25 06:43:03 +09:00
hikari
3bad5cd2bf fixup! Fix ToCancellationToken linkToken 2020-08-25 02:55:09 +09:00
hikari
7432c0073a Fix ToCancellationToken linkToken 2020-08-24 17:10:26 +09:00
Mayuki Sawatari
f1813a7c94 Continue to subscribe if an exception is raised when calling onNext 2020-08-24 14:44:48 +09:00
Mayuki Sawatari
9e45c0a4d1 Add UniTaskAsyncEnumerable.SubscribeAwait 2020-08-24 14:35:20 +09:00
LSH
2c652cdde7 Add TMP Generic BindTo Support 2020-08-20 03:30:41 +09:00
neuecc
7718d345c8 2.0.30 2020-08-14 17:36:50 +09:00
neuecc
9f39708325 Merge remote-tracking branch 'origin/master' 2020-08-14 17:29:39 +09:00
neuecc
bb6dbfa920 Fix await IEnumerator + WaitForSeconds does not follow timescale(to behave same as StartCoroutine) #133 2020-08-14 17:29:30 +09:00
Yoshifumi Kawai
ba265005bb Merge pull request #132 from RamType-0/Disable-AutoReference-EditorAssembly
Disable "Auto Referenced" of UniTask.Editor
2020-08-14 06:25:21 +09:00
Ram.Type-0
4d7cc7ed61 Disable auto reference of UniTask.Editor 2020-08-14 04:56:58 +09:00
neuecc
b64f31eb0b 2.0.28 2020-08-13 17:44:15 +09:00
neuecc
38d159b69e IEnumerator.ToUniTask() behave same as StartCoroutine #120 2020-08-13 17:44:06 +09:00
neuecc
d5455f3716 Merge remote-tracking branch 'origin/master' 2020-08-13 16:33:41 +09:00
neuecc
a72ceeba11 UNITASK_ASSETBUNDLE_SUPPORT #131 2020-08-13 16:33:32 +09:00
Yoshifumi Kawai
c6b7d332b2 Update README.md 2020-08-12 11:41:21 +09:00
neuecc
f37278f2a6 docs: update TOC 2020-08-12 02:23:09 +00:00
Yoshifumi Kawai
3f3e03b83d Update README.md 2020-08-12 11:22:52 +09:00
Yoshifumi Kawai
c99d3eb3c3 Update README.md 2020-08-05 15:55:09 +09:00
Yoshifumi Kawai
08d5183e7e Merge pull request #121 from Cysharp/chore/unity_activation
chore: remove generate unity alf (activation license file)
2020-08-04 14:54:50 +09:00
Ikiru Yoshizaki
51769b2224 chore: remove generate unity alf (activation license file) 2020-08-04 13:20:33 +09:00
neuecc
6ec0ed8d61 docs: update TOC 2020-07-29 23:18:59 +00:00
neuecc
2e35324403 ready for 2.0.27 2020-07-30 08:12:32 +09:00
neuecc
e9474649c4 Add AssetBundleRequest.AwaitForAllAssets 2020-07-30 08:12:24 +09:00
neuecc
a8e0ce50c8 Fix Addressables async extensions when autoReleaseHandle: true 2020-07-30 08:12:03 +09:00
neuecc
db7ddba735 Add UniTaskSynchronizationContext, PlayerLoopHelper.IsInjectedUniTaskPlayerLoop, DumpCurrentPlayerLoop 2020-07-30 08:11:07 +09:00
neuecc
1999d94b33 Add UniTask.WithCancellation 2020-07-30 08:10:16 +09:00
neuecc
44af123b6c Update project version to 2019.4.5f1 2020-07-30 07:01:29 +09:00
neuecc
547b700ba7 AsyncOperation/Addressables returns Cacnceled UniTask when already canceled. 2020-07-16 06:12:39 +09:00
neuecc
6b87d5d2b0 2.0.26 2020-07-16 06:05:04 +09:00
neuecc
023894d45e Merge remote-tracking branch 'origin/master' 2020-07-16 05:37:08 +09:00
Yoshifumi Kawai
009715c0da Merge pull request #115 from IllusionCui/master
[addressable]:fix CancellationToken can't stop UniTask #114
2020-07-16 05:37:01 +09:00
neuecc
c2824027d4 more strict handle cancel timing on asyncOperation.WithCancellation() 2020-07-16 05:36:21 +09:00
cuibeibei
65b6553a1a [addressable]:fix CancellationToken can't stop UniTask #114 2020-07-15 15:14:08 +08:00
neuecc
9d3b7adc8e Add CancellationToken cancellationToken = default to UniTask.Run #113 2020-07-14 11:25:30 +09:00
neuecc
3724fc204c memo of IL2CPP VM bug. 2020-07-14 06:40:32 +09:00
neuecc
b97451a915 UnityWebRequest, Addressables returns exception when already isDone and AsyncOperation has error. 2020-07-13 09:34:43 +09:00
neuecc
9ddcac4c6c ignore1 2020-07-13 01:00:16 +09:00
neuecc
b61e3c347f remove unity csproj 2020-07-11 02:19:40 +09:00
neuecc
0bb44066c0 2.0.25 2020-07-09 22:39:48 +09:00
neuecc
305c4aaa07 fix UnityWebRequestException String access is not supported #110 2020-07-09 22:17:49 +09:00
neuecc
42d627f3ba fix again #108 2020-07-09 17:29:04 +09:00
neuecc
6dd2b464a3 NRE on head request with invalid url #108 2020-07-09 01:31:27 +09:00
neuecc
32f24cf8f8 cleanup unused code 2020-07-04 22:08:48 +09:00
neuecc
4a89e3ea86 r 2020-07-04 06:44:57 +09:00
neuecc
887db5b281 2.0.24 2020-07-04 06:36:14 +09:00
neuecc
fee5518a82 Improve, Select/Where add UniTaskTracker 2020-07-04 06:29:50 +09:00
neuecc
551128e64c Add UniTaskAsyncEnumerable.Create 2020-07-04 06:29:33 +09:00
neuecc
c65ae8d18e error detail 2020-07-04 06:29:08 +09:00
neuecc
c1f75d9ebd Add UniTask.Never 2020-06-30 04:01:00 +09:00
neuecc
73a5ff6648 r 2020-06-29 03:24:10 +09:00
neuecc
1c264f380e 2.0.23 2020-06-29 03:22:33 +09:00
neuecc
f02bfa0a1e Fix lose exception stacktrace info when task is sync completed 2020-06-29 03:20:13 +09:00
neuecc
9d684006fc test 2020-06-29 03:02:23 +09:00
neuecc
c49f1ed028 r 2020-06-29 01:18:16 +09:00
neuecc
d935b226c0 2.0.22 2020-06-29 01:13:37 +09:00
neuecc
d9e20de8a5 Add UniTaskAsyncEnumerable.SkipUntil, TakeUntil. Fix SkipUntilCanceled behaviour. 2020-06-29 01:10:18 +09:00
neuecc
23997f0f93 SwitchToMainThread, ReturnToMainThread, SwitchToSynchronizationContext, ReturnToSynchronizationContext supports CancellationToken 2020-06-28 20:57:42 +09:00
neuecc
529272d11b more ignore 2020-06-28 18:01:01 +09:00
neuecc
1194c38568 Add AsyncGPUReadbackRequest await support 2020-06-28 17:59:03 +09:00
neuecc
f0d2ee2beb typo 2020-06-26 17:57:27 +09:00
neuecc
68cdda086a async UniTaskVoid Start 2020-06-26 17:53:07 +09:00
neuecc
54ceca6ceb R 2020-06-25 23:03:13 +09:00
neuecc
50bdf7460c 2.0.21 2020-06-25 23:02:40 +09:00
neuecc
c06e45d0bb ToCancellationToken(linkeToken) 2020-06-25 23:02:30 +09:00
neuecc
3ed6e28a00 Removed Timeout(bool ignoreTimeScale), changed to Timeout(DelayType delayType) 2020-06-25 22:35:55 +09:00
neuecc
ab76098895 Removed CancelAfterSlim(bool ignoreTimeScale), changed to CancelAfterSlim(DelayType delayType) 2020-06-25 22:35:35 +09:00
neuecc
0a447e43b0 Add UniTask.ToCancellationToken 2020-06-25 22:34:45 +09:00
neuecc
8df44f2768 Remove UniTask.DelayRealtime, Add UniTask.Delay(DelayType) overload 2020-06-25 22:15:44 +09:00
neuecc
a7ec64d644 more 20 2020-06-24 05:09:00 +09:00
neuecc
868e104d85 2.0.20 2020-06-24 05:04:18 +09:00
neuecc
7ced7f5764 separate external assemblies, add TMP_InputField extensions 2020-06-24 05:04:09 +09:00
neuecc
12b39c6ba1 Add UniTask.DelayRealtime 2020-06-24 01:40:43 +09:00
neuecc
93df9d7693 Prevent -> Preserve in ReadMe 2020-06-21 23:11:47 +09:00
neuecc
3980f314fa Fix UnityWebRequestExtensions obsolete warning in Unity 2020.2 2020-06-19 16:46:30 +09:00
neuecc
c2538da1cd Improve UnityWebRequestError performance 2020-06-19 16:45:55 +09:00
neuecc
5ed943bca2 Add UniTask.Post 2020-06-19 16:42:05 +09:00
neuecc
d6a0563319 ready 2.0.19 2020-06-18 03:39:44 +09:00
neuecc
af82a94b87 Fix TimeoutWithoutException does not suppress exception 2020-06-18 03:30:53 +09:00
neuecc
82219e6111 Fix UniTask.Delay does not run on threadpool thread 2020-06-18 03:28:03 +09:00
neuecc
81f9c55c7f Add UniTask.Run(Func<UniTask>) overload 2020-06-18 03:27:26 +09:00
neuecc
0640f278cc Fix AsyncLazy can not await multiple times when task is not completed 2020-06-18 03:02:01 +09:00
neuecc
769b5c6bab UniTaskCompletionSource can await multiple times(same behaviour as TaskCompletionSource) 2020-06-18 02:34:56 +09:00
neuecc
bdd569e213 fix can not close application in mono build 2020-06-17 21:37:45 +09:00
neuecc
5bfff5bc24 Subscribe ReadMe 2020-06-15 22:46:01 +09:00
neuecc
edf32496e4 re 2020-06-15 15:29:12 +09:00
neuecc
785f5837d1 Merge remote-tracking branch 'origin/master' 2020-06-15 15:28:29 +09:00
neuecc
8c9272bc9f 2.0.18 2020-06-15 15:26:28 +09:00
neuecc
3e00735b3d Add AsUniTaskAsyncEnumerable/AsAsyncEnumerable in .NET Core 2020-06-15 15:23:25 +09:00
neuecc
00a1be8666 await UnityWebRequestAsyncOperation throws UnityWebRequestException when isHttpError or isNetworkError 2020-06-15 15:23:07 +09:00
neuecc
a2783d3c8a yes meta 2020-06-15 12:29:46 +09:00
Yoshifumi Kawai
85d1a8a4a4 Update README.md 2020-06-14 15:44:40 +09:00
neuecc
bbfb8354bb 2.0.17 2020-06-14 15:32:37 +09:00
Yoshifumi Kawai
2f68e47443 Merge pull request #92 from yashihei/fix-cancellation-token
Fixed CancelationToken not being passed correctly
2020-06-14 15:08:37 +09:00
yashihei
89339ffb29 Fixed CancelationToken not being passed correctly 2020-06-14 08:24:25 +09:00
neuecc
0535862fe6 Merge remote-tracking branch 'origin/master' 2020-06-13 18:58:56 +09:00
neuecc
a9e5fd4589 Add UniTaskAsyncEnumerable.Subscribe 2020-06-13 18:58:43 +09:00
Yoshifumi Kawai
a3f3a28ea1 Update README.md 2020-06-12 17:18:31 +09:00
Yoshifumi Kawai
59020df965 Update README.md 2020-06-12 17:18:22 +09:00
Yoshifumi Kawai
ac01be79bf Update README.md 2020-06-12 16:59:14 +09:00
neuecc
de5951f208 lock q 2020-06-12 10:46:29 +09:00
neuecc
ded9a561db 2.0.16 2020-06-12 03:16:06 +09:00
neuecc
a2c18eb343 AsyncReactiveProperty document 2020-06-11 23:50:13 +09:00
neuecc
0b27c3a342 Merge remote-tracking branch 'origin/master' 2020-06-11 23:32:00 +09:00
neuecc
9c86cfb508 Add IDisposable.AddTo(cancellationToken) 2020-06-11 23:31:55 +09:00
Yoshifumi Kawai
7e5e6ed6c2 Update README.md 2020-06-11 20:21:51 +09:00
Yoshifumi Kawai
d081e5f40b Update README.md 2020-06-11 17:48:41 +09:00
neuecc
344ae0738c docs: update TOC 2020-06-11 08:44:42 +00:00
neuecc
1b553f67b0 compare table 2020-06-11 17:44:22 +09:00
neuecc
bf0adad427 test for IL2CPP bug 2020-06-11 17:10:29 +09:00
neuecc
11ca42a527 Merge remote-tracking branch 'origin/master' 2020-06-11 16:43:11 +09:00
neuecc
4898e4c7bf Add (I/ReadOnly)AsyncReactiveProperty.WaitAsync 2020-06-11 16:42:59 +09:00
Yoshifumi Kawai
d494e0b9e3 Merge pull request #88 from guitarrapc/master
chore: stop run build-unity via fork PR
2020-06-11 14:35:12 +09:00
Ikiru Yoshizaki
be26ab249b chore: strict filter 2020-06-10 18:17:53 +09:00
Ikiru Yoshizaki
611d8d5513 chore: filter build-unity to be run on Cysharp only 2020-06-10 18:06:50 +09:00
Yoshifumi Kawai
95c93b7c3d Merge pull request #87 from guitarrapc/master
feat: Android il2cpp support
2020-06-10 17:07:56 +09:00
Ikiru Yoshizaki
1dd0c49eec faet: android il2cpp 2020-06-10 13:06:45 +09:00
neuecc
5d8e0e61ad workaround for IL2CPP bug, back to zero allocation 2020-06-10 11:36:58 +09:00
neuecc
478126e256 v 2020-06-09 15:06:34 +09:00
neuecc
80704e489d Merge branch 'fix-il2cpp' 2020-06-09 14:52:23 +09:00
neuecc
3c0aa03643 if def 2020-06-09 14:52:04 +09:00
neuecc
37cd00d347 2.0.15? 2020-06-09 12:16:28 +09:00
neuecc
859c4d706f logging 2020-06-09 12:00:12 +09:00
neuecc
7289fe6e25 ToCoroutine throws exception when error detected 2020-06-09 11:26:03 +09:00
neuecc
0c33977f5a timer and delay skip current frame 2020-06-09 11:25:45 +09:00
neuecc
4d4466e801 Add UniTask.WaitForFixedUpdate 2020-06-08 02:22:10 +09:00
neuecc
79330d7cdb ReadMe 2020-06-08 01:50:20 +09:00
neuecc
680ce1098b ReadMe 2020-06-08 01:45:57 +09:00
neuecc
2337d705ec WithCancellation uses native timing of asyncOperation 2020-06-08 01:45:41 +09:00
neuecc
d2880a818f Add UniTask.NextFrame, UniTask.WaitForEndOfFrame 2020-06-08 01:44:31 +09:00
neuecc
86ea128bf4 t4gen 2020-06-08 00:30:27 +09:00
neuecc
a66f378622 // for CI 2020-06-07 00:27:33 +09:00
neuecc
265f88584b store application.datapath on initialize #86 2020-06-06 14:21:31 +09:00
neuecc
686394c861 2.0.14 pkg version 2020-06-05 17:16:34 +09:00
neuecc
8bad158ab4 fix ci 2020-06-05 17:05:36 +09:00
neuecc
5e59e7ec86 netstandard2.0 support #84 2020-06-05 16:45:40 +09:00
neuecc
8bb0a48720 workaround for struct enumerator await 2020-06-05 15:08:13 +09:00
neuecc
b4468b4eba improve DOTween Extensions 2020-06-05 12:32:16 +09:00
neuecc
0725bd1b30 Merge remote-tracking branch 'origin/master' 2020-06-05 00:44:16 +09:00
neuecc
ebd80243e0 Fix UniTask.ReturnToCurrentSynchronizationContext 2020-06-05 00:43:54 +09:00
neuecc
f1ac469058 Fix UniTask.Defer 2020-06-05 00:43:03 +09:00
neuecc
2e0b603d25 docs: update TOC 2020-06-04 08:54:24 +00:00
neuecc
1d90a40f66 more ReadMe 2020-06-04 17:53:57 +09:00
Mayuki Sawatari
1bec3f507e Merge pull request #75 from Cysharp/feature/docs
Generate and publish documentation to GitHub Pages
2020-06-04 16:16:03 +09:00
Mayuki Sawatari
1d5ecbb3ab Include sub-directory source codes 2020-06-04 15:46:21 +09:00
Mayuki Sawatari
e1a4aeb9da Update toc.yml 2020-06-04 15:46:21 +09:00
Mayuki Sawatari
43f1bb4d85 Generate and publish documentation to GitHub Pages 2020-06-04 15:46:21 +09:00
Yoshifumi Kawai
8b7a0e9b15 Update README.md 2020-06-04 15:31:42 +09:00
neuecc
da329e19d1 CI more 2020-06-04 15:24:09 +09:00
Yoshifumi Kawai
b8260d4e91 Merge pull request #61 from Cysharp/unitask2
UniTask2
2020-06-04 15:17:19 +09:00
neuecc
345e32aaf0 CI again 2020-06-04 15:14:40 +09:00
neuecc
d7bef8c5b5 setup release 2020-06-04 15:11:51 +09:00
neuecc
b2f82df4d3 fix action 2020-06-04 14:58:11 +09:00
neuecc
83596b3d1f docs: update TOC 2020-06-04 05:53:07 +00:00
neuecc
0022598a1c readme 2020-06-04 14:52:42 +09:00
neuecc
f4294d3752 Task Pool moved from Internal 2020-06-04 14:52:36 +09:00
neuecc
a1444c0b39 refactor DistinctUntilChanged 2020-06-03 02:03:31 +09:00
neuecc
e1d5359d73 refactor Where 2020-06-03 01:18:39 +09:00
neuecc
239bf749b6 refactor UniTaskAsyncEnumerable.Select 2020-06-03 00:50:39 +09:00
neuecc
2bf3b1e172 pj 2020-06-02 23:27:42 +09:00
neuecc
d225de201f gitignore 2020-06-02 23:00:47 +09:00
neuecc
c3b8a3852d UniTask.Linq.asmdef 2020-06-02 23:00:20 +09:00
neuecc
c31dab888e fix UnITaskTracker causes Stackoverflow 2020-06-02 22:49:40 +09:00
neuecc
d4cf59bd2f CI 2020-06-01 14:50:25 +09:00
neuecc
d5edc3acd3 guard for trigger invalid ops 2020-06-01 13:51:37 +09:00
neuecc
130286e8c2 TriggerEvent becomes linkedlist 2020-06-01 13:40:35 +09:00
neuecc
a9baa52309 Add ReturnToMainThread, ReturnToSynchronizationContext, ReturnToCurrentSynchronizationContext 2020-05-31 04:30:03 +09:00
neuecc
3001996298 Q check 2020-05-31 04:28:05 +09:00
neuecc
bfcd18aabb MoveNextRunner -> StateMachineRunner 2020-05-29 14:35:50 +09:00
neuecc
96aa299e7e support version -> unity 2018.4 2020-05-29 14:25:26 +09:00
neuecc
24faa34418 TaskPoolMonitor -> TaskPool 2020-05-29 03:58:22 +09:00
neuecc
21bf08a6b3 replace all promisepool to new taskpool system 2020-05-29 03:43:18 +09:00
neuecc
d5db96b913 rename StackNode to TaskPool 2020-05-29 02:08:11 +09:00
neuecc
a8455af16d Improve pooling mechanism 2020-05-29 01:22:46 +09:00
neuecc
2290b14532 reduce AsyncBuilder heap allocation 2020-05-28 22:20:06 +09:00
neuecc
90c5a6311b check il2cpp generics limitation 2020-05-28 21:18:35 +09:00
neuecc
6e0ad3623b non public 2020-05-27 09:16:46 +09:00
neuecc
005e02a1fa sealed 2020-05-27 07:39:19 +09:00
neuecc
10fb8060fa use PooledDelegate to avoid convert Action to Action<AsyncOperation> allocation 2020-05-27 07:37:16 +09:00
neuecc
35b933730b 2.0.11-rc8 2020-05-25 19:44:57 +09:00
neuecc
7ab9467069 IObservable<T>.ToUniTask parameter order changed from CancellationToken cancellationToken, bool useFirst to bool useFirst, CancellationToken cancellationToken 2020-05-25 19:42:51 +09:00
neuecc
598312ba61 DOTween's WithCancellation remove = default 2020-05-25 19:38:08 +09:00
neuecc
985aa5c43a add allocationchecker in netcore sandbox project 2020-05-25 19:37:11 +09:00
neuecc
10eff95a42 fix: does not work zero-allocation mechanism in release build 2020-05-25 19:36:39 +09:00
neuecc
d27d6d5d9d UniTask.Yield in .NET Core becomes zero allocation 2020-05-25 19:33:54 +09:00
neuecc
b8c109848e UniTaskVoid can not await. 2020-05-25 19:33:11 +09:00
neuecc
8b7f832c0f JobHandle.WaitAsync accepts CancellationToken 2020-05-25 09:58:06 +09:00
neuecc
7cce0f48e5 call webRequest.Abort() on canceled 2020-05-25 09:54:26 +09:00
neuecc
8a56838111 modify cannot await twice message 2020-05-25 01:55:35 +09:00
neuecc
ff15e00003 f 2020-05-24 03:33:25 +09:00
neuecc
f60d2c51fb fix in UnityEditor performance issue 2020-05-24 03:27:05 +09:00
neuecc
6dfb969015 Add ValueTask.AsUniTask only for .NET Core 2020-05-24 01:30:52 +09:00
neuecc
da7e9fc4b3 UniTask marked StructLayout(LayoutKind.Auto) 2020-05-24 01:30:33 +09:00
neuecc
70385c4115 Fix channel's cancellationTokenRegistration does not handle correctly 2020-05-24 00:19:13 +09:00
neuecc
51ba740413 In .NET Core, IUniTaskSource implements IValueTaskSource and implicit, zero overhead conversion 2020-05-24 00:18:39 +09:00
neuecc
f3e3ba8864 fix DOTweenExt's TweenCancelBehaviour.CancelAwait 2020-05-23 02:53:48 +09:00
neuecc
07cf65c1ec lower support is 2019.1 2020-05-23 02:12:19 +09:00
neuecc
eca5b1c096 2.0.10-rc7 2020-05-23 02:11:00 +09:00
neuecc
c74ce14ad1 DoTween -> DOTween 2020-05-23 02:10:18 +09:00
neuecc
f59c56506f Remove ConfigureAwait method from all async object extensions(renamed to ToUniTask). Add WithCancellation method to all async object extensions. Improved performance when async object is done. 2020-05-23 02:07:46 +09:00
neuecc
896eef1ee4 Add DoTween Extension 2020-05-23 01:10:04 +09:00
neuecc
ec0123eec7 rename UniTask.VoidAction -> UniTask.Action, UniTask.VoidUnityAction -> UniTask.UnityAction, there return type Func<UniTask> -> Func<UniTaskVoid> 2020-05-22 21:42:23 +09:00
neuecc
78f56b9b33 OCS 2020-05-22 17:15:36 +09:00
neuecc
1d88ed85bc fix ComibineLatest 2020-05-22 11:08:03 +09:00
neuecc
2b7986da19 2.0.9-rc6 2020-05-22 10:45:16 +09:00
neuecc
c3d22968e1 State rename to AsyncReadOnlyReactiveProperty and removed setter and implicit conversion. 2020-05-22 10:44:41 +09:00
neuecc
0e25122ee2 Add Pairwise 2020-05-22 03:19:54 +09:00
neuecc
4504d84aa8 cl2 2020-05-22 02:34:04 +09:00
neuecc
2b87cadba3 Add CombineLatest 2020-05-22 02:25:36 +09:00
neuecc
21dc83c641 2.0.8-rc5 2020-05-21 02:26:55 +09:00
neuecc
3b593f349c Add UnityEvent<T> and InputField AsyncEventHandler and extensions 2020-05-21 02:26:09 +09:00
neuecc
962c215e3b Fix UniTask.WaitUntilValueChanged does not handle UnityEngine.Object is destroyed correctly 2020-05-21 02:24:12 +09:00
neuecc
42dcfdbcdc Add UniTaskAsyncEnumerable.EveryValueChanged 2020-05-21 02:23:40 +09:00
neuecc
6d7e6ec871 Impl AsyncReactiveProperty.ToString, Add State 2020-05-21 02:22:24 +09:00
neuecc
36d53a3bcb rc4 2020-05-20 11:05:58 +09:00
neuecc
ea9e61c2e1 Add CancellationToken.WaitUntilCanceled 2020-05-20 11:04:59 +09:00
neuecc
a52c26102b guard for ForEachAsync 2020-05-20 10:48:28 +09:00
neuecc
e31c87b8a8 Add IUniTaskAsyncEnumerable.Publish 2020-05-19 15:58:04 +09:00
neuecc
cc165a6897 2.0.6-rc3 2020-05-19 04:14:23 +09:00
neuecc
f99910d802 Add TaskTracker to AsyncLINQ 2020-05-19 04:13:46 +09:00
neuecc
997b0b3710 Merge remote-tracking branch 'origin/unitask2' into unitask2 2020-05-19 03:43:09 +09:00
neuecc
ec7064083a Add TaskTracker to Channel 2020-05-19 03:43:06 +09:00
neuecc
07cccfddd6 docs: update TOC 2020-05-18 18:34:05 +00:00
neuecc
f07527cd06 ECS? 2020-05-19 03:33:44 +09:00
neuecc
7b273c4bd1 Add UniTask.Defer 2020-05-19 03:10:37 +09:00
neuecc
d36e7987b3 Add SkipUntilCanceled, TakeUntilCanceled 2020-05-19 02:41:45 +09:00
neuecc
bbd5686816 Add UniTask.WaitUntilCanceled 2020-05-19 01:35:16 +09:00
neuecc
fb1152d8f4 IAsyncReadOnlyReactiveProperty -> IReadOnlyAsyncReactiveProperty, .Dipose retrurns MoveNext -> false 2020-05-19 01:20:20 +09:00
neuecc
7a306118f5 AsyncTrigger returns MoveNext -> false when destroyed 2020-05-19 01:19:46 +09:00
neuecc
efaf3ee8f5 IAsyncReadOnlyReactiveProperty.WithoutCurrent 2020-05-18 23:36:26 +09:00
neuecc
2e4fe90956 Fix ChannelReader.Completion throws UnobservedException when not touched 2020-05-18 23:33:13 +09:00
neuecc
e33d572104 2.0.5-rc2 2020-05-18 11:33:24 +09:00
neuecc
2b2af9e455 meta 2020-05-18 11:31:23 +09:00
neuecc
d003597662 Changed AsyncReactiveProperty produce current value at first, Add AsyncReactiveProperty.WithoutCurrent 2020-05-18 11:30:49 +09:00
neuecc
ec0a8f5a8b Add IUniTaskAsyncEnumerable.Queue 2020-05-18 11:30:04 +09:00
neuecc
49ba57f20a Fix IUniTaskAsyncEnumerable.Take 2020-05-18 11:29:35 +09:00
neuecc
6f4d1183cc Add BindTo<TSource, TObject>(Action<TObject, TSource> bindAction) 2020-05-18 02:35:13 +09:00
neuecc
dd18c9fff8 Add Channel.CreateSingleConsumerUnbounded 2020-05-18 02:34:29 +09:00
neuecc
21f5f78ff1 Merge remote-tracking branch 'origin/unitask2' into unitask2 2020-05-17 16:51:20 +09:00
neuecc
1729f389db fix ReactiveProperty implements IAsyncReactiveProperty 2020-05-17 16:51:10 +09:00
neuecc
6d37bb7bac docs: update TOC 2020-05-17 07:50:05 +00:00
neuecc
957adfad7a fix Await UniTaskAsyncEnumerable.Timer is not over. #76 2020-05-17 16:49:44 +09:00
neuecc
3ef889e17d no artifact name? 2020-05-17 03:02:55 +09:00
neuecc
c73af7390f removed manifestjson 2020-05-17 02:51:51 +09:00
neuecc
c5b4376486 gh-actions 2020-05-17 02:43:54 +09:00
neuecc
ba65049dd8 2.0.4-rc1 2020-05-17 01:39:56 +09:00
neuecc
ee58aab0a9 Add AsyncReactiveProperty 2020-05-17 01:29:45 +09:00
neuecc
859eaa2278 reduce AsyncTrigger allocation 2020-05-16 23:31:49 +09:00
neuecc
79f770e687 Improve Stacktrace Part2 2020-05-13 11:36:33 +09:00
neuecc
8ff4de67a1 improving stacktrace 2020-05-13 05:49:48 +09:00
254 changed files with 34127 additions and 4282 deletions

View File

@@ -1,114 +0,0 @@
version: 2.1
executors:
unity:
# https://hub.docker.com/r/gableroux/unity3d/tags
parameters:
version: {type: string}
docker:
- image: gableroux/unity3d:<< parameters.version >>
go:
docker:
- image: circleci/golang
commands:
unity_activate:
parameters:
unity_version: {type: string}
unity_license: {type: string}
steps:
# get activation file, if fail to activate unity, use this key and activate from https://license.unity3d.com/manual
- run: apt update && apt install libunwind8 -y
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -logFile -createManualActivationFile || exit 0
- run: cat Unity_v<< parameters.unity_version >>.alf
# get from UNITY_LICENSE envvar(base64 encoded(cat foo.ulf | base64 )), this file is generated from above manual activation
- run: echo << parameters.unity_license >> | base64 -di >> .circleci/Unity.ulf
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -manualLicenseFile .circleci/Unity.ulf || exit 0
jobs:
build-and-test:
parameters:
unity_version: {type: string}
unity_license: {type: string}
executor:
name: unity
version: << parameters.unity_version >>
steps:
- checkout
- unity_activate:
unity_version: << parameters.unity_version >>
unity_license: << parameters.unity_license >>
- run:
name: Build Linux(Mono)
command: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend Mono2x /BuildTarget StandaloneLinux64
working_directory: .
# TODO:check unity version and packages...
# - run: ./bin/UnitTest/StandaloneLinux64_Mono2x/test
build-and-create-package:
parameters:
unity_version: {type: string}
unity_license: {type: string}
executor:
name: unity
version: << parameters.unity_version >>
steps:
- checkout
- unity_activate:
unity_version: << parameters.unity_version >>
unity_license: << parameters.unity_license >>
- run:
name: Export unitypackage
command: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
working_directory: .
- store_artifacts:
path: ./UniRx.Async.unitypackage
destination: /UniRx.Async.unitypackage
# upload to github by ghr
upload-github:
executor: go
steps:
- attach_workspace:
at: .
- run: go get github.com/tcnksm/ghr
- run: ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} ${CIRCLE_TAG} .
- store_artifacts:
path: UniRx.Async.unitypackage
destination: UniRx.Async.unitypackage
workflows:
version: 2
build-unity:
jobs:
# does not exists yet.
# - build-and-test:
# unity_version: 2019.3.0a2
# unity_license: ${UNITY_LICENSE_2019_3}
# - build-and-test:
# unity_version: 2019.2.0b2
# unity_license: ${UNITY_LICENSE_2019_2}
- build-and-test:
unity_version: 2019.1.2f1
unity_license: ${UNITY_LICENSE_2019_1}
filters:
tags:
only: /.*/
# test asmdef will not found.
# - build-and-test:
# unity_version: 2018.4.0f1
# unity_license: ${UNITY_LICENSE_2018_4}
# # UniTask minimum support version is 2018.3(C# 7.x)
# - build-and-test:
# unity_version: 2018.3.12f1
# unity_license: ${UNITY_LICENSE_2018_3}
- build-and-create-package:
unity_version: 2019.1.2f1
unity_license: ${UNITY_LICENSE_2019_1}
filters:
tags:
only: /^\d\.\d\.\d.*/
branches:
ignore: /.*/
- upload-github:
requires:
- build-and-create-package
filters:
tags:
only: /^\d\.\d\.\d.*/
branches:
ignore: /.*/

67
.github/workflows/build-debug.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: Build-Debug
on:
push:
branches:
- "**"
tags:
- "!*" # not a tag push
pull_request:
types:
- opened
- synchronize
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.101
- run: dotnet test -c Debug ./src/UniTask.NetCoreTests/UniTask.NetCoreTests.csproj
build-unity:
if: "(github.event == 'push' && github.repository_owner == 'Cysharp') || startsWith(github.event.pull_request.head.label, 'Cysharp:')"
strategy:
matrix:
unity: ["2019.3.9f1", "2020.1.0b5"]
include:
- unity: 2019.3.9f1
license: UNITY_2019_3
- unity: 2020.1.0b5
license: UNITY_2020_1
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
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
# Execute scripts: RuntimeUnitTestToolkit
- name: Build UnitTest(Linux64, mono)
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend mono /BuildTarget StandaloneLinux64
working-directory: src/UniTask
- name: Execute UnitTest
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_Mono2x/test
# Execute scripts: Export Package
- name: Export unitypackage
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
working-directory: src/UniTask
# Store artifacts.
- uses: actions/upload-artifact@v2
with:
name: UniTask.unitypackage.zip
path: ./src/UniTask/*.unitypackage

31
.github/workflows/build-docs.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: build-docs
on:
push:
branches:
- master
- feature/docs
jobs:
run-docfx:
if: "!(contains(github.event.head_commit.message, '[skip ci]') || contains(github.event.head_commit.message, '[ci skip]'))"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v2
with:
repository: Cysharp/DocfxTemplate
path: docs/_DocfxTemplate
- uses: Kirbyrawr/docfx-action@master
name: Docfx metadata
with:
args: metadata docs/docfx.json
- uses: Kirbyrawr/docfx-action@master
name: Docfx build
with:
args: build docs/docfx.json
- name: Publish to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/_site

105
.github/workflows/build-release.yml vendored Normal file
View File

@@ -0,0 +1,105 @@
name: Build-Release
on:
push:
tags:
- "[0-9]+.[0-9]+.[0-9]+*"
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.101
# set release tag(*.*.*) to env.GIT_TAG
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
# 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 }}
# Store artifacts.
- uses: actions/upload-artifact@v1
with:
name: nuget
path: ./src/UniTask.NetCore/bin/Release/UniTask.${{ env.GIT_TAG }}.nupkg
build-unity:
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
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 ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
# Execute scripts: Export Package
- name: Export unitypackage
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
working-directory: src/UniTask
# Store artifacts.
- uses: actions/upload-artifact@v2
with:
name: UniTask.${{ env.GIT_TAG }}.unitypackage
path: ./src/UniTask/UniTask.${{ env.GIT_TAG }}.unitypackage
create-release:
needs: [build-dotnet, build-unity]
runs-on: ubuntu-latest
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
NUGET_XMLDOC_MODE: skip
steps:
# setup dotnet for nuget push
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.101
# set release tag(*.*.*) to env.GIT_TAG
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
# Create Releases
- uses: actions/create-release@v1
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Ver.${{ github.ref }}
# Download(All) Artifacts to current directory
- uses: actions/download-artifact@v2-preview
# Upload to NuGet
- run: dotnet nuget push "./nuget/*.nupkg" -s https://www.nuget.org/api/v2/package -k ${{ secrets.NUGET_KEY }}
# Upload to Releases(unitypackage)
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./UniTask.${{ env.GIT_TAG }}.unitypackage/UniTask.${{ env.GIT_TAG }}.unitypackage
asset_name: UniTask.${{ env.GIT_TAG }}.unitypackage
asset_content_type: application/octet-stream

15
.github/workflows/toc.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: TOC Generator
on:
push:
paths:
- 'README.md'
jobs:
generateTOC:
name: TOC Generator
runs-on: ubuntu-latest
steps:
- uses: technote-space/toc-generator@v2.4.0
with:
TOC_TITLE: "## Table of Contents"

98
.gitignore vendored
View File

@@ -157,3 +157,101 @@ src/UniTask/UniTask.Tests.csproj
src/UniTask/UniTask.Tests.Editor.csproj
src/UniTask/UniTask.*.unitypackage
src/UniTask/UniTask.Linq.csproj
src/UniTask/DOTween.Modules.csproj
src/UniTask/Unity.Addressables.csproj
src/UniTask/Unity.Addressables.Editor.csproj
src/UniTask/Unity.Analytics.DataPrivacy.csproj
src/UniTask/Unity.Recorder.csproj
src/UniTask/Unity.Recorder.Editor.csproj
src/UniTask/Unity.ResourceManager.csproj
src/UniTask/Unity.Rider.Editor.csproj
src/UniTask/Unity.ScriptableBuildPipeline.csproj
src/UniTask/Unity.ScriptableBuildPipeline.Editor.csproj
src/UniTask/Unity.TextMeshPro.csproj
src/UniTask/Unity.TextMeshPro.Editor.csproj
src/UniTask/Unity.Timeline.csproj
src/UniTask/Unity.Timeline.Editor.csproj
src/UniTask/Unity.VisualStudio.Editor.csproj
src/UniTask/Unity.VSCode.Editor.csproj
src/UniTask/UnityEditor.CacheServer.csproj
src/UniTask/UnityEditor.TestRunner.csproj
src/UniTask/UnityEditor.UI.csproj
src/UniTask/UnityEngine.Advertisements.csproj
src/UniTask/UnityEngine.Monetization.csproj
src/UniTask/UnityEngine.TestRunner.csproj
src/UniTask/UnityEngine.UI.csproj
src/UniTask/TempAsm.csproj
src/UniTask/UniTask.Addressables.csproj
src/UniTask/UniTask.DOTween.csproj
src/UniTask/UniTask.TextMeshPro.csproj
src/UniTask/RuntimeUnitTestToolkit.Player.csproj
src/UniTask/TempAsm.Player.csproj
src/UniTask/UniTask.Addressables.Player.csproj
src/UniTask/UniTask.DOTween.Player.csproj
src/UniTask/UniTask.Linq.Player.csproj
src/UniTask/UniTask.Player.csproj
src/UniTask/UniTask.Tests.Player.csproj
src/UniTask/UniTask.TextMeshPro.Player.csproj
src/UniTask/Unity.Addressables.Player.csproj
src/UniTask/Unity.Analytics.DataPrivacy.Player.csproj
src/UniTask/Unity.ResourceManager.Player.csproj
src/UniTask/Unity.ScriptableBuildPipeline.Player.csproj
src/UniTask/Unity.TextMeshPro.Player.csproj
src/UniTask/Unity.Timeline.Player.csproj
src/UniTask/UnityEngine.Advertisements.Player.csproj
src/UniTask/UnityEngine.Monetization.Player.csproj
src/UniTask/UnityEngine.TestRunner.Player.csproj
src/UniTask/UnityEngine.UI.Player.csproj
src/UniTask/DOTween.Modules.Player.csproj
src/UniTask/Assembly-CSharp.Player.csproj
src/UniTask/Unity.EditorCoroutines.Editor.csproj

806
README.md
View File

@@ -1,50 +1,115 @@
# UniTask
UniTask
===
[![GitHub Actions](https://github.com/Cysharp/UniTask/workflows/Build-Debug/badge.svg)](https://github.com/Cysharp/UniTask/actions) [![Releases](https://img.shields.io/github/release/Cysharp/UniTask.svg)](https://github.com/Cysharp/UniTask/releases)
[![CircleCI](https://circleci.com/gh/Cysharp/UniTask.svg?style=svg)](https://circleci.com/gh/Cysharp/UniTask)
Provides an efficient allocation free async/await integration to Unity.
Provides an efficient async/await integration to Unity.
* Struct based `UniTask<T>` and custom AsyncMethodBuilder to achive zero allocation
* All Unity AsyncOperations and Coroutine to awaitable
* PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc..) that enable to replace all coroutine operation
* MonoBehaviour Message Events and uGUI Events as awaitable/async-enumerable
* Completely run on Unity's PlayerLoop so don't use thread and run on WebGL, wasm, etc.
* Asynchronous LINQ, with Channel and AsyncReactiveProperty
* TaskTracker window to prevent memory leak
* Highly compatible behaviour with Task/ValueTask/IValueTaskSource
> UniTask was included in UniRx before v7 but now completely separated, it no dependent each other.
Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await for Unity, with Asynchronous LINQ
](https://medium.com/@neuecc/unitask-v2-zero-allocation-async-await-for-unity-with-asynchronous-linq-1aa9c96aa7dd)
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
## Table of Contents
- [Getting started](#getting-started)
- [Basics of UniTask and AsyncOperation](#basics-of-unitask-and-asyncoperation)
- [Cancellation and Exception handling](#cancellation-and-exception-handling)
- [Progress](#progress)
- [PlayerLoop](#playerloop)
- [async void vs async UniTaskVoid](#async-void-vs-async-unitaskvoid)
- [UniTaskTracker](#unitasktracker)
- [External Assets](#external-assets)
- [AsyncEnumerable and Async LINQ](#asyncenumerable-and-async-linq)
- [Awaitable Events](#awaitable-events)
- [Channel](#channel)
- [For Unit Testing](#for-unit-testing)
- [ThreadPool limitation](#threadpool-limitation)
- [IEnumerator.ToUniTask limitation](#ienumeratortounitask-limitation)
- [For UnityEditor](#for-unityeditor)
- [Compare with Standard Task API](#compare-with-standard-task-api)
- [Pooling Configuration](#pooling-configuration)
- [Allocation on Profiler](#allocation-on-profiler)
- [UniTaskSynchronizationContext](#unitasksynchronizationcontext)
- [API References](#api-references)
- [UPM Package](#upm-package)
- [Install via git URL](#install-via-git-url)
- [Install via OpenUPM](#install-via-openupm)
- [.NET Core](#net-core)
- [License](#license)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
Getting started
---
Install package(`UniRx.Async.unitypackage`) is available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases) page.
Install via [UPM package](#upm-package) or asset package(`UniTask.*.*.*.unitypackage`) available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases) page.
```csharp
// extension awaiter/methods can be used by this namespace
using UniRx.Async;
using Cysharp.Threading.Tasks;
// You can return type as struct UniTask<T>(or UniTask), it is unity specialized lightweight alternative of Task<T>
// no(or less) allocation and fast excution for zero overhead async/await integrate with Unity
// zero allocation and fast excution for zero overhead async/await integrate with Unity
async UniTask<string> DemoAsync()
{
// You can await Unity's AsyncObject
var asset = await Resources.LoadAsync<TextAsset>("foo");
// .ConfigureAwait accepts progress callback
await SceneManager.LoadSceneAsync("scene2").ConfigureAwait(Progress.Create<float>(x => Debug.Log(x)));
var txt = (await UnityWebRequest.Get("https://...").SendWebRequest()).downloadHandler.text;
await SceneManager.LoadSceneAsync("scene2");
// .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy());
// .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x)));
// await frame-based operation like coroutine
await UniTask.DelayFrame(100);
// replacement of WaitForSeconds/WaitForSecondsRealtime
// replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
await UniTask.Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false);
// replacement of WaitForEndOfFrame(or other timing like yield return null, yield return WaitForFixedUpdate)
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
// yield any playerloop timing(PreUpdate, Update, LateUpdate, etc...)
await UniTask.Yield(PlayerLoopTiming.PreLateUpdate);
// replacement of yield return null
await UniTask.Yield();
await UniTask.NextFrame();
// replacement of WaitForEndOfFrame(same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate))
await UniTask.WaitForEndOfFrame();
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
await UniTask.WaitForFixedUpdate();
// replacement of yield return WaitUntil
await UniTask.WaitUntil(() => isActive == false);
// special helper of WaitUntil
await UniTask.WaitUntilValueChanged(this, x => x.isActive);
// You can await IEnumerator coroutine
await FooCoroutineEnumerator();
// You can await standard task
await Task.Run(() => 100);
// Multithreading, run on ThreadPool under this code(use SwitchToMainThread, same as `ObserveOnMainThreadDispatcher`)
// Multithreading, run on ThreadPool under this code
await UniTask.SwitchToThreadPool();
/* work on ThreadPool */
// return to MainThread(same as `ObserveOnMainThread` in UniRx)
await UniTask.SwitchToMainThread();
// get async webrequest
async UniTask<string> GetTextAsync(UnityWebRequest req)
{
@@ -58,6 +123,9 @@ async UniTask<string> DemoAsync()
// concurrent async-wait and get result easily by tuple syntax
var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3);
// shorthand of WhenAll, tuple can await directly
var (google2, bing2, yahoo2) = await (task1, task2, task3);
// You can handle timeout easily
await GetTextAsync(UnityWebRequest.Get("http://unity.com")).Timeout(TimeSpan.FromMilliseconds(300));
@@ -67,54 +135,44 @@ async UniTask<string> DemoAsync()
}
```
`UniTask<T>`
Basics of UniTask and AsyncOperation
---
UniTask feature rely on C# 7.0([task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)) so required Unity version is after `Unity 2018.3`.
UniTask feature rely on C# 7.0([task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)) so required Unity version is after `Unity 2018.3`, officialy lower support version is `Unity 2018.4.13f1`.
Why UniTask(custom task-like object) is required? Because Task is too heavy, not matched to Unity threading(single-thread). UniTask does not use thread and SynchronizationContext because almost Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It acquires more fast and more less allocation, completely integrated with Unity.
Why UniTask(custom task-like object) is required? Because Task is too heavy, not matched to Unity threading(single-thread). UniTask does not use thread and SynchronizationContext/ExecutionContext because almost Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It acquires more fast and more less allocation, completely integrated with Unity.
> More details, please see this slide: [Deep Dive async/await in Unity with UniTask(EN)
](https://www.slideshare.net/neuecc/deep-dive-asyncawait-in-unity-with-unitasken)
You can await `AsyncOperation`, `ResourceRequest`, `AssetBundleRequest`, `AssetBundleCreateRequest`, `UnityWebRequestAsyncOperation`, `AsyncGPUReadbackRequest`, `IEnumerator` and others when `using Cysharp.Threading.Tasks;`.
You can await `AsyncOperation`, `ResourceRequest`, `UnityWebRequestAsyncOperation`, `IEnumerator` and others when using `UniRx.Async`.
`UniTask.Delay`, `UniTask.Yield`, `UniTask.Timeout` that is frame-based timer operators(no uses thread so works on WebGL publish) driven by custom PlayerLoop(Unity 2018 experimental feature). In default, UniTask initialize automatically when application begin, but it is override all. If you want to append PlayerLoop, please call `PlayerLoopHelper.Initialize(ref yourCustomizedPlayerLoop)` manually.
> Before Unity 2019.3, Unity does not have `PlayerLooop.GetCurrentPlayerLoop` so you can't use with Unity ECS package in default. If you want to use with ECS and before Unity 2019.3, you can use this hack below.
UniTask provides three pattern of extension methods.
```csharp
// Get ECS Loop.
var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
// Setup UniTask's PlayerLoop.
PlayerLoopHelper.Initialize(ref playerLoop);
* await asyncOperation;
* .WithCancellation(CancellationToken);
* .ToUniTask(IProgress, PlayerLoopTiming, CancellationToken);
```
`UniTask.WhenAll`, `UniTask.WhenAny` is like Task.WhenAll/WhenAny but return type is more useful.
`WithCancellation` is a simple version of `ToUniTask`, both returns `UniTask`. Details of cancellation, see: [Cancellation and Exception handling](#cancellation-and-exception-handling) section.
`UniTask.ctor(Func<UniTask>)` is like the embeded [`AsyncLazy<T>`](https://blogs.msdn.microsoft.com/pfxteam/2011/01/15/asynclazyt/)
> Note: WithCancellation is returned from native timing of PlayerLoop but ToUniTask is returned from specified PlayerLoopTiming. Details of timing, see: [PlayerLoop](#playerloop) section.
> Note: AssetBundleRequest has `asset` and `allAssets`, in default await returns `asset`. If you want to get `allAssets`, you can use `AwaitForAllAssets()` method.
The type of `UniTask` can use utility like `UniTask.WhenAll`, `UniTask.WhenAny`. It is like Task.WhenAll/WhenAny but return type is more useful, returns value tuple so can deconsrtuct each result and pass multiple type.
```csharp
public class SceneAssets
public async UniTaskVoid LoadManyAsync()
{
public readonly UniTask<Sprite> Front;
public readonly UniTask<Sprite> Background;
public readonly UniTask<Sprite> Effect;
// parallel load.
var (a, b, c) = await UniTask.WhenAll(
LoadAsSprite("foo"),
LoadAsSprite("bar"),
LoadAsSprite("baz"));
}
public SceneAssets()
{
// ctor(Func) overload is AsyncLazy, initialized once when await.
// and after it, await returns zero-allocation value immediately.
Front = new UniTask<Sprite>(() => LoadAsSprite("foo"));
Background = new UniTask<Sprite>(() => LoadAsSprite("bar"));
Effect = new UniTask<Sprite>(() => LoadAsSprite("baz"));
}
async UniTask<Sprite> LoadAsSprite(string path)
{
var resource = await Resources.LoadAsync<Sprite>(path);
return (resource as Sprite);
}
async UniTask<Sprite> LoadAsSprite(string path)
{
var resource = await Resources.LoadAsync<Sprite>(path);
return (resource as Sprite);
}
```
@@ -133,13 +191,32 @@ public UniTask<int> WrapByUniTaskCompletionSource()
}
```
You can convert Task -> UniTask: `AsUniTask`, `UniTask` -> `UniTask<AsyncUnit>`: `AsAsyncUnitUniTask`(this is useful to use WhenAll/WhenAny), `UniTask<T>` -> `UniTask`: `AsUniTask`.
You can convert Task -> UniTask: `AsUniTask`, `UniTask` -> `UniTask<AsyncUnit>`: `AsAsyncUnitUniTask`, `UniTask<T>` -> `UniTask`: `AsUniTask`. `UniTask<T>` -> `UniTask`'s conversion cost is free.
If you want to convert async to coroutine, you can use `UniTask.ToCoroutine`, this is useful to use only allow coroutine system.
If you want to convert async to coroutine, you can use `.ToCoroutine()`, this is useful to use only allow coroutine system.
UniTask can not await twice. This is a similar constraint to the [ValueTask/IValueTaskSource](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1?view=netcore-3.1) introduced in .NET Standard 2.1.
> The following operations should never be performed on a ValueTask<TResult> instance:
>
> * Awaiting the instance multiple times.
> * Calling AsTask multiple times.
> * Using .Result or .GetAwaiter().GetResult() when the operation hasn't yet completed, or using them multiple times.
> * Using more than one of these techniques to consume the instance.
>
> If you do any of the above, the results are undefined.
```csharp
var task = UniTask.DelayFrame(10);
await task;
await task; // NG, throws Exception
```
Store to the class field, you can use `UniTask.Lazy` that gurantee call multipletimes. `.Preserve()` allows for multiple calls (internally cached results). This is useful when multiple calls in a function scope.
Cancellation and Exception handling
---
Some UniTask factory methods have `CancellationToken cancellation = default(CancellationToken)` parameter. Andalso some async operation for unity have `ConfigureAwait(..., CancellationToken cancellation = default(CancellationToken))` extension methods.
Some UniTask factory methods have `CancellationToken cancellationToken = default` parameter. Andalso some async operation for unity have `WithCancellation(CancellationToken)` and `ToUniTask(..., CancellationToken cancellation = default)` extension methods.
You can pass `CancellationToken` to parameter by standard [`CancellationTokenSource`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource).
@@ -151,7 +228,7 @@ cancelButton.onClick.AddListener(() =>
cts.Cancel();
});
await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().ConfigureAwait(cancellation: cts.Token);
await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().WithCancellation(cts.Token);
await UniTask.DelayFrame(1000, cancellationToken: cts.Token);
```
@@ -165,7 +242,7 @@ await UniTask.DelayFrame(1000, cancellationToken: this.GetCancellationTokenOnDes
When detect cancellation, all methods throws `OperationCanceledException` and propagate to upstream. `OperationCanceledException` is special exception, if not handled this exception, finally it is propagated to `UniTaskScheduler.UnobservedTaskException`.
Default behaviour of received unhandled exception is write log as warning. Log level can change by `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to change custom beavhiour, set action to `UniTaskScheduler.UnobservedTaskException.`
Default behaviour of received unhandled exception is write log as exception. Log level can change by `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to change custom beavhiour, set action to `UniTaskScheduler.UnobservedTaskException.`
If you want to cancel behaviour in async UniTask method, throws `OperationCanceledException` manually.
@@ -204,23 +281,23 @@ if (isCanceled)
}
```
Note: Only suppress throws if you call it directly into the most source method.
Note: Only suppress throws if you call it directly into the most source method. Otherwise, the return value will be converted, but the entire pipeline will not be suppressed throws.
Progress
---
Some async operation for unity have `ConfigureAwait(IProgress<float> progress = null, ...)` extension methods.
Some async operation for unity have `ToUniTask(IProgress<float> progress = null, ...)` extension methods.
```csharp
var progress = Progress.Create<float>(x => Debug.Log(x));
var request = await UnityWebRequest.Get("http://google.co.jp")
.SendWebRequest()
.ConfigureAwait(progress: progress);
.ToUniTask(progress: progress);
```
Should not use `new System.Progress<T>`, because it allocate every times. Use `UniRx.Async.Progress` instead. Progress factory has two methods, `Create` and `CreateOnlyValueChanged`. `CreateOnlyValueChanged` calls only when progress value changed. Should not use `new System.Progress<T>`, it allocate every times.
You should not use standard `new System.Progress<T>`, because it causes allocation every times. Use `Cysharp.Threading.Tasks.Progress` instead. This progress factory has two methods, `Create` and `CreateOnlyValueChanged`. `CreateOnlyValueChanged` calls only when progress value changed.
Implements interface is more better.
Implements IProgress interface to caller is more better, there is no allocation of lambda.
```csharp
public class Foo : MonoBehaviour, IProgress<float>
@@ -234,7 +311,148 @@ public class Foo : MonoBehaviour, IProgress<float>
{
var request = await UnityWebRequest.Get("http://google.co.jp")
.SendWebRequest()
.ConfigureAwait(progress: this); // pass this
.ToUniTask(progress: this); // pass this
}
}
```
PlayerLoop
---
UniTask is run on custom [PlayerLoop](https://docs.unity3d.com/ScriptReference/LowLevel.PlayerLoop.html). UniTask's playerloop based method(such as `Delay`, `DelayFrame`, `asyncOperation.ToUniTask`, etc...) accepts this `PlayerLoopTiming`.
```csharp
public enum PlayerLoopTiming
{
Initialization = 0,
LastInitialization = 1,
EarlyUpdate = 2,
LastEarlyUpdate = 3,
FixedUpdate = 4,
LastFixedUpdate = 5,
PreUpdate = 6,
LastPreUpdate = 7,
Update = 8,
LastUpdate = 9,
PreLateUpdate = 10,
LastPreLateUpdate = 11,
PostLateUpdate = 12,
LastPostLateUpdate = 13
}
```
It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop.
`PlayerLoopTiming.Update` is similar as `yield return null` in coroutine, but it is called before Update(Update and uGUI events(button.onClick, etc...) are called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). `PlayerLoopTiming.FixedUpdate` is similar as `WaitForFixedUpdate`, `PlayerLoopTiming.LastPostLateUpdate` is similar as `WaitForEndOfFrame` in coroutine.
`yield return null` and `UniTask.Yield` is similar but different. `yield return null` always return next frame but `UniTask.Yield` return next called, that is, call `UniTask.Yield(PlayerLoopTiming.Update)` on `PreUpdate`, it returns same frame. `UniTask.NextFrame()` gurantees return next frame, this would be expected to behave exactly the same as `yield return null`.
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and run on YieldRunner. It is most lightweight and faster.
AsyncOperation is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after called, loaded scene's `Start` called from `EarlyUpdate.ScriptRunDelayedStartupFrame`. Also `await UnityWebRequest` is returned from `EarlyUpdate.ExecuteMainThreadJobs`.
In UniTask, await directly and `WithCancellation` use native timing, `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, causes different order of Start and continuation after await. so recommend not to use `LoadSceneAsync.ToUniTask`.
In stacktrace, you can check where is running in playerloop.
![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)]`.
The order in which methods are called in BeforeSceneLoad is indeterminate, so if you want to use UniTask in other BeforeSceneLoad methods, you should try to initialize it before this.
```csharp
// AfterAssembliesLoaded is called before BeforeSceneLoad
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
public static void InitUniTaskLoop()
{
var loop = PlayerLoop.GetCurrentPlayerLoop();
Cysharp.Threading.Tasks.PlayerLoopHelper.Initialize(ref loop);
}
```
If you import Unity's `Entities` package, that reset custom player loop to default at `BeforeSceneLoad` and inject ECS's loop. When Unity call ECS's inject method after UniTask's initialize method, UniTask will no longer work.
To solve this issue, you can re-initialize UniTask PlayerLoop after ECS initialized.
```csharp
// Get ECS Loop.
var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
// Setup UniTask's PlayerLoop.
PlayerLoopHelper.Initialize(ref playerLoop);
```
You can diagnostic UniTask's player loop is ready by `PlayerLoopHelper.IsInjectedUniTaskPlayerLoop()`. And also `PlayerLoopHelper.DumpCurrentPlayerLoop` shows current all playerloop to console.
```csharp
void Start()
{
UnityEngine.Debug.Log("UniTaskPlayerLoop ready? " + PlayerLoopHelper.IsInjectedUniTaskPlayerLoop());
PlayerLoopHelper.DumpCurrentPlayerLoop();
}
```
async void vs async UniTaskVoid
---
`async void` is a standard C# task system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is a lightweight version of `async UniTask` because it does not have awaitable completion and report error immediately to `UniTaskScheduler.UnobservedTaskException`. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
```csharp
public async UniTaskVoid FireAndForgetMethod()
{
// do anything...
await UniTask.Yield();
}
public void Caller()
{
FireAndForgetMethod().Forget();
}
```
Also UniTask have `Forget` method, it is similar with UniTaskVoid and same effects with it. However still UniTaskVoid is more efficient if completely do not use await。
```csharp
public async UniTask DoAsync()
{
// do anything...
await UniTask.Yield();
}
public void Caller()
{
DoAsync().Forget();
}
```
Using async lambda in register event, it is used `async void`. To avoid it, you can use `UniTask.Action` or `UniTask.UnityAction` that creates delegate via `async UniTaskVoid` lambda.
```csharp
Action actEvent;
UnityAction unityEvent; // especially used in uGUI
// Bad: async void
actEvent += async () => { };
unityEvent += async () => { };
// Ok: create Action delegate by lambda
actEvent += UniTask.Action(async () => { await UniTask.Yield(); });
unityEvent += UniTask.UnityAction(async () => { await UniTask.Yield(); });
```
`UniTaskVoid` can also use in MonoBehaviour's `Start` method.
```csharp
class Sample : MonoBehaviour
{
async UniTaskVoid Start()
{
// async init code.
}
}
```
@@ -243,7 +461,7 @@ UniTaskTracker
---
useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTask Tracker`.
![](https://user-images.githubusercontent.com/46207/50421527-abf1cf80-0883-11e9-928a-ffcd47b8c454.png)
![image](https://user-images.githubusercontent.com/46207/83527073-4434bf00-a522-11ea-86e9-3b3975b26266.png)
* Enable AutoReload(Toggle) - Reload automatically.
* Reload - Reload view.
@@ -253,38 +471,133 @@ useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTa
For debug use, enable tracking and capture stacktrace is useful but it it decline performance. Recommended usage is enable both to find task leak, and when done, finally disable both.
Reusable Promises
External Assets
---
Some UniTask factory can reuse to reduce allocation. The list is `Yield`, `Delay`, `DelayFrame`, `WaitUntil`, `WaitWhile`, `WaitUntilValueChanged`.
In default, UniTask supports TextMeshPro(`BindTo(TMP_Text)` and `TMP_InputField` event extensions like standard uGUI `InputField`), DOTween(`Tween` as awaitable) and Addressables(`AsyncOperationHandle` and `AsyncOpereationHandle<T>` as awaitable).
There are defined in separated asmdef like `UniTask.TextMeshPro`, `UniTask.DOTween`, `UniTask.Addressables`.
TextMeshPro and Addressables support are automatically enabled when import there package from package manager. However DOTween support, require to `com.demigiant.dotween` import from [OpenUPM](https://openupm.com/packages/com.demigiant.dotween/) or define `UNITASK_DOTWEEN_SUPPORT` to enable it.
```csharp
var reusePromise = UniTask.DelayFrame(10);
// sequential
await transform.DOMoveX(2, 10);
await transform.DOMoveZ(5, 20);
for (int i = 0; i < 10; i++)
// parallel with cancellation
var ct = this.GetCancellationTokenOnDestroy();
await UniTask.WhenAll(
transform.DOMoveX(10, 3).WithCancellation(ct),
transform.DOScale(10, 3).WithCancellation(ct));
```
DOTween support's default behaviour(`await`, `WithCancellation`, `ToUniTask`) awaits tween is killed. It works both Complete(true/false) and Kill(true/false). But if you want to tween reuse(`SetAutoKill(false)`), it does not work you expected. Or, if you want to await for another timing, the following extension methods exist in Tween, `AwaitForComplete`, `AwaitForPause`, `AwaitForPlay`, `AwaitForRewind`, `AwaitForStepComplete`.
AsyncEnumerable and Async LINQ
---
Unity 2020.2.0a12 supports C# 8.0 so you can use `await foreach`. This is the new Update notation in async era.
```csharp
// Unity 2020.2.0a12, C# 8.0
await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate(token))
{
await reusePromise;
Debug.Log("Update() " + Time.frameCount);
}
```
awaitable Events
---
Unity events can await like `OnClickAsync`, `OnCollisionEnterAsync`. It can use by `UniRx.Async.Triggers`.
In a C# 7.3 environment, you can use the `ForEachAsync` method to work in almost the same way.
```csharp
using UniRx.Async.Triggers;
async UniTask TripleClick(CancellationToken token)
// C# 7.3(Unity 2018.3~)
await UniTaskAsyncEnumerable.EveryUpdate(token).ForEachAsync(_ =>
{
await button.OnClickAsync(token);
await button.OnClickAsync(token);
await button.OnClickAsync(token);
Debug.Log("Update() " + Time.frameCount);
});
```
UniTaskAsyncEnumerable implements asynchronous LINQ, similar to LINQ in `IEnumerable<T>` or Rx in `IObservable<T>`. All standard LINQ query operators can be applied to asynchronous streams. For example, the following code shows how to apply a Where filter to a button-click asynchronous stream that runs once every two clicks.
```csharp
await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsync(_ =>
{
});
```
Fire and Forget style(for example, event handling), also you can use `Subscribe`.
```csharp
okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).Subscribe(_ =>
{
});
```
Async LINQ is enabled when `using Cysharp.Threading.Tasks.Linq;`, and `UniTaskAsyncEnumerable` is defined in `UniTask.Linq` asmdef.
It's closer to UniRx (Reactive Extensions), but UniTaskAsyncEnumerable is a pull-based asynchronous stream, whereas Rx was a push-based asynchronous stream. Note that although similar, the characteristics are different and the details behave differently along with them.
`UniTaskAsyncEnumerable` is the entry point like `Enumerbale`. In addition to the standard query operators, there are other generators for Unity such as `EveryUpdate`, `Timer`, `TimerFrame`, `Interval`, `IntervalFrame`, and `EveryValueChanged`. And also added additional UniTask original query operators like `Append`, `Prepend`, `DistinctUntilChanged`, `ToHashSet`, `Buffer`, `CombineLatest`, `Do`, `Never`, `ForEachAsync`, `Pairwise`, `Publish`, `Queue`, `Return`, `SkipUntil`, `TakeUntil`, `SkipUntilCanceled`, `TakeUntilCanceled`, `TakeLast`, `Subscribe`.
The method with Func as an argument has three additional overloads, `***Await`, `***AwaitWithCancellation`.
```csharp
Select(Func<T, TR> selector)
SelectAwait(Func<T, UniTask<TR>> selector)
SelectAwaitWithCancellation(Func<T, CancellationToken, UniTask<TR>> selector)
```
If you want to use the `async` method inside the func, use the `***Await` or `***AwaitWithCancellation`.
How to create async iterator, C# 8.0 supports async iterator(`async yield return`) but it only allows `IAsyncEnumerable<T>` and of course requires C# 8.0. UniTask supports `UniTaskAsyncEnumerable.Create` method to create custom async iterator.
```csharp
// IAsyncEnumerable, C# 8.0 version of async iterator. ( do not use this style, IAsyncEnumerable is not controled in UniTask).
public async IAsyncEnumerable<int> MyEveryUpdate([EnumeratorCancellation]CancellationToken cancelationToken = default)
{
var frameCount = 0;
await UniTask.Yield();
while (!token.IsCancellationRequested)
{
yield return frameCount++;
await UniTask.Yield();
}
}
// UniTaskAsyncEnumerable.Create and use `await writer.YieldAsync` instead of `yield return`.
public IUniTaskAsyncEnumerable<int> MyEveryUpdate()
{
// writer(IAsyncWriter<T>) has `YieldAsync(value)` method.
return UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
{
var frameCount = 0;
await UniTask.Yield();
while (!token.IsCancellationRequested)
{
await writer.YieldAsync(frameCount++); // instead of `yield return`
await UniTask.Yield();
}
});
}
```
Awaitable Events
---
All uGUI component implements `***AsAsyncEnumerable` to convert asynchronous streams of events.
```csharp
async UniTask TripleClick()
{
// In default, used button.GetCancellationTokenOnDestroy to manage lieftime of async
await button.OnClickAsync();
await button.OnClickAsync();
await button.OnClickAsync();
Debug.Log("Three times clicked");
}
// more efficient way
async UniTask TripleClick(CancellationToken token)
async UniTask TripleClick()
{
using (var handler = button.GetAsyncClickEventHandler(token))
using (var handler = button.GetAsyncClickEventHandler())
{
await handler.OnClickAsync();
await handler.OnClickAsync();
@@ -292,11 +605,156 @@ async UniTask TripleClick(CancellationToken token)
Debug.Log("Three times clicked");
}
}
// use async LINQ
async UniTask TripleClick(CancellationToken token)
{
await button.OnClickAsAsyncEnumerable().Take(3).Last();
Debug.Log("Three times clicked");
}
// use async LINQ2
async UniTask TripleClick(CancellationToken token)
{
await button.OnClickAsAsyncEnumerable().Take(3).ForEachAsync(_ =>
{
Debug.Log("Every clicked");
});
Debug.Log("Three times clicked, complete.");
}
```
async void vs async UniTask/UniTaskVoid
All MonoBehaviour message events can convert async-streams by `AsyncTriggers` that can enable by `using Cysharp.Threading.Tasks.Triggers;`.
```csharp
using Cysharp.Threading.Tasks.Triggers;
async UniTaskVoid MonitorCollision()
{
await gameObject.OnCollisionEnterAsync();
Debug.Log("Collision Enter");
/* do anything */
await gameObject.OnCollisionExitAsync();
Debug.Log("Collision Exit");
}
```
Similar as uGUI event, AsyncTrigger can get by `GetAsync***Trigger` and trigger it self is UniTaskAsyncEnumerable.
```csharp
// use await multiple times, get AsyncTriggerHandler is more efficient.
using(var trigger = this.GetOnCollisionEnterAsyncHandler())
{
await OnCollisionEnterAsync();
await OnCollisionEnterAsync();
await OnCollisionEnterAsync();
}
// every moves.
await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
{
});
```
`AsyncReactiveProperty`, `AsyncReadOnlyReactiveProperty` is UniTask version of UniTask's ReactiveProperty. `BindTo` extension method of `IUniTaskAsyncEnumerable<T>` for binding asynchronous stream values to Unity components(Text/Selectable/TMP/Text).
```csharp
var rp = new AsyncReactiveProperty<int>(99);
// AsyncReactiveProperty itself is IUniTaskAsyncEnumerable, you can query by LINQ
rp.ForEachAsync(x =>
{
Debug.Log(x);
}, this.GetCancellationTokenOnDestroy()).Forget();
rp.Value = 10; // push 10 to all subscriber
rp.Value = 11; // push 11 to all subscriber
// WithoutCurrent ignore initial value
// BindTo bind stream value to unity components.
rp.WithoutCurrent().BindTo(this.textComponent);
await rp.WaitAsync(); // wait until next value set
// also exists ToReadOnlyReactiveProperty
var rp2 = new AsyncReactiveProperty<int>(99);
var rorp = rp.CombineLatest(rp2, (x, y) => (x, y)).ToReadOnlyReactiveProperty();
```
A pull-type asynchronous stream does not get the next values until the asynchronous processing in the sequence is complete. This could spill data from push-type events such as buttons.
```csharp
// can not get click event during 3 seconds complete.
await button.OnClickAsAsyncEnumerable().ForEachAwaitAsync(async x =>
{
await UniTask.Delay(TimeSpan.FromSeconds(3));
});
```
It is useful(prevent double-click) but not useful in sometimes.
Using `Queue()` method, which will also queue events during asynchronous processing.
```csharp
// queued message in asynchronous processing
await button.OnClickAsAsyncEnumerable().Queue().ForEachAwaitAsync(async x =>
{
await UniTask.Delay(TimeSpan.FromSeconds(3));
});
```
Or use `Subscribe`, fire and forget style.
```csharp
button.OnClickAsAsyncEnumerable().Subscribe(async x =>
{
await UniTask.Delay(TimeSpan.FromSeconds(3));
});
```
Channel
---
`async void` is standard C# system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is lightweight version of `async UniTask` because it does not have awaitable completion. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
`Channel` is same as [System.Threading.Tasks.Channels](https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.channels?view=netcore-3.1) that is similar as GoLang Channel.
Currently only supports multiple-producer, single-consumer unbounded channel. It can create by `Channel.CreateSingleConsumerUnbounded<T>()`.
For producer(`.Writer`), `TryWrite` to push value and `TryComplete` to complete channel. For consumer(`.Reader`), `TryRead`, `WaitToReadAsync`, `ReadAsync`, `Completion` and `ReadAllAsync` to read queued messages.
`ReadAllAsync` returns `IUniTaskAsyncEnumerable<T>` so query LINQ operators. Reader only allows single-consumer but use `.Publish()` query operator to enable multicast message. For example, make pub/sub utility.
```csharp
public class AsyncMessageBroker<T> : IDisposable
{
Channel<T> channel;
IConnectableUniTaskAsyncEnumerable<T> multicastSource;
IDisposable connection;
public AsyncMessageBroker()
{
channel = Channel.CreateSingleConsumerUnbounded<T>();
multicastSource = channel.Reader.ReadAllAsync().Publish();
connection = multicastSource.Connect(); // Publish returns IConnectableUniTaskAsyncEnumerable.
}
public void Publish(T value)
{
channel.Writer.TryWrite(value);
}
public IUniTaskAsyncEnumerable<T> Subscribe()
{
return multicastSource;
}
public void Dispose()
{
channel.Writer.TryComplete();
connection.Dispose();
}
}
```
For Unit Testing
---
@@ -323,37 +781,185 @@ public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
});
```
Method List
UniTask itself's unit test is written by Unity Test Runner and [Cysharp/RuntimeUnitTestToolkit](https://github.com/Cysharp/RuntimeUnitTestToolkit) to check on CI and IL2CPP working.
ThreadPool limitation
---
Most UniTask methods run in a single thread (PlayerLoop), but only `UniTask.Run` and `UniTask.SwitchToThreadPool` run on a thread pool. If you use a thread pool, it won't work with WebGL and so on.
`UniTask.Run` will be deprecated in the future (marked with an Obsolete) and only `RunOnThreadPool` will be used. Also, if you use `UniTask.Run`, consider whether you can use `UniTask.Create` or `UniTask.Void`.
IEnumerator.ToUniTask limitation
---
You can convert coroutine(IEnumerator) to UniTask(or await directly) but has some limitations.
* `WaitForEndOfFrame`/`WaitForFixedUpdate` is not supported, used `yield return null` instead.
* Consuming loop timing is not same as StartCoroutine, it is used specified PlayerLoopTiming, and default's `PlayerLoopTiming.Update` is run before MonoBehaviour's Update and StartCoroutine's loop.
For UnityEditor
---
UniTask can run on Unity Edtitor like Editor Coroutine. However, there are some limitations.
* Delay, DelayFrame is not work correctly because can not get deltaTime in editor. Return the result of the await immediately; you can use `DelayType.Realtime` to wait for the right time.
* All PlayerLoopTiming run on timing, `EditorApplication.update`.
Compare with Standard Task API
---
UniTask has many standard Task-like APIs. This table shows what is the alternative apis.
Use standard type.
| .NET Type | UniTask Type |
| --- | --- |
| `IProgress<T>` | --- |
| `CancellationToken` | --- |
| `CancellationTokenSource` | --- |
Use UniTask type.
| .NET Type | UniTask Type |
| --- | --- |
| `Task`/`ValueTask` | `UniTask` |
| `Task<T>`/`ValueTask<T>` | `UniTask<T>` |
| `async void` | `async UniTaskVoid` |
| `+= async () => { }` | `UniTask.Void`, `UniTask.Action`, `UniTask.UnityAction` |
| --- | `UniTaskCompletionSource` |
| `TaskCompletionSource<T>` | `UniTaskCompletionSource<T>`/`AutoResetUniTaskCompletionSource<T>` |
| `ManualResetValueTaskSourceCore<T>` | `UniTaskCompletionSourceCore<T>` |
| `IValueTaskSource` | `IUniTaskSource` |
| `IValueTaskSource<T>` | `IUniTaskSource<T>` |
| `ValueTask.IsCompleted` | `UniTask.Status.IsCompleted()` |
| `ValueTask<T>.IsCompleted` | `UniTask<T>.Status.IsCompleted()` |
| `new Progress<T>` | `Progress.Create<T>` |
| `CancellationToken.Register(UnsafeRegister)` | `CancellationToken.RegisterWithoutCaptureExecutionContext` |
| `CancellationTokenSource.CancelAfter` | `CancellationTokenSource.CancelAfterSlim` |
| `Channel.CreateUnbounded<T>(false){ SingleReader = true }` | `Channel.CreateSingleConsumerUnbounded<T>` |
| `IAsyncEnumerable<T>` | `IUniTaskAsyncEnumerable<T>` |
| `IAsyncEnumerator<T>` | `IUniTaskAsyncEnumerator<T>` |
| `IAsyncDisposable` | `IUniTaskAsyncDisposable` |
| `Task.Delay` | `UniTask.Delay` |
| `Task.Yield` | `UniTask.Yield` |
| `Task.Run` | `UniTask.Run` |
| `Task.WhenAll` | `UniTask.WhenAll` |
| `Task.WhenAny` | `UniTask.WhenAny` |
| `Task.CompletedTask` | `UniTask.CompletedTask` |
| `Task.FromException` | `UniTask.FromException` |
| `Task.FromResult` | `UniTask.FromResult` |
| `Task.FromCanceled` | `UniTask.FromCanceled` |
| `Task.ContinueWith` | `UniTask.ContinueWith` |
| `TaskScheduler.UnobservedTaskException` | `UniTaskScheduler.UnobservedTaskException` |
Pooling Configuration
---
UniTask is aggressively caching async promise object to achive zero allocation. In default, cache all promises but you can configure `TaskPool.SetMaxPoolSize` to your value, the value indicates cache size per type. `TaskPool.GetCacheSizeInfo` returns current cached object in pool.
```csharp
UniTask.WaitUntil
UniTask.WaitWhile
UniTask.WaitUntilValueChanged
UniTask.SwitchToThreadPool
UniTask.SwitchToTaskPool
UniTask.SwitchToMainThread
UniTask.SwitchToSynchronizationContext
UniTask.Yield
UniTask.Run
UniTask.Lazy
UniTask.Void
UniTask.ConfigureAwait
UniTask.DelayFrame
UniTask.Delay(..., bool ignoreTimeScale = false, ...) parameter
foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
{
Debug.Log(type + ":" + size);
}
```
Allocation on Profiler
---
In UnityEditor profiler shows allocation of compiler generated AsyncStateMachine but it only occurs in debug(development) build. C# Compiler generate AsyncStateMachine as class on Debug build and as struct on Release build.
After Unity 2020.1 supports Code Optimization option on UnityEditor(right, footer).
![](https://user-images.githubusercontent.com/46207/89967342-2f944600-dc8c-11ea-99fc-0b74527a16f6.png)
You can change C# compiler optimization to release, it removes AsyncStateMachine allocation. Andalso optimization option can set via `Compilation.CompilationPipeline-codeOptimization`, and `Compilation.CodeOptimization`.
UniTaskSynchronizationContext
---
Unity's default SynchronizationContext(`UnitySynchronizationContext`) is poor implementation for performance. UniTask itself is bypass `SynchronizationContext`(and `ExecutionContext`) so does not use it but if exists in `async Task`, still used it. `UniTaskSynchronizationContext` is replacement of `UnitySynchronizationContext`, it is better for performance.
```csharp
public class SyncContextInjecter
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
public static void Inject()
{
SynchronizationContext.SetSynchronizationContext(new UniTaskSynchronizationContext());
}
}
```
This is an optional choice and is not always recommended; `UniTaskSynchronizationContext` is less performance than `async UniTask` and is not a complete UniTask replacement. It also does not guarantee full behavioral compatibility with the `UnitySynchronizationContext`.
API References
---
UniTask's API References is hosted at [cysharp.github.io/UniTask](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.html) by [DocFX](https://dotnet.github.io/docfx/) and [Cysharp/DocfXTemplate](https://github.com/Cysharp/DocfxTemplate).
For example, UniTask's factory methods can see at [UniTask#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.UniTask.html#methods-1). UniTaskAsyncEnumerable's factory/extension methods can see at [UniTaskAsyncEnumerable#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.Linq.UniTaskAsyncEnumerable.html#methods-1).
UPM Package
---
After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of git package. You can add `https://github.com/Cysharp/UniTask.git?path=Assets/UniRx.Async` to Package Manager
### Install via git URL
After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of git package. You can add `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask` to Package Manager
![image](https://user-images.githubusercontent.com/46207/79450714-3aadd100-8020-11ea-8aae-b8d87fc4d7be.png)
![image](https://user-images.githubusercontent.com/46207/79450774-56b17280-8020-11ea-91eb-21e1f51eb60c.png)
![image](https://user-images.githubusercontent.com/46207/83702872-e0f17c80-a648-11ea-8183-7469dcd4f810.png)
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=Assets/UniRx.Async"` to `Packages/manifest.json`.
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"` to `Packages/manifest.json`.
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#1.3.0`. For example `https://github.com/Cysharp/UniTask.git?path=Assets/UniRx.Async#1.3.1`.
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#2.0.31`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.0.31`.
### 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
---
For .NET Core, use NuGet.
> PM> Install-Package [UniTask](https://www.nuget.org/packages/UniTask)
UniTask of .NET Core version is a subset of Unity UniTask, removed PlayerLoop dependent methods.
It runs at higher performance than the standard Task/ValueTask, but you should be careful to ignore the ExecutionContext/SynchronizationContext when using it. `AysncLocal` also does not work because it ignores ExecutionContext.
If you use UniTask internally, but provide ValueTask as an external API, you can write like the following(Inspired by [PooledAwait](https://github.com/mgravell/PooledAwait)).
```csharp
public class ZeroAllocAsyncAwaitInDotNetCore
{
public ValueTask<int> DoAsync(int x, int y)
{
return Core(this, x, y);
static async UniTask<int> Core(ZeroAllocAsyncAwaitInDotNetCore self, int x, int y)
{
// do anything...
await Task.Delay(TimeSpan.FromSeconds(x + y));
await UniTask.Yield();
return 10;
}
}
}
// UniTask does not return to original SynchronizationContext but you can use helper `ReturnToCurrentSynchronizationContext`.
public ValueTask TestAsync()
{
await using (UniTask.ReturnToCurrentSynchronizationContext())
{
await UniTask.SwitchToThreadPool();
// do anything..
}
}
```
.NET Core version is intended to allow users to use UniTask as an interface when sharing code with Unity (such as [Cysharp/MagicOnion](https://github.com/Cysharp/MagicOnion/)). .NET Core version of UniTask enables smooth code sharing.
Utility methods such as WhenAll which is equivalent to UniTask are provided as [Cysharp/ValueTaskSupplement](https://github.com/Cysharp/ValueTaskSupplement).
License
---
This library is under the MIT License.
This library is under the MIT License.

10
docs/.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
###############
# folder #
###############
/**/DROP/
/**/TEMP/
/**/packages/
/**/bin/
/**/obj/
_site
_DocfxTemplate

5
docs/api/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
###############
# temp file #
###############
*.yml
.manifest

70
docs/docfx.json Normal file
View File

@@ -0,0 +1,70 @@
{
"metadata": [
{
"src": [
{
"files": [
"UniTask/Assets/Plugins/UniTask/Runtime/**/*.cs"
],
"src": "../src"
}
],
"dest": "api",
"disableGitFeatures": false,
"disableDefaultFilter": false
}
],
"build": {
"globalMetadata": {
"_disableContribution": true,
"_appTitle": "UniTask"
},
"content": [
{
"files": [
"api/**.yml",
"api/index.md"
]
},
{
"files": [
"articles/**.md",
"articles/**/toc.yml",
"toc.yml",
"*.md"
]
}
],
"resource": [
{
"files": [
"images/**"
]
}
],
"overwrite": [
{
"files": [
"apidoc/**.md"
],
"exclude": [
"obj/**",
"_site/**"
]
}
],
"dest": "_site",
"globalMetadataFiles": [],
"fileMetadataFiles": [],
"template": [
"_DocfxTemplate/templates/default-v2.5.2",
"_DocfxTemplate/templates/cysharp"
],
"postProcessors": [],
"markdownEngineName": "markdig",
"noLangKeyword": false,
"keepFileLink": false,
"cleanupCacheHistory": false
}
}

8
docs/index.md Normal file
View File

@@ -0,0 +1,8 @@
---
title: Home
---
# UniTask
Provides an efficient async/await integration to Unity.
https://github.com/Cysharp/UniTask

11
docs/toc.yml Normal file
View File

@@ -0,0 +1,11 @@
- name: API Documentation
href: api/
homepage: api/Cysharp.Threading.Tasks.html
- name: Repository
href: https://github.com/Cysharp/UniTask
homepage: https://github.com/Cysharp/UniTask
- name: Releases
href: https://github.com/Cysharp/UniTask/releases
homepage: https://github.com/Cysharp/UniTask/releases

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,101 @@
#if !NETSTANDARD2_0
#pragma warning disable 0649
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Sources;
namespace Cysharp.Threading.Tasks
{
public static class AsyncEnumerableExtensions
{
public static IUniTaskAsyncEnumerable<T> AsUniTaskAsyncEnumerable<T>(this IAsyncEnumerable<T> source)
{
return new AsyncEnumerableToUniTaskAsyncEnumerable<T>(source);
}
public static IAsyncEnumerable<T> AsAsyncEnumerable<T>(this IUniTaskAsyncEnumerable<T> source)
{
return new UniTaskAsyncEnumerableToAsyncEnumerable<T>(source);
}
sealed class AsyncEnumerableToUniTaskAsyncEnumerable<T> : IUniTaskAsyncEnumerable<T>
{
readonly IAsyncEnumerable<T> source;
public AsyncEnumerableToUniTaskAsyncEnumerable(IAsyncEnumerable<T> source)
{
this.source = source;
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source.GetAsyncEnumerator(cancellationToken));
}
sealed class Enumerator : IUniTaskAsyncEnumerator<T>
{
readonly IAsyncEnumerator<T> enumerator;
public Enumerator(IAsyncEnumerator<T> enumerator)
{
this.enumerator = enumerator;
}
public T Current => enumerator.Current;
public async UniTask DisposeAsync()
{
await enumerator.DisposeAsync();
}
public async UniTask<bool> MoveNextAsync()
{
return await enumerator.MoveNextAsync();
}
}
}
sealed class UniTaskAsyncEnumerableToAsyncEnumerable<T> : IAsyncEnumerable<T>
{
readonly IUniTaskAsyncEnumerable<T> source;
public UniTaskAsyncEnumerableToAsyncEnumerable(IUniTaskAsyncEnumerable<T> source)
{
this.source = source;
}
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source.GetAsyncEnumerator(cancellationToken));
}
sealed class Enumerator : IAsyncEnumerator<T>
{
readonly IUniTaskAsyncEnumerator<T> enumerator;
public Enumerator(IUniTaskAsyncEnumerator<T> enumerator)
{
this.enumerator = enumerator;
}
public T Current => enumerator.Current;
public ValueTask DisposeAsync()
{
return enumerator.DisposeAsync();
}
public ValueTask<bool> MoveNextAsync()
{
return enumerator.MoveNextAsync();
}
}
}
}
}
#endif

View File

@@ -1,7 +1,6 @@
#pragma warning disable 0649
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Threading.Tasks.Sources;
@@ -9,115 +8,90 @@ namespace Cysharp.Threading.Tasks
{
public static class UniTaskValueTaskExtensions
{
public static ValueTask AsValueTask(this UniTask task)
public static ValueTask AsValueTask(this in UniTask task)
{
ref var core = ref Unsafe.As<UniTask, UniTaskToValueTask>(ref task);
if (core.source == null)
{
return default;
}
return new ValueTask(new UniTaskValueTaskSource(core.source), core.token);
#if NETSTANDARD2_0
return new ValueTask(new UniTaskValueTaskSource(task), 0);
#else
return task;
#endif
}
public static ValueTask<T> AsValueTask<T>(this UniTask<T> task)
public static ValueTask<T> AsValueTask<T>(this in UniTask<T> task)
{
ref var core = ref Unsafe.As<UniTask<T>, UniTaskToValueTask<T>>(ref task);
if (core.source == null)
{
return new ValueTask<T>(core.result);
}
return new ValueTask<T>(new UniTaskValueTaskSource<T>(core.source), core.token);
#if NETSTANDARD2_0
return new ValueTask<T>(new UniTaskValueTaskSource<T>(task), 0);
#else
return task;
#endif
}
struct UniTaskToValueTask
public static async UniTask<T> AsUniTask<T>(this ValueTask<T> task)
{
public IUniTaskSource source;
public short token;
return await task;
}
public static async UniTask AsUniTask(this ValueTask task)
{
await task;
}
#if NETSTANDARD2_0
class UniTaskValueTaskSource : IValueTaskSource
{
readonly IUniTaskSource source;
readonly UniTask task;
readonly UniTask.Awaiter awaiter;
public UniTaskValueTaskSource(IUniTaskSource source)
public UniTaskValueTaskSource(UniTask task)
{
this.source = source;
this.task = task;
this.awaiter = task.GetAwaiter();
}
public void GetResult(short token)
{
source.GetResult(token);
awaiter.GetResult();
}
public ValueTaskSourceStatus GetStatus(short token)
{
var status = source.GetStatus(token);
switch (status)
{
case UniTaskStatus.Pending:
return ValueTaskSourceStatus.Pending;
case UniTaskStatus.Succeeded:
return ValueTaskSourceStatus.Succeeded;
case UniTaskStatus.Faulted:
return ValueTaskSourceStatus.Faulted;
case UniTaskStatus.Canceled:
return ValueTaskSourceStatus.Canceled;
default:
return (ValueTaskSourceStatus)status;
}
return (ValueTaskSourceStatus)task.Status;
}
public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
{
source.OnCompleted(continuation, state, token);
awaiter.SourceOnCompleted(continuation, state);
}
}
struct UniTaskToValueTask<T>
{
public IUniTaskSource<T> source;
public T result;
public short token;
}
class UniTaskValueTaskSource<T> : IValueTaskSource<T>
{
readonly IUniTaskSource<T> source;
readonly UniTask<T> task;
readonly UniTask<T>.Awaiter awaiter;
public UniTaskValueTaskSource(IUniTaskSource<T> source)
public UniTaskValueTaskSource(UniTask<T> task)
{
this.source = source;
this.task = task;
this.awaiter = task.GetAwaiter();
}
public T GetResult(short token)
{
return source.GetResult(token);
return awaiter.GetResult();
}
public ValueTaskSourceStatus GetStatus(short token)
{
var status = source.GetStatus(token);
switch (status)
{
case UniTaskStatus.Pending:
return ValueTaskSourceStatus.Pending;
case UniTaskStatus.Succeeded:
return ValueTaskSourceStatus.Succeeded;
case UniTaskStatus.Faulted:
return ValueTaskSourceStatus.Faulted;
case UniTaskStatus.Canceled:
return ValueTaskSourceStatus.Canceled;
default:
return (ValueTaskSourceStatus)status;
}
return (ValueTaskSourceStatus)task.Status;
}
public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
{
source.OnCompleted(continuation, state, token);
awaiter.SourceOnCompleted(continuation, state);
}
}
#endif
}
}

View File

@@ -0,0 +1,39 @@
//using Cysharp.Threading.Tasks.Internal;
//using System;
//using System.Collections.Concurrent;
//using System.Runtime.CompilerServices;
//using System.Threading;
//namespace Cysharp.Threading.Tasks
//{
// public partial struct UniTask
// {
// public static UniTask Delay()
// {
// return default;
// }
// sealed class DelayPromise : IUniTaskSource
// {
// public void GetResult(short token)
// {
// throw new NotImplementedException();
// }
// public UniTaskStatus GetStatus(short token)
// {
// throw new NotImplementedException();
// }
// public void OnCompleted(Action<object> continuation, object state, short token)
// {
// throw new NotImplementedException();
// }
// public UniTaskStatus UnsafeGetStatus()
// {
// throw new NotImplementedException();
// }
// }
// }
//}

View File

@@ -1,4 +1,6 @@
using System;
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Threading;
@@ -41,7 +43,11 @@ namespace Cysharp.Threading.Tasks
}
else
{
#if NETCOREAPP3_1
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
#else
ThreadPool.UnsafeQueueUserWorkItem(WaitCallbackDelegate, continuation);
#endif
}
}
@@ -50,6 +56,48 @@ namespace Cysharp.Threading.Tasks
((Action)state).Invoke();
}
}
#if NETCOREAPP3_1
sealed class ThreadPoolWorkItem : IThreadPoolWorkItem, ITaskPoolNode<ThreadPoolWorkItem>
{
static TaskPool<ThreadPoolWorkItem> pool;
ThreadPoolWorkItem nextNode;
public ref ThreadPoolWorkItem NextNode => ref nextNode;
static ThreadPoolWorkItem()
{
TaskPool.RegisterSizeGetter(typeof(ThreadPoolWorkItem), () => pool.Size);
}
Action continuation;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ThreadPoolWorkItem Create(Action continuation)
{
if (!pool.TryPop(out var item))
{
item = new ThreadPoolWorkItem();
}
item.continuation = continuation;
return item;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Execute()
{
var call = continuation;
continuation = null;
if (call != null)
{
pool.TryPush(this);
call.Invoke();
}
}
}
#endif
}
}
}

View File

@@ -1,11 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netcoreapp3.1;netstandard2.1;netstandard2.0</TargetFrameworks>
<AssemblyName>UniTask</AssemblyName>
<LangVersion>8.0</LangVersion>
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
<!-- NuGet Packaging -->
<Id>UniTask</Id>
<PackageVersion>$(Version)</PackageVersion>
<Company>Cysharp</Company>
<Authors>Cysharp</Authors>
<Copyright>© Cysharp, Inc.</Copyright>
<PackageTags>task;async</PackageTags>
<Description>Provides an efficient async/await integration to Unity and .NET Core.</Description>
<PackageProjectUrl>https://github.com/Cysharp/UniTask</PackageProjectUrl>
<RepositoryUrl>$(PackageProjectUrl)</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Icon.png</PackageIcon>
</PropertyGroup>
<ItemGroup>
<None Include="Icon.png" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\UniTask\Assets\Plugins\UniTask\Runtime\**\*.cs"
Exclude="
@@ -16,7 +35,10 @@
..\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;
@@ -24,9 +46,8 @@
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.uGUI.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.MonoBehaviour.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.*;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;
" />
</ItemGroup>

View File

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

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

View File

@@ -1,4 +1,6 @@
using Cysharp.Threading.Tasks;
#pragma warning disable CS1998
using Cysharp.Threading.Tasks;
using System.Linq;
using System;
@@ -15,6 +17,193 @@ using System.Reactive.Concurrency;
namespace NetCoreSandbox
{
public class MySyncContext : SynchronizationContext
{
public MySyncContext()
{
}
public override void Post(SendOrPostCallback d, object state)
{
Console.WriteLine("Called SyncContext Post!");
base.Post(d, state);
}
}
public class Text
{
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)
@@ -37,33 +226,116 @@ namespace NetCoreSandbox
await Task.Delay(10, cancellationToken);
}
static async Task Main(string[] args)
public class MyDisposable : IDisposable
{
await foreach (var item in UniTaskAsyncEnumerable.Range(1, 10)
.SelectAwait(x => UniTask.Run(() => x))
.TakeLast(6)
)
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);
}
// AsyncEnumerable.Range(1,10).FirstAsync(
// AsyncEnumerable.Range(1, 10).GroupBy(x=>x).Select(x=>x.first
}
// AsyncEnumerable.Range(1,10).WithCancellation(CancellationToken.None).WithCancellation
static async Task Main(string[] args)
{
#if !DEBUG
//Enumerable.Range(1,10).ToHashSet(
//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()
{
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()
{

View File

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

View File

@@ -7,6 +7,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="PooledAwait" Version="1.0.49" />
<PackageReference Include="System.Interactive.Async" Version="4.1.1" />
<PackageReference Include="System.Reactive" Version="4.4.1" />
</ItemGroup>

View File

@@ -0,0 +1,167 @@
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using Cysharp.Threading.Tasks.Linq;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests
{
public class AsyncLazyTest
{
[Fact]
public async Task LazyLazy()
{
{
var l = UniTask.Lazy(() => After());
var a = AwaitAwait(l.Task);
var b = AwaitAwait(l.Task);
var c = AwaitAwait(l.Task);
await a;
await b;
await c;
}
{
var l = UniTask.Lazy(() => AfterException());
var a = AwaitAwait(l.Task);
var b = AwaitAwait(l.Task);
var c = AwaitAwait(l.Task);
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
}
}
[Fact]
public async Task LazyImmediate()
{
{
var l = UniTask.Lazy(() => UniTask.FromResult(1).AsUniTask());
var a = AwaitAwait(l.Task);
var b = AwaitAwait(l.Task);
var c = AwaitAwait(l.Task);
await a;
await b;
await c;
}
{
var l = UniTask.Lazy(() => UniTask.FromException(new TaskTestException()));
var a = AwaitAwait(l.Task);
var b = AwaitAwait(l.Task);
var c = AwaitAwait(l.Task);
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
}
}
static async UniTask AwaitAwait(UniTask t)
{
await t;
}
async UniTask After()
{
await UniTask.Yield();
Thread.Sleep(TimeSpan.FromSeconds(1));
await UniTask.Yield();
await UniTask.Yield();
}
async UniTask AfterException()
{
await UniTask.Yield();
Thread.Sleep(TimeSpan.FromSeconds(1));
await UniTask.Yield();
throw new TaskTestException();
}
}
public class AsyncLazyTest2
{
[Fact]
public async Task LazyLazy()
{
{
var l = UniTask.Lazy(() => After());
var a = AwaitAwait(l.Task);
var b = AwaitAwait(l.Task);
var c = AwaitAwait(l.Task);
var a2 = await a;
var b2 = await b;
var c2 = await c;
(a2, b2, c2).Should().Be((10, 10, 10));
}
{
var l = UniTask.Lazy(() => AfterException());
var a = AwaitAwait(l.Task);
var b = AwaitAwait(l.Task);
var c = AwaitAwait(l.Task);
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
}
}
[Fact]
public async Task LazyImmediate()
{
{
var l = UniTask.Lazy(() => UniTask.FromResult(1));
var a = AwaitAwait(l.Task);
var b = AwaitAwait(l.Task);
var c = AwaitAwait(l.Task);
var a2 = await a;
var b2 = await b;
var c2 = await c;
(a2, b2, c2).Should().Be((1, 1, 1));
}
{
var l = UniTask.Lazy(() => UniTask.FromException<int>(new TaskTestException()));
var a = AwaitAwait(l.Task);
var b = AwaitAwait(l.Task);
var c = AwaitAwait(l.Task);
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
}
}
static async UniTask<int> AwaitAwait(UniTask<int> t)
{
return await t;
}
async UniTask<int> After()
{
await UniTask.Yield();
Thread.Sleep(TimeSpan.FromSeconds(1));
await UniTask.Yield();
await UniTask.Yield();
return 10;
}
async UniTask<int> AfterException()
{
await UniTask.Yield();
Thread.Sleep(TimeSpan.FromSeconds(1));
await UniTask.Yield();
throw new TaskTestException();
}
}
}

View File

@@ -0,0 +1,197 @@
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using Cysharp.Threading.Tasks.Linq;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests
{
public class AsyncReactivePropertyTest
{
[Fact]
public async Task Iteration()
{
var rp = new AsyncReactiveProperty<int>(99);
var f = await rp.FirstAsync();
f.Should().Be(99);
var array = rp.Take(5).ToArrayAsync();
rp.Value = 100;
rp.Value = 100;
rp.Value = 100;
rp.Value = 131;
var ar = await array;
ar.Should().BeEquivalentTo(new[] { 99, 100, 100, 100, 131 });
}
[Fact]
public async Task WithoutCurrent()
{
var rp = new AsyncReactiveProperty<int>(99);
var array = rp.WithoutCurrent().Take(5).ToArrayAsync();
rp.Value = 100;
rp.Value = 100;
rp.Value = 100;
rp.Value = 131;
rp.Value = 191;
var ar = await array;
ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 });
}
//[Fact]
//public async Task StateIteration()
//{
// var rp = new ReadOnlyAsyncReactiveProperty<int>(99);
// var setter = rp.GetSetter();
// var f = await rp.FirstAsync();
// f.Should().Be(99);
// var array = rp.Take(5).ToArrayAsync();
// setter(100);
// setter(100);
// setter(100);
// setter(131);
// var ar = await array;
// ar.Should().BeEquivalentTo(new[] { 99, 100, 100, 100, 131 });
//}
//[Fact]
//public async Task StateWithoutCurrent()
//{
// var rp = new ReadOnlyAsyncReactiveProperty<int>(99);
// var setter = rp.GetSetter();
// var array = rp.WithoutCurrent().Take(5).ToArrayAsync();
// setter(100);
// setter(100);
// setter(100);
// setter(131);
// setter(191);
// var ar = await array;
// ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 });
//}
[Fact]
public void StateFromEnumeration()
{
var rp = new AsyncReactiveProperty<int>(10);
var state = rp.ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
rp.Value = 10;
state.Value.Should().Be(10);
rp.Value = 20;
state.Value.Should().Be(20);
state.Dispose();
rp.Value = 30;
state.Value.Should().Be(20);
}
[Fact]
public async Task WaitAsyncTest()
{
var rp = new AsyncReactiveProperty<int>(128);
var f = await rp.FirstAsync();
f.Should().Be(128);
{
var t = rp.WaitAsync();
rp.Value = 99;
rp.Value = 100;
var v = await t;
v.Should().Be(99);
}
{
var t = rp.WaitAsync();
rp.Value = 99;
rp.Value = 100;
var v = await t;
v.Should().Be(99);
}
}
[Fact]
public async Task WaitAsyncCancellationTest()
{
var cts = new CancellationTokenSource();
var rp = new AsyncReactiveProperty<int>(128);
var t = rp.WaitAsync(cts.Token);
cts.Cancel();
rp.Value = 99;
rp.Value = 100;
await Assert.ThrowsAsync<OperationCanceledException>(async () => { await t; });
}
[Fact]
public async Task ReadOnlyWaitAsyncTest()
{
var rp = new AsyncReactiveProperty<int>(128);
var rrp = rp.ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
var t = rrp.WaitAsync();
rp.Value = 99;
rp.Value = 100;
var v = await t;
v.Should().Be(99);
}
[Fact]
public async Task ReadOnlyWaitAsyncCancellationTest()
{
var cts = new CancellationTokenSource();
var rp = new AsyncReactiveProperty<int>(128);
var rrp = rp.ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
var t = rrp.WaitAsync(cts.Token);
cts.Cancel();
rp.Value = 99;
rp.Value = 100;
await Assert.ThrowsAsync<OperationCanceledException>(async () => { await t; });
}
}
}

View File

@@ -0,0 +1,51 @@
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using Cysharp.Threading.Tasks.Linq;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests
{
public class CancellationTokenTest
{
[Fact]
public async Task WaitUntilCanceled()
{
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(1.5));
var now = DateTime.UtcNow;
await cts.Token.WaitUntilCanceled();
var elapsed = DateTime.UtcNow - now;
elapsed.Should().BeGreaterThan(TimeSpan.FromSeconds(1));
}
[Fact]
public void AlreadyCanceled()
{
var cts = new CancellationTokenSource();
cts.Cancel();
cts.Token.WaitUntilCanceled().GetAwaiter().IsCompleted.Should().BeTrue();
}
[Fact]
public void None()
{
CancellationToken.None.WaitUntilCanceled().GetAwaiter().IsCompleted.Should().BeTrue();
}
}
}

View File

@@ -0,0 +1,370 @@
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using Cysharp.Threading.Tasks.Linq;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests
{
public class ChannelTest
{
(System.Threading.Channels.Channel<int>, Cysharp.Threading.Tasks.Channel<int>) CreateChannel()
{
var reference = System.Threading.Channels.Channel.CreateUnbounded<int>(new UnboundedChannelOptions
{
AllowSynchronousContinuations = true,
SingleReader = true,
SingleWriter = false
});
var channel = Cysharp.Threading.Tasks.Channel.CreateSingleConsumerUnbounded<int>();
return (reference, channel);
}
[Fact]
public async Task SingleWriteSingleRead()
{
var (reference, channel) = CreateChannel();
foreach (var item in new[] { 10, 20, 30 })
{
var t1 = reference.Reader.WaitToReadAsync();
var t2 = channel.Reader.WaitToReadAsync();
t1.IsCompleted.Should().BeFalse();
t2.Status.IsCompleted().Should().BeFalse();
reference.Writer.TryWrite(item);
channel.Writer.TryWrite(item);
(await t1).Should().BeTrue();
(await t2).Should().BeTrue();
reference.Reader.TryRead(out var refitem).Should().BeTrue();
channel.Reader.TryRead(out var chanitem).Should().BeTrue();
refitem.Should().Be(item);
chanitem.Should().Be(item);
}
}
[Fact]
public async Task MultiWrite()
{
var (reference, channel) = CreateChannel();
foreach (var item in new[] { 10, 20, 30 })
{
var t1 = reference.Reader.WaitToReadAsync();
var t2 = channel.Reader.WaitToReadAsync();
t1.IsCompleted.Should().BeFalse();
t2.Status.IsCompleted().Should().BeFalse();
foreach (var i in Enumerable.Range(1, 3))
{
reference.Writer.TryWrite(item * i);
channel.Writer.TryWrite(item * i);
}
(await t1).Should().BeTrue();
(await t2).Should().BeTrue();
foreach (var i in Enumerable.Range(1, 3))
{
(await reference.Reader.WaitToReadAsync()).Should().BeTrue();
(await channel.Reader.WaitToReadAsync()).Should().BeTrue();
reference.Reader.TryRead(out var refitem).Should().BeTrue();
channel.Reader.TryRead(out var chanitem).Should().BeTrue();
refitem.Should().Be(item * i);
chanitem.Should().Be(item * i);
}
}
}
[Fact]
public async Task CompleteOnEmpty()
{
var (reference, channel) = CreateChannel();
foreach (var item in new[] { 10, 20, 30 })
{
reference.Writer.TryWrite(item);
channel.Writer.TryWrite(item);
reference.Reader.TryRead(out var refitem);
channel.Reader.TryRead(out var chanitem);
}
// Empty.
var completion1 = reference.Reader.Completion;
var wait1 = reference.Reader.WaitToReadAsync();
var completion2 = channel.Reader.Completion;
var wait2 = channel.Reader.WaitToReadAsync();
reference.Writer.TryComplete();
channel.Writer.TryComplete();
completion1.Status.Should().Be(TaskStatus.RanToCompletion);
completion2.Status.Should().Be(UniTaskStatus.Succeeded);
(await wait1).Should().BeFalse();
(await wait2).Should().BeFalse();
}
[Fact]
public async Task CompleteErrorOnEmpty()
{
var (reference, channel) = CreateChannel();
foreach (var item in new[] { 10, 20, 30 })
{
reference.Writer.TryWrite(item);
channel.Writer.TryWrite(item);
reference.Reader.TryRead(out var refitem);
channel.Reader.TryRead(out var chanitem);
}
// Empty.
var completion1 = reference.Reader.Completion;
var wait1 = reference.Reader.WaitToReadAsync();
var completion2 = channel.Reader.Completion;
var wait2 = channel.Reader.WaitToReadAsync();
var ex = new Exception();
reference.Writer.TryComplete(ex);
channel.Writer.TryComplete(ex);
completion1.Status.Should().Be(TaskStatus.Faulted);
completion2.Status.Should().Be(UniTaskStatus.Faulted);
(await Assert.ThrowsAsync<Exception>(async () => await wait1)).Should().Be(ex);
(await Assert.ThrowsAsync<Exception>(async () => await wait2)).Should().Be(ex);
}
[Fact]
public async Task CompleteWithRest()
{
var (reference, channel) = CreateChannel();
foreach (var item in new[] { 10, 20, 30 })
{
reference.Writer.TryWrite(item);
channel.Writer.TryWrite(item);
}
// Three Item2.
var completion1 = reference.Reader.Completion;
var wait1 = reference.Reader.WaitToReadAsync();
var completion2 = channel.Reader.Completion;
var wait2 = channel.Reader.WaitToReadAsync();
reference.Writer.TryComplete();
channel.Writer.TryComplete();
// completion1.Status.Should().Be(TaskStatus.WaitingForActivation);
completion2.Status.Should().Be(UniTaskStatus.Pending);
(await wait1).Should().BeTrue();
(await wait2).Should().BeTrue();
foreach (var item in new[] { 10, 20, 30 })
{
reference.Reader.TryRead(out var i1).Should().BeTrue();
channel.Reader.TryRead(out var i2).Should().BeTrue();
i1.Should().Be(item);
i2.Should().Be(item);
}
(await reference.Reader.WaitToReadAsync()).Should().BeFalse();
(await channel.Reader.WaitToReadAsync()).Should().BeFalse();
completion1.Status.Should().Be(TaskStatus.RanToCompletion);
completion2.Status.Should().Be(UniTaskStatus.Succeeded);
}
[Fact]
public async Task CompleteErrorWithRest()
{
var (reference, channel) = CreateChannel();
foreach (var item in new[] { 10, 20, 30 })
{
reference.Writer.TryWrite(item);
channel.Writer.TryWrite(item);
}
// Three Item2.
var completion1 = reference.Reader.Completion;
var wait1 = reference.Reader.WaitToReadAsync();
var completion2 = channel.Reader.Completion;
var wait2 = channel.Reader.WaitToReadAsync();
var ex = new Exception();
reference.Writer.TryComplete(ex);
channel.Writer.TryComplete(ex);
// completion1.Status.Should().Be(TaskStatus.WaitingForActivation);
completion2.Status.Should().Be(UniTaskStatus.Pending);
(await wait1).Should().BeTrue();
(await wait2).Should().BeTrue();
foreach (var item in new[] { 10, 20, 30 })
{
reference.Reader.TryRead(out var i1).Should().BeTrue();
channel.Reader.TryRead(out var i2).Should().BeTrue();
i1.Should().Be(item);
i2.Should().Be(item);
}
wait1 = reference.Reader.WaitToReadAsync();
wait2 = channel.Reader.WaitToReadAsync();
(await Assert.ThrowsAsync<Exception>(async () => await wait1)).Should().Be(ex);
(await Assert.ThrowsAsync<Exception>(async () => await wait2)).Should().Be(ex);
completion1.Status.Should().Be(TaskStatus.Faulted);
completion2.Status.Should().Be(UniTaskStatus.Faulted);
}
[Fact]
public async Task Cancellation()
{
var (reference, channel) = CreateChannel();
var cts = new CancellationTokenSource();
var wait1 = reference.Reader.WaitToReadAsync(cts.Token);
var wait2 = channel.Reader.WaitToReadAsync(cts.Token);
cts.Cancel();
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await wait1)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await wait2)).CancellationToken.Should().Be(cts.Token);
}
[Fact]
public async Task AsyncEnumerator()
{
var (reference, channel) = CreateChannel();
var ta1 = reference.Reader.ReadAllAsync().ToArrayAsync();
var ta2 = channel.Reader.ReadAllAsync().ToArrayAsync();
foreach (var item in new[] { 10, 20, 30 })
{
reference.Writer.TryWrite(item);
channel.Writer.TryWrite(item);
}
reference.Writer.TryComplete();
channel.Writer.TryComplete();
(await ta1).Should().BeEquivalentTo(new[] { 10, 20, 30 });
(await ta2).Should().BeEquivalentTo(new[] { 10, 20, 30 });
}
[Fact]
public async Task AsyncEnumeratorCancellation()
{
// Token1, Token2 and Cancel1
{
var cts1 = new CancellationTokenSource();
var cts2 = new CancellationTokenSource();
var (reference, channel) = CreateChannel();
var ta1 = reference.Reader.ReadAllAsync(cts1.Token).ToArrayAsync(cts2.Token);
var ta2 = channel.Reader.ReadAllAsync(cts1.Token).ToArrayAsync(cts2.Token);
foreach (var item in new[] { 10, 20, 30 })
{
reference.Writer.TryWrite(item);
channel.Writer.TryWrite(item);
}
cts1.Cancel();
await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta1);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta2)).CancellationToken.Should().Be(cts1.Token);
}
// Token1, Token2 and Cancel2
{
var cts1 = new CancellationTokenSource();
var cts2 = new CancellationTokenSource();
var (reference, channel) = CreateChannel();
var ta1 = reference.Reader.ReadAllAsync(cts1.Token).ToArrayAsync(cts2.Token);
var ta2 = channel.Reader.ReadAllAsync(cts1.Token).ToArrayAsync(cts2.Token);
foreach (var item in new[] { 10, 20, 30 })
{
reference.Writer.TryWrite(item);
channel.Writer.TryWrite(item);
}
cts2.Cancel();
await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta1);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta2)).CancellationToken.Should().Be(cts2.Token);
}
// Token1 and Cancel1
{
var cts1 = new CancellationTokenSource();
var (reference, channel) = CreateChannel();
var ta1 = reference.Reader.ReadAllAsync(cts1.Token).ToArrayAsync();
var ta2 = channel.Reader.ReadAllAsync(cts1.Token).ToArrayAsync();
foreach (var item in new[] { 10, 20, 30 })
{
reference.Writer.TryWrite(item);
channel.Writer.TryWrite(item);
}
cts1.Cancel();
await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta1);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta2)).CancellationToken.Should().Be(cts1.Token);
}
// Token2 and Cancel2
{
var cts2 = new CancellationTokenSource();
var (reference, channel) = CreateChannel();
var ta1 = reference.Reader.ReadAllAsync().ToArrayAsync(cts2.Token);
var ta2 = channel.Reader.ReadAllAsync().ToArrayAsync(cts2.Token);
foreach (var item in new[] { 10, 20, 30 })
{
reference.Writer.TryWrite(item);
channel.Writer.TryWrite(item);
}
cts2.Cancel();
await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta1);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta2)).CancellationToken.Should().Be(cts2.Token);
}
}
}
}

View File

@@ -0,0 +1,590 @@
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using Cysharp.Threading.Tasks.Linq;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests
{
public class CompletionSourceTest
{
[Fact]
public async Task SetFirst()
{
{
var tcs = new UniTaskCompletionSource();
tcs.TrySetResult();
await tcs.Task; // ok.
await tcs.Task; // ok.
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
}
{
var tcs = new UniTaskCompletionSource();
tcs.TrySetException(new TestException());
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
}
var cts = new CancellationTokenSource();
{
var tcs = new UniTaskCompletionSource();
tcs.TrySetException(new OperationCanceledException(cts.Token));
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
{
var tcs = new UniTaskCompletionSource();
tcs.TrySetCanceled(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
}
[Fact]
public async Task SingleOnFirst()
{
{
var tcs = new UniTaskCompletionSource();
async UniTask Await()
{
await tcs.Task;
}
var a = Await();
tcs.TrySetResult();
await a;
await tcs.Task; // ok.
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
}
{
var tcs = new UniTaskCompletionSource();
async UniTask Await()
{
await tcs.Task;
}
var a = Await();
tcs.TrySetException(new TestException());
await Assert.ThrowsAsync<TestException>(async () => await a);
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
}
var cts = new CancellationTokenSource();
{
var tcs = new UniTaskCompletionSource();
async UniTask Await()
{
await tcs.Task;
}
var a = Await();
tcs.TrySetException(new OperationCanceledException(cts.Token));
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
{
var tcs = new UniTaskCompletionSource();
async UniTask Await()
{
await tcs.Task;
}
var a = Await();
tcs.TrySetCanceled(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
}
[Fact]
public async Task MultiOne()
{
{
var tcs = new UniTaskCompletionSource();
async UniTask Await()
{
await tcs.Task;
}
var a = Await();
var b = Await();
tcs.TrySetResult();
await a;
await b;
await tcs.Task; // ok.
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
}
{
var tcs = new UniTaskCompletionSource();
async UniTask Await()
{
await tcs.Task;
}
var a = Await();
var b = Await();
tcs.TrySetException(new TestException());
await Assert.ThrowsAsync<TestException>(async () => await a);
await Assert.ThrowsAsync<TestException>(async () => await b);
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
}
var cts = new CancellationTokenSource();
{
var tcs = new UniTaskCompletionSource();
async UniTask Await()
{
await tcs.Task;
}
var a = Await();
var b = Await();
tcs.TrySetException(new OperationCanceledException(cts.Token));
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
{
var tcs = new UniTaskCompletionSource();
async UniTask Await()
{
await tcs.Task;
}
var a = Await();
var b = Await();
tcs.TrySetCanceled(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
}
[Fact]
public async Task MultiTwo()
{
{
var tcs = new UniTaskCompletionSource();
async UniTask Await()
{
await tcs.Task;
}
var a = Await();
var b = Await();
var c = Await();
tcs.TrySetResult();
await a;
await b;
await c;
await tcs.Task; // ok.
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
}
{
var tcs = new UniTaskCompletionSource();
async UniTask Await()
{
await tcs.Task;
}
var a = Await();
var b = Await();
var c = Await();
tcs.TrySetException(new TestException());
await Assert.ThrowsAsync<TestException>(async () => await a);
await Assert.ThrowsAsync<TestException>(async () => await b);
await Assert.ThrowsAsync<TestException>(async () => await c);
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
}
var cts = new CancellationTokenSource();
{
var tcs = new UniTaskCompletionSource();
async UniTask Await()
{
await tcs.Task;
}
var a = Await();
var b = Await();
var c = Await();
tcs.TrySetException(new OperationCanceledException(cts.Token));
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
{
var tcs = new UniTaskCompletionSource();
async UniTask Await()
{
await tcs.Task;
}
var a = Await();
var b = Await();
var c = Await();
tcs.TrySetCanceled(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
}
class TestException : Exception
{
}
}
public class CompletionSourceTest2
{
[Fact]
public async Task SetFirst()
{
{
var tcs = new UniTaskCompletionSource<int>();
tcs.TrySetResult(10);
var a = await tcs.Task; // ok.
var b = await tcs.Task; // ok.
a.Should().Be(10);
b.Should().Be(10);
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
}
{
var tcs = new UniTaskCompletionSource<int>();
tcs.TrySetException(new TestException());
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
}
var cts = new CancellationTokenSource();
{
var tcs = new UniTaskCompletionSource<int>();
tcs.TrySetException(new OperationCanceledException(cts.Token));
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
{
var tcs = new UniTaskCompletionSource<int>();
tcs.TrySetCanceled(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
}
[Fact]
public async Task SingleOnFirst()
{
{
var tcs = new UniTaskCompletionSource<int>();
async UniTask<int> Await()
{
return await tcs.Task;
}
var a = Await();
tcs.TrySetResult(10);
var r1 = await a;
var r2 = await tcs.Task; // ok.
r1.Should().Be(10);
r2.Should().Be(10);
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
}
{
var tcs = new UniTaskCompletionSource<int>();
async UniTask<int> Await()
{
return await tcs.Task;
}
var a = Await();
tcs.TrySetException(new TestException());
await Assert.ThrowsAsync<TestException>(async () => await a);
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
}
var cts = new CancellationTokenSource();
{
var tcs = new UniTaskCompletionSource<int>();
async UniTask<int> Await()
{
return await tcs.Task;
}
var a = Await();
tcs.TrySetException(new OperationCanceledException(cts.Token));
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
{
var tcs = new UniTaskCompletionSource<int>();
async UniTask<int> Await()
{
return await tcs.Task;
}
var a = Await();
tcs.TrySetCanceled(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
}
[Fact]
public async Task MultiOne()
{
{
var tcs = new UniTaskCompletionSource<int>();
async UniTask<int> Await()
{
return await tcs.Task;
}
var a = Await();
var b = Await();
tcs.TrySetResult(10);
var r1 = await a;
var r2 = await b;
var r3 = await tcs.Task; // ok.
(r1, r2, r3).Should().Be((10, 10, 10));
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
}
{
var tcs = new UniTaskCompletionSource<int>();
async UniTask<int> Await()
{
return await tcs.Task;
}
var a = Await();
var b = Await();
tcs.TrySetException(new TestException());
await Assert.ThrowsAsync<TestException>(async () => await a);
await Assert.ThrowsAsync<TestException>(async () => await b);
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
}
var cts = new CancellationTokenSource();
{
var tcs = new UniTaskCompletionSource<int>();
async UniTask<int> Await()
{
return await tcs.Task;
}
var a = Await();
var b = Await();
tcs.TrySetException(new OperationCanceledException(cts.Token));
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
{
var tcs = new UniTaskCompletionSource<int>();
async UniTask<int> Await()
{
return await tcs.Task;
}
var a = Await();
var b = Await();
tcs.TrySetCanceled(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
}
[Fact]
public async Task MultiTwo()
{
{
var tcs = new UniTaskCompletionSource<int>();
async UniTask<int> Await()
{
return await tcs.Task;
}
var a = Await();
var b = Await();
var c = Await();
tcs.TrySetResult(10);
var r1 = await a;
var r2 = await b;
var r3 = await c;
var r4 = await tcs.Task; // ok.
(r1, r2, r3, r4).Should().Be((10, 10, 10, 10));
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
}
{
var tcs = new UniTaskCompletionSource<int>();
async UniTask<int> Await()
{
return await tcs.Task;
}
var a = Await();
var b = Await();
var c = Await();
tcs.TrySetException(new TestException());
await Assert.ThrowsAsync<TestException>(async () => await a);
await Assert.ThrowsAsync<TestException>(async () => await b);
await Assert.ThrowsAsync<TestException>(async () => await c);
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
}
var cts = new CancellationTokenSource();
{
var tcs = new UniTaskCompletionSource<int>();
async UniTask<int> Await()
{
return await tcs.Task;
}
var a = Await();
var b = Await();
var c = Await();
tcs.TrySetException(new OperationCanceledException(cts.Token));
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
{
var tcs = new UniTaskCompletionSource<int>();
async UniTask<int> Await()
{
return await tcs.Task;
}
var a = Await();
var b = Await();
var c = Await();
tcs.TrySetCanceled(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
}
}
class TestException : Exception
{
}
}
}

View File

@@ -0,0 +1,47 @@
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using Cysharp.Threading.Tasks.Linq;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests
{
public class DeferTest
{
[Fact]
public async Task D()
{
var created = false;
var v = UniTask.Defer(() => { created = true; return UniTask.Run(() => 10); });
created.Should().BeFalse();
var t = await v;
created.Should().BeTrue();
t.Should().Be(10);
}
[Fact]
public async Task D2()
{
var created = false;
var v = UniTask.Defer(() => { created = true; return UniTask.Run(() => 10).AsUniTask(); });
created.Should().BeFalse();
await v;
created.Should().BeTrue();
}
}
}

View File

@@ -35,7 +35,7 @@ namespace NetCoreTests.Linq
xs.Should().Be(ys);
}
{
var xs = await UniTaskAsyncEnumerable.Range(start, count).SumAwaitCancellationAsync((x, _) => UniTask.Run(() => x));
var xs = await UniTaskAsyncEnumerable.Range(start, count).SumAwaitWithCancellationAsync((x, _) => UniTask.Run(() => x));
var ys = Enumerable.Range(start, count).Sum(x => x);
xs.Should().Be(ys);
}

View File

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

View File

@@ -319,5 +319,119 @@ namespace NetCoreTests.Linq
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
}
}
[Fact]
public async Task CombineLatestOK()
{
var a = new AsyncReactiveProperty<int>(0);
var b = new AsyncReactiveProperty<int>(0);
var list = new List<(int, int)>();
var complete = a.WithoutCurrent().CombineLatest(b.WithoutCurrent(), (x, y) => (x, y)).ForEachAsync(x => list.Add(x));
list.Count.Should().Be(0);
a.Value = 10;
list.Count.Should().Be(0);
a.Value = 20;
list.Count.Should().Be(0);
b.Value = 1;
list.Count.Should().Be(1);
list[0].Should().Be((20, 1));
a.Value = 30;
list.Last().Should().Be((30, 1));
b.Value = 2;
list.Last().Should().Be((30, 2));
a.Dispose();
b.Value = 3;
list.Last().Should().Be((30, 3));
b.Dispose();
await complete;
}
[Fact]
public async Task CombineLatestLong()
{
var a = UniTaskAsyncEnumerable.Range(1, 100000);
var b = new AsyncReactiveProperty<int>(0);
var list = new List<(int, int)>();
var complete = a.CombineLatest(b.WithoutCurrent(), (x, y) => (x, y)).ForEachAsync(x => list.Add(x));
b.Value = 1;
list[0].Should().Be((100000, 1));
b.Dispose();
await complete;
}
[Fact]
public async Task CombineLatestError()
{
var a = new AsyncReactiveProperty<int>(0);
var b = new AsyncReactiveProperty<int>(0);
var list = new List<(int, int)>();
var complete = a.WithoutCurrent()
.Select(x => { if (x == 0) { throw new MyException(); } return x; })
.CombineLatest(b.WithoutCurrent(), (x, y) => (x, y)).ForEachAsync(x => list.Add(x));
a.Value = 10;
b.Value = 1;
list.Last().Should().Be((10, 1));
a.Value = 0;
await Assert.ThrowsAsync<MyException>(async () => await complete);
}
[Fact]
public async Task PariwiseImmediate()
{
var xs = await UniTaskAsyncEnumerable.Range(1, 5).Pairwise().ToArrayAsync();
xs.Should().BeEquivalentTo((1, 2), (2, 3), (3, 4), (4, 5));
}
[Fact]
public async Task Pariwise()
{
var a = new AsyncReactiveProperty<int>(0);
var list = new List<(int, int)>();
var complete = a.WithoutCurrent().Pairwise().ForEachAsync(x => list.Add(x));
list.Count.Should().Be(0);
a.Value = 10;
list.Count.Should().Be(0);
a.Value = 20;
list.Count.Should().Be(1);
a.Value = 30;
a.Value = 40;
a.Value = 50;
a.Dispose();
await complete;
list.Should().BeEquivalentTo((10, 20), (20, 30), (30, 40), (40, 50));
}
class MyException : Exception
{
}
}
}

View File

@@ -0,0 +1,78 @@
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Linq;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests.Linq
{
public class PublishTest
{
[Fact]
public async Task Normal()
{
var rp = new AsyncReactiveProperty<int>(1);
var multicast = rp.Publish();
var a = multicast.ToArrayAsync();
var b = multicast.Take(2).ToArrayAsync();
var disp = multicast.Connect();
rp.Value = 2;
(await b).Should().BeEquivalentTo(1, 2);
var c = multicast.ToArrayAsync();
rp.Value = 3;
rp.Value = 4;
rp.Value = 5;
rp.Dispose();
(await a).Should().BeEquivalentTo(1, 2, 3, 4, 5);
(await c).Should().BeEquivalentTo(3, 4, 5);
disp.Dispose();
}
[Fact]
public async Task Cancel()
{
var rp = new AsyncReactiveProperty<int>(1);
var multicast = rp.Publish();
var a = multicast.ToArrayAsync();
var b = multicast.Take(2).ToArrayAsync();
var disp = multicast.Connect();
rp.Value = 2;
(await b).Should().BeEquivalentTo(1, 2);
var c = multicast.ToArrayAsync();
rp.Value = 3;
disp.Dispose();
rp.Value = 4;
rp.Value = 5;
rp.Dispose();
await Assert.ThrowsAsync<OperationCanceledException>(async () => await a);
await Assert.ThrowsAsync<OperationCanceledException>(async () => await c);
}
}
}

View File

@@ -0,0 +1,29 @@
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Linq;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests.Linq
{
public class QueueTest
{
[Fact]
public async Task Q()
{
var rp = new AsyncReactiveProperty<int>(100);
var l = new List<int>();
await rp.Take(10).Queue().ForEachAsync(x =>
{
rp.Value += 10;
l.Add(x);
});
l.Should().BeEquivalentTo(100, 110, 120, 130, 140, 150, 160, 170, 180, 190);
}
}
}

View File

@@ -0,0 +1,165 @@
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Linq;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests.Linq
{
public class TakeInfinityTest
{
[Fact]
public async Task Take()
{
var rp = new AsyncReactiveProperty<int>(1);
var xs = rp.Take(5).ToArrayAsync();
rp.Value = 2;
rp.Value = 3;
rp.Value = 4;
rp.Value = 5;
(await xs).Should().BeEquivalentTo(1, 2, 3, 4, 5);
}
[Fact]
public async Task TakeWhile()
{
var rp = new AsyncReactiveProperty<int>(1);
var xs = rp.TakeWhile(x => x != 5).ToArrayAsync();
rp.Value = 2;
rp.Value = 3;
rp.Value = 4;
rp.Value = 5;
(await xs).Should().BeEquivalentTo(1, 2, 3, 4);
}
[Fact]
public async Task TakeUntilCanceled()
{
var cts = new CancellationTokenSource();
var rp = new AsyncReactiveProperty<int>(1);
var xs = rp.TakeUntilCanceled(cts.Token).ToArrayAsync();
var c = CancelAsync();
await c;
var foo = await xs;
foo.Should().BeEquivalentTo(new[] { 1, 10, 20 });
async Task CancelAsync()
{
rp.Value = 10;
await Task.Yield();
rp.Value = 20;
await Task.Yield();
cts.Cancel();
rp.Value = 30;
await Task.Yield();
rp.Value = 40;
}
}
[Fact]
public async Task SkipUntilCanceled()
{
var cts = new CancellationTokenSource();
var rp = new AsyncReactiveProperty<int>(1);
var xs = rp.SkipUntilCanceled(cts.Token).ToArrayAsync();
var c = CancelAsync();
await c;
var foo = await xs;
foo.Should().BeEquivalentTo(new[] { 20, 30, 40 });
async Task CancelAsync()
{
rp.Value = 10;
await Task.Yield();
rp.Value = 20;
await Task.Yield();
cts.Cancel();
rp.Value = 30;
await Task.Yield();
rp.Value = 40;
rp.Dispose(); // complete.
}
}
[Fact]
public async Task TakeUntil()
{
var cts = new AsyncReactiveProperty<int>(0);
var rp = new AsyncReactiveProperty<int>(1);
var xs = rp.TakeUntil(cts.WaitAsync()).ToArrayAsync();
var c = CancelAsync();
await c;
var foo = await xs;
foo.Should().BeEquivalentTo(new[] { 1, 10, 20 });
async Task CancelAsync()
{
rp.Value = 10;
await Task.Yield();
rp.Value = 20;
await Task.Yield();
cts.Value = 9999;
rp.Value = 30;
await Task.Yield();
rp.Value = 40;
}
}
[Fact]
public async Task SkipUntil()
{
var cts = new AsyncReactiveProperty<int>(0);
var rp = new AsyncReactiveProperty<int>(1);
var xs = rp.SkipUntil(cts.WaitAsync()).ToArrayAsync();
var c = CancelAsync();
await c;
var foo = await xs;
foo.Should().BeEquivalentTo(new[] { 20, 30, 40 });
async Task CancelAsync()
{
rp.Value = 10;
await Task.Yield();
rp.Value = 20;
await Task.Yield();
cts.Value = 9999;
rp.Value = 30;
await Task.Yield();
rp.Value = 40;
rp.Dispose(); // complete.
}
}
}
}

View File

@@ -0,0 +1,301 @@
#pragma warning disable CS1998
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using Cysharp.Threading.Tasks.Linq;
using System.Threading.Tasks;
using Xunit;
using System.Runtime.CompilerServices;
namespace NetCoreTests
{
public class UniTaskBuilderTest
{
[Fact]
public async Task Empty()
{
await Core();
static async UniTask Core()
{
}
}
[Fact]
public async Task EmptyThrow()
{
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
static async UniTask Core()
{
throw new TaskTestException();
}
}
[Fact]
public async Task Task_Done()
{
await Core();
static async UniTask Core()
{
await new TestAwaiter(true, UniTaskStatus.Succeeded);
}
}
[Fact]
public async Task Task_Fail()
{
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
static async UniTask Core()
{
await new TestAwaiter(true, UniTaskStatus.Faulted);
}
}
[Fact]
public async Task Task_Cancel()
{
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
static async UniTask Core()
{
await new TestAwaiter(true, UniTaskStatus.Canceled);
}
}
[Fact]
public async Task AwaitUnsafeOnCompletedCall_Task_SetResult()
{
await Core();
static async UniTask Core()
{
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
}
}
[Fact]
public async Task AwaitUnsafeOnCompletedCall_Task_SetException()
{
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
static async UniTask Core()
{
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Faulted);
throw new InvalidOperationException();
}
}
[Fact]
public async Task AwaitUnsafeOnCompletedCall_Task_SetCancelException()
{
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
static async UniTask Core()
{
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Canceled);
throw new InvalidOperationException();
}
}
}
public class UniTask_T_BuilderTest
{
[Fact]
public async Task Empty()
{
(await Core()).Should().Be(10);
static async UniTask<int> Core()
{
return 10;
}
}
[Fact]
public async Task EmptyThrow()
{
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
static async UniTask<int> Core()
{
throw new TaskTestException();
}
}
[Fact]
public async Task Task_Done()
{
(await Core()).Should().Be(10);
static async UniTask<int> Core()
{
return await new TestAwaiter<int>(true, UniTaskStatus.Succeeded, 10);
}
}
[Fact]
public async Task Task_Fail()
{
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
static async UniTask<int> Core()
{
return await new TestAwaiter<int>(true, UniTaskStatus.Faulted, 10);
}
}
[Fact]
public async Task Task_Cancel()
{
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
static async UniTask<int> Core()
{
return await new TestAwaiter<int>(true, UniTaskStatus.Canceled, 10);
}
}
[Fact]
public async Task AwaitUnsafeOnCompletedCall_Task_SetResult()
{
(await Core()).Should().Be(6);
static async UniTask<int> Core()
{
var sum = 0;
sum += await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 1);
sum += await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 2);
sum += await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 3);
return sum;
}
}
[Fact]
public async Task AwaitUnsafeOnCompletedCall_Task_SetException()
{
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
static async UniTask<int> Core()
{
await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
await new TestAwaiter<int>(false, UniTaskStatus.Faulted, 10);
throw new InvalidOperationException();
}
}
[Fact]
public async Task AwaitUnsafeOnCompletedCall_Task_SetCancelException()
{
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
static async UniTask<int> Core()
{
await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
await new TestAwaiter<int>(false, UniTaskStatus.Canceled, 10);
throw new InvalidOperationException();
}
}
}
public class TaskTestException : Exception
{
}
public struct TestAwaiter : ICriticalNotifyCompletion
{
readonly UniTaskStatus status;
readonly bool isCompleted;
public TestAwaiter(bool isCompleted, UniTaskStatus status)
{
this.isCompleted = isCompleted;
this.status = status;
}
public TestAwaiter GetAwaiter() => this;
public bool IsCompleted => isCompleted;
public void GetResult()
{
switch (status)
{
case UniTaskStatus.Faulted:
throw new TaskTestException();
case UniTaskStatus.Canceled:
throw new OperationCanceledException();
case UniTaskStatus.Pending:
case UniTaskStatus.Succeeded:
default:
break;
}
}
public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
}
public void UnsafeOnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
}
}
public struct TestAwaiter<T> : ICriticalNotifyCompletion
{
readonly UniTaskStatus status;
readonly bool isCompleted;
readonly T value;
public TestAwaiter(bool isCompleted, UniTaskStatus status, T value)
{
this.isCompleted = isCompleted;
this.status = status;
this.value = value;
}
public TestAwaiter<T> GetAwaiter() => this;
public bool IsCompleted => isCompleted;
public T GetResult()
{
switch (status)
{
case UniTaskStatus.Faulted:
throw new TaskTestException();
case UniTaskStatus.Canceled:
throw new OperationCanceledException();
case UniTaskStatus.Pending:
case UniTaskStatus.Succeeded:
default:
return value;
}
}
public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
}
public void UnsafeOnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
}
}
}

View File

@@ -0,0 +1,637 @@
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using Cysharp.Threading.Tasks.Linq;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests
{
public class TriggerEventTest
{
[Fact]
public void SimpleAdd()
{
var ev = new TriggerEvent<int>();
// do nothing
ev.SetResult(0);
ev.SetError(null);
ev.SetCompleted();
ev.SetCanceled(default);
{
var one = new TestEvent(1);
ev.Add(one);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
ev.SetCompleted();
one.CompletedCalled.Count.Should().Be(1);
// do nothing
ev.SetResult(0);
ev.SetError(null);
ev.SetCompleted();
ev.SetCanceled(default);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
one.CompletedCalled.Count.Should().Be(1);
}
// after removed, onemore
{
var one = new TestEvent(1);
ev.Add(one);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
ev.SetCompleted();
one.CompletedCalled.Count.Should().Be(1);
// do nothing
ev.SetResult(0);
ev.SetError(null);
ev.SetCompleted();
ev.SetCanceled(default);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
one.CompletedCalled.Count.Should().Be(1);
}
}
[Fact]
public void AddFour()
{
var ev = new TriggerEvent<int>();
// do nothing
ev.SetResult(0);
ev.SetError(null);
ev.SetCompleted();
ev.SetCanceled(default);
{
var one = new TestEvent(1);
var two = new TestEvent(2);
var three = new TestEvent(3);
var four = new TestEvent(4);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.Add(four);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
ev.SetCompleted();
one.CompletedCalled.Count.Should().Be(1);
two.CompletedCalled.Count.Should().Be(1);
three.CompletedCalled.Count.Should().Be(1);
// do nothing
ev.SetResult(0);
ev.SetError(null);
ev.SetCompleted();
ev.SetCanceled(default);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
one.CompletedCalled.Count.Should().Be(1);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.CompletedCalled.Count.Should().Be(1);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.CompletedCalled.Count.Should().Be(1);
}
// after removed, onemore.
{
var one = new TestEvent(1);
var two = new TestEvent(2);
var three = new TestEvent(3);
var four = new TestEvent(4);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.Add(four);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
ev.Add(four);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
ev.SetCompleted();
one.CompletedCalled.Count.Should().Be(1);
two.CompletedCalled.Count.Should().Be(1);
three.CompletedCalled.Count.Should().Be(1);
// do nothing
ev.SetResult(0);
ev.SetError(null);
ev.SetCompleted();
ev.SetCanceled(default);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
one.CompletedCalled.Count.Should().Be(1);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.CompletedCalled.Count.Should().Be(1);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.CompletedCalled.Count.Should().Be(1);
}
}
[Fact]
public void OneRemove()
{
var ev = new TriggerEvent<int>();
{
var one = new TestEvent(1);
var two = new TestEvent(2);
var three = new TestEvent(3);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
ev.Remove(one);
ev.SetResult(40);
ev.SetResult(50);
ev.SetResult(60);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
}
}
[Fact]
public void TwoRemove()
{
var ev = new TriggerEvent<int>();
{
var one = new TestEvent(1);
var two = new TestEvent(2);
var three = new TestEvent(3);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
ev.Remove(two);
ev.SetResult(40);
ev.SetResult(50);
ev.SetResult(60);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
}
}
[Fact]
public void ThreeRemove()
{
var ev = new TriggerEvent<int>();
{
var one = new TestEvent(1);
var two = new TestEvent(2);
var three = new TestEvent(3);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
ev.Remove(three);
ev.SetResult(40);
ev.SetResult(50);
ev.SetResult(60);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
}
[Fact]
public void RemoveSelf()
{
new RemoveMe().Run1();
new RemoveMe().Run2();
new RemoveMe().Run3();
}
[Fact]
public void RemoveNextInIterating()
{
new RemoveNext().Run1();
new RemoveNext().Run2();
new RemoveNext().Run3();
}
[Fact]
public void RemoveNextNextTest()
{
new RemoveNextNext().Run1();
new RemoveNextNext().Run2();
}
[Fact]
public void AddTest()
{
new AddMe().Run1();
new AddMe().Run2();
}
public class RemoveMe
{
TriggerEvent<int> ev;
public void Run1()
{
TestEvent one = default;
one = new TestEvent(1, () => ev.Remove(one));
var two = new TestEvent(2);
var three = new TestEvent(3);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
public void Run2()
{
TestEvent one = default;
one = new TestEvent(1, () => ev.Remove(one));
var two = new TestEvent(2);
var three = new TestEvent(3);
ev.Add(two);
ev.Add(one); // add second.
ev.Add(three);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
public void Run3()
{
TestEvent one = default;
one = new TestEvent(1, () => ev.Remove(one));
var two = new TestEvent(2);
var three = new TestEvent(3);
ev.Add(two);
ev.Add(three);
ev.Add(one); // add thired.
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
}
public class RemoveNext
{
TriggerEvent<int> ev;
public void Run1()
{
TestEvent one = default;
TestEvent two = default;
TestEvent three = default;
one = new TestEvent(1, () => ev.Remove(two));
two = new TestEvent(2);
three = new TestEvent(3);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Count.Should().Be(0);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
public void Run2()
{
TestEvent one = default;
TestEvent two = default;
TestEvent three = default;
one = new TestEvent(1, () => ev.Remove(two));
two = new TestEvent(2);
three = new TestEvent(3);
ev.Add(two);
ev.Add(one); // add second
ev.Add(three);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
public void Run3()
{
TestEvent one = default;
TestEvent two = default;
TestEvent three = default;
one = new TestEvent(1, () => ev.Remove(two));
two = new TestEvent(2);
three = new TestEvent(3);
ev.Add(two);
ev.Add(three);
ev.Add(one); // add thired.
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
}
public class RemoveNextNext
{
TriggerEvent<int> ev;
public void Run1()
{
TestEvent one = default;
TestEvent two = default;
TestEvent three = default;
TestEvent four = default;
one = new TestEvent(1, () => { ev.Remove(two); ev.Remove(three); });
two = new TestEvent(2);
three = new TestEvent(3);
four = new TestEvent(4);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.Add(four);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Count.Should().Be(0);
three.NextCalled.Count.Should().Be(0);
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
public void Run2()
{
TestEvent one = default;
TestEvent two = default;
TestEvent three = default;
TestEvent four = default;
one = new TestEvent(1, () => { ev.Remove(one); ev.Remove(two); ev.Remove(three); });
two = new TestEvent(2);
three = new TestEvent(3);
four = new TestEvent(4);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.Add(four);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10);
two.NextCalled.Count.Should().Be(0);
three.NextCalled.Count.Should().Be(0);
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
}
public class AddMe
{
TriggerEvent<int> ev;
public void Run1()
{
TestEvent one = default;
TestEvent two = default;
TestEvent three = default;
TestEvent four = default;
one = new TestEvent(1, () =>
{
if (two == null)
{
ev.Add(two = new TestEvent(2));
}
else if (three == null)
{
ev.Add(three = new TestEvent(3));
}
else if (four == null)
{
ev.Add(four = new TestEvent(4));
}
});
ev.Add(one);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
ev.SetResult(40);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40);
two.NextCalled.Should().BeEquivalentTo(20, 30, 40);
three.NextCalled.Should().BeEquivalentTo(30, 40);
four.NextCalled.Should().BeEquivalentTo(40);
}
public void Run2()
{
TestEvent one = default;
TestEvent two = default;
TestEvent three = default;
TestEvent four = default;
one = new TestEvent(1, () =>
{
if (two == null)
{
ev.Add(two = new TestEvent(2, () =>
{
if (three == null)
{
ev.Add(three = new TestEvent(3, () =>
{
if (four == null)
{
ev.Add(four = new TestEvent(4));
}
}));
}
}));
}
});
ev.Add(one);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
ev.SetResult(40);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40);
two.NextCalled.Should().BeEquivalentTo(20, 30, 40);
three.NextCalled.Should().BeEquivalentTo(30, 40);
four.NextCalled.Should().BeEquivalentTo(40);
}
}
}
public class TestEvent : ITriggerHandler<int>
{
public readonly int Id;
readonly Action iteratingEvent;
public TestEvent(int id)
{
this.Id = id;
}
public TestEvent(int id, Action iteratingEvent)
{
this.Id = id;
this.iteratingEvent = iteratingEvent;
}
public List<int> NextCalled = new List<int>();
public List<Exception> ErrorCalled = new List<Exception>();
public List<object> CompletedCalled = new List<object>();
public List<CancellationToken> CancelCalled = new List<CancellationToken>();
public ITriggerHandler<int> Prev { get; set; }
public ITriggerHandler<int> Next { get; set; }
public void OnCanceled(CancellationToken cancellationToken)
{
CancelCalled.Add(cancellationToken);
}
public void OnCompleted()
{
CompletedCalled.Add(new object());
}
public void OnError(Exception ex)
{
ErrorCalled.Add(ex);
}
public void OnNext(int value)
{
NextCalled.Add(value);
iteratingEvent?.Invoke();
}
public override string ToString()
{
return Id.ToString();
}
}
}

View File

@@ -0,0 +1,40 @@
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests
{
public class WithCancellationTest
{
[Fact]
public async Task Standard()
{
CancellationTokenSource cts = new CancellationTokenSource();
var v = await UniTask.Run(() => 10).WithCancellation(cts.Token);
v.Should().Be(10);
}
[Fact]
public async Task Cancel()
{
CancellationTokenSource cts = new CancellationTokenSource();
var t = UniTask.Create(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(1));
return 10;
}).WithCancellation(cts.Token);
cts.Cancel();
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await t)).CancellationToken.Should().Be(cts.Token);
}
}
}

View File

@@ -0,0 +1,30 @@
#if UNITY_EDITOR
using Cysharp.Threading.Tasks;
using System;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
public static class EditorRunnerChecker
{
[MenuItem("Tools/UniTaskEditorRunnerChecker")]
public static void RunUniTaskAsync()
{
RunCore().Forget();
}
static async UniTaskVoid RunCore()
{
Debug.Log("Start");
var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask();
Debug.Log(r.downloadHandler.text.Substring(0, 100));
Debug.Log("End");
}
}
#endif

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 4d5a9a3e1f0f069478969f752fde29a9
guid: e51b78c06cb410f42b36e0af9de3b065
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

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

View File

@@ -7,113 +7,239 @@ namespace Cysharp.Threading.Tasks
{
public class AsyncLazy
{
Func<UniTask> valueFactory;
UniTask target;
static Action<object> continuation = SetCompletionSource;
Func<UniTask> taskFactory;
UniTaskCompletionSource completionSource;
UniTask.Awaiter awaiter;
object syncLock;
bool initialized;
public AsyncLazy(Func<UniTask> valueFactory)
public AsyncLazy(Func<UniTask> taskFactory)
{
this.valueFactory = valueFactory;
this.target = default;
this.taskFactory = taskFactory;
this.completionSource = new UniTaskCompletionSource();
this.syncLock = new object();
this.initialized = false;
}
internal AsyncLazy(UniTask value)
internal AsyncLazy(UniTask task)
{
this.valueFactory = null;
this.target = value;
this.taskFactory = null;
this.completionSource = new UniTaskCompletionSource();
this.syncLock = null;
this.initialized = true;
var awaiter = task.GetAwaiter();
if (awaiter.IsCompleted)
{
SetCompletionSource(awaiter);
}
else
{
this.awaiter = awaiter;
awaiter.SourceOnCompleted(continuation, this);
}
}
public UniTask Task => EnsureInitialized();
public UniTask Task
{
get
{
EnsureInitialized();
return completionSource.Task;
}
}
public UniTask.Awaiter GetAwaiter() => EnsureInitialized().GetAwaiter();
UniTask EnsureInitialized()
public UniTask.Awaiter GetAwaiter() => Task.GetAwaiter();
void EnsureInitialized()
{
if (Volatile.Read(ref initialized))
{
return target;
return;
}
return EnsureInitializedCore();
EnsureInitializedCore();
}
UniTask EnsureInitializedCore()
void EnsureInitializedCore()
{
lock (syncLock)
{
if (!Volatile.Read(ref initialized))
{
var f = Interlocked.Exchange(ref valueFactory, null);
var f = Interlocked.Exchange(ref taskFactory, null);
if (f != null)
{
target = f().Preserve(); // with preserve(allow multiple await).
var task = f();
var awaiter = task.GetAwaiter();
if (awaiter.IsCompleted)
{
SetCompletionSource(awaiter);
}
else
{
this.awaiter = awaiter;
awaiter.SourceOnCompleted(continuation, this);
}
Volatile.Write(ref initialized, true);
}
}
}
}
return target;
void SetCompletionSource(in UniTask.Awaiter awaiter)
{
try
{
awaiter.GetResult();
completionSource.TrySetResult();
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
}
}
static void SetCompletionSource(object state)
{
var self = (AsyncLazy)state;
try
{
self.awaiter.GetResult();
self.completionSource.TrySetResult();
}
catch (Exception ex)
{
self.completionSource.TrySetException(ex);
}
finally
{
self.awaiter = default;
}
}
}
public class AsyncLazy<T>
{
Func<UniTask<T>> valueFactory;
UniTask<T> target;
static Action<object> continuation = SetCompletionSource;
Func<UniTask<T>> taskFactory;
UniTaskCompletionSource<T> completionSource;
UniTask<T>.Awaiter awaiter;
object syncLock;
bool initialized;
public AsyncLazy(Func<UniTask<T>> valueFactory)
public AsyncLazy(Func<UniTask<T>> taskFactory)
{
this.valueFactory = valueFactory;
this.target = default;
this.taskFactory = taskFactory;
this.completionSource = new UniTaskCompletionSource<T>();
this.syncLock = new object();
this.initialized = false;
}
internal AsyncLazy(UniTask<T> value)
internal AsyncLazy(UniTask<T> task)
{
this.valueFactory = null;
this.target = value;
this.taskFactory = null;
this.completionSource = new UniTaskCompletionSource<T>();
this.syncLock = null;
this.initialized = true;
var awaiter = task.GetAwaiter();
if (awaiter.IsCompleted)
{
SetCompletionSource(awaiter);
}
else
{
this.awaiter = awaiter;
awaiter.SourceOnCompleted(continuation, this);
}
}
public UniTask<T> Task => EnsureInitialized();
public UniTask<T> Task
{
get
{
EnsureInitialized();
return completionSource.Task;
}
}
public UniTask<T>.Awaiter GetAwaiter() => EnsureInitialized().GetAwaiter();
UniTask<T> EnsureInitialized()
public UniTask<T>.Awaiter GetAwaiter() => Task.GetAwaiter();
void EnsureInitialized()
{
if (Volatile.Read(ref initialized))
{
return target;
return;
}
return EnsureInitializedCore();
EnsureInitializedCore();
}
UniTask<T> EnsureInitializedCore()
void EnsureInitializedCore()
{
lock (syncLock)
{
if (!Volatile.Read(ref initialized))
{
var f = Interlocked.Exchange(ref valueFactory, null);
var f = Interlocked.Exchange(ref taskFactory, null);
if (f != null)
{
target = f().Preserve(); // with preserve(allow multiple await).
var task = f();
var awaiter = task.GetAwaiter();
if (awaiter.IsCompleted)
{
SetCompletionSource(awaiter);
}
else
{
this.awaiter = awaiter;
awaiter.SourceOnCompleted(continuation, this);
}
Volatile.Write(ref initialized, true);
}
}
}
}
return target;
void SetCompletionSource(in UniTask<T>.Awaiter awaiter)
{
try
{
var result = awaiter.GetResult();
completionSource.TrySetResult(result);
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
}
}
static void SetCompletionSource(object state)
{
var self = (AsyncLazy<T>)state;
try
{
var result = self.awaiter.GetResult();
self.completionSource.TrySetResult(result);
}
catch (Exception ex)
{
self.completionSource.TrySetException(ex);
}
finally
{
self.awaiter = default;
}
}
}
}

View File

@@ -0,0 +1,644 @@
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public interface IReadOnlyAsyncReactiveProperty<T> : IUniTaskAsyncEnumerable<T>
{
T Value { get; }
IUniTaskAsyncEnumerable<T> WithoutCurrent();
UniTask<T> WaitAsync(CancellationToken cancellationToken = default);
}
public interface IAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>
{
new T Value { get; set; }
}
[Serializable]
public class AsyncReactiveProperty<T> : IAsyncReactiveProperty<T>, IDisposable
{
TriggerEvent<T> triggerEvent;
#if UNITY_2018_3_OR_NEWER
[UnityEngine.SerializeField]
#endif
T latestValue;
public T Value
{
get
{
return latestValue;
}
set
{
this.latestValue = value;
triggerEvent.SetResult(value);
}
}
public AsyncReactiveProperty(T value)
{
this.latestValue = value;
this.triggerEvent = default;
}
public IUniTaskAsyncEnumerable<T> WithoutCurrent()
{
return new WithoutCurrentEnumerable(this);
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken)
{
return new Enumerator(this, cancellationToken, true);
}
public void Dispose()
{
triggerEvent.SetCompleted();
}
public static implicit operator T(AsyncReactiveProperty<T> value)
{
return value.Value;
}
public override string ToString()
{
if (isValueType) return latestValue.ToString();
return latestValue?.ToString();
}
public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
{
return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
}
static bool isValueType;
static AsyncReactiveProperty()
{
isValueType = typeof(T).IsValueType;
}
sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
{
static Action<object> cancellationCallback = CancellationCallback;
static TaskPool<WaitAsyncSource> pool;
WaitAsyncSource nextNode;
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
static WaitAsyncSource()
{
TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size);
}
AsyncReactiveProperty<T> parent;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
UniTaskCompletionSourceCore<T> core;
WaitAsyncSource()
{
}
public static IUniTaskSource<T> Create(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new WaitAsyncSource();
}
result.parent = parent;
result.cancellationToken = cancellationToken;
if (cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result);
}
result.parent.triggerEvent.Add(result);
TaskTracker.TrackActiveTask(result, 3);
token = result.core.Version;
return result;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
cancellationTokenRegistration.Dispose();
cancellationTokenRegistration = default;
parent.triggerEvent.Remove(this);
parent = null;
cancellationToken = default;
return pool.TryPush(this);
}
static void CancellationCallback(object state)
{
var self = (WaitAsyncSource)state;
self.OnCanceled(self.cancellationToken);
}
// IUniTaskSource
public T GetResult(short token)
{
try
{
return core.GetResult(token);
}
finally
{
TryReturn();
}
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
// ITriggerHandler
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
public void OnCanceled(CancellationToken cancellationToken)
{
core.TrySetCanceled(cancellationToken);
}
public void OnCompleted()
{
// Complete as Cancel.
core.TrySetCanceled(CancellationToken.None);
}
public void OnError(Exception ex)
{
core.TrySetException(ex);
}
public void OnNext(T value)
{
core.TrySetResult(value);
}
}
sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
{
readonly AsyncReactiveProperty<T> parent;
public WithoutCurrentEnumerable(AsyncReactiveProperty<T> parent)
{
this.parent = parent;
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(parent, cancellationToken, false);
}
}
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
{
static Action<object> cancellationCallback = CancellationCallback;
readonly AsyncReactiveProperty<T> parent;
readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
T value;
bool isDisposed;
bool firstCall;
public Enumerator(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue)
{
this.parent = parent;
this.cancellationToken = cancellationToken;
this.firstCall = publishCurrentValue;
parent.triggerEvent.Add(this);
TaskTracker.TrackActiveTask(this, 3);
if (cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
}
}
public T Current => value;
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
public UniTask<bool> MoveNextAsync()
{
// raise latest value on first call.
if (firstCall)
{
firstCall = false;
value = parent.Value;
return CompletedTasks.True;
}
completionSource.Reset();
return new UniTask<bool>(this, completionSource.Version);
}
public UniTask DisposeAsync()
{
if (!isDisposed)
{
isDisposed = true;
TaskTracker.RemoveTracking(this);
completionSource.TrySetCanceled(cancellationToken);
parent.triggerEvent.Remove(this);
}
return default;
}
public void OnNext(T value)
{
this.value = value;
completionSource.TrySetResult(true);
}
public void OnCanceled(CancellationToken cancellationToken)
{
DisposeAsync().Forget();
}
public void OnCompleted()
{
completionSource.TrySetResult(false);
}
public void OnError(Exception ex)
{
completionSource.TrySetException(ex);
}
static void CancellationCallback(object state)
{
var self = (Enumerator)state;
self.DisposeAsync().Forget();
}
}
}
public class ReadOnlyAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>, IDisposable
{
TriggerEvent<T> triggerEvent;
T latestValue;
IUniTaskAsyncEnumerator<T> enumerator;
public T Value
{
get
{
return latestValue;
}
}
public ReadOnlyAsyncReactiveProperty(T initialValue, IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
{
latestValue = initialValue;
ConsumeEnumerator(source, cancellationToken).Forget();
}
public ReadOnlyAsyncReactiveProperty(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
{
ConsumeEnumerator(source, cancellationToken).Forget();
}
async UniTaskVoid ConsumeEnumerator(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
{
enumerator = source.GetAsyncEnumerator(cancellationToken);
try
{
while (await enumerator.MoveNextAsync())
{
var value = enumerator.Current;
this.latestValue = value;
triggerEvent.SetResult(value);
}
}
finally
{
await enumerator.DisposeAsync();
enumerator = null;
}
}
public IUniTaskAsyncEnumerable<T> WithoutCurrent()
{
return new WithoutCurrentEnumerable(this);
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken)
{
return new Enumerator(this, cancellationToken, true);
}
public void Dispose()
{
if (enumerator != null)
{
enumerator.DisposeAsync().Forget();
}
triggerEvent.SetCompleted();
}
public static implicit operator T(ReadOnlyAsyncReactiveProperty<T> value)
{
return value.Value;
}
public override string ToString()
{
if (isValueType) return latestValue.ToString();
return latestValue?.ToString();
}
public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
{
return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
}
static bool isValueType;
static ReadOnlyAsyncReactiveProperty()
{
isValueType = typeof(T).IsValueType;
}
sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
{
static Action<object> cancellationCallback = CancellationCallback;
static TaskPool<WaitAsyncSource> pool;
WaitAsyncSource nextNode;
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
static WaitAsyncSource()
{
TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size);
}
ReadOnlyAsyncReactiveProperty<T> parent;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
UniTaskCompletionSourceCore<T> core;
WaitAsyncSource()
{
}
public static IUniTaskSource<T> Create(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new WaitAsyncSource();
}
result.parent = parent;
result.cancellationToken = cancellationToken;
if (cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result);
}
result.parent.triggerEvent.Add(result);
TaskTracker.TrackActiveTask(result, 3);
token = result.core.Version;
return result;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
cancellationTokenRegistration.Dispose();
cancellationTokenRegistration = default;
parent.triggerEvent.Remove(this);
parent = null;
cancellationToken = default;
return pool.TryPush(this);
}
static void CancellationCallback(object state)
{
var self = (WaitAsyncSource)state;
self.OnCanceled(self.cancellationToken);
}
// IUniTaskSource
public T GetResult(short token)
{
try
{
return core.GetResult(token);
}
finally
{
TryReturn();
}
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
// ITriggerHandler
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
public void OnCanceled(CancellationToken cancellationToken)
{
core.TrySetCanceled(cancellationToken);
}
public void OnCompleted()
{
// Complete as Cancel.
core.TrySetCanceled(CancellationToken.None);
}
public void OnError(Exception ex)
{
core.TrySetException(ex);
}
public void OnNext(T value)
{
core.TrySetResult(value);
}
}
sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
{
readonly ReadOnlyAsyncReactiveProperty<T> parent;
public WithoutCurrentEnumerable(ReadOnlyAsyncReactiveProperty<T> parent)
{
this.parent = parent;
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(parent, cancellationToken, false);
}
}
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
{
static Action<object> cancellationCallback = CancellationCallback;
readonly ReadOnlyAsyncReactiveProperty<T> parent;
readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
T value;
bool isDisposed;
bool firstCall;
public Enumerator(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue)
{
this.parent = parent;
this.cancellationToken = cancellationToken;
this.firstCall = publishCurrentValue;
parent.triggerEvent.Add(this);
TaskTracker.TrackActiveTask(this, 3);
if (cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
}
}
public T Current => value;
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
public UniTask<bool> MoveNextAsync()
{
// raise latest value on first call.
if (firstCall)
{
firstCall = false;
value = parent.Value;
return CompletedTasks.True;
}
completionSource.Reset();
return new UniTask<bool>(this, completionSource.Version);
}
public UniTask DisposeAsync()
{
if (!isDisposed)
{
isDisposed = true;
TaskTracker.RemoveTracking(this);
completionSource.TrySetCanceled(cancellationToken);
parent.triggerEvent.Remove(this);
}
return default;
}
public void OnNext(T value)
{
this.value = value;
completionSource.TrySetResult(true);
}
public void OnCanceled(CancellationToken cancellationToken)
{
DisposeAsync().Forget();
}
public void OnCompleted()
{
completionSource.TrySetResult(false);
}
public void OnError(Exception ex)
{
completionSource.TrySetException(ex);
}
static void CancellationCallback(object state)
{
var self = (Enumerator)state;
self.DisposeAsync().Forget();
}
}
}
public static class StateExtensions
{
public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
{
return new ReadOnlyAsyncReactiveProperty<T>(source, cancellationToken);
}
public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, T initialValue, CancellationToken cancellationToken)
{
return new ReadOnlyAsyncReactiveProperty<T>(initialValue, source, cancellationToken);
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: fcb1f7467a3e2b64c8a016c8aee2f9b4
guid: 8ef320b87f537ee4fb2282e765dc6166
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -4,7 +4,7 @@ using System;
namespace Cysharp.Threading.Tasks
{
public struct AsyncUnit : IEquatable<AsyncUnit>
public readonly struct AsyncUnit : IEquatable<AsyncUnit>
{
public static readonly AsyncUnit Default = new AsyncUnit();

View File

@@ -1,6 +1,7 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Cysharp.Threading.Tasks
@@ -8,16 +9,66 @@ namespace Cysharp.Threading.Tasks
public static class CancellationTokenExtensions
{
static readonly Action<object> cancellationTokenCallback = Callback;
static readonly Action<object> disposeCallback = DisposeCallback;
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cts)
public static CancellationToken ToCancellationToken(this UniTask task)
{
if (cts.IsCancellationRequested)
var cts = new CancellationTokenSource();
ToCancellationTokenCore(task, cts).Forget();
return cts.Token;
}
public static CancellationToken ToCancellationToken(this UniTask task, CancellationToken linkToken)
{
if (linkToken.IsCancellationRequested)
{
return (UniTask.FromCanceled(cts), default(CancellationTokenRegistration));
return linkToken;
}
if (!linkToken.CanBeCanceled)
{
return ToCancellationToken(task);
}
var cts = CancellationTokenSource.CreateLinkedTokenSource(linkToken);
ToCancellationTokenCore(task, cts).Forget();
return cts.Token;
}
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task)
{
return ToCancellationToken(task.AsUniTask());
}
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task, CancellationToken linkToken)
{
return ToCancellationToken(task.AsUniTask(), linkToken);
}
static async UniTaskVoid ToCancellationTokenCore(UniTask task, CancellationTokenSource cts)
{
try
{
await task;
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
cts.Cancel();
cts.Dispose();
}
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return (UniTask.FromCanceled(cancellationToken), default(CancellationTokenRegistration));
}
var promise = new UniTaskCompletionSource();
return (promise.Task, cts.RegisterWithoutCaptureExecutionContext(cancellationTokenCallback, promise));
return (promise.Task, cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationTokenCallback, promise));
}
static void Callback(object state)
@@ -26,6 +77,11 @@ namespace Cysharp.Threading.Tasks
promise.TrySetResult();
}
public static CancellationTokenAwaitable WaitUntilCanceled(this CancellationToken cancellationToken)
{
return new CancellationTokenAwaitable(cancellationToken);
}
public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action callback)
{
var restoreFlow = false;
@@ -69,6 +125,58 @@ namespace Cysharp.Threading.Tasks
}
}
}
public static CancellationTokenRegistration AddTo(this IDisposable disposable, CancellationToken cancellationToken)
{
return cancellationToken.RegisterWithoutCaptureExecutionContext(disposeCallback, disposable);
}
static void DisposeCallback(object state)
{
var d = (IDisposable)state;
d.Dispose();
}
}
public struct CancellationTokenAwaitable
{
CancellationToken cancellationToken;
public CancellationTokenAwaitable(CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
}
public Awaiter GetAwaiter()
{
return new Awaiter(cancellationToken);
}
public struct Awaiter : ICriticalNotifyCompletion
{
CancellationToken cancellationToken;
public Awaiter(CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
}
public bool IsCompleted => !cancellationToken.CanBeCanceled || cancellationToken.IsCancellationRequested;
public void GetResult()
{
}
public void OnCompleted(Action continuation)
{
UnsafeOnCompleted(continuation);
}
public void UnsafeOnCompleted(Action continuation)
{
cancellationToken.RegisterWithoutCaptureExecutionContext(continuation);
}
}
}
}

View File

@@ -9,15 +9,15 @@ namespace Cysharp.Threading.Tasks
{
public static class CancellationTokenSourceExtensions
{
public static void CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
public static void CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
{
var delay = UniTask.Delay(millisecondsDelay, ignoreTimeScale, delayTiming, cts.Token);
var delay = UniTask.Delay(millisecondsDelay, delayType, delayTiming, cts.Token);
CancelAfterCore(cts, delay).Forget();
}
public static void CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
public static void CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
{
var delay = UniTask.Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cts.Token);
var delay = UniTask.Delay(delayTimeSpan, delayType, delayTiming, cts.Token);
CancelAfterCore(cts, delay).Forget();
}

View File

@@ -0,0 +1,450 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public static class Channel
{
public static Channel<T> CreateSingleConsumerUnbounded<T>()
{
return new SingleConsumerUnboundedChannel<T>();
}
}
public abstract class Channel<TWrite, TRead>
{
public ChannelReader<TRead> Reader { get; protected set; }
public ChannelWriter<TWrite> Writer { get; protected set; }
public static implicit operator ChannelReader<TRead>(Channel<TWrite, TRead> channel) => channel.Reader;
public static implicit operator ChannelWriter<TWrite>(Channel<TWrite, TRead> channel) => channel.Writer;
}
public abstract class Channel<T> : Channel<T, T>
{
}
public abstract class ChannelReader<T>
{
public abstract bool TryRead(out T item);
public abstract UniTask<bool> WaitToReadAsync(CancellationToken cancellationToken = default(CancellationToken));
public abstract UniTask Completion { get; }
public virtual UniTask<T> ReadAsync(CancellationToken cancellationToken = default(CancellationToken))
{
if (this.TryRead(out var item))
{
return UniTask.FromResult(item);
}
return ReadAsyncCore(cancellationToken);
}
async UniTask<T> ReadAsyncCore(CancellationToken cancellationToken = default(CancellationToken))
{
if (await WaitToReadAsync(cancellationToken))
{
if (TryRead(out var item))
{
return item;
}
}
throw new ChannelClosedException();
}
public abstract IUniTaskAsyncEnumerable<T> ReadAllAsync(CancellationToken cancellationToken = default(CancellationToken));
}
public abstract class ChannelWriter<T>
{
public abstract bool TryWrite(T item);
public abstract bool TryComplete(Exception error = null);
public void Complete(Exception error = null)
{
if (!TryComplete(error))
{
throw new ChannelClosedException();
}
}
}
public partial class ChannelClosedException : InvalidOperationException
{
public ChannelClosedException() :
base("Channel is already closed.")
{ }
public ChannelClosedException(string message) : base(message) { }
public ChannelClosedException(Exception innerException) :
base("Channel is already closed", innerException)
{ }
public ChannelClosedException(string message, Exception innerException) : base(message, innerException) { }
}
internal class SingleConsumerUnboundedChannel<T> : Channel<T>
{
readonly Queue<T> items;
readonly SingleConsumerUnboundedChannelReader readerSource;
UniTaskCompletionSource completedTaskSource;
UniTask completedTask;
Exception completionError;
bool closed;
public SingleConsumerUnboundedChannel()
{
items = new Queue<T>();
Writer = new SingleConsumerUnboundedChannelWriter(this);
readerSource = new SingleConsumerUnboundedChannelReader(this);
Reader = readerSource;
}
sealed class SingleConsumerUnboundedChannelWriter : ChannelWriter<T>
{
readonly SingleConsumerUnboundedChannel<T> parent;
public SingleConsumerUnboundedChannelWriter(SingleConsumerUnboundedChannel<T> parent)
{
this.parent = parent;
}
public override bool TryWrite(T item)
{
bool waiting;
lock (parent.items)
{
if (parent.closed) return false;
parent.items.Enqueue(item);
waiting = parent.readerSource.isWaiting;
}
if (waiting)
{
parent.readerSource.SingalContinuation();
}
return true;
}
public override bool TryComplete(Exception error = null)
{
bool waiting;
lock (parent.items)
{
if (parent.closed) return false;
parent.closed = true;
waiting = parent.readerSource.isWaiting;
if (parent.items.Count == 0)
{
if (error == null)
{
if (parent.completedTaskSource != null)
{
parent.completedTaskSource.TrySetResult();
}
else
{
parent.completedTask = UniTask.CompletedTask;
}
}
else
{
if (parent.completedTaskSource != null)
{
parent.completedTaskSource.TrySetException(error);
}
else
{
parent.completedTask = UniTask.FromException(error);
}
}
if (waiting)
{
parent.readerSource.SingalCompleted(error);
}
}
parent.completionError = error;
}
return true;
}
}
sealed class SingleConsumerUnboundedChannelReader : ChannelReader<T>, IUniTaskSource<bool>
{
readonly Action<object> CancellationCallbackDelegate = CancellationCallback;
readonly SingleConsumerUnboundedChannel<T> parent;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
UniTaskCompletionSourceCore<bool> core;
internal bool isWaiting;
public SingleConsumerUnboundedChannelReader(SingleConsumerUnboundedChannel<T> parent)
{
this.parent = parent;
TaskTracker.TrackActiveTask(this, 4);
}
public override UniTask Completion
{
get
{
if (parent.completedTaskSource != null) return parent.completedTaskSource.Task;
if (parent.closed)
{
return parent.completedTask;
}
parent.completedTaskSource = new UniTaskCompletionSource();
return parent.completedTaskSource.Task;
}
}
public override bool TryRead(out T item)
{
lock (parent.items)
{
if (parent.items.Count != 0)
{
item = parent.items.Dequeue();
// complete when all value was consumed.
if (parent.closed && parent.items.Count == 0)
{
if (parent.completionError != null)
{
if (parent.completedTaskSource != null)
{
parent.completedTaskSource.TrySetException(parent.completionError);
}
else
{
parent.completedTask = UniTask.FromException(parent.completionError);
}
}
else
{
if (parent.completedTaskSource != null)
{
parent.completedTaskSource.TrySetResult();
}
else
{
parent.completedTask = UniTask.CompletedTask;
}
}
}
}
else
{
item = default;
return false;
}
}
return true;
}
public override UniTask<bool> WaitToReadAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return UniTask.FromCanceled<bool>(cancellationToken);
}
lock (parent.items)
{
if (parent.items.Count != 0)
{
return CompletedTasks.True;
}
if (parent.closed)
{
if (parent.completionError == null)
{
return CompletedTasks.False;
}
else
{
return UniTask.FromException<bool>(parent.completionError);
}
}
cancellationTokenRegistration.Dispose();
core.Reset();
isWaiting = true;
this.cancellationToken = cancellationToken;
if (this.cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = this.cancellationToken.RegisterWithoutCaptureExecutionContext(CancellationCallbackDelegate, this);
}
return new UniTask<bool>(this, core.Version);
}
}
public void SingalContinuation()
{
core.TrySetResult(true);
}
public void SingalCancellation(CancellationToken cancellationToken)
{
TaskTracker.RemoveTracking(this);
core.TrySetCanceled(cancellationToken);
}
public void SingalCompleted(Exception error)
{
if (error != null)
{
TaskTracker.RemoveTracking(this);
core.TrySetException(error);
}
else
{
TaskTracker.RemoveTracking(this);
core.TrySetResult(false);
}
}
public override IUniTaskAsyncEnumerable<T> ReadAllAsync(CancellationToken cancellationToken = default)
{
return new ReadAllAsyncEnumerable(this, cancellationToken);
}
bool IUniTaskSource<bool>.GetResult(short token)
{
return core.GetResult(token);
}
void IUniTaskSource.GetResult(short token)
{
core.GetResult(token);
}
UniTaskStatus IUniTaskSource.GetStatus(short token)
{
return core.GetStatus(token);
}
void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
UniTaskStatus IUniTaskSource.UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
static void CancellationCallback(object state)
{
var self = (SingleConsumerUnboundedChannelReader)state;
self.SingalCancellation(self.cancellationToken);
}
sealed class ReadAllAsyncEnumerable : IUniTaskAsyncEnumerable<T>, IUniTaskAsyncEnumerator<T>
{
readonly Action<object> CancellationCallback1Delegate = CancellationCallback1;
readonly Action<object> CancellationCallback2Delegate = CancellationCallback2;
readonly SingleConsumerUnboundedChannelReader parent;
CancellationToken cancellationToken1;
CancellationToken cancellationToken2;
CancellationTokenRegistration cancellationTokenRegistration1;
CancellationTokenRegistration cancellationTokenRegistration2;
T current;
bool cacheValue;
bool running;
public ReadAllAsyncEnumerable(SingleConsumerUnboundedChannelReader parent, CancellationToken cancellationToken)
{
this.parent = parent;
this.cancellationToken1 = cancellationToken;
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
if (running)
{
throw new InvalidOperationException("Enumerator is already running, does not allow call GetAsyncEnumerator twice.");
}
if (this.cancellationToken1 != cancellationToken)
{
this.cancellationToken2 = cancellationToken;
}
if (this.cancellationToken1.CanBeCanceled)
{
this.cancellationTokenRegistration1 = this.cancellationToken1.RegisterWithoutCaptureExecutionContext(CancellationCallback1Delegate, this);
}
if (this.cancellationToken2.CanBeCanceled)
{
this.cancellationTokenRegistration2 = this.cancellationToken2.RegisterWithoutCaptureExecutionContext(CancellationCallback2Delegate, this);
}
running = true;
return this;
}
public T Current
{
get
{
if (cacheValue)
{
return current;
}
parent.TryRead(out current);
return current;
}
}
public UniTask<bool> MoveNextAsync()
{
cacheValue = false;
return parent.WaitToReadAsync(CancellationToken.None); // ok to use None, registered in ctor.
}
public UniTask DisposeAsync()
{
cancellationTokenRegistration1.Dispose();
cancellationTokenRegistration2.Dispose();
return default;
}
static void CancellationCallback1(object state)
{
var self = (ReadAllAsyncEnumerable)state;
self.parent.SingalCancellation(self.cancellationToken1);
}
static void CancellationCallback2(object state)
{
var self = (ReadAllAsyncEnumerable)state;
self.parent.SingalCancellation(self.cancellationToken2);
}
}
}
}
}

View File

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

View File

@@ -12,9 +12,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[StructLayout(LayoutKind.Auto)]
public struct AsyncUniTaskMethodBuilder
{
// cache items.
AutoResetUniTaskCompletionSource promise;
IMoveNextRunner runner;
IStateMachineRunnerPromise runnerPromise;
Exception ex;
// 1. Static Create method.
[DebuggerHidden]
@@ -31,98 +30,81 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (promise != null)
if (runnerPromise != null)
{
return promise.Task;
return runnerPromise.Task;
}
if (runner == null)
else if (ex != null)
{
return UniTask.FromException(ex);
}
else
{
return UniTask.CompletedTask;
}
promise = AutoResetUniTaskCompletionSource.Create();
return promise.Task;
}
}
// 3. SetException
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetException(Exception exception)
{
// runner is finished, return first.
if (runner != null)
if (runnerPromise == null)
{
runner.Return();
runner = null;
}
if (promise != null)
{
promise.TrySetException(exception);
ex = exception;
}
else
{
promise = AutoResetUniTaskCompletionSource.CreateFromException(exception, out _);
runnerPromise.SetException(exception);
}
}
// 4. SetResult
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetResult()
{
// runner is finished, return first.
if (runner != null)
if (runnerPromise != null)
{
runner.Return();
runner = null;
}
if (promise != null)
{
promise.TrySetResult();
runnerPromise.SetResult();
}
}
// 5. AwaitOnCompleted
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : INotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (promise == null)
if (runnerPromise == null)
{
promise = AutoResetUniTaskCompletionSource.Create();
}
if (runner == null)
{
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise);
}
awaiter.OnCompleted(runner.CallMoveNext);
awaiter.OnCompleted(runnerPromise.MoveNext);
}
// 6. AwaitUnsafeOnCompleted
[DebuggerHidden]
[SecuritySafeCritical]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (promise == null)
if (runnerPromise == null)
{
promise = AutoResetUniTaskCompletionSource.Create();
}
if (runner == null)
{
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise);
}
awaiter.OnCompleted(runner.CallMoveNext);
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
}
// 7. Start
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<TStateMachine>(ref TStateMachine stateMachine)
where TStateMachine : IAsyncStateMachine
{
@@ -156,9 +138,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[StructLayout(LayoutKind.Auto)]
public struct AsyncUniTaskMethodBuilder<T>
{
// cache items.
AutoResetUniTaskCompletionSource<T> promise;
IMoveNextRunner runner;
IStateMachineRunnerPromise<T> runnerPromise;
Exception ex;
T result;
// 1. Static Create method.
@@ -170,106 +151,91 @@ namespace Cysharp.Threading.Tasks.CompilerServices
}
// 2. TaskLike Task property.
[DebuggerHidden]
public UniTask<T> Task
{
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (promise != null)
if (runnerPromise != null)
{
return promise.Task;
return runnerPromise.Task;
}
if (runner == null)
else if (ex != null)
{
return UniTask.FromException<T>(ex);
}
else
{
return UniTask.FromResult(result);
}
promise = AutoResetUniTaskCompletionSource<T>.Create();
return promise.Task;
}
}
// 3. SetException
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetException(Exception exception)
{
// runner is finished, return first.
if (runner != null)
if (runnerPromise == null)
{
runner.Return();
runner = null;
}
if (promise == null)
{
promise = AutoResetUniTaskCompletionSource<T>.CreateFromException(exception, out _);
ex = exception;
}
else
{
promise.TrySetException(exception);
runnerPromise.SetException(exception);
}
}
// 4. SetResult
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetResult(T result)
{
// runner is finished, return first.
if (runner != null)
{
runner.Return();
runner = null;
}
if (promise == null)
if (runnerPromise == null)
{
this.result = result;
return;
}
promise.TrySetResult(result);
else
{
runnerPromise.SetResult(result);
}
}
// 5. AwaitOnCompleted
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : INotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (promise == null)
if (runnerPromise == null)
{
promise = AutoResetUniTaskCompletionSource<T>.Create();
}
if (runner == null)
{
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise);
}
awaiter.OnCompleted(runner.CallMoveNext);
awaiter.OnCompleted(runnerPromise.MoveNext);
}
// 6. AwaitUnsafeOnCompleted
[DebuggerHidden]
[SecuritySafeCritical]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (promise == null)
if (runnerPromise == null)
{
promise = AutoResetUniTaskCompletionSource<T>.Create();
}
if (runner == null)
{
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise);
}
awaiter.OnCompleted(runner.CallMoveNext);
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
}
// 7. Start
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<TStateMachine>(ref TStateMachine stateMachine)
where TStateMachine : IAsyncStateMachine
{

View File

@@ -4,13 +4,15 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
namespace Cysharp.Threading.Tasks.CompilerServices
{
[StructLayout(LayoutKind.Auto)]
public struct AsyncUniTaskVoidMethodBuilder
{
IMoveNextRunner runner;
IStateMachineRunner runner;
// 1. Static Create method.
[DebuggerHidden]
@@ -33,12 +35,18 @@ namespace Cysharp.Threading.Tasks.CompilerServices
// 3. SetException
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetException(Exception exception)
{
// runner is finished, return first.
if (runner != null)
{
#if ENABLE_IL2CPP
// workaround for IL2CPP bug.
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, runner.ReturnAction);
#else
runner.Return();
#endif
runner = null;
}
@@ -47,43 +55,51 @@ namespace Cysharp.Threading.Tasks.CompilerServices
// 4. SetResult
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetResult()
{
// runner is finished, return.
if (runner != null)
{
#if ENABLE_IL2CPP
// workaround for IL2CPP bug.
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, runner.ReturnAction);
#else
runner.Return();
#endif
runner = null;
}
}
// 5. AwaitOnCompleted
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : INotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (runner == null)
{
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner);
}
awaiter.OnCompleted(runner.CallMoveNext);
awaiter.OnCompleted(runner.MoveNext);
}
// 6. AwaitUnsafeOnCompleted
[DebuggerHidden]
[SecuritySafeCritical]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (runner == null)
{
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner);
}
awaiter.OnCompleted(runner.CallMoveNext);
awaiter.UnsafeOnCompleted(runner.MoveNext);
}
// 7. Start

View File

@@ -1,57 +0,0 @@

#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Cysharp.Threading.Tasks.Internal;
namespace Cysharp.Threading.Tasks.CompilerServices
{
internal interface IMoveNextRunner
{
Action CallMoveNext { get; }
void Return();
}
internal sealed class MoveNextRunner<TStateMachine> : IMoveNextRunner, IPromisePoolItem
where TStateMachine : IAsyncStateMachine
{
static PromisePool<MoveNextRunner<TStateMachine>> pool = new PromisePool<MoveNextRunner<TStateMachine>>();
TStateMachine stateMachine;
internal readonly Action callMoveNext;
public Action CallMoveNext => callMoveNext;
MoveNextRunner()
{
callMoveNext = MoveNext;
}
public static MoveNextRunner<TStateMachine> Create(ref TStateMachine stateMachine)
{
var result = pool.TryRent() ?? new MoveNextRunner<TStateMachine>();
result.stateMachine = stateMachine;
return result;
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void MoveNext()
{
stateMachine.MoveNext();
}
public void Return()
{
pool.TryReturn(this);
}
void IPromisePoolItem.Reset()
{
stateMachine = default;
}
}
}

View File

@@ -0,0 +1,380 @@
#pragma warning disable CS1591
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Linq;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Cysharp.Threading.Tasks.CompilerServices
{
// #ENABLE_IL2CPP in this file is to avoid bug of IL2CPP VM.
// Issue is tracked on https://issuetracker.unity3d.com/issues/il2cpp-incorrect-results-when-calling-a-method-from-outside-class-in-a-struct
// but currently it is labeled `Won't Fix`.
internal interface IStateMachineRunner
{
Action MoveNext { get; }
void Return();
#if ENABLE_IL2CPP
Action ReturnAction { get; }
#endif
}
internal interface IStateMachineRunnerPromise : IUniTaskSource
{
Action MoveNext { get; }
UniTask Task { get; }
void SetResult();
void SetException(Exception exception);
}
internal interface IStateMachineRunnerPromise<T> : IUniTaskSource<T>
{
Action MoveNext { get; }
UniTask<T> Task { get; }
void SetResult(T result);
void SetException(Exception exception);
}
internal static class StateMachineUtility
{
// Get AsyncStateMachine internal state to check IL2CPP bug
public static int GetState(IAsyncStateMachine stateMachine)
{
var info = stateMachine.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
.First(x => x.Name.EndsWith("__state"));
return (int)info.GetValue(stateMachine);
}
}
internal sealed class AsyncUniTaskVoid<TStateMachine> : IStateMachineRunner, ITaskPoolNode<AsyncUniTaskVoid<TStateMachine>>, IUniTaskSource
where TStateMachine : IAsyncStateMachine
{
static TaskPool<AsyncUniTaskVoid<TStateMachine>> pool;
#if ENABLE_IL2CPP
public Action ReturnAction { get; }
#endif
TStateMachine stateMachine;
public Action MoveNext { get; }
public AsyncUniTaskVoid()
{
MoveNext = Run;
#if ENABLE_IL2CPP
ReturnAction = Return;
#endif
}
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunner runnerFieldRef)
{
if (!pool.TryPop(out var result))
{
result = new AsyncUniTaskVoid<TStateMachine>();
}
TaskTracker.TrackActiveTask(result, 3);
runnerFieldRef = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
static AsyncUniTaskVoid()
{
TaskPool.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size);
}
AsyncUniTaskVoid<TStateMachine> nextNode;
public ref AsyncUniTaskVoid<TStateMachine> NextNode => ref nextNode;
public void Return()
{
TaskTracker.RemoveTracking(this);
stateMachine = default;
pool.TryPush(this);
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Run()
{
stateMachine.MoveNext();
}
// dummy interface implementation for TaskTracker.
UniTaskStatus IUniTaskSource.GetStatus(short token)
{
return UniTaskStatus.Pending;
}
UniTaskStatus IUniTaskSource.UnsafeGetStatus()
{
return UniTaskStatus.Pending;
}
void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token)
{
}
void IUniTaskSource.GetResult(short token)
{
}
}
internal sealed class AsyncUniTask<TStateMachine> : IStateMachineRunnerPromise, IUniTaskSource, ITaskPoolNode<AsyncUniTask<TStateMachine>>
where TStateMachine : IAsyncStateMachine
{
static TaskPool<AsyncUniTask<TStateMachine>> pool;
#if ENABLE_IL2CPP
readonly Action returnDelegate;
#endif
public Action MoveNext { get; }
TStateMachine stateMachine;
UniTaskCompletionSourceCore<AsyncUnit> core;
AsyncUniTask()
{
MoveNext = Run;
#if ENABLE_IL2CPP
returnDelegate = Return;
#endif
}
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise runnerPromiseFieldRef)
{
if (!pool.TryPop(out var result))
{
result = new AsyncUniTask<TStateMachine>();
}
TaskTracker.TrackActiveTask(result, 3);
runnerPromiseFieldRef = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
AsyncUniTask<TStateMachine> nextNode;
public ref AsyncUniTask<TStateMachine> NextNode => ref nextNode;
static AsyncUniTask()
{
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine>), () => pool.Size);
}
void Return()
{
TaskTracker.RemoveTracking(this);
core.Reset();
stateMachine = default;
pool.TryPush(this);
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
stateMachine = default;
return pool.TryPush(this);
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Run()
{
stateMachine.MoveNext();
}
public UniTask Task
{
[DebuggerHidden]
get
{
return new UniTask(this, core.Version);
}
}
[DebuggerHidden]
public void SetResult()
{
core.TrySetResult(AsyncUnit.Default);
}
[DebuggerHidden]
public void SetException(Exception exception)
{
core.TrySetException(exception);
}
[DebuggerHidden]
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
#if ENABLE_IL2CPP
// workaround for IL2CPP bug.
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate);
#else
TryReturn();
#endif
}
}
[DebuggerHidden]
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
[DebuggerHidden]
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
[DebuggerHidden]
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
}
internal sealed class AsyncUniTask<TStateMachine, T> : IStateMachineRunnerPromise<T>, IUniTaskSource<T>, ITaskPoolNode<AsyncUniTask<TStateMachine, T>>
where TStateMachine : IAsyncStateMachine
{
static TaskPool<AsyncUniTask<TStateMachine, T>> pool;
#if ENABLE_IL2CPP
readonly Action returnDelegate;
#endif
public Action MoveNext { get; }
TStateMachine stateMachine;
UniTaskCompletionSourceCore<T> core;
AsyncUniTask()
{
MoveNext = Run;
#if ENABLE_IL2CPP
returnDelegate = Return;
#endif
}
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise<T> runnerPromiseFieldRef)
{
if (!pool.TryPop(out var result))
{
result = new AsyncUniTask<TStateMachine, T>();
}
TaskTracker.TrackActiveTask(result, 3);
runnerPromiseFieldRef = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
AsyncUniTask<TStateMachine, T> nextNode;
public ref AsyncUniTask<TStateMachine, T> NextNode => ref nextNode;
static AsyncUniTask()
{
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine, T>), () => pool.Size);
}
void Return()
{
TaskTracker.RemoveTracking(this);
core.Reset();
stateMachine = default;
pool.TryPush(this);
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
stateMachine = default;
return pool.TryPush(this);
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Run()
{
// UnityEngine.Debug.Log($"MoveNext State:" + StateMachineUtility.GetState(stateMachine));
stateMachine.MoveNext();
}
public UniTask<T> Task
{
[DebuggerHidden]
get
{
return new UniTask<T>(this, core.Version);
}
}
[DebuggerHidden]
public void SetResult(T result)
{
core.TrySetResult(result);
}
[DebuggerHidden]
public void SetException(Exception exception)
{
core.TrySetException(exception);
}
[DebuggerHidden]
public T GetResult(short token)
{
try
{
return core.GetResult(token);
}
finally
{
#if ENABLE_IL2CPP
// workaround for IL2CPP bug.
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate);
#else
TryReturn();
#endif
}
}
[DebuggerHidden]
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
[DebuggerHidden]
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
[DebuggerHidden]
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
[DebuggerHidden]
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
}
}

View File

@@ -12,27 +12,40 @@ namespace Cysharp.Threading.Tasks
{
public static class EnumeratorAsyncExtensions
{
public static UniTask.Awaiter GetAwaiter(this IEnumerator enumerator)
public static UniTask.Awaiter GetAwaiter<T>(this T enumerator)
where T : IEnumerator
{
return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, CancellationToken.None, out var token), token).GetAwaiter();
var e = (IEnumerator)enumerator;
Error.ThrowArgumentNullException(e, nameof(enumerator));
return new UniTask(EnumeratorPromise.Create(e, PlayerLoopTiming.Update, CancellationToken.None, out var token), token).GetAwaiter();
}
public static UniTask ToUniTask(this IEnumerator enumerator)
public static UniTask WithCancellation(this IEnumerator enumerator, CancellationToken cancellationToken)
{
return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, CancellationToken.None, out var token), token);
Error.ThrowArgumentNullException(enumerator, nameof(enumerator));
return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, cancellationToken, out var token), token);
}
public static UniTask ConfigureAwait(this IEnumerator enumerator, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
public static UniTask ToUniTask(this IEnumerator enumerator, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
Error.ThrowArgumentNullException(enumerator, nameof(enumerator));
return new UniTask(EnumeratorPromise.Create(enumerator, timing, cancellationToken, out var token), token);
}
class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
sealed class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<EnumeratorPromise>
{
static readonly PromisePool<EnumeratorPromise> pool = new PromisePool<EnumeratorPromise>();
static TaskPool<EnumeratorPromise> pool;
EnumeratorPromise nextNode;
public ref EnumeratorPromise NextNode => ref nextNode;
static EnumeratorPromise()
{
TaskPool.RegisterSizeGetter(typeof(EnumeratorPromise), () => pool.Size);
}
IEnumerator innerEnumerator;
CancellationToken cancellationToken;
int initialFrame;
UniTaskCompletionSourceCore<object> core;
@@ -47,16 +60,21 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new EnumeratorPromise();
if (!pool.TryPop(out var result))
{
result = new EnumeratorPromise();
}
TaskTracker.TrackActiveTask(result, 3);
result.innerEnumerator = ConsumeEnumerator(innerEnumerator);
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
result.initialFrame = -1;
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
result.MoveNext(); // run immediately.
return result;
}
@@ -64,12 +82,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -96,6 +113,19 @@ namespace Cysharp.Threading.Tasks
return false;
}
if (initialFrame == -1)
{
// Time can not touch in threadpool.
if (PlayerLoopHelper.IsMainThread)
{
initialFrame = Time.frameCount;
}
}
else if (initialFrame == Time.frameCount)
{
return true; // already executed in first frame, skip.
}
try
{
if (innerEnumerator.MoveNext())
@@ -113,19 +143,13 @@ namespace Cysharp.Threading.Tasks
return false;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
innerEnumerator = default;
cancellationToken = default;
}
~EnumeratorPromise()
{
if (pool.TryReturn(this))
{
GC.ReRegisterForFinalize(this);
}
return pool.TryPush(this);
}
// Unwrap YieldInstructions
@@ -139,11 +163,10 @@ namespace Cysharp.Threading.Tasks
{
yield return null;
}
else if (current is CustomYieldInstruction)
else if (current is CustomYieldInstruction cyi)
{
// WWW, WaitForSecondsRealtime
var e2 = UnwrapWaitCustomYieldInstruction((CustomYieldInstruction)current);
while (e2.MoveNext())
while (cyi.keepWaiting)
{
yield return null;
}
@@ -188,26 +211,17 @@ namespace Cysharp.Threading.Tasks
}
}
// WWW and others as CustomYieldInstruction.
static IEnumerator UnwrapWaitCustomYieldInstruction(CustomYieldInstruction yieldInstruction)
{
while (yieldInstruction.keepWaiting)
{
yield return null;
}
}
static readonly FieldInfo waitForSeconds_Seconds = typeof(WaitForSeconds).GetField("m_Seconds", BindingFlags.Instance | BindingFlags.GetField | BindingFlags.NonPublic);
static IEnumerator UnwrapWaitForSeconds(WaitForSeconds waitForSeconds)
{
var second = (float)waitForSeconds_Seconds.GetValue(waitForSeconds);
var startTime = DateTimeOffset.UtcNow;
var elapsed = 0.0f;
while (true)
{
yield return null;
var elapsed = (DateTimeOffset.UtcNow - startTime).TotalSeconds;
elapsed += Time.deltaTime;
if (elapsed >= second)
{
break;

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a5b9231662e24c942b544bd85d4b39cb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -11,23 +11,39 @@ using UnityEngine.ResourceManagement.AsyncOperations;
namespace Cysharp.Threading.Tasks
{
public static class AddressableAsyncExtensions
public static class AddressablesAsyncExtensions
{
#region AsyncOperationHandle
public static AsyncOperationHandleAwaiter GetAwaiter(this AsyncOperationHandle handle)
public static UniTask.Awaiter GetAwaiter(this AsyncOperationHandle handle)
{
return new AsyncOperationHandleAwaiter(handle);
return ToUniTask(handle).GetAwaiter();
}
public static UniTask ToUniTask(this AsyncOperationHandle handle)
public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken)
{
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token);
return ToUniTask(handle, cancellationToken: cancellationToken);
}
public static UniTask ConfigureAwait(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellation, out var token), token);
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);
if (!handle.IsValid())
{
throw new Exception("Attempting to use an invalid operation handle");
}
if (handle.IsDone)
{
if (handle.Status == AsyncOperationStatus.Failed)
{
return UniTask.FromException(handle.OperationException);
}
return UniTask.CompletedTask;
}
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, out var token), token);
}
public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion
@@ -70,24 +86,33 @@ namespace Cysharp.Threading.Tasks
public void UnsafeOnCompleted(Action continuation)
{
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
continuationAction = continuation.AsFuncOfT<AsyncOperationHandle>(); // allocate delegate.
continuationAction = PooledDelegate<AsyncOperationHandle>.Create(continuation);
handle.Completed += continuationAction;
}
}
class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
{
static readonly PromisePool<AsyncOperationHandleConfiguredSource> pool = new PromisePool<AsyncOperationHandleConfiguredSource>();
static TaskPool<AsyncOperationHandleConfiguredSource> pool;
AsyncOperationHandleConfiguredSource nextNode;
public ref AsyncOperationHandleConfiguredSource NextNode => ref nextNode;
static AsyncOperationHandleConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
}
readonly Action<AsyncOperationHandle> continuationAction;
AsyncOperationHandle handle;
IProgress<float> progress;
CancellationToken cancellationToken;
IProgress<float> progress;
bool completed;
UniTaskCompletionSourceCore<AsyncUnit> core;
AsyncOperationHandleConfiguredSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
@@ -97,31 +122,55 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new AsyncOperationHandleConfiguredSource();
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleConfiguredSource();
}
result.handle = handle;
result.progress = progress;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
handle.Completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperationHandle _)
{
handle.Completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
}
else if (handle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(handle.OperationException);
}
else
{
core.TrySetResult(AsyncUnit.Default);
}
}
}
public void GetResult(short token)
{
try
{
TaskTracker.RemoveTracking(this);
core.GetResult(token);
}
finally
{
pool.TryReturn(this);
}
core.GetResult(token);
}
public UniTaskStatus GetStatus(short token)
@@ -141,47 +190,35 @@ namespace Cysharp.Threading.Tasks
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
core.TrySetCanceled(cancellationToken);
return false;
}
if (progress != null)
if (progress != null && handle.IsValid())
{
progress.Report(handle.PercentComplete);
}
if (handle.IsDone)
{
if (handle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(handle.OperationException);
}
else
{
core.TrySetResult(AsyncUnit.Default);
}
return false;
}
return true;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
handle = default;
progress = default;
cancellationToken = default;
}
~AsyncOperationHandleConfiguredSource()
{
if (pool.TryReturn(this))
{
GC.ReRegisterForFinalize(this);
}
return pool.TryPush(this);
}
}
@@ -189,80 +226,59 @@ namespace Cysharp.Threading.Tasks
#region AsyncOperationHandle_T
public static AsyncOperationHandleAwaiter<T> GetAwaiter<T>(this AsyncOperationHandle<T> handle)
public static UniTask<T>.Awaiter GetAwaiter<T>(this AsyncOperationHandle<T> handle)
{
return new AsyncOperationHandleAwaiter<T>(handle);
return ToUniTask(handle).GetAwaiter();
}
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle)
public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken)
{
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token);
return ToUniTask(handle, cancellationToken: cancellationToken);
}
public static UniTask<T> ConfigureAwait<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellation, out var token), token);
}
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T>(cancellationToken);
public struct AsyncOperationHandleAwaiter<T> : ICriticalNotifyCompletion
{
AsyncOperationHandle<T> handle;
Action<AsyncOperationHandle> continuationAction;
public AsyncOperationHandleAwaiter(AsyncOperationHandle<T> handle)
if (!handle.IsValid())
{
this.handle = handle;
this.continuationAction = null;
throw new Exception("Attempting to use an invalid operation handle");
}
public bool IsCompleted => handle.IsDone;
public T GetResult()
if (handle.IsDone)
{
if (continuationAction != null)
{
handle.CompletedTypeless -= continuationAction;
continuationAction = null;
}
if (handle.Status == AsyncOperationStatus.Failed)
{
var e = handle.OperationException;
handle = default;
ExceptionDispatchInfo.Capture(e).Throw();
return UniTask.FromException<T>(handle.OperationException);
}
var result = handle.Result;
handle = default;
return result;
return UniTask.FromResult(handle.Result);
}
public void OnCompleted(Action continuation)
{
UnsafeOnCompleted(continuation);
}
public void UnsafeOnCompleted(Action continuation)
{
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
continuationAction = continuation.AsFuncOfT<AsyncOperationHandle>(); // allocate delegate.
handle.CompletedTypeless += continuationAction;
}
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, out var token), token);
}
class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, IPromisePoolItem
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
{
static readonly PromisePool<AsyncOperationHandleConfiguredSource<T>> pool = new PromisePool<AsyncOperationHandleConfiguredSource<T>>();
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
AsyncOperationHandleConfiguredSource<T> nextNode;
public ref AsyncOperationHandleConfiguredSource<T> NextNode => ref nextNode;
static AsyncOperationHandleConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
}
readonly Action<AsyncOperationHandle<T>> continuationAction;
AsyncOperationHandle<T> handle;
IProgress<float> progress;
CancellationToken cancellationToken;
IProgress<float> progress;
bool completed;
UniTaskCompletionSourceCore<T> core;
AsyncOperationHandleConfiguredSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
@@ -272,32 +288,55 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new AsyncOperationHandleConfiguredSource<T>();
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleConfiguredSource<T>();
}
result.handle = handle;
result.progress = progress;
result.cancellationToken = cancellationToken;
result.completed = false;
result.progress = progress;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
handle.Completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperationHandle<T> argHandle)
{
handle.Completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
}
else if (argHandle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(argHandle.OperationException);
}
else
{
core.TrySetResult(argHandle.Result);
}
}
}
public T GetResult(short token)
{
try
{
TaskTracker.RemoveTracking(this);
return core.GetResult(token);
}
finally
{
pool.TryReturn(this);
}
return core.GetResult(token);
}
void IUniTaskSource.GetResult(short token)
@@ -322,47 +361,35 @@ namespace Cysharp.Threading.Tasks
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
core.TrySetCanceled(cancellationToken);
return false;
}
if (progress != null)
if (progress != null && handle.IsValid())
{
progress.Report(handle.PercentComplete);
}
if (handle.IsDone)
{
if (handle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(handle.OperationException);
}
else
{
core.TrySetResult(handle.Result);
}
return false;
}
return true;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
handle = default;
progress = default;
cancellationToken = default;
}
~AsyncOperationHandleConfiguredSource()
{
if (pool.TryReturn(this))
{
GC.ReRegisterForFinalize(this);
}
return pool.TryPush(this);
}
}

View File

@@ -0,0 +1,22 @@
{
"name": "UniTask.Addressables",
"references": [
"UniTask",
"Unity.ResourceManager"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.addressables",
"expression": "",
"define": "UNITASK_ADDRESSABLE_SUPPORT"
}
],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 593a5b492d29ac6448b1ebf7f035ef33
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 25cb2f742bfeb1d48a4e65d3140b955d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,447 @@
// asmdef Version Defines, enabled when com.demigiant.dotween is imported.
#if UNITASK_DOTWEEN_SUPPORT
using Cysharp.Threading.Tasks.Internal;
using DG.Tweening;
using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public enum TweenCancelBehaviour
{
Kill,
KillWithCompleteCallback,
Complete,
CompleteWithSeqeunceCallback,
CancelAwait,
// AndCancelAwait
KillAndCancelAwait,
KillWithCompleteCallbackAndCancelAwait,
CompleteAndCancelAwait,
CompleteWithSeqeunceCallbackAndCancelAwait
}
public static class DOTweenAsyncExtensions
{
enum CallbackType
{
Kill,
Complete,
Pause,
Play,
Rewind,
StepComplete
}
public static TweenAwaiter GetAwaiter(this Tween tween)
{
return new TweenAwaiter(tween);
}
public static UniTask WithCancellation(this Tween tween, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, TweenCancelBehaviour.Kill, cancellationToken, CallbackType.Kill, out var token), token);
}
public static UniTask ToUniTask(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Kill, out var token), token);
}
public static UniTask AwaitForComplete(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Complete, out var token), token);
}
public static UniTask AwaitForPause(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Pause, out var token), token);
}
public static UniTask AwaitForPlay(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Play, out var token), token);
}
public static UniTask AwaitForRewind(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Rewind, out var token), token);
}
public static UniTask AwaitForStepComplete(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.StepComplete, out var token), token);
}
public struct TweenAwaiter : ICriticalNotifyCompletion
{
readonly Tween tween;
// killed(non active) as completed.
public bool IsCompleted => !tween.IsActive();
public TweenAwaiter(Tween tween)
{
this.tween = tween;
}
public TweenAwaiter GetAwaiter()
{
return this;
}
public void GetResult()
{
}
public void OnCompleted(System.Action continuation)
{
UnsafeOnCompleted(continuation);
}
public void UnsafeOnCompleted(System.Action continuation)
{
// onKill is called after OnCompleted, both Complete(false/true) and Kill(false/true).
tween.onKill = PooledTweenCallback.Create(continuation);
}
}
sealed class TweenConfiguredSource : IUniTaskSource, ITaskPoolNode<TweenConfiguredSource>
{
static TaskPool<TweenConfiguredSource> pool;
TweenConfiguredSource nextNode;
public ref TweenConfiguredSource NextNode => ref nextNode;
static TweenConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
}
static readonly TweenCallback EmptyTweenCallback = () => { };
readonly TweenCallback onCompleteCallbackDelegate;
readonly TweenCallback onUpdateDelegate;
Tween tween;
TweenCancelBehaviour cancelBehaviour;
CancellationToken cancellationToken;
CallbackType callbackType;
bool canceled;
TweenCallback originalUpdateAction;
UniTaskCompletionSourceCore<AsyncUnit> core;
TweenConfiguredSource()
{
onCompleteCallbackDelegate = OnCompleteCallbackDelegate;
onUpdateDelegate = OnUpdate;
}
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, CallbackType callbackType, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
DoCancelBeforeCreate(tween, cancelBehaviour);
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new TweenConfiguredSource();
}
result.tween = tween;
result.cancelBehaviour = cancelBehaviour;
result.cancellationToken = cancellationToken;
result.callbackType = callbackType;
result.originalUpdateAction = tween.onUpdate;
result.canceled = false;
if (result.originalUpdateAction == result.onUpdateDelegate)
{
result.originalUpdateAction = null;
}
tween.onUpdate = result.onUpdateDelegate;
switch (callbackType)
{
case CallbackType.Kill:
tween.onKill = result.onCompleteCallbackDelegate;
break;
case CallbackType.Complete:
tween.onComplete = result.onCompleteCallbackDelegate;
break;
case CallbackType.Pause:
tween.onPause = result.onCompleteCallbackDelegate;
break;
case CallbackType.Play:
tween.onPlay = result.onCompleteCallbackDelegate;
break;
case CallbackType.Rewind:
tween.onRewind = result.onCompleteCallbackDelegate;
break;
case CallbackType.StepComplete:
tween.onStepComplete = result.onCompleteCallbackDelegate;
break;
default:
break;
}
TaskTracker.TrackActiveTask(result, 3);
token = result.core.Version;
return result;
}
void OnCompleteCallbackDelegate()
{
if (canceled)
{
core.TrySetCanceled(cancellationToken);
}
else
{
core.TrySetResult(AsyncUnit.Default);
}
}
void OnUpdate()
{
originalUpdateAction?.Invoke();
if (!cancellationToken.IsCancellationRequested)
{
return;
}
switch (this.cancelBehaviour)
{
case TweenCancelBehaviour.Kill:
default:
this.tween.Kill(false);
break;
case TweenCancelBehaviour.KillAndCancelAwait:
this.canceled = true;
this.tween.Kill(false);
break;
case TweenCancelBehaviour.KillWithCompleteCallback:
this.tween.Kill(true);
break;
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
this.canceled = true;
this.tween.Kill(true);
break;
case TweenCancelBehaviour.Complete:
this.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteAndCancelAwait:
this.canceled = true;
this.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
this.tween.Complete(true);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
this.canceled = true;
this.tween.Complete(true);
break;
case TweenCancelBehaviour.CancelAwait:
// replace to empty(avoid callback after Canceled(instance is returned to pool.)
switch (callbackType)
{
case CallbackType.Kill:
tween.onKill = EmptyTweenCallback;
break;
case CallbackType.Complete:
tween.onComplete = EmptyTweenCallback;
break;
case CallbackType.Pause:
tween.onPause = EmptyTweenCallback;
break;
case CallbackType.Play:
tween.onPlay = EmptyTweenCallback;
break;
case CallbackType.Rewind:
tween.onRewind = EmptyTweenCallback;
break;
case CallbackType.StepComplete:
tween.onStepComplete = EmptyTweenCallback;
break;
default:
break;
}
this.core.TrySetCanceled(this.cancellationToken);
break;
}
}
static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour)
{
switch (tweenCancelBehaviour)
{
case TweenCancelBehaviour.Kill:
default:
tween.Kill(false);
break;
case TweenCancelBehaviour.KillAndCancelAwait:
tween.Kill(false);
break;
case TweenCancelBehaviour.KillWithCompleteCallback:
tween.Kill(true);
break;
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
tween.Kill(true);
break;
case TweenCancelBehaviour.Complete:
tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteAndCancelAwait:
tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
tween.Complete(true);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
tween.Complete(true);
break;
case TweenCancelBehaviour.CancelAwait:
break;
}
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
TryReturn();
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
tween.onUpdate = originalUpdateAction;
switch (callbackType)
{
case CallbackType.Kill:
tween.onKill = null;
break;
case CallbackType.Complete:
tween.onComplete = null;
break;
case CallbackType.Pause:
tween.onPause = null;
break;
case CallbackType.Play:
tween.onPlay = null;
break;
case CallbackType.Rewind:
tween.onRewind = null;
break;
case CallbackType.StepComplete:
tween.onStepComplete = null;
break;
default:
break;
}
tween = default;
cancellationToken = default;
originalUpdateAction = default;
return pool.TryPush(this);
}
}
}
sealed class PooledTweenCallback
{
static readonly ConcurrentQueue<PooledTweenCallback> pool = new ConcurrentQueue<PooledTweenCallback>();
readonly TweenCallback runDelegate;
Action continuation;
PooledTweenCallback()
{
runDelegate = Run;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TweenCallback Create(Action continuation)
{
if (!pool.TryDequeue(out var item))
{
item = new PooledTweenCallback();
}
item.continuation = continuation;
return item.runDelegate;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Run()
{
var call = continuation;
continuation = null;
if (call != null)
{
pool.Enqueue(this);
call.Invoke();
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,22 @@
{
"name": "UniTask.DOTween",
"references": [
"UniTask",
"DOTween.Modules"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.demigiant.dotween",
"expression": "",
"define": "UNITASK_DOTWEEN_SUPPORT"
}
],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 029c1c1b674aaae47a6841a0b89ad80e
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f89da606bde9a4e4e94ae1189a029887
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,224 @@
#if UNITASK_TEXTMESHPRO_SUPPORT
using System;
using System.Threading;
using TMPro;
namespace Cysharp.Threading.Tasks
{
public static partial class TextMeshProAsyncExtensions
{
public static IAsyncValueChangedEventHandler<string> GetAsyncValueChangedEventHandler(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy(), false);
}
public static IAsyncValueChangedEventHandler<string> GetAsyncValueChangedEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, cancellationToken, false);
}
public static UniTask<string> OnValueChangedAsync(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
}
public static UniTask<string> OnValueChangedAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, cancellationToken, true).OnInvokeAsync();
}
public static IUniTaskAsyncEnumerable<string> OnValueChangedAsAsyncEnumerable(this TMP_InputField inputField)
{
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy());
}
public static IUniTaskAsyncEnumerable<string> OnValueChangedAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onValueChanged, cancellationToken);
}
public static IAsyncEndEditEventHandler<string> GetAsyncEndEditEventHandler(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy(), false);
}
public static IAsyncEndEditEventHandler<string> GetAsyncEndEditEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, cancellationToken, false);
}
public static UniTask<string> OnEndEditAsync(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
}
public static UniTask<string> OnEndEditAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, cancellationToken, true).OnInvokeAsync();
}
public static IUniTaskAsyncEnumerable<string> OnEndEditAsAsyncEnumerable(this TMP_InputField inputField)
{
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy());
}
public static IUniTaskAsyncEnumerable<string> OnEndEditAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onEndEdit, cancellationToken);
}
public static IAsyncEndTextSelectionEventHandler<(string, int, int)> GetAsyncEndTextSelectionEventHandler(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy(), false);
}
public static IAsyncEndTextSelectionEventHandler<(string, int, int)> GetAsyncEndTextSelectionEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken, false);
}
public static UniTask<(string, int, int)> OnEndTextSelectionAsync(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
}
public static UniTask<(string, int, int)> OnEndTextSelectionAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken, true).OnInvokeAsync();
}
public static IUniTaskAsyncEnumerable<(string, int, int)> OnEndTextSelectionAsAsyncEnumerable(this TMP_InputField inputField)
{
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy());
}
public static IUniTaskAsyncEnumerable<(string, int, int)> OnEndTextSelectionAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken);
}
public static IAsyncTextSelectionEventHandler<(string, int, int)> GetAsyncTextSelectionEventHandler(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy(), false);
}
public static IAsyncTextSelectionEventHandler<(string, int, int)> GetAsyncTextSelectionEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken, false);
}
public static UniTask<(string, int, int)> OnTextSelectionAsync(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
}
public static UniTask<(string, int, int)> OnTextSelectionAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken, true).OnInvokeAsync();
}
public static IUniTaskAsyncEnumerable<(string, int, int)> OnTextSelectionAsAsyncEnumerable(this TMP_InputField inputField)
{
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy());
}
public static IUniTaskAsyncEnumerable<(string, int, int)> OnTextSelectionAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken);
}
public static IAsyncDeselectEventHandler<string> GetAsyncDeselectEventHandler(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<string>(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy(), false);
}
public static IAsyncDeselectEventHandler<string> GetAsyncDeselectEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<string>(inputField.onDeselect, cancellationToken, false);
}
public static UniTask<string> OnDeselectAsync(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<string>(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
}
public static UniTask<string> OnDeselectAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<string>(inputField.onDeselect, cancellationToken, true).OnInvokeAsync();
}
public static IUniTaskAsyncEnumerable<string> OnDeselectAsAsyncEnumerable(this TMP_InputField inputField)
{
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy());
}
public static IUniTaskAsyncEnumerable<string> OnDeselectAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onDeselect, cancellationToken);
}
public static IAsyncSelectEventHandler<string> GetAsyncSelectEventHandler(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<string>(inputField.onSelect, inputField.GetCancellationTokenOnDestroy(), false);
}
public static IAsyncSelectEventHandler<string> GetAsyncSelectEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<string>(inputField.onSelect, cancellationToken, false);
}
public static UniTask<string> OnSelectAsync(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<string>(inputField.onSelect, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
}
public static UniTask<string> OnSelectAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<string>(inputField.onSelect, cancellationToken, true).OnInvokeAsync();
}
public static IUniTaskAsyncEnumerable<string> OnSelectAsAsyncEnumerable(this TMP_InputField inputField)
{
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSelect, inputField.GetCancellationTokenOnDestroy());
}
public static IUniTaskAsyncEnumerable<string> OnSelectAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSelect, cancellationToken);
}
public static IAsyncSubmitEventHandler<string> GetAsyncSubmitEventHandler(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<string>(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy(), false);
}
public static IAsyncSubmitEventHandler<string> GetAsyncSubmitEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<string>(inputField.onSubmit, cancellationToken, false);
}
public static UniTask<string> OnSubmitAsync(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<string>(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
}
public static UniTask<string> OnSubmitAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<string>(inputField.onSubmit, cancellationToken, true).OnInvokeAsync();
}
public static IUniTaskAsyncEnumerable<string> OnSubmitAsAsyncEnumerable(this TMP_InputField inputField)
{
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy());
}
public static IUniTaskAsyncEnumerable<string> OnSubmitAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSubmit, cancellationToken);
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,66 @@
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#
var handlers = new (string name, string type)[] {
("ValueChanged", "string"),
("EndEdit", "string"),
("EndTextSelection", "(string, int, int)"),
("TextSelection", "(string, int, int)"),
("Deselect", "string"),
("Select", "string"),
("Submit", "string"),
};
Func<string, bool> shouldConvert = x => x.EndsWith("TextSelection");
Func<string, string> eventName = x => shouldConvert(x) ? $"new TextSelectionEventConverter(inputField.on{x})" : $"inputField.on{x}";
#>
#if UNITASK_TEXTMESHPRO_SUPPORT
using System;
using System.Threading;
using TMPro;
namespace Cysharp.Threading.Tasks
{
public static partial class TextMeshProAsyncExtensions
{
<# foreach(var (name, type) in handlers) { #>
public static IAsync<#= (name) #>EventHandler<<#= type #>> GetAsync<#= (name) #>EventHandler(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, inputField.GetCancellationTokenOnDestroy(), false);
}
public static IAsync<#= (name) #>EventHandler<<#= type #>> GetAsync<#= (name) #>EventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, cancellationToken, false);
}
public static UniTask<<#= type #>> On<#= (name) #>Async(this TMP_InputField inputField)
{
return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
}
public static UniTask<<#= type #>> On<#= (name) #>Async(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, cancellationToken, true).OnInvokeAsync();
}
public static IUniTaskAsyncEnumerable<<#= type #>> On<#= (name) #>AsAsyncEnumerable(this TMP_InputField inputField)
{
return new UnityEventHandlerAsyncEnumerable<<#= type #>>(<#= eventName(name) #>, inputField.GetCancellationTokenOnDestroy());
}
public static IUniTaskAsyncEnumerable<<#= type #>> On<#= (name) #>AsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
{
return new UnityEventHandlerAsyncEnumerable<<#= type #>>(<#= eventName(name) #>, cancellationToken);
}
<# } #>
}
}
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e9bb9fc551a975d44a7180e022a2debe
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,130 @@
#if UNITASK_TEXTMESHPRO_SUPPORT
using System;
using System.Threading;
using TMPro;
using UnityEngine.Events;
namespace Cysharp.Threading.Tasks
{
public static partial class TextMeshProAsyncExtensions
{
// <string> -> Text
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, TMP_Text text, bool rebindOnError = true)
{
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
}
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError = true)
{
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
}
static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<string> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError)
{
var repeat = false;
BIND_AGAIN:
var e = source.GetAsyncEnumerator(cancellationToken);
try
{
while (true)
{
bool moveNext;
try
{
moveNext = await e.MoveNextAsync();
repeat = false;
}
catch (Exception ex)
{
if (ex is OperationCanceledException) return;
if (rebindOnError && !repeat)
{
repeat = true;
goto BIND_AGAIN;
}
else
{
throw;
}
}
if (!moveNext) return;
text.text = e.Current;
}
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
// <T> -> Text
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, TMP_Text text, bool rebindOnError = true)
{
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
}
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError = true)
{
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
}
public static void BindTo<T>(this AsyncReactiveProperty<T> source, TMP_Text text, bool rebindOnError = true)
{
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
}
static async UniTaskVoid BindToCore<T>(IUniTaskAsyncEnumerable<T> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError)
{
var repeat = false;
BIND_AGAIN:
var e = source.GetAsyncEnumerator(cancellationToken);
try
{
while (true)
{
bool moveNext;
try
{
moveNext = await e.MoveNextAsync();
repeat = false;
}
catch (Exception ex)
{
if (ex is OperationCanceledException) return;
if (rebindOnError && !repeat)
{
repeat = true;
goto BIND_AGAIN;
}
else
{
throw;
}
}
if (!moveNext) return;
text.text = e.Current.ToString();
}
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,22 @@
{
"name": "UniTask.TextMeshPro",
"references": [
"UniTask",
"Unity.TextMeshPro"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.textmeshpro",
"expression": "",
"define": "UNITASK_TEXTMESHPRO_SUPPORT"
}
],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: dc47925d1a5fa2946bdd37746b2b5d48
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -28,6 +28,12 @@ namespace Cysharp.Threading.Tasks
IUniTaskOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, CancellationToken, UniTask<TKey>> keySelector, IComparer<TKey> comparer, bool descending);
}
public interface IConnectableUniTaskAsyncEnumerable<out T> : IUniTaskAsyncEnumerable<T>
{
IDisposable Connect();
}
// don't use AsyncGrouping.
//public interface IUniTaskAsyncGrouping<out TKey, out TElement> : IUniTaskAsyncEnumerable<TElement>
//{
// TKey Key { get; }

View File

@@ -19,17 +19,75 @@ namespace Cysharp.Threading.Tasks
// similar as IValueTaskSource
public interface IUniTaskSource
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
: System.Threading.Tasks.Sources.IValueTaskSource
#pragma warning disable CS0108
#endif
{
UniTaskStatus GetStatus(short token);
void OnCompleted(Action<object> continuation, object state, short token);
void GetResult(short token);
UniTaskStatus UnsafeGetStatus(); // only for debug use.
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
#pragma warning restore CS0108
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(short token)
{
return (System.Threading.Tasks.Sources.ValueTaskSourceStatus)(int)((IUniTaskSource)this).GetStatus(token);
}
void System.Threading.Tasks.Sources.IValueTaskSource.GetResult(short token)
{
((IUniTaskSource)this).GetResult(token);
}
void System.Threading.Tasks.Sources.IValueTaskSource.OnCompleted(Action<object> continuation, object state, short token, System.Threading.Tasks.Sources.ValueTaskSourceOnCompletedFlags flags)
{
// ignore flags, always none.
((IUniTaskSource)this).OnCompleted(continuation, state, token);
}
#endif
}
public interface IUniTaskSource<out T> : IUniTaskSource
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
, System.Threading.Tasks.Sources.IValueTaskSource<T>
#endif
{
new T GetResult(short token);
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
new public UniTaskStatus GetStatus(short token)
{
return ((IUniTaskSource)this).GetStatus(token);
}
new public void OnCompleted(Action<object> continuation, object state, short token)
{
((IUniTaskSource)this).OnCompleted(continuation, state, token);
}
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource<T>.GetStatus(short token)
{
return (System.Threading.Tasks.Sources.ValueTaskSourceStatus)(int)((IUniTaskSource)this).GetStatus(token);
}
T System.Threading.Tasks.Sources.IValueTaskSource<T>.GetResult(short token)
{
return ((IUniTaskSource<T>)this).GetResult(token);
}
void System.Threading.Tasks.Sources.IValueTaskSource<T>.OnCompleted(Action<object> continuation, object state, short token, System.Threading.Tasks.Sources.ValueTaskSourceOnCompletedFlags flags)
{
// ignore flags, always none.
((IUniTaskSource)this).OnCompleted(continuation, state, token);
}
#endif
}
public static class UniTaskStatusExtensions

View File

@@ -10,6 +10,8 @@ namespace Cysharp.Threading.Tasks.Internal
const int MaxArrayLength = 0X7FEFFFFF;
const int InitialSize = 16;
readonly PlayerLoopTiming timing;
SpinLock gate = new SpinLock();
bool dequing = false;
@@ -19,6 +21,11 @@ namespace Cysharp.Threading.Tasks.Internal
int waitingListCount = 0;
Action[] waitingList = new Action[InitialSize];
public ContinuationQueue(PlayerLoopTiming timing)
{
this.timing = timing;
}
public void Enqueue(Action continuation)
{
bool lockTaken = false;
@@ -72,7 +79,80 @@ namespace Cysharp.Threading.Tasks.Internal
waitingList = new Action[InitialSize];
}
// delegate entrypoint.
public void Run()
{
// for debugging, create named stacktrace.
#if DEBUG
switch (timing)
{
case PlayerLoopTiming.Initialization:
Initialization();
break;
case PlayerLoopTiming.LastInitialization:
LastInitialization();
break;
case PlayerLoopTiming.EarlyUpdate:
EarlyUpdate();
break;
case PlayerLoopTiming.LastEarlyUpdate:
LastEarlyUpdate();
break;
case PlayerLoopTiming.FixedUpdate:
FixedUpdate();
break;
case PlayerLoopTiming.LastFixedUpdate:
LastFixedUpdate();
break;
case PlayerLoopTiming.PreUpdate:
PreUpdate();
break;
case PlayerLoopTiming.LastPreUpdate:
LastPreUpdate();
break;
case PlayerLoopTiming.Update:
Update();
break;
case PlayerLoopTiming.LastUpdate:
LastUpdate();
break;
case PlayerLoopTiming.PreLateUpdate:
PreLateUpdate();
break;
case PlayerLoopTiming.LastPreLateUpdate:
LastPreLateUpdate();
break;
case PlayerLoopTiming.PostLateUpdate:
PostLateUpdate();
break;
case PlayerLoopTiming.LastPostLateUpdate:
LastPostLateUpdate();
break;
default:
break;
}
#else
RunCore();
#endif
}
void Initialization() => RunCore();
void LastInitialization() => RunCore();
void EarlyUpdate() => RunCore();
void LastEarlyUpdate() => RunCore();
void FixedUpdate() => RunCore();
void LastFixedUpdate() => RunCore();
void PreUpdate() => RunCore();
void LastPreUpdate() => RunCore();
void Update() => RunCore();
void LastUpdate() => RunCore();
void PreLateUpdate() => RunCore();
void LastPreLateUpdate() => RunCore();
void PostLateUpdate() => RunCore();
void LastPostLateUpdate() => RunCore();
[System.Diagnostics.DebuggerHidden]
void RunCore()
{
{
bool lockTaken = false;
@@ -90,10 +170,17 @@ namespace Cysharp.Threading.Tasks.Internal
for (int i = 0; i < actionListCount; i++)
{
var action = actionList[i];
actionList[i] = null;
action();
try
{
action();
}
catch (Exception ex)
{
UnityEngine.Debug.LogException(ex);
}
}
{

View File

@@ -239,7 +239,7 @@ namespace Cysharp.Threading.Tasks.Internal
}
else
{
var fname = fi.FullName.Replace(Path.DirectorySeparatorChar, '/').Replace(Application.dataPath, "");
var fname = fi.FullName.Replace(Path.DirectorySeparatorChar, '/').Replace(PlayerLoopHelper.ApplicationDataPath, "");
var withAssetsPath = "Assets/" + fname;
return "<a href=\"" + withAssetsPath + "\" line=\"" + line + "\">" + withAssetsPath + ":" + line + "</a>";
}

View File

@@ -1,23 +0,0 @@
#if NET_4_6 || NET_STANDARD_2_0 || CSHARP_7_OR_LATER
using System;
namespace Cysharp.Threading.Tasks.Internal
{
internal static class FuncExtensions
{
// avoid lambda capture
internal static Action<T> AsFuncOfT<T>(this Action action)
{
return new Action<T>(action.Invoke);
}
static void Invoke<T>(this Action action, T unused)
{
action();
}
}
}
#endif

View File

@@ -8,6 +8,7 @@ namespace Cysharp.Threading.Tasks.Internal
{
const int InitialSize = 16;
readonly PlayerLoopTiming timing;
readonly object runningAndQueueLock = new object();
readonly object arrayLock = new object();
readonly Action<Exception> unhandledExceptionCallback;
@@ -17,9 +18,12 @@ namespace Cysharp.Threading.Tasks.Internal
IPlayerLoopItem[] loopItems = new IPlayerLoopItem[InitialSize];
MinimumQueue<IPlayerLoopItem> waitQueue = new MinimumQueue<IPlayerLoopItem>(InitialSize);
public PlayerLoopRunner()
public PlayerLoopRunner(PlayerLoopTiming timing)
{
this.unhandledExceptionCallback = ex => Debug.LogException(ex);
this.timing = timing;
}
public void AddAction(IPlayerLoopItem item)
@@ -55,7 +59,80 @@ namespace Cysharp.Threading.Tasks.Internal
}
}
// delegate entrypoint.
public void Run()
{
// for debugging, create named stacktrace.
#if DEBUG
switch (timing)
{
case PlayerLoopTiming.Initialization:
Initialization();
break;
case PlayerLoopTiming.LastInitialization:
LastInitialization();
break;
case PlayerLoopTiming.EarlyUpdate:
EarlyUpdate();
break;
case PlayerLoopTiming.LastEarlyUpdate:
LastEarlyUpdate();
break;
case PlayerLoopTiming.FixedUpdate:
FixedUpdate();
break;
case PlayerLoopTiming.LastFixedUpdate:
LastFixedUpdate();
break;
case PlayerLoopTiming.PreUpdate:
PreUpdate();
break;
case PlayerLoopTiming.LastPreUpdate:
LastPreUpdate();
break;
case PlayerLoopTiming.Update:
Update();
break;
case PlayerLoopTiming.LastUpdate:
LastUpdate();
break;
case PlayerLoopTiming.PreLateUpdate:
PreLateUpdate();
break;
case PlayerLoopTiming.LastPreLateUpdate:
LastPreLateUpdate();
break;
case PlayerLoopTiming.PostLateUpdate:
PostLateUpdate();
break;
case PlayerLoopTiming.LastPostLateUpdate:
LastPostLateUpdate();
break;
default:
break;
}
#else
RunCore();
#endif
}
void Initialization() => RunCore();
void LastInitialization() => RunCore();
void EarlyUpdate() => RunCore();
void LastEarlyUpdate() => RunCore();
void FixedUpdate() => RunCore();
void LastFixedUpdate() => RunCore();
void PreUpdate() => RunCore();
void LastPreUpdate() => RunCore();
void Update() => RunCore();
void LastUpdate() => RunCore();
void PreLateUpdate() => RunCore();
void LastPreLateUpdate() => RunCore();
void PostLateUpdate() => RunCore();
void LastPostLateUpdate() => RunCore();
[System.Diagnostics.DebuggerHidden]
void RunCore()
{
lock (runningAndQueueLock)
{
@@ -66,6 +143,7 @@ namespace Cysharp.Threading.Tasks.Internal
{
var j = tail - 1;
var loopItems = this.loopItems;
// eliminate array-bound check for i
for (int i = 0; i < loopItems.Length; i++)
{

View File

@@ -0,0 +1,50 @@
using System;
using System.Runtime.CompilerServices;
namespace Cysharp.Threading.Tasks.Internal
{
internal sealed class PooledDelegate<T> : ITaskPoolNode<PooledDelegate<T>>
{
static TaskPool<PooledDelegate<T>> pool;
PooledDelegate<T> nextNode;
public ref PooledDelegate<T> NextNode => ref nextNode;
static PooledDelegate()
{
TaskPool.RegisterSizeGetter(typeof(PooledDelegate<T>), () => pool.Size);
}
readonly Action<T> runDelegate;
Action continuation;
PooledDelegate()
{
runDelegate = Run;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Action<T> Create(Action continuation)
{
if (!pool.TryPop(out var item))
{
item = new PooledDelegate<T>();
}
item.continuation = continuation;
return item.runDelegate;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Run(T _)
{
var call = continuation;
continuation = null;
if (call != null)
{
pool.TryPush(this);
call.Invoke();
}
}
}
}

View File

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

View File

@@ -1,55 +0,0 @@
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Cysharp.Threading.Tasks.Internal
{
// public, allow to user create custom operator with pool.
public interface IPromisePoolItem
{
void Reset();
}
public class PromisePool<T>
where T : class, IPromisePoolItem
{
int count = 0;
readonly ConcurrentQueue<T> queue = new ConcurrentQueue<T>();
readonly int maxSize;
public PromisePool(int maxSize = 256)
{
this.maxSize = maxSize;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T TryRent()
{
if (queue.TryDequeue(out var value))
{
Interlocked.Decrement(ref count);
return value;
}
return null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryReturn(T value)
{
value.Reset(); // reset when return.
if (count < maxSize)
{
queue.Enqueue(value);
Interlocked.Increment(ref count);
return true;
}
else
{
return false;
}
}
}
}

View File

@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using Cysharp.Threading.Tasks.Internal;
@@ -59,9 +60,9 @@ namespace Cysharp.Threading.Tasks
#endif
static List<KeyValuePair<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>> listPool = new List<KeyValuePair<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>>();
static List<KeyValuePair<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>> listPool = new List<KeyValuePair<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>>();
static readonly WeakDictionary<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)> tracking = new WeakDictionary<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>();
static readonly WeakDictionary<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)> tracking = new WeakDictionary<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>();
[Conditional("UNITY_EDITOR")]
public static void TrackActiveTask(IUniTaskSource task, int skipFrame)
@@ -70,7 +71,19 @@ namespace Cysharp.Threading.Tasks
dirty = true;
if (!EditorEnableState.EnableTracking) return;
var stackTrace = EditorEnableState.EnableStackTrace ? new StackTrace(skipFrame, true).CleanupAsyncStackTrace() : "";
tracking.TryAdd(task, (Interlocked.Increment(ref trackingId), DateTime.UtcNow, stackTrace));
string typeName;
if (EditorEnableState.EnableStackTrace)
{
var sb = new StringBuilder();
TypeBeautify(task.GetType(), sb);
typeName = sb.ToString();
}
else
{
typeName = task.GetType().Name;
}
tracking.TryAdd(task, (typeName, Interlocked.Increment(ref trackingId), DateTime.UtcNow, stackTrace));
#endif
}
@@ -103,19 +116,8 @@ namespace Cysharp.Threading.Tasks
{
for (int i = 0; i < count; i++)
{
string typeName = null;
var keyType = listPool[i].Key.GetType();
if (keyType.IsNested)
{
typeName = keyType.DeclaringType.Name + "." + keyType.Name;
}
else
{
typeName = keyType.Name;
}
action(listPool[i].Value.trackingId, typeName, listPool[i].Key.UnsafeGetStatus(), listPool[i].Value.addTime, listPool[i].Value.stackTrace);
listPool[i] = new KeyValuePair<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>(null, (0, default(DateTime), null)); // clear
action(listPool[i].Value.trackingId, listPool[i].Value.formattedType, listPool[i].Key.UnsafeGetStatus(), listPool[i].Value.addTime, listPool[i].Value.stackTrace);
listPool[i] = default;
}
}
catch
@@ -125,6 +127,52 @@ namespace Cysharp.Threading.Tasks
}
}
}
static void TypeBeautify(Type type, StringBuilder sb)
{
if (type.IsNested)
{
// TypeBeautify(type.DeclaringType, sb);
sb.Append(type.DeclaringType.Name.ToString());
sb.Append(".");
}
if (type.IsGenericType)
{
var genericsStart = type.Name.IndexOf("`");
if (genericsStart != -1)
{
sb.Append(type.Name.Substring(0, genericsStart));
}
else
{
sb.Append(type.Name);
}
sb.Append("<");
var first = true;
foreach (var item in type.GetGenericArguments())
{
if (!first)
{
sb.Append(", ");
}
first = false;
TypeBeautify(item, sb);
}
sb.Append(">");
}
else
{
sb.Append(type.Name);
}
}
//static string RemoveUniTaskNamespace(string str)
//{
// return str.Replace("Cysharp.Threading.Tasks.CompilerServices", "")
// .Replace("Cysharp.Threading.Tasks.Linq", "")
// .Replace("Cysharp.Threading.Tasks", "");
//}
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine.Networking;
namespace Cysharp.Threading.Tasks.Internal
{
internal static class UnityWebRequestResultExtensions
{
public static bool IsError(this UnityWebRequest unityWebRequest)
{
#if UNITY_2020_2_OR_NEWER
var result = unityWebRequest.result;
return (result == UnityWebRequest.Result.ConnectionError)
|| (result == UnityWebRequest.Result.DataProcessingError)
|| (result == UnityWebRequest.Result.ProtocolError);
#else
return unityWebRequest.isHttpError || unityWebRequest.isNetworkError;
#endif
}
}
}

View File

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

View File

@@ -0,0 +1,35 @@
using System;
using System.Diagnostics;
namespace Cysharp.Threading.Tasks.Internal
{
internal readonly struct ValueStopwatch
{
static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
readonly long startTimestamp;
public static ValueStopwatch StartNew() => new ValueStopwatch(Stopwatch.GetTimestamp());
ValueStopwatch(long startTimestamp)
{
this.startTimestamp = startTimestamp;
}
public TimeSpan Elapsed => TimeSpan.FromTicks(this.ElapsedTicks);
public long ElapsedTicks
{
get
{
if (startTimestamp == 0)
{
throw new InvalidOperationException("Detected invalid initialization(use 'default'), only to create from StartNew().");
}
var delta = Stopwatch.GetTimestamp() - startTimestamp;
return (long)(delta * TimestampToTicks);
}
}
}
}

View File

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

View File

@@ -12,7 +12,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(accumulator, nameof(accumulator));
return Aggregate.InvokeAsync(source, accumulator, cancellationToken);
return Aggregate.AggregateAsync(source, accumulator, cancellationToken);
}
public static UniTask<TAccumulate> AggregateAsync<TSource, TAccumulate>(this IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator, CancellationToken cancellationToken = default)
@@ -20,7 +20,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(accumulator, nameof(accumulator));
return Aggregate.InvokeAsync(source, seed, accumulator, cancellationToken);
return Aggregate.AggregateAsync(source, seed, accumulator, cancellationToken);
}
public static UniTask<TResult> AggregateAsync<TSource, TAccumulate, TResult>(this IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator, Func<TAccumulate, TResult> resultSelector, CancellationToken cancellationToken = default)
@@ -29,7 +29,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(accumulator, nameof(accumulator));
Error.ThrowArgumentNullException(accumulator, nameof(resultSelector));
return Aggregate.InvokeAsync(source, seed, accumulator, resultSelector, cancellationToken);
return Aggregate.AggregateAsync(source, seed, accumulator, resultSelector, cancellationToken);
}
public static UniTask<TSource> AggregateAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TSource, UniTask<TSource>> accumulator, CancellationToken cancellationToken = default)
@@ -37,7 +37,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(accumulator, nameof(accumulator));
return Aggregate.InvokeAsync(source, accumulator, cancellationToken);
return Aggregate.AggregateAwaitAsync(source, accumulator, cancellationToken);
}
public static UniTask<TAccumulate> AggregateAwaitAsync<TSource, TAccumulate>(this IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, UniTask<TAccumulate>> accumulator, CancellationToken cancellationToken = default)
@@ -45,7 +45,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(accumulator, nameof(accumulator));
return Aggregate.InvokeAsync(source, seed, accumulator, cancellationToken);
return Aggregate.AggregateAwaitAsync(source, seed, accumulator, cancellationToken);
}
public static UniTask<TResult> AggregateAwaitAsync<TSource, TAccumulate, TResult>(this IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, UniTask<TAccumulate>> accumulator, Func<TAccumulate, UniTask<TResult>> resultSelector, CancellationToken cancellationToken = default)
@@ -54,7 +54,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(accumulator, nameof(accumulator));
Error.ThrowArgumentNullException(accumulator, nameof(resultSelector));
return Aggregate.InvokeAsync(source, seed, accumulator, resultSelector, cancellationToken);
return Aggregate.AggregateAwaitAsync(source, seed, accumulator, resultSelector, cancellationToken);
}
public static UniTask<TSource> AggregateAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TSource, CancellationToken, UniTask<TSource>> accumulator, CancellationToken cancellationToken = default)
@@ -62,7 +62,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(accumulator, nameof(accumulator));
return Aggregate.InvokeAsync(source, accumulator, cancellationToken);
return Aggregate.AggregateAwaitWithCancellationAsync(source, accumulator, cancellationToken);
}
public static UniTask<TAccumulate> AggregateAwaitWithCancellationAsync<TSource, TAccumulate>(this IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, UniTask<TAccumulate>> accumulator, CancellationToken cancellationToken = default)
@@ -70,7 +70,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(accumulator, nameof(accumulator));
return Aggregate.InvokeAsync(source, seed, accumulator, cancellationToken);
return Aggregate.AggregateAwaitWithCancellationAsync(source, seed, accumulator, cancellationToken);
}
public static UniTask<TResult> AggregateAwaitWithCancellationAsync<TSource, TAccumulate, TResult>(this IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, UniTask<TAccumulate>> accumulator, Func<TAccumulate, CancellationToken, UniTask<TResult>> resultSelector, CancellationToken cancellationToken = default)
@@ -79,13 +79,13 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(accumulator, nameof(accumulator));
Error.ThrowArgumentNullException(accumulator, nameof(resultSelector));
return Aggregate.InvokeAsync(source, seed, accumulator, resultSelector, cancellationToken);
return Aggregate.AggregateAwaitWithCancellationAsync(source, seed, accumulator, resultSelector, cancellationToken);
}
}
internal static class Aggregate
{
internal static async UniTask<TSource> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TSource, TSource> accumulator, CancellationToken cancellationToken)
internal static async UniTask<TSource> AggregateAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TSource, TSource> accumulator, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
@@ -116,7 +116,7 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
internal static async UniTask<TAccumulate> InvokeAsync<TSource, TAccumulate>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator, CancellationToken cancellationToken)
internal static async UniTask<TAccumulate> AggregateAsync<TSource, TAccumulate>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
@@ -138,7 +138,7 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
internal static async UniTask<TResult> InvokeAsync<TSource, TAccumulate, TResult>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator, Func<TAccumulate, TResult> resultSelector, CancellationToken cancellationToken)
internal static async UniTask<TResult> AggregateAsync<TSource, TAccumulate, TResult>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator, Func<TAccumulate, TResult> resultSelector, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
@@ -162,7 +162,7 @@ namespace Cysharp.Threading.Tasks.Linq
// with async
internal static async UniTask<TSource> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TSource, UniTask<TSource>> accumulator, CancellationToken cancellationToken)
internal static async UniTask<TSource> AggregateAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TSource, UniTask<TSource>> accumulator, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
@@ -193,7 +193,7 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
internal static async UniTask<TAccumulate> InvokeAsync<TSource, TAccumulate>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, UniTask<TAccumulate>> accumulator, CancellationToken cancellationToken)
internal static async UniTask<TAccumulate> AggregateAwaitAsync<TSource, TAccumulate>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, UniTask<TAccumulate>> accumulator, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
@@ -215,7 +215,7 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
internal static async UniTask<TResult> InvokeAsync<TSource, TAccumulate, TResult>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, UniTask<TAccumulate>> accumulator, Func<TAccumulate, UniTask<TResult>> resultSelector, CancellationToken cancellationToken)
internal static async UniTask<TResult> AggregateAwaitAsync<TSource, TAccumulate, TResult>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, UniTask<TAccumulate>> accumulator, Func<TAccumulate, UniTask<TResult>> resultSelector, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
@@ -240,7 +240,7 @@ namespace Cysharp.Threading.Tasks.Linq
// with cancellation
internal static async UniTask<TSource> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TSource, CancellationToken, UniTask<TSource>> accumulator, CancellationToken cancellationToken)
internal static async UniTask<TSource> AggregateAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TSource, CancellationToken, UniTask<TSource>> accumulator, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
@@ -271,7 +271,7 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
internal static async UniTask<TAccumulate> InvokeAsync<TSource, TAccumulate>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, UniTask<TAccumulate>> accumulator, CancellationToken cancellationToken)
internal static async UniTask<TAccumulate> AggregateAwaitWithCancellationAsync<TSource, TAccumulate>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, UniTask<TAccumulate>> accumulator, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
@@ -293,7 +293,7 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
internal static async UniTask<TResult> InvokeAsync<TSource, TAccumulate, TResult>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, UniTask<TAccumulate>> accumulator, Func<TAccumulate, CancellationToken, UniTask<TResult>> resultSelector, CancellationToken cancellationToken)
internal static async UniTask<TResult> AggregateAwaitWithCancellationAsync<TSource, TAccumulate, TResult>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, UniTask<TAccumulate>> accumulator, Func<TAccumulate, CancellationToken, UniTask<TResult>> resultSelector, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try

View File

@@ -11,7 +11,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(predicate, nameof(predicate));
return All.InvokeAsync(source, predicate, cancellationToken);
return All.AllAsync(source, predicate, cancellationToken);
}
public static UniTask<Boolean> AllAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Boolean>> predicate, CancellationToken cancellationToken = default)
@@ -19,7 +19,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(predicate, nameof(predicate));
return All.InvokeAsync(source, predicate, cancellationToken);
return All.AllAwaitAsync(source, predicate, cancellationToken);
}
public static UniTask<Boolean> AllAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Boolean>> predicate, CancellationToken cancellationToken = default)
@@ -27,13 +27,13 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(predicate, nameof(predicate));
return All.InvokeAsync(source, predicate, cancellationToken);
return All.AllAwaitWithCancellationAsync(source, predicate, cancellationToken);
}
}
internal static class All
{
internal static async UniTask<bool> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Boolean> predicate, CancellationToken cancellationToken)
internal static async UniTask<bool> AllAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Boolean> predicate, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
@@ -57,7 +57,7 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
internal static async UniTask<bool> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Boolean>> predicate, CancellationToken cancellationToken)
internal static async UniTask<bool> AllAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Boolean>> predicate, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
@@ -81,7 +81,7 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
internal static async UniTask<bool> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Boolean>> predicate, CancellationToken cancellationToken)
internal static async UniTask<bool> AllAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Boolean>> predicate, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try

View File

@@ -10,7 +10,7 @@ namespace Cysharp.Threading.Tasks.Linq
{
Error.ThrowArgumentNullException(source, nameof(source));
return Any.InvokeAsync(source, cancellationToken);
return Any.AnyAsync(source, cancellationToken);
}
public static UniTask<Boolean> AnyAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Boolean> predicate, CancellationToken cancellationToken = default)
@@ -18,7 +18,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(predicate, nameof(predicate));
return Any.InvokeAsync(source, predicate, cancellationToken);
return Any.AnyAsync(source, predicate, cancellationToken);
}
public static UniTask<Boolean> AnyAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Boolean>> predicate, CancellationToken cancellationToken = default)
@@ -26,7 +26,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(predicate, nameof(predicate));
return Any.InvokeAsync(source, predicate, cancellationToken);
return Any.AnyAwaitAsync(source, predicate, cancellationToken);
}
public static UniTask<Boolean> AnyAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Boolean>> predicate, CancellationToken cancellationToken = default)
@@ -34,13 +34,13 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(predicate, nameof(predicate));
return Any.InvokeAsync(source, predicate, cancellationToken);
return Any.AnyAwaitWithCancellationAsync(source, predicate, cancellationToken);
}
}
internal static class Any
{
internal static async UniTask<bool> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
internal static async UniTask<bool> AnyAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
@@ -61,7 +61,7 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
internal static async UniTask<bool> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Boolean> predicate, CancellationToken cancellationToken)
internal static async UniTask<bool> AnyAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Boolean> predicate, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
@@ -85,7 +85,7 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
internal static async UniTask<bool> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Boolean>> predicate, CancellationToken cancellationToken)
internal static async UniTask<bool> AnyAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Boolean>> predicate, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
@@ -109,7 +109,7 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
internal static async UniTask<bool> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Boolean>> predicate, CancellationToken cancellationToken)
internal static async UniTask<bool> AnyAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Boolean>> predicate, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try

View File

@@ -36,10 +36,10 @@ namespace Cysharp.Threading.Tasks.Linq
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, element, append, cancellationToken);
return new _AppendPrepend(source, element, append, cancellationToken);
}
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
sealed class _AppendPrepend : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
{
enum State : byte
{
@@ -59,12 +59,14 @@ namespace Cysharp.Threading.Tasks.Linq
IUniTaskAsyncEnumerator<TSource> enumerator;
UniTask<bool>.Awaiter awaiter;
public Enumerator(IUniTaskAsyncEnumerable<TSource> source, TSource element, bool append, CancellationToken cancellationToken)
public _AppendPrepend(IUniTaskAsyncEnumerable<TSource> source, TSource element, bool append, CancellationToken cancellationToken)
{
this.source = source;
this.element = element;
this.state = append ? State.RequireAppend : State.RequirePrepend;
this.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(this, 3);
}
public TSource Current { get; private set; }
@@ -108,7 +110,7 @@ namespace Cysharp.Threading.Tasks.Linq
static void MoveNextCore(object state)
{
var self = (Enumerator)state;
var self = (_AppendPrepend)state;
if (self.TryGetResult(self.awaiter, out var result))
{
@@ -136,6 +138,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync()
{
TaskTracker.RemoveTracking(this);
if (enumerator != null)
{
return enumerator.DisposeAsync();

View File

@@ -3,66 +3,9 @@ using System.Threading;
namespace Cysharp.Threading.Tasks.Linq
{
public abstract class MoveNextSource : IUniTaskSource<bool>
{
protected UniTaskCompletionSourceCore<bool> completionSource;
public bool GetResult(short token)
{
return completionSource.GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return completionSource.GetStatus(token);
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
completionSource.OnCompleted(continuation, state, token);
}
public UniTaskStatus UnsafeGetStatus()
{
return completionSource.UnsafeGetStatus();
}
void IUniTaskSource.GetResult(short token)
{
completionSource.GetResult(token);
}
protected bool TryGetResult<T>(UniTask<T>.Awaiter awaiter, out T result)
{
try
{
result = awaiter.GetResult();
return true;
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
result = default;
return false;
}
}
protected bool TryGetResult(UniTask.Awaiter awaiter)
{
try
{
awaiter.GetResult();
return true;
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
return false;
}
}
}
public abstract class AsyncEnumeratorBase<TSource, TResult> : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
// note: refactor all inherit class and should remove this.
// see Select and Where.
internal abstract class AsyncEnumeratorBase<TSource, TResult> : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
{
static readonly Action<object> moveNextCallbackDelegate = MoveNextCallBack;
@@ -76,6 +19,7 @@ namespace Cysharp.Threading.Tasks.Linq
{
this.source = source;
this.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(this, 4);
}
// abstract
@@ -178,6 +122,7 @@ namespace Cysharp.Threading.Tasks.Linq
// if require additional resource to dispose, override and call base.DisposeAsync.
public virtual UniTask DisposeAsync()
{
TaskTracker.RemoveTracking(this);
if (enumerator != null)
{
return enumerator.DisposeAsync();
@@ -186,7 +131,7 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
public abstract class AsyncEnumeratorAwaitSelectorBase<TSource, TResult, TAwait> : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
internal abstract class AsyncEnumeratorAwaitSelectorBase<TSource, TResult, TAwait> : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
{
static readonly Action<object> moveNextCallbackDelegate = MoveNextCallBack;
static readonly Action<object> setCurrentCallbackDelegate = SetCurrentCallBack;
@@ -204,6 +149,7 @@ namespace Cysharp.Threading.Tasks.Linq
{
this.source = source;
this.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(this, 4);
}
// abstract
@@ -399,6 +345,7 @@ namespace Cysharp.Threading.Tasks.Linq
// if require additional resource to dispose, override and call base.DisposeAsync.
public virtual UniTask DisposeAsync()
{
TaskTracker.RemoveTracking(this);
if (enumerator != null)
{
return enumerator.DisposeAsync();
@@ -406,5 +353,4 @@ namespace Cysharp.Threading.Tasks.Linq
return default;
}
}
}

View File

@@ -10,7 +10,7 @@ namespace Cysharp.Threading.Tasks.Linq
{
Error.ThrowArgumentNullException(source, nameof(source));
return Average.InvokeAsync(source, cancellationToken);
return Average.AverageAsync(source, cancellationToken);
}
public static UniTask<double> AverageAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int32> selector, CancellationToken cancellationToken = default)
@@ -18,7 +18,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAsync(source, selector, cancellationToken);
}
public static UniTask<double> AverageAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Int32>> selector, CancellationToken cancellationToken = default)
@@ -26,22 +26,22 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitAsync(source, selector, cancellationToken);
}
public static UniTask<double> AverageAwaitCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int32>> selector, CancellationToken cancellationToken = default)
public static UniTask<double> AverageAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int32>> selector, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitWithCancellationAsync(source, selector, cancellationToken);
}
public static UniTask<double> AverageAsync(this IUniTaskAsyncEnumerable<Int64> source, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
return Average.InvokeAsync(source, cancellationToken);
return Average.AverageAsync(source, cancellationToken);
}
public static UniTask<double> AverageAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int64> selector, CancellationToken cancellationToken = default)
@@ -49,7 +49,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAsync(source, selector, cancellationToken);
}
public static UniTask<double> AverageAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Int64>> selector, CancellationToken cancellationToken = default)
@@ -57,22 +57,22 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitAsync(source, selector, cancellationToken);
}
public static UniTask<double> AverageAwaitCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int64>> selector, CancellationToken cancellationToken = default)
public static UniTask<double> AverageAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int64>> selector, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitWithCancellationAsync(source, selector, cancellationToken);
}
public static UniTask<float> AverageAsync(this IUniTaskAsyncEnumerable<Single> source, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
return Average.InvokeAsync(source, cancellationToken);
return Average.AverageAsync(source, cancellationToken);
}
public static UniTask<float> AverageAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Single> selector, CancellationToken cancellationToken = default)
@@ -80,7 +80,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAsync(source, selector, cancellationToken);
}
public static UniTask<float> AverageAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Single>> selector, CancellationToken cancellationToken = default)
@@ -88,22 +88,22 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitAsync(source, selector, cancellationToken);
}
public static UniTask<float> AverageAwaitCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Single>> selector, CancellationToken cancellationToken = default)
public static UniTask<float> AverageAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Single>> selector, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitWithCancellationAsync(source, selector, cancellationToken);
}
public static UniTask<double> AverageAsync(this IUniTaskAsyncEnumerable<Double> source, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
return Average.InvokeAsync(source, cancellationToken);
return Average.AverageAsync(source, cancellationToken);
}
public static UniTask<double> AverageAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Double> selector, CancellationToken cancellationToken = default)
@@ -111,7 +111,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAsync(source, selector, cancellationToken);
}
public static UniTask<double> AverageAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Double>> selector, CancellationToken cancellationToken = default)
@@ -119,22 +119,22 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitAsync(source, selector, cancellationToken);
}
public static UniTask<double> AverageAwaitCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Double>> selector, CancellationToken cancellationToken = default)
public static UniTask<double> AverageAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Double>> selector, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitWithCancellationAsync(source, selector, cancellationToken);
}
public static UniTask<decimal> AverageAsync(this IUniTaskAsyncEnumerable<Decimal> source, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
return Average.InvokeAsync(source, cancellationToken);
return Average.AverageAsync(source, cancellationToken);
}
public static UniTask<decimal> AverageAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Decimal> selector, CancellationToken cancellationToken = default)
@@ -142,7 +142,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAsync(source, selector, cancellationToken);
}
public static UniTask<decimal> AverageAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Decimal>> selector, CancellationToken cancellationToken = default)
@@ -150,22 +150,22 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitAsync(source, selector, cancellationToken);
}
public static UniTask<decimal> AverageAwaitCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Decimal>> selector, CancellationToken cancellationToken = default)
public static UniTask<decimal> AverageAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Decimal>> selector, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitWithCancellationAsync(source, selector, cancellationToken);
}
public static UniTask<double?> AverageAsync(this IUniTaskAsyncEnumerable<Int32?> source, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
return Average.InvokeAsync(source, cancellationToken);
return Average.AverageAsync(source, cancellationToken);
}
public static UniTask<double?> AverageAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int32?> selector, CancellationToken cancellationToken = default)
@@ -173,7 +173,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAsync(source, selector, cancellationToken);
}
public static UniTask<double?> AverageAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Int32?>> selector, CancellationToken cancellationToken = default)
@@ -181,22 +181,22 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitAsync(source, selector, cancellationToken);
}
public static UniTask<double?> AverageAwaitCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int32?>> selector, CancellationToken cancellationToken = default)
public static UniTask<double?> AverageAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int32?>> selector, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitWithCancellationAsync(source, selector, cancellationToken);
}
public static UniTask<double?> AverageAsync(this IUniTaskAsyncEnumerable<Int64?> source, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
return Average.InvokeAsync(source, cancellationToken);
return Average.AverageAsync(source, cancellationToken);
}
public static UniTask<double?> AverageAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int64?> selector, CancellationToken cancellationToken = default)
@@ -204,7 +204,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAsync(source, selector, cancellationToken);
}
public static UniTask<double?> AverageAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Int64?>> selector, CancellationToken cancellationToken = default)
@@ -212,22 +212,22 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitAsync(source, selector, cancellationToken);
}
public static UniTask<double?> AverageAwaitCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int64?>> selector, CancellationToken cancellationToken = default)
public static UniTask<double?> AverageAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int64?>> selector, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitWithCancellationAsync(source, selector, cancellationToken);
}
public static UniTask<float?> AverageAsync(this IUniTaskAsyncEnumerable<Single?> source, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
return Average.InvokeAsync(source, cancellationToken);
return Average.AverageAsync(source, cancellationToken);
}
public static UniTask<float?> AverageAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Single?> selector, CancellationToken cancellationToken = default)
@@ -235,7 +235,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAsync(source, selector, cancellationToken);
}
public static UniTask<float?> AverageAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Single?>> selector, CancellationToken cancellationToken = default)
@@ -243,22 +243,22 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitAsync(source, selector, cancellationToken);
}
public static UniTask<float?> AverageAwaitCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Single?>> selector, CancellationToken cancellationToken = default)
public static UniTask<float?> AverageAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Single?>> selector, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitWithCancellationAsync(source, selector, cancellationToken);
}
public static UniTask<double?> AverageAsync(this IUniTaskAsyncEnumerable<Double?> source, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
return Average.InvokeAsync(source, cancellationToken);
return Average.AverageAsync(source, cancellationToken);
}
public static UniTask<double?> AverageAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Double?> selector, CancellationToken cancellationToken = default)
@@ -266,7 +266,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAsync(source, selector, cancellationToken);
}
public static UniTask<double?> AverageAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Double?>> selector, CancellationToken cancellationToken = default)
@@ -274,22 +274,22 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitAsync(source, selector, cancellationToken);
}
public static UniTask<double?> AverageAwaitCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Double?>> selector, CancellationToken cancellationToken = default)
public static UniTask<double?> AverageAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Double?>> selector, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitWithCancellationAsync(source, selector, cancellationToken);
}
public static UniTask<decimal?> AverageAsync(this IUniTaskAsyncEnumerable<Decimal?> source, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
return Average.InvokeAsync(source, cancellationToken);
return Average.AverageAsync(source, cancellationToken);
}
public static UniTask<decimal?> AverageAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Decimal?> selector, CancellationToken cancellationToken = default)
@@ -297,7 +297,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAsync(source, selector, cancellationToken);
}
public static UniTask<decimal?> AverageAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Decimal?>> selector, CancellationToken cancellationToken = default)
@@ -305,22 +305,22 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitAsync(source, selector, cancellationToken);
}
public static UniTask<decimal?> AverageAwaitCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Decimal?>> selector, CancellationToken cancellationToken = default)
public static UniTask<decimal?> AverageAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Decimal?>> selector, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitWithCancellationAsync(source, selector, cancellationToken);
}
}
internal static class Average
{
public static async UniTask<double> InvokeAsync(IUniTaskAsyncEnumerable<Int32> source, CancellationToken cancellationToken)
public static async UniTask<double> AverageAsync(IUniTaskAsyncEnumerable<Int32> source, CancellationToken cancellationToken)
{
long count = 0;
Int32 sum = 0;
@@ -348,7 +348,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<double> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int32> selector, CancellationToken cancellationToken)
public static async UniTask<double> AverageAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int32> selector, CancellationToken cancellationToken)
{
long count = 0;
Int32 sum = 0;
@@ -376,7 +376,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<double> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Int32>> selector, CancellationToken cancellationToken)
public static async UniTask<double> AverageAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Int32>> selector, CancellationToken cancellationToken)
{
long count = 0;
Int32 sum = 0;
@@ -404,7 +404,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<double> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int32>> selector, CancellationToken cancellationToken)
public static async UniTask<double> AverageAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int32>> selector, CancellationToken cancellationToken)
{
long count = 0;
Int32 sum = 0;
@@ -432,7 +432,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<double> InvokeAsync(IUniTaskAsyncEnumerable<Int64> source, CancellationToken cancellationToken)
public static async UniTask<double> AverageAsync(IUniTaskAsyncEnumerable<Int64> source, CancellationToken cancellationToken)
{
long count = 0;
Int64 sum = 0;
@@ -460,7 +460,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<double> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int64> selector, CancellationToken cancellationToken)
public static async UniTask<double> AverageAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int64> selector, CancellationToken cancellationToken)
{
long count = 0;
Int64 sum = 0;
@@ -488,7 +488,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<double> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Int64>> selector, CancellationToken cancellationToken)
public static async UniTask<double> AverageAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Int64>> selector, CancellationToken cancellationToken)
{
long count = 0;
Int64 sum = 0;
@@ -516,7 +516,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<double> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int64>> selector, CancellationToken cancellationToken)
public static async UniTask<double> AverageAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int64>> selector, CancellationToken cancellationToken)
{
long count = 0;
Int64 sum = 0;
@@ -544,7 +544,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<float> InvokeAsync(IUniTaskAsyncEnumerable<Single> source, CancellationToken cancellationToken)
public static async UniTask<float> AverageAsync(IUniTaskAsyncEnumerable<Single> source, CancellationToken cancellationToken)
{
long count = 0;
Single sum = 0;
@@ -572,7 +572,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (float)(sum / count);
}
public static async UniTask<float> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Single> selector, CancellationToken cancellationToken)
public static async UniTask<float> AverageAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Single> selector, CancellationToken cancellationToken)
{
long count = 0;
Single sum = 0;
@@ -600,7 +600,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (float)(sum / count);
}
public static async UniTask<float> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Single>> selector, CancellationToken cancellationToken)
public static async UniTask<float> AverageAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Single>> selector, CancellationToken cancellationToken)
{
long count = 0;
Single sum = 0;
@@ -628,7 +628,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (float)(sum / count);
}
public static async UniTask<float> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Single>> selector, CancellationToken cancellationToken)
public static async UniTask<float> AverageAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Single>> selector, CancellationToken cancellationToken)
{
long count = 0;
Single sum = 0;
@@ -656,7 +656,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (float)(sum / count);
}
public static async UniTask<double> InvokeAsync(IUniTaskAsyncEnumerable<Double> source, CancellationToken cancellationToken)
public static async UniTask<double> AverageAsync(IUniTaskAsyncEnumerable<Double> source, CancellationToken cancellationToken)
{
long count = 0;
Double sum = 0;
@@ -684,7 +684,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<double> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Double> selector, CancellationToken cancellationToken)
public static async UniTask<double> AverageAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Double> selector, CancellationToken cancellationToken)
{
long count = 0;
Double sum = 0;
@@ -712,7 +712,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<double> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Double>> selector, CancellationToken cancellationToken)
public static async UniTask<double> AverageAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Double>> selector, CancellationToken cancellationToken)
{
long count = 0;
Double sum = 0;
@@ -740,7 +740,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<double> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Double>> selector, CancellationToken cancellationToken)
public static async UniTask<double> AverageAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Double>> selector, CancellationToken cancellationToken)
{
long count = 0;
Double sum = 0;
@@ -768,7 +768,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<decimal> InvokeAsync(IUniTaskAsyncEnumerable<Decimal> source, CancellationToken cancellationToken)
public static async UniTask<decimal> AverageAsync(IUniTaskAsyncEnumerable<Decimal> source, CancellationToken cancellationToken)
{
long count = 0;
Decimal sum = 0;
@@ -796,7 +796,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<decimal> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Decimal> selector, CancellationToken cancellationToken)
public static async UniTask<decimal> AverageAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Decimal> selector, CancellationToken cancellationToken)
{
long count = 0;
Decimal sum = 0;
@@ -824,7 +824,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<decimal> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Decimal>> selector, CancellationToken cancellationToken)
public static async UniTask<decimal> AverageAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Decimal>> selector, CancellationToken cancellationToken)
{
long count = 0;
Decimal sum = 0;
@@ -852,7 +852,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<decimal> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Decimal>> selector, CancellationToken cancellationToken)
public static async UniTask<decimal> AverageAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Decimal>> selector, CancellationToken cancellationToken)
{
long count = 0;
Decimal sum = 0;
@@ -880,7 +880,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<double?> InvokeAsync(IUniTaskAsyncEnumerable<Int32?> source, CancellationToken cancellationToken)
public static async UniTask<double?> AverageAsync(IUniTaskAsyncEnumerable<Int32?> source, CancellationToken cancellationToken)
{
long count = 0;
Int32? sum = 0;
@@ -893,7 +893,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = e.Current;
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -912,7 +912,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<double?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int32?> selector, CancellationToken cancellationToken)
public static async UniTask<double?> AverageAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int32?> selector, CancellationToken cancellationToken)
{
long count = 0;
Int32? sum = 0;
@@ -925,7 +925,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = selector(e.Current);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -944,7 +944,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<double?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Int32?>> selector, CancellationToken cancellationToken)
public static async UniTask<double?> AverageAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Int32?>> selector, CancellationToken cancellationToken)
{
long count = 0;
Int32? sum = 0;
@@ -957,7 +957,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = await selector(e.Current);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -976,7 +976,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<double?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int32?>> selector, CancellationToken cancellationToken)
public static async UniTask<double?> AverageAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int32?>> selector, CancellationToken cancellationToken)
{
long count = 0;
Int32? sum = 0;
@@ -989,7 +989,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = await selector(e.Current, cancellationToken);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1008,7 +1008,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<double?> InvokeAsync(IUniTaskAsyncEnumerable<Int64?> source, CancellationToken cancellationToken)
public static async UniTask<double?> AverageAsync(IUniTaskAsyncEnumerable<Int64?> source, CancellationToken cancellationToken)
{
long count = 0;
Int64? sum = 0;
@@ -1021,7 +1021,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = e.Current;
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1040,7 +1040,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<double?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int64?> selector, CancellationToken cancellationToken)
public static async UniTask<double?> AverageAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int64?> selector, CancellationToken cancellationToken)
{
long count = 0;
Int64? sum = 0;
@@ -1053,7 +1053,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = selector(e.Current);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1072,7 +1072,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<double?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Int64?>> selector, CancellationToken cancellationToken)
public static async UniTask<double?> AverageAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Int64?>> selector, CancellationToken cancellationToken)
{
long count = 0;
Int64? sum = 0;
@@ -1085,7 +1085,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = await selector(e.Current);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1104,7 +1104,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<double?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int64?>> selector, CancellationToken cancellationToken)
public static async UniTask<double?> AverageAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Int64?>> selector, CancellationToken cancellationToken)
{
long count = 0;
Int64? sum = 0;
@@ -1117,7 +1117,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = await selector(e.Current, cancellationToken);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1136,7 +1136,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (double)sum / count;
}
public static async UniTask<float?> InvokeAsync(IUniTaskAsyncEnumerable<Single?> source, CancellationToken cancellationToken)
public static async UniTask<float?> AverageAsync(IUniTaskAsyncEnumerable<Single?> source, CancellationToken cancellationToken)
{
long count = 0;
Single? sum = 0;
@@ -1149,7 +1149,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = e.Current;
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1168,7 +1168,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (float)(sum / count);
}
public static async UniTask<float?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Single?> selector, CancellationToken cancellationToken)
public static async UniTask<float?> AverageAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Single?> selector, CancellationToken cancellationToken)
{
long count = 0;
Single? sum = 0;
@@ -1181,7 +1181,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = selector(e.Current);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1200,7 +1200,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (float)(sum / count);
}
public static async UniTask<float?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Single?>> selector, CancellationToken cancellationToken)
public static async UniTask<float?> AverageAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Single?>> selector, CancellationToken cancellationToken)
{
long count = 0;
Single? sum = 0;
@@ -1213,7 +1213,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = await selector(e.Current);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1232,7 +1232,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (float)(sum / count);
}
public static async UniTask<float?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Single?>> selector, CancellationToken cancellationToken)
public static async UniTask<float?> AverageAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Single?>> selector, CancellationToken cancellationToken)
{
long count = 0;
Single? sum = 0;
@@ -1245,7 +1245,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = await selector(e.Current, cancellationToken);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1264,7 +1264,7 @@ namespace Cysharp.Threading.Tasks.Linq
return (float)(sum / count);
}
public static async UniTask<double?> InvokeAsync(IUniTaskAsyncEnumerable<Double?> source, CancellationToken cancellationToken)
public static async UniTask<double?> AverageAsync(IUniTaskAsyncEnumerable<Double?> source, CancellationToken cancellationToken)
{
long count = 0;
Double? sum = 0;
@@ -1277,7 +1277,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = e.Current;
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1296,7 +1296,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<double?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Double?> selector, CancellationToken cancellationToken)
public static async UniTask<double?> AverageAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Double?> selector, CancellationToken cancellationToken)
{
long count = 0;
Double? sum = 0;
@@ -1309,7 +1309,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = selector(e.Current);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1328,7 +1328,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<double?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Double?>> selector, CancellationToken cancellationToken)
public static async UniTask<double?> AverageAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Double?>> selector, CancellationToken cancellationToken)
{
long count = 0;
Double? sum = 0;
@@ -1341,7 +1341,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = await selector(e.Current);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1360,7 +1360,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<double?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Double?>> selector, CancellationToken cancellationToken)
public static async UniTask<double?> AverageAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Double?>> selector, CancellationToken cancellationToken)
{
long count = 0;
Double? sum = 0;
@@ -1373,7 +1373,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = await selector(e.Current, cancellationToken);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1392,7 +1392,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<decimal?> InvokeAsync(IUniTaskAsyncEnumerable<Decimal?> source, CancellationToken cancellationToken)
public static async UniTask<decimal?> AverageAsync(IUniTaskAsyncEnumerable<Decimal?> source, CancellationToken cancellationToken)
{
long count = 0;
Decimal? sum = 0;
@@ -1405,7 +1405,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = e.Current;
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1424,7 +1424,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<decimal?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Decimal?> selector, CancellationToken cancellationToken)
public static async UniTask<decimal?> AverageAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Decimal?> selector, CancellationToken cancellationToken)
{
long count = 0;
Decimal? sum = 0;
@@ -1437,7 +1437,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = selector(e.Current);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1456,7 +1456,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<decimal?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Decimal?>> selector, CancellationToken cancellationToken)
public static async UniTask<decimal?> AverageAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Decimal?>> selector, CancellationToken cancellationToken)
{
long count = 0;
Decimal? sum = 0;
@@ -1469,7 +1469,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = await selector(e.Current);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;
@@ -1488,7 +1488,7 @@ namespace Cysharp.Threading.Tasks.Linq
return sum / count;
}
public static async UniTask<decimal?> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Decimal?>> selector, CancellationToken cancellationToken)
public static async UniTask<decimal?> AverageAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Decimal?>> selector, CancellationToken cancellationToken)
{
long count = 0;
Decimal? sum = 0;
@@ -1501,7 +1501,7 @@ namespace Cysharp.Threading.Tasks.Linq
var v = await selector(e.Current, cancellationToken);
if (v.HasValue)
{
checked
checked
{
sum += v.Value;
count++;

View File

@@ -39,7 +39,7 @@ namespace Cysharp.Threading.Tasks.Linq
{
Error.ThrowArgumentNullException(source, nameof(source));
return Average.InvokeAsync(source, cancellationToken);
return Average.AverageAsync(source, cancellationToken);
}
public static UniTask<<#= ret #>> AverageAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, <#= TypeName(t) #>> selector, CancellationToken cancellationToken = default)
@@ -47,7 +47,7 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAsync(source, selector, cancellationToken);
}
public static UniTask<<#= ret #>> AverageAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<<#= TypeName(t) #>>> selector, CancellationToken cancellationToken = default)
@@ -55,15 +55,15 @@ namespace Cysharp.Threading.Tasks.Linq
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitAsync(source, selector, cancellationToken);
}
public static UniTask<<#= ret #>> AverageAwaitCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<<#= TypeName(t) #>>> selector, CancellationToken cancellationToken = default)
public static UniTask<<#= ret #>> AverageAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<<#= TypeName(t) #>>> selector, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(selector));
return Average.InvokeAsync(source, selector, cancellationToken);
return Average.AverageAwaitWithCancellationAsync(source, selector, cancellationToken);
}
<# } #>
@@ -72,7 +72,7 @@ namespace Cysharp.Threading.Tasks.Linq
internal static class Average
{
<# foreach(var (t, ret) in types) { #>
public static async UniTask<<#= ret #>> InvokeAsync(IUniTaskAsyncEnumerable<<#= TypeName(t) #>> source, CancellationToken cancellationToken)
public static async UniTask<<#= ret #>> AverageAsync(IUniTaskAsyncEnumerable<<#= TypeName(t) #>> source, CancellationToken cancellationToken)
{
long count = 0;
<#= TypeName(t) #> sum = 0;
@@ -112,7 +112,7 @@ namespace Cysharp.Threading.Tasks.Linq
return <#= CalcResult(t) #>;
}
public static async UniTask<<#= ret #>> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, <#= TypeName(t) #>> selector, CancellationToken cancellationToken)
public static async UniTask<<#= ret #>> AverageAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, <#= TypeName(t) #>> selector, CancellationToken cancellationToken)
{
long count = 0;
<#= TypeName(t) #> sum = 0;
@@ -152,7 +152,7 @@ namespace Cysharp.Threading.Tasks.Linq
return <#= CalcResult(t) #>;
}
public static async UniTask<<#= ret #>> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<<#= TypeName(t) #>>> selector, CancellationToken cancellationToken)
public static async UniTask<<#= ret #>> AverageAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<<#= TypeName(t) #>>> selector, CancellationToken cancellationToken)
{
long count = 0;
<#= TypeName(t) #> sum = 0;
@@ -192,7 +192,7 @@ namespace Cysharp.Threading.Tasks.Linq
return <#= CalcResult(t) #>;
}
public static async UniTask<<#= ret #>> InvokeAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<<#= TypeName(t) #>>> selector, CancellationToken cancellationToken)
public static async UniTask<<#= ret #>> AverageAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<<#= TypeName(t) #>>> selector, CancellationToken cancellationToken)
{
long count = 0;
<#= TypeName(t) #> sum = 0;

View File

@@ -38,10 +38,10 @@ namespace Cysharp.Threading.Tasks.Linq
public IUniTaskAsyncEnumerator<IList<TSource>> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, count, cancellationToken);
return new _Buffer(source, count, cancellationToken);
}
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<IList<TSource>>
sealed class _Buffer : MoveNextSource, IUniTaskAsyncEnumerator<IList<TSource>>
{
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
@@ -56,11 +56,13 @@ namespace Cysharp.Threading.Tasks.Linq
bool completed;
List<TSource> buffer;
public Enumerator(IUniTaskAsyncEnumerable<TSource> source, int count, CancellationToken cancellationToken)
public _Buffer(IUniTaskAsyncEnumerable<TSource> source, int count, CancellationToken cancellationToken)
{
this.source = source;
this.count = count;
this.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(this, 3);
}
public IList<TSource> Current { get; private set; }
@@ -128,7 +130,7 @@ namespace Cysharp.Threading.Tasks.Linq
static void MoveNextCore(object state)
{
var self = (Enumerator)state;
var self = (_Buffer)state;
if (self.TryGetResult(self.awaiter, out var result))
{
@@ -167,6 +169,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync()
{
TaskTracker.RemoveTracking(this);
if (enumerator != null)
{
return enumerator.DisposeAsync();
@@ -191,10 +194,10 @@ namespace Cysharp.Threading.Tasks.Linq
public IUniTaskAsyncEnumerator<IList<TSource>> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, count, skip, cancellationToken);
return new _BufferSkip(source, count, skip, cancellationToken);
}
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<IList<TSource>>
sealed class _BufferSkip : MoveNextSource, IUniTaskAsyncEnumerator<IList<TSource>>
{
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
@@ -211,12 +214,13 @@ namespace Cysharp.Threading.Tasks.Linq
Queue<List<TSource>> buffers;
int index = 0;
public Enumerator(IUniTaskAsyncEnumerable<TSource> source, int count, int skip, CancellationToken cancellationToken)
public _BufferSkip(IUniTaskAsyncEnumerable<TSource> source, int count, int skip, CancellationToken cancellationToken)
{
this.source = source;
this.count = count;
this.skip = skip;
this.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(this, 3);
}
public IList<TSource> Current { get; private set; }
@@ -282,7 +286,7 @@ namespace Cysharp.Threading.Tasks.Linq
static void MoveNextCore(object state)
{
var self = (Enumerator)state;
var self = (_BufferSkip)state;
if (self.TryGetResult(self.awaiter, out var result))
{
@@ -329,6 +333,7 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync()
{
TaskTracker.RemoveTracking(this);
if (enumerator != null)
{
return enumerator.DisposeAsync();

View File

@@ -25,12 +25,12 @@ namespace Cysharp.Threading.Tasks.Linq
public IUniTaskAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, cancellationToken);
return new _Cast(source, cancellationToken);
}
class Enumerator : AsyncEnumeratorBase<object, TResult>
class _Cast : AsyncEnumeratorBase<object, TResult>
{
public Enumerator(IUniTaskAsyncEnumerable<object> source, CancellationToken cancellationToken)
public _Cast(IUniTaskAsyncEnumerable<object> source, CancellationToken cancellationToken)
: base(source, cancellationToken)
{

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,221 @@
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#
var tMax = 15;
Func<int, string> typeArgs = x => string.Join(", ", Enumerable.Range(1, x).Select(x => $"T{x}")) + ", TResult";
Func<int, string> paramArgs = x => string.Join(", ", Enumerable.Range(1, x).Select(x => $"IUniTaskAsyncEnumerable<T{x}> source{x}"));
Func<int, string> parameters = x => string.Join(", ", Enumerable.Range(1, x).Select(x => $"source{x}"));
#>
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Linq
{
public static partial class UniTaskAsyncEnumerable
{
<# for(var i = 2; i <= tMax; i++) { #>
public static IUniTaskAsyncEnumerable<TResult> CombineLatest<<#= typeArgs(i) #>>(this <#= paramArgs(i) #>, Func<<#= typeArgs(i) #>> resultSelector)
{
<# for(var j = 1; j <= i; j++) { #>
Error.ThrowArgumentNullException(source<#= j #>, nameof(source<#= j #>));
<# } #>
Error.ThrowArgumentNullException(resultSelector, nameof(resultSelector));
return new CombineLatest<<#= typeArgs(i) #>>(<#= parameters(i) #>, resultSelector);
}
<# } #>
}
<# for(var i = 2; i <= tMax; i++) { #>
internal class CombineLatest<<#= typeArgs(i) #>> : IUniTaskAsyncEnumerable<TResult>
{
<# for(var j = 1; j <= i; j++) { #>
readonly IUniTaskAsyncEnumerable<T<#= j #>> source<#= j #>;
<# } #>
readonly Func<<#= typeArgs(i) #>> resultSelector;
public CombineLatest(<#= paramArgs(i) #>, Func<<#= typeArgs(i) #>> resultSelector)
{
<# for(var j = 1; j <= i; j++) { #>
this.source<#= j #> = source<#= j #>;
<# } #>
this.resultSelector = resultSelector;
}
public IUniTaskAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new _CombineLatest(<#= parameters(i) #>, resultSelector, cancellationToken);
}
class _CombineLatest : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
{
<# for(var j = 1; j <= i; j++) { #>
static readonly Action<object> Completed<#= j #>Delegate = Completed<#= j #>;
<# } #>
const int CompleteCount = <#= i #>;
<# for(var j = 1; j <= i; j++) { #>
readonly IUniTaskAsyncEnumerable<T<#= j #>> source<#= j #>;
<# } #>
readonly Func<<#= typeArgs(i) #>> resultSelector;
CancellationToken cancellationToken;
<# for(var j = 1; j <= i; j++) { #>
IUniTaskAsyncEnumerator<T<#= j #>> enumerator<#= j #>;
UniTask<bool>.Awaiter awaiter<#= j #>;
bool hasCurrent<#= j #>;
bool running<#= j #>;
T<#= j #> current<#= j #>;
<# } #>
int completedCount;
bool syncRunning;
TResult result;
public _CombineLatest(<#= paramArgs(i) #>, Func<<#= typeArgs(i) #>> resultSelector, CancellationToken cancellationToken)
{
<# for(var j = 1; j <= i; j++) { #>
this.source<#= j #> = source<#= j #>;
<# } #>
this.resultSelector = resultSelector;
this.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(this, 3);
}
public TResult Current => result;
public UniTask<bool> MoveNextAsync()
{
cancellationToken.ThrowIfCancellationRequested();
if (completedCount == CompleteCount) return CompletedTasks.False;
if (enumerator1 == null)
{
<# for(var j = 1; j <= i; j++) { #>
enumerator<#= j #> = source<#= j #>.GetAsyncEnumerator(cancellationToken);
<# } #>
}
completionSource.Reset();
AGAIN:
syncRunning = true;
<# for(var j = 1; j <= i; j++) { #>
if (!running<#= j #>)
{
running<#= j #> = true;
awaiter<#= j #> = enumerator<#= j #>.MoveNextAsync().GetAwaiter();
if (awaiter<#= j #>.IsCompleted)
{
Completed<#= j #>(this);
}
else
{
awaiter<#= j #>.SourceOnCompleted(Completed<#= j #>Delegate, this);
}
}
<# } #>
if (<#= string.Join(" || ", Enumerable.Range(1, i).Select(x => $"!running{x}")) #>)
{
goto AGAIN;
}
syncRunning = false;
return new UniTask<bool>(this, completionSource.Version);
}
<# for(var j = 1; j <= i; j++) { #>
static void Completed<#= j #>(object state)
{
var self = (_CombineLatest)state;
self.running<#= j #> = false;
try
{
if (self.awaiter<#= j #>.GetResult())
{
self.hasCurrent<#= j #> = true;
self.current<#= j #> = self.enumerator<#= j #>.Current;
goto SUCCESS;
}
else
{
self.running<#= j #> = true; // as complete, no more call MoveNextAsync.
if (Interlocked.Increment(ref self.completedCount) == CompleteCount)
{
goto COMPLETE;
}
return;
}
}
catch (Exception ex)
{
self.running<#= j #> = true; // as complete, no more call MoveNextAsync.
self.completedCount = CompleteCount;
self.completionSource.TrySetException(ex);
return;
}
SUCCESS:
if (!self.TrySetResult())
{
if (self.syncRunning) return;
self.running<#= j #> = true; // as complete, no more call MoveNextAsync.
try
{
self.awaiter<#= j #> = self.enumerator<#= j #>.MoveNextAsync().GetAwaiter();
}
catch (Exception ex)
{
self.completedCount = CompleteCount;
self.completionSource.TrySetException(ex);
return;
}
self.awaiter<#= j #>.SourceOnCompleted(Completed<#= j #>Delegate, self);
}
return;
COMPLETE:
self.completionSource.TrySetResult(false);
return;
}
<# } #>
bool TrySetResult()
{
if (<#= string.Join(" && ", Enumerable.Range(1, i).Select(x => $"hasCurrent{x}")) #>)
{
result = resultSelector(<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"current{x}")) #>);
completionSource.TrySetResult(true);
return true;
}
else
{
return false;
}
}
public async UniTask DisposeAsync()
{
TaskTracker.RemoveTracking(this);
<# for(var j = 1; j <= i; j++) { #>
if (enumerator<#= j #> != null)
{
await enumerator<#= j #>.DisposeAsync();
}
<# } #>
}
}
}
<# } #>
}

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