Compare commits

...

97 Commits

Author SHA1 Message Date
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
115 changed files with 10272 additions and 1462 deletions

View File

@@ -26,9 +26,10 @@ jobs:
- run: dotnet test -c Debug ./src/UniTask.NetCoreTests/UniTask.NetCoreTests.csproj
build-unity:
if: "(github.event == 'push' && github.repository_owner == 'Cysharp') || startsWith(github.event.pull_request.head.label, 'Cysharp:')"
strategy:
matrix:
unity: ['2019.3.9f1', '2020.1.0b5']
unity: ["2019.3.9f1", "2020.1.0b5"]
include:
- unity: 2019.3.9f1
license: UNITY_2019_3
@@ -66,8 +67,8 @@ jobs:
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
working-directory: src/UniTask
# Store artifacts.
# Store artifacts.
- uses: actions/upload-artifact@v2
with:
name: UniTask.unitypackage.zip
path: ./src/UniTask/*.unitypackage
path: ./src/UniTask/*.unitypackage

31
.github/workflows/build-docs.yml vendored Normal file
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

View File

@@ -61,8 +61,8 @@ jobs:
# Store artifacts.
- uses: actions/upload-artifact@v2
with:
name: UniTask.unitypackage.zip
path: ./src/UniTask/*.unitypackage
name: UniTask.${{ env.GIT_TAG }}.unitypackage
path: ./src/UniTask/UniTask.${{ env.GIT_TAG }}.unitypackage
create-release:
needs: [build-dotnet, build-unity]

46
.gitignore vendored
View File

@@ -159,3 +159,49 @@ 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

207
README.md
View File

@@ -2,17 +2,20 @@ 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)
Provides an efficient async/await integration to Unity.
Provides an efficient allocation free async/await integration to Unity.
* Struct based `UniTask<T>` and custom AsyncMethodBuilder to achive zero allocation
* All Unity AsyncOperations and Coroutine to awaitable
* PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc...) that enable to replace all coroutine operation
* PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc..) that enable to replace all coroutine operation
* MonoBehaviour Message Events and uGUI Events as awaitable/async-enumerable
* Completely run on Unity's PlayerLoop so don't use thread and run on WebGL, wasm, etc.
* Asynchronous LINQ, with Channel and AsyncReactiveProperty
* TaskTracker window to prevent memory leak
* Highly compatible behaviour with Task/ValueTask/IValueTaskSource
Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await for Unity, with Asynchronous LINQ
](https://medium.com/@neuecc/unitask-v2-zero-allocation-async-await-for-unity-with-asynchronous-linq-1aa9c96aa7dd)
<!-- 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
@@ -29,6 +32,8 @@ Provides an efficient async/await integration to Unity.
- [Awaitable Events](#awaitable-events)
- [Channel](#channel)
- [For Unit Testing](#for-unit-testing)
- [Compare with Standard Task API](#compare-with-standard-task-api)
- [Pooling Configuration](#pooling-configuration)
- [API References](#api-references)
- [UPM Package](#upm-package)
- [Install via git URL](#install-via-git-url)
@@ -52,21 +57,33 @@ async UniTask<string> DemoAsync()
{
// You can await Unity's AsyncObject
var asset = await Resources.LoadAsync<TextAsset>("foo");
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>("foo").WithCancellation(this.GetCancellationTokenOnDestroy());
var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy());
// .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
await SceneManager.LoadSceneAsync("scene2").ToUniTask(Progress.Create<float>(x => Debug.Log(x)));
var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x)));
// await frame-based operation like coroutine
await 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.LastPostLateUpdate);
// 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);
@@ -80,9 +97,14 @@ async UniTask<string> DemoAsync()
// 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)
{
@@ -114,7 +136,7 @@ UniTask feature rely on C# 7.0([task-like custom async method builder feature](h
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.
You can await `AsyncOperation`, `ResourceRequest`, `AssetBundleRequest`, `AssetBundleCreateRequest`, `UnityWebRequestAsyncOperation`, `IEnumerator` and others when `using Cysharp.Threading.Tasks;`.
You can await `AsyncOperation`, `ResourceRequest`, `AssetBundleRequest`, `AssetBundleCreateRequest`, `UnityWebRequestAsyncOperation`, `AsyncGPUReadbackRequest`, `IEnumerator` and others when `using Cysharp.Threading.Tasks;`.
UniTask provides three pattern of extension methods.
@@ -126,6 +148,8 @@ UniTask provides three pattern of extension methods.
`WithCancellation` is a simple version of `ToUniTask`, both returns `UniTask`. Details of cancellation, see: [Cancellation and Exception handling](#cancellation-and-exception-handling) section.
> Note: WithCancellation is returned from native timing of PlayerLoop but ToUniTask is returned from specified PlayerLoopTiming. Details of timing, see: [PlayerLoop](#playerloop) section.
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
@@ -184,7 +208,7 @@ await task;
await task; // NG, throws Exception
```
Store to the class field, you can use `UniTask.Lazy` that gurantee call multipletimes. `.Prevent()` allows for multiple calls (internally cached results). This is useful when multiple calls in a function scope.
Store to the class field, you can use `UniTask.Lazy` that 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
---
@@ -320,9 +344,19 @@ public enum PlayerLoopTiming
It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop.
`PlayerLoopTiming.Update` is similar as `yield return null` in coroutine, but it is called before Update(Update is called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). If change timing to `PlayerLoopTiming.LastUpdate`, called after these Unity's update methods.
`PlayerLoopTiming.Update` is similar as `yield return null` in coroutine, but it is called before Update(Update and uGUI events(button.onClick, etc...) are called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). `PlayerLoopTiming.FixedUpdate` is similar as `WaitForFixedUpdate`, `PlayerLoopTiming.LastPostLateUpdate` is similar as `WaitForEndOfFrame` in coroutine.
`PlayerLoopTiming.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)]`.
@@ -352,7 +386,7 @@ PlayerLoopHelper.Initialize(ref playerLoop);
async void vs async UniTaskVoid
---
`async void` is a standard C# taks system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is a lightweight version of `async UniTask` because it does not have awaitable completion and report error immediately to `UniTaskScheduler.UnobservedTaskException`. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
`async void` is a standard C# task system so 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()
@@ -397,6 +431,18 @@ 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.
}
}
```
UniTaskTracker
---
useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTask Tracker`.
@@ -413,9 +459,11 @@ For debug use, enable tracking and capture stacktrace is useful but it it declin
External Assets
---
In default, UniTask supports DOTween and Addressable(`AsyncOperationHandle` and `AsyncOpereationHandle<T>` as awaitable).
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).
For 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.
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
// sequential
@@ -460,11 +508,19 @@ await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsy
});
```
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`, `SkipUntilCanceled`, `TakeUntilCanceled`, `TakeLast`.
`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`.
@@ -553,18 +609,39 @@ await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
});
```
`AsyncReactiveProperty`, `AsyncReadOnlyReactiveProperty` is UniTask version of UniTask's ReactiveProperty.
`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).
`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()
await button.OnClickAsAsyncEnumerable().ForEachAwaitAsync(async x =>
{
await UniTask.Delay(TimeSpan.FromSeconds(3));
}
});
```
It is useful(prevent double-click) but not useful in sometimes.
@@ -573,10 +650,19 @@ Using `Queue()` method, which will also queue events during asynchronous process
```csharp
// queued message in asynchronous processing
await button.OnClickAsAsyncEnumerable().Queue().ForEachAwaitAsync()
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
@@ -649,6 +735,65 @@ public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
UniTask itself's unit test is written by Unity Test Runner and [Cysharp/RuntimeUnitTestToolkit](https://github.com/Cysharp/RuntimeUnitTestToolkit) to check on CI and IL2CPP working.
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
foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
{
Debug.Log(type + ":" + size);
}
```
> 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.
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).
@@ -667,7 +812,7 @@ After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of gi
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"` to `Packages/manifest.json`.
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#2.0.13`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.0.13`.
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#2.0.22`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.0.22`.
### Install via OpenUPM
@@ -706,6 +851,16 @@ public class ZeroAllocAsyncAwaitInDotNetCore
}
}
}
// 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.
@@ -714,4 +869,4 @@ Utility methods such as WhenAll which is equivalent to UniTask are provided as [
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

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,6 +1,8 @@
#pragma warning disable 0649
using System;
using System.Threading.Tasks;
using System.Threading.Tasks.Sources;
namespace Cysharp.Threading.Tasks
{
@@ -8,23 +10,88 @@ namespace Cysharp.Threading.Tasks
{
public static ValueTask AsValueTask(this in UniTask task)
{
#if NETSTANDARD2_0
return new ValueTask(new UniTaskValueTaskSource(task), 0);
#else
return task;
#endif
}
public static ValueTask<T> AsValueTask<T>(this in UniTask<T> task)
{
#if NETSTANDARD2_0
return new ValueTask<T>(new UniTaskValueTaskSource<T>(task), 0);
#else
return task;
#endif
}
public static UniTask<T> AsUniTask<T>(this ValueTask<T> task, bool useCurrentSynchronizationContext = true)
public static async UniTask<T> AsUniTask<T>(this ValueTask<T> task)
{
// NOTE: get _obj and _token directly for low overhead conversion but not yet implemented.
return task.AsTask().AsUniTask(useCurrentSynchronizationContext);
return await task;
}
public static UniTask AsUniTask(this ValueTask task, bool useCurrentSynchronizationContext = true)
public static async UniTask AsUniTask(this ValueTask task)
{
return task.AsTask().AsUniTask(useCurrentSynchronizationContext);
await task;
}
#if NETSTANDARD2_0
class UniTaskValueTaskSource : IValueTaskSource
{
readonly UniTask task;
readonly UniTask.Awaiter awaiter;
public UniTaskValueTaskSource(UniTask task)
{
this.task = task;
this.awaiter = task.GetAwaiter();
}
public void GetResult(short token)
{
awaiter.GetResult();
}
public ValueTaskSourceStatus GetStatus(short token)
{
return (ValueTaskSourceStatus)task.Status;
}
public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
{
awaiter.SourceOnCompleted(continuation, state);
}
}
class UniTaskValueTaskSource<T> : IValueTaskSource<T>
{
readonly UniTask<T> task;
readonly UniTask<T>.Awaiter awaiter;
public UniTaskValueTaskSource(UniTask<T> task)
{
this.task = task;
this.awaiter = task.GetAwaiter();
}
public T GetResult(short token)
{
return awaiter.GetResult();
}
public ValueTaskSourceStatus GetStatus(short token)
{
return (ValueTaskSourceStatus)task.Status;
}
public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
{
awaiter.SourceOnCompleted(continuation, state);
}
}
#endif
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;netstandard2.1</TargetFrameworks>
<TargetFrameworks>netcoreapp3.1;netstandard2.1;netstandard2.0</TargetFrameworks>
<AssemblyName>UniTask</AssemblyName>
<LangVersion>8.0</LangVersion>
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
@@ -36,6 +36,7 @@
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\DiagnosticsExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\PlayerLoopRunner.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\ContinuationQueue.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityWebRequestExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\CancellationTokenSourceExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\EnumeratorAsyncExtensions.cs;
@@ -44,9 +45,7 @@
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.uGUI.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.MonoBehaviour.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.*;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;
" />
</ItemGroup>

View File

@@ -1,4 +1,6 @@
using Cysharp.Threading.Tasks;
#pragma warning disable CS1998
using Cysharp.Threading.Tasks;
using System.Linq;
using System;
@@ -56,6 +58,24 @@ namespace NetCoreSandbox
}
class Foo
{
public async UniTask MethodFooAsync()
{
await MethodBarAsync();
}
private async UniTask MethodBarAsync()
{
Throw();
}
private void Throw()
{
throw new Exception();
}
}
public struct TestAwaiter : ICriticalNotifyCompletion
{
@@ -277,7 +297,29 @@ namespace NetCoreSandbox
#endif
// await new AllocationCheck().ViaUniTaskVoid();
// AsyncTest().Forget();
var buttonTest = new AsyncReactiveProperty<AsyncUnit>(AsyncUnit.Default);
buttonTest
.Subscribe(async _ =>
{
try
{
await new Foo().MethodFooAsync();
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
}
});
buttonTest.Value = AsyncUnit.Default;
// AsyncTest().Forge
Console.WriteLine("A?");
var a = await new ZeroAllocAsyncAwaitInDotNetCore().NanikaAsync(1, 2);
Console.WriteLine("RET:" + a);
await WhereSelect();
SynchronizationContext.SetSynchronizationContext(new MySyncContext());

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

@@ -112,6 +112,85 @@ namespace NetCoreTests
state.Value.Should().Be(20);
}
[Fact]
public async Task WaitAsyncTest()
{
var rp = new AsyncReactiveProperty<int>(128);
var f = await rp.FirstAsync();
f.Should().Be(128);
{
var t = rp.WaitAsync();
rp.Value = 99;
rp.Value = 100;
var v = await t;
v.Should().Be(99);
}
{
var t = rp.WaitAsync();
rp.Value = 99;
rp.Value = 100;
var v = await t;
v.Should().Be(99);
}
}
[Fact]
public async Task WaitAsyncCancellationTest()
{
var cts = new CancellationTokenSource();
var rp = new AsyncReactiveProperty<int>(128);
var t = rp.WaitAsync(cts.Token);
cts.Cancel();
rp.Value = 99;
rp.Value = 100;
await Assert.ThrowsAsync<OperationCanceledException>(async () => { await t; });
}
[Fact]
public async Task ReadOnlyWaitAsyncTest()
{
var rp = new AsyncReactiveProperty<int>(128);
var rrp = rp.ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
var t = rrp.WaitAsync();
rp.Value = 99;
rp.Value = 100;
var v = await t;
v.Should().Be(99);
}
[Fact]
public async Task ReadOnlyWaitAsyncCancellationTest()
{
var cts = new CancellationTokenSource();
var rp = new AsyncReactiveProperty<int>(128);
var rrp = rp.ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
var t = rrp.WaitAsync(cts.Token);
cts.Cancel();
rp.Value = 99;
rp.Value = 100;
await Assert.ThrowsAsync<OperationCanceledException>(async () => { await t; });
}
}

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

@@ -43,7 +43,7 @@ namespace NetCoreTests.Linq
}
[Fact]
public async Task TakeUntil()
public async Task TakeUntilCanceled()
{
var cts = new CancellationTokenSource();
@@ -72,7 +72,7 @@ namespace NetCoreTests.Linq
}
[Fact]
public async Task SkipUntil()
public async Task SkipUntilCanceled()
{
var cts = new CancellationTokenSource();
@@ -85,7 +85,7 @@ namespace NetCoreTests.Linq
await c;
var foo = await xs;
foo.Should().BeEquivalentTo(new[] { 30, 40 });
foo.Should().BeEquivalentTo(new[] { 20, 30, 40 });
async Task CancelAsync()
{
@@ -102,5 +102,64 @@ namespace NetCoreTests.Linq
}
}
[Fact]
public async Task TakeUntil()
{
var cts = new AsyncReactiveProperty<int>(0);
var rp = new AsyncReactiveProperty<int>(1);
var xs = rp.TakeUntil(cts.WaitAsync()).ToArrayAsync();
var c = CancelAsync();
await c;
var foo = await xs;
foo.Should().BeEquivalentTo(new[] { 1, 10, 20 });
async Task CancelAsync()
{
rp.Value = 10;
await Task.Yield();
rp.Value = 20;
await Task.Yield();
cts.Value = 9999;
rp.Value = 30;
await Task.Yield();
rp.Value = 40;
}
}
[Fact]
public async Task SkipUntil()
{
var cts = new AsyncReactiveProperty<int>(0);
var rp = new AsyncReactiveProperty<int>(1);
var xs = rp.SkipUntil(cts.WaitAsync()).ToArrayAsync();
var c = CancelAsync();
await c;
var foo = await xs;
foo.Should().BeEquivalentTo(new[] { 20, 30, 40 });
async Task CancelAsync()
{
rp.Value = 10;
await Task.Yield();
rp.Value = 20;
await Task.Yield();
cts.Value = 9999;
rp.Value = 30;
await Task.Yield();
rp.Value = 40;
rp.Dispose(); // complete.
}
}
}
}

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

@@ -7,6 +7,7 @@ namespace Cysharp.Threading.Tasks
{
T Value { get; }
IUniTaskAsyncEnumerable<T> WithoutCurrent();
UniTask<T> WaitAsync(CancellationToken cancellationToken = default);
}
public interface IAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>
@@ -69,6 +70,11 @@ namespace Cysharp.Threading.Tasks
return latestValue?.ToString();
}
public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
{
return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
}
static bool isValueType;
static AsyncReactiveProperty()
@@ -76,7 +82,135 @@ namespace Cysharp.Threading.Tasks
isValueType = typeof(T).IsValueType;
}
class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
{
static Action<object> cancellationCallback = CancellationCallback;
static TaskPool<WaitAsyncSource> pool;
WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode { get; set; }
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;
@@ -253,6 +387,11 @@ namespace Cysharp.Threading.Tasks
return latestValue?.ToString();
}
public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
{
return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
}
static bool isValueType;
static ReadOnlyAsyncReactiveProperty()
@@ -260,7 +399,135 @@ namespace Cysharp.Threading.Tasks
isValueType = typeof(T).IsValueType;
}
class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
{
static Action<object> cancellationCallback = CancellationCallback;
static TaskPool<WaitAsyncSource> pool;
WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode { get; set; }
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;

View File

@@ -9,6 +9,56 @@ namespace Cysharp.Threading.Tasks
public static class CancellationTokenExtensions
{
static readonly Action<object> cancellationTokenCallback = Callback;
static readonly Action<object> disposeCallback = DisposeCallback;
public static CancellationToken ToCancellationToken(this UniTask task)
{
var cts = new CancellationTokenSource();
ToCancellationTokenCore(task, cts).Forget();
return cts.Token;
}
public static CancellationToken ToCancellationToken(this UniTask task, CancellationToken linkToken)
{
if (linkToken.IsCancellationRequested)
{
return linkToken;
}
if (!linkToken.CanBeCanceled)
{
return ToCancellationToken(task);
}
var cts = new CancellationTokenSource();
ToCancellationTokenCore(task, cts).Forget();
return CancellationTokenSource.CreateLinkedTokenSource(linkToken).Token;
}
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task)
{
return ToCancellationToken(task.AsUniTask());
}
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task, CancellationToken linkToken)
{
return ToCancellationToken(task.AsUniTask(), linkToken);
}
static async UniTaskVoid ToCancellationTokenCore(UniTask task, CancellationTokenSource cts)
{
try
{
await task;
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
cts.Cancel();
cts.Dispose();
}
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken)
{
@@ -75,6 +125,17 @@ namespace Cysharp.Threading.Tasks
}
}
}
public static CancellationTokenRegistration AddTo(this IDisposable disposable, CancellationToken cancellationToken)
{
return cancellationToken.RegisterWithoutCaptureExecutionContext(disposeCallback, disposable);
}
static void DisposeCallback(object state)
{
var d = (IDisposable)state;
d.Dispose();
}
}
public struct CancellationTokenAwaitable

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

@@ -12,7 +12,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[StructLayout(LayoutKind.Auto)]
public struct AsyncUniTaskMethodBuilder
{
internal IStateMachineRunnerPromise runnerPromise;
IStateMachineRunnerPromise runnerPromise;
Exception ex;
// 1. Static Create method.
@@ -80,7 +80,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{
if (runnerPromise == null)
{
AsyncUniTask<TStateMachine>.SetStateMachine(ref this, ref stateMachine);
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise);
}
awaiter.OnCompleted(runnerPromise.MoveNext);
@@ -96,7 +96,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{
if (runnerPromise == null)
{
AsyncUniTask<TStateMachine>.SetStateMachine(ref this, ref stateMachine);
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise);
}
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
@@ -138,7 +138,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[StructLayout(LayoutKind.Auto)]
public struct AsyncUniTaskMethodBuilder<T>
{
internal IStateMachineRunnerPromise<T> runnerPromise;
IStateMachineRunnerPromise<T> runnerPromise;
Exception ex;
T result;
@@ -211,7 +211,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{
if (runnerPromise == null)
{
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref this, ref stateMachine);
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise);
}
awaiter.OnCompleted(runnerPromise.MoveNext);
@@ -227,7 +227,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{
if (runnerPromise == null)
{
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref this, ref stateMachine);
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise);
}
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);

View File

@@ -12,7 +12,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[StructLayout(LayoutKind.Auto)]
public struct AsyncUniTaskVoidMethodBuilder
{
internal IStateMachineRunner runner;
IStateMachineRunner runner;
// 1. Static Create method.
[DebuggerHidden]
@@ -41,7 +41,12 @@ namespace Cysharp.Threading.Tasks.CompilerServices
// 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;
}
@@ -56,7 +61,12 @@ namespace Cysharp.Threading.Tasks.CompilerServices
// 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;
}
}
@@ -70,7 +80,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{
if (runner == null)
{
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref this, ref stateMachine);
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner);
}
awaiter.OnCompleted(runner.MoveNext);
@@ -86,7 +96,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{
if (runner == null)
{
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref this, ref stateMachine);
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner);
}
awaiter.UnsafeOnCompleted(runner.MoveNext);

View File

@@ -2,6 +2,7 @@
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Linq;
using System.Diagnostics;
using System.Runtime.CompilerServices;
@@ -11,6 +12,10 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{
Action MoveNext { get; }
void Return();
#if ENABLE_IL2CPP
Action ReturnAction { get; }
#endif
}
internal interface IStateMachineRunnerPromise : IUniTaskSource
@@ -29,11 +34,26 @@ namespace Cysharp.Threading.Tasks.CompilerServices
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; }
@@ -41,9 +61,12 @@ namespace Cysharp.Threading.Tasks.CompilerServices
public AsyncUniTaskVoid()
{
MoveNext = Run;
#if ENABLE_IL2CPP
ReturnAction = Return;
#endif
}
public static void SetStateMachine(ref AsyncUniTaskVoidMethodBuilder builder, ref TStateMachine stateMachine)
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunner runnerFieldRef)
{
if (!pool.TryPop(out var result))
{
@@ -51,7 +74,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
}
TaskTracker.TrackActiveTask(result, 3);
builder.runner = result; // set runner before copied.
runnerFieldRef = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
@@ -102,18 +125,23 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{
static TaskPool<AsyncUniTask<TStateMachine>> pool;
TStateMachine stateMachine;
#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 AsyncUniTaskMethodBuilder builder, ref TStateMachine stateMachine)
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise runnerPromiseFieldRef)
{
if (!pool.TryPop(out var result))
{
@@ -121,7 +149,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
}
TaskTracker.TrackActiveTask(result, 3);
builder.runnerPromise = result; // set runner before copied.
runnerPromiseFieldRef = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
@@ -132,6 +160,14 @@ namespace Cysharp.Threading.Tasks.CompilerServices
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine>), () => pool.Size);
}
void Return()
{
TaskTracker.RemoveTracking(this);
core.Reset();
stateMachine = default;
pool.TryPush(this);
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
@@ -177,7 +213,12 @@ namespace Cysharp.Threading.Tasks.CompilerServices
}
finally
{
#if ENABLE_IL2CPP
// workaround for IL2CPP bug.
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate);
#else
TryReturn();
#endif
}
}
@@ -198,14 +239,6 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{
core.OnCompleted(continuation, state, token);
}
~AsyncUniTask()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
internal sealed class AsyncUniTask<TStateMachine, T> : IStateMachineRunnerPromise<T>, IUniTaskSource<T>, ITaskPoolNode<AsyncUniTask<TStateMachine, T>>
@@ -213,18 +246,24 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{
static TaskPool<AsyncUniTask<TStateMachine, T>> pool;
TStateMachine stateMachine;
#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 AsyncUniTaskMethodBuilder<T> builder, ref TStateMachine stateMachine)
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise<T> runnerPromiseFieldRef)
{
if (!pool.TryPop(out var result))
{
@@ -232,7 +271,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
}
TaskTracker.TrackActiveTask(result, 3);
builder.runnerPromise = result; // set runner before copied.
runnerPromiseFieldRef = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
@@ -243,6 +282,14 @@ namespace Cysharp.Threading.Tasks.CompilerServices
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);
@@ -255,6 +302,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Run()
{
// UnityEngine.Debug.Log($"MoveNext State:" + StateMachineUtility.GetState(stateMachine));
stateMachine.MoveNext();
}
@@ -288,7 +336,12 @@ namespace Cysharp.Threading.Tasks.CompilerServices
}
finally
{
#if ENABLE_IL2CPP
// workaround for IL2CPP bug.
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate);
#else
TryReturn();
#endif
}
}
@@ -315,14 +368,6 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{
core.OnCompleted(continuation, state, token);
}
~AsyncUniTask()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
}

View File

@@ -12,10 +12,12 @@ namespace Cysharp.Threading.Tasks
{
public static class EnumeratorAsyncExtensions
{
public static UniTask.Awaiter GetAwaiter(this IEnumerator enumerator)
public static UniTask.Awaiter GetAwaiter<T>(this T enumerator)
where T : IEnumerator
{
Error.ThrowArgumentNullException(enumerator, nameof(enumerator));
return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, CancellationToken.None, out var token), token).GetAwaiter();
var e = (IEnumerator)enumerator;
Error.ThrowArgumentNullException(e, nameof(enumerator));
return new UniTask(EnumeratorPromise.Create(e, PlayerLoopTiming.Update, CancellationToken.None, out var token), token).GetAwaiter();
}
public static UniTask WithCancellation(this IEnumerator enumerator, CancellationToken cancellationToken)
@@ -132,14 +134,6 @@ namespace Cysharp.Threading.Tasks
return pool.TryPush(this);
}
~EnumeratorPromise()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
// Unwrap YieldInstructions
static IEnumerator ConsumeEnumerator(IEnumerator enumerator)

View File

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

View File

@@ -11,7 +11,7 @@ using UnityEngine.ResourceManagement.AsyncOperations;
namespace Cysharp.Threading.Tasks
{
public static class AddressableAsyncExtensions
public static class AddressablesAsyncExtensions
{
#region AsyncOperationHandle
@@ -23,7 +23,7 @@ namespace Cysharp.Threading.Tasks
public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken)
{
if (handle.IsDone) return UniTask.CompletedTask;
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
return new UniTask(AsyncOperationHandleWithCancellationSource.Create(handle, cancellationToken, out var token), token);
}
public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -77,6 +77,124 @@ namespace Cysharp.Threading.Tasks
}
}
sealed class AsyncOperationHandleWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleWithCancellationSource>
{
static TaskPool<AsyncOperationHandleWithCancellationSource> pool;
public AsyncOperationHandleWithCancellationSource NextNode { get; set; }
static AsyncOperationHandleWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperationHandle> continuationAction;
AsyncOperationHandle handle;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<AsyncUnit> core;
AsyncOperationHandleWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource Create(AsyncOperationHandle handle, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleWithCancellationSource();
}
result.handle = handle;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
handle.Completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperationHandle _)
{
handle.Completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (handle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(handle.OperationException);
}
else
{
core.TrySetResult(AsyncUnit.Default);
}
}
}
public void GetResult(short token)
{
core.GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
handle = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
{
static TaskPool<AsyncOperationHandleConfiguredSource> pool;
@@ -188,14 +306,6 @@ namespace Cysharp.Threading.Tasks
cancellationToken = default;
return pool.TryPush(this);
}
~AsyncOperationHandleConfiguredSource()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
#endregion
@@ -210,7 +320,7 @@ namespace Cysharp.Threading.Tasks
public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken)
{
if (handle.IsDone) return UniTask.FromResult(handle.Result);
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
return new UniTask<T>(AsyncOperationHandleWithCancellationSource<T>.Create(handle, cancellationToken, out var token), token);
}
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -265,6 +375,129 @@ namespace Cysharp.Threading.Tasks
}
}
sealed class AsyncOperationHandleWithCancellationSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleWithCancellationSource<T>>
{
static TaskPool<AsyncOperationHandleWithCancellationSource<T>> pool;
public AsyncOperationHandleWithCancellationSource<T> NextNode { get; set; }
static AsyncOperationHandleWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleWithCancellationSource<T>), () => pool.Size);
}
readonly Action<AsyncOperationHandle<T>> continuationAction;
AsyncOperationHandle<T> handle;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<T> core;
AsyncOperationHandleWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleWithCancellationSource<T>();
}
result.handle = handle;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
handle.Completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperationHandle<T> _)
{
handle.Completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (handle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(handle.OperationException);
}
else
{
core.TrySetResult(handle.Result);
}
}
}
public T GetResult(short token)
{
return core.GetResult(token);
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
handle = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
{
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
@@ -380,14 +613,6 @@ namespace Cysharp.Threading.Tasks
cancellationToken = default;
return pool.TryPush(this);
}
~AsyncOperationHandleConfiguredSource()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
#endregion

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

@@ -93,28 +93,30 @@ namespace Cysharp.Threading.Tasks
TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
}
static readonly Action<object> CancellationCallbackDelegate = CancellationCallback;
static readonly TweenCallback EmptyTweenCallback = () => { };
readonly TweenCallback onKillDelegate;
readonly TweenCallback onUpdateDelegate;
Tween tween;
TweenCancelBehaviour cancelBehaviour;
CancellationToken cancellationToken;
bool canceled;
CancellationTokenRegistration cancellationTokenRegistration;
TweenCallback originalUpdateAction;
UniTaskCompletionSourceCore<AsyncUnit> core;
TweenConfiguredSource()
{
onKillDelegate = OnKill;
onUpdateDelegate = OnUpdate;
}
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
DoCancelBeforeCreate(tween, cancelBehaviour);
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
@@ -127,27 +129,25 @@ namespace Cysharp.Threading.Tasks
result.cancelBehaviour = cancelBehaviour;
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
result.originalUpdateAction = tween.onUpdate;
result.canceled = false;
result.RegisterEvent();
if (result.originalUpdateAction == result.onUpdateDelegate)
{
result.originalUpdateAction = null;
}
tween.onUpdate = result.onUpdateDelegate;
tween.onKill = result.onKillDelegate;
TaskTracker.TrackActiveTask(result, 3);
token = result.core.Version;
return result;
}
void RegisterEvent()
{
if (cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(CancellationCallbackDelegate, this);
}
tween.OnKill(onKillDelegate);
}
void OnKill()
{
cancellationTokenRegistration.Dispose();
if (canceled)
{
core.TrySetCanceled(cancellationToken);
@@ -158,44 +158,84 @@ namespace Cysharp.Threading.Tasks
}
}
static void CancellationCallback(object state)
void OnUpdate()
{
var self = (TweenConfiguredSource)state;
originalUpdateAction?.Invoke();
switch (self.cancelBehaviour)
if (!cancellationToken.IsCancellationRequested)
{
return;
}
switch (this.cancelBehaviour)
{
case TweenCancelBehaviour.Kill:
default:
self.tween.Kill(false);
this.tween.Kill(false);
break;
case TweenCancelBehaviour.KillAndCancelAwait:
self.canceled = true;
self.tween.Kill(false);
this.canceled = true;
this.tween.Kill(false);
break;
case TweenCancelBehaviour.KillWithCompleteCallback:
self.tween.Kill(true);
this.tween.Kill(true);
break;
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
self.canceled = true;
self.tween.Kill(true);
this.canceled = true;
this.tween.Kill(true);
break;
case TweenCancelBehaviour.Complete:
self.tween.Complete(false);
this.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteAndCancelAwait:
self.canceled = true;
self.tween.Complete(false);
this.canceled = true;
this.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
self.tween.Complete(true);
this.tween.Complete(true);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
self.canceled = true;
self.tween.Complete(true);
this.canceled = true;
this.tween.Complete(true);
break;
case TweenCancelBehaviour.CancelAwait:
this.tween.onKill = EmptyTweenCallback; // replace to empty(avoid callback after Canceled(instance is returned to pool.)
this.core.TrySetCanceled(this.cancellationToken);
break;
}
}
static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour)
{
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:
self.tween.onKill = EmptyTweenCallback; // replace to empty(avoid callback after Caceled(instance is returned to pool.)
self.core.TrySetCanceled(self.cancellationToken);
break;
}
}
@@ -212,7 +252,6 @@ namespace Cysharp.Threading.Tasks
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
@@ -232,18 +271,13 @@ namespace Cysharp.Threading.Tasks
{
TaskTracker.RemoveTracking(this);
core.Reset();
tween.onUpdate = originalUpdateAction;
tween.onKill = null;
tween = default;
cancellationToken = default;
originalUpdateAction = default;
return pool.TryPush(this);
}
~TweenConfiguredSource()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
}

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,68 @@
#if UNITASK_TEXTMESHPRO_SUPPORT
using System;
using System.Threading;
using TMPro;
using UnityEngine.Events;
namespace Cysharp.Threading.Tasks
{
public static partial class TextMeshProAsyncExtensions
{
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();
}
}
}
}
}
#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

@@ -19,7 +19,7 @@ namespace Cysharp.Threading.Tasks
// similar as IValueTaskSource
public interface IUniTaskSource
#if !UNITY_2018_3_OR_NEWER
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
: System.Threading.Tasks.Sources.IValueTaskSource
#pragma warning disable CS0108
#endif
@@ -30,7 +30,7 @@ namespace Cysharp.Threading.Tasks
UniTaskStatus UnsafeGetStatus(); // only for debug use.
#if !UNITY_2018_3_OR_NEWER
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
#pragma warning restore CS0108
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(short token)
@@ -53,13 +53,13 @@ namespace Cysharp.Threading.Tasks
}
public interface IUniTaskSource<out T> : IUniTaskSource
#if !UNITY_2018_3_OR_NEWER
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
, System.Threading.Tasks.Sources.IValueTaskSource<T>
#endif
{
new T GetResult(short token);
#if !UNITY_2018_3_OR_NEWER
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
new public UniTaskStatus GetStatus(short token)
{

View File

@@ -9,7 +9,7 @@ namespace Cysharp.Threading.Tasks.Internal
{
const int MaxArrayLength = 0X7FEFFFFF;
const int InitialSize = 16;
readonly PlayerLoopTiming timing;
SpinLock gate = new SpinLock();
@@ -173,7 +173,14 @@ namespace Cysharp.Threading.Tasks.Internal
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

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

@@ -0,0 +1,187 @@
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Linq
{
public static partial class UniTaskAsyncEnumerable
{
public static IUniTaskAsyncEnumerable<TSource> SkipUntil<TSource>(this IUniTaskAsyncEnumerable<TSource> source, UniTask other)
{
Error.ThrowArgumentNullException(source, nameof(source));
return new SkipUntil<TSource>(source, other, null);
}
public static IUniTaskAsyncEnumerable<TSource> SkipUntil<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<CancellationToken, UniTask> other)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(other));
return new SkipUntil<TSource>(source, default, other);
}
}
internal sealed class SkipUntil<TSource> : IUniTaskAsyncEnumerable<TSource>
{
readonly IUniTaskAsyncEnumerable<TSource> source;
readonly UniTask other;
readonly Func<CancellationToken, UniTask> other2;
public SkipUntil(IUniTaskAsyncEnumerable<TSource> source, UniTask other, Func<CancellationToken, UniTask> other2)
{
this.source = source;
this.other = other;
this.other2 = other2;
}
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
if (other2 != null)
{
return new _SkipUntil(source, this.other2(cancellationToken), cancellationToken);
}
else
{
return new _SkipUntil(source, this.other, cancellationToken);
}
}
sealed class _SkipUntil : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
{
static readonly Action<object> CancelDelegate1 = OnCanceled1;
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
readonly IUniTaskAsyncEnumerable<TSource> source;
CancellationToken cancellationToken1;
bool completed;
CancellationTokenRegistration cancellationTokenRegistration1;
IUniTaskAsyncEnumerator<TSource> enumerator;
UniTask<bool>.Awaiter awaiter;
bool continueNext;
Exception exception;
public _SkipUntil(IUniTaskAsyncEnumerable<TSource> source, UniTask other, CancellationToken cancellationToken1)
{
this.source = source;
this.cancellationToken1 = cancellationToken1;
if (cancellationToken1.CanBeCanceled)
{
this.cancellationTokenRegistration1 = cancellationToken1.RegisterWithoutCaptureExecutionContext(CancelDelegate1, this);
}
TaskTracker.TrackActiveTask(this, 3);
RunOther(other).Forget();
}
public TSource Current { get; private set; }
public UniTask<bool> MoveNextAsync()
{
if (exception != null)
{
return UniTask.FromException<bool>(exception);
}
if (cancellationToken1.IsCancellationRequested)
{
return UniTask.FromCanceled<bool>(cancellationToken1);
}
if (enumerator == null)
{
enumerator = source.GetAsyncEnumerator(cancellationToken1);
}
completionSource.Reset();
if (completed)
{
SourceMoveNext();
}
return new UniTask<bool>(this, completionSource.Version);
}
void SourceMoveNext()
{
try
{
LOOP:
awaiter = enumerator.MoveNextAsync().GetAwaiter();
if (awaiter.IsCompleted)
{
continueNext = true;
MoveNextCore(this);
if (continueNext)
{
continueNext = false;
goto LOOP;
}
}
else
{
awaiter.SourceOnCompleted(MoveNextCoreDelegate, this);
}
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
}
}
static void MoveNextCore(object state)
{
var self = (_SkipUntil)state;
if (self.TryGetResult(self.awaiter, out var result))
{
if (result)
{
self.Current = self.enumerator.Current;
self.completionSource.TrySetResult(true);
if (self.continueNext)
{
self.SourceMoveNext();
}
}
else
{
self.completionSource.TrySetResult(false);
}
}
}
async UniTaskVoid RunOther(UniTask other)
{
try
{
await other;
completed = true;
SourceMoveNext();
}
catch (Exception ex)
{
exception = ex;
completionSource.TrySetException(ex);
}
}
static void OnCanceled1(object state)
{
var self = (_SkipUntil)state;
self.completionSource.TrySetCanceled(self.cancellationToken1);
}
public UniTask DisposeAsync()
{
TaskTracker.RemoveTracking(this);
cancellationTokenRegistration1.Dispose();
if (enumerator != null)
{
return enumerator.DisposeAsync();
}
return default;
}
}
}
}

View File

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

View File

@@ -32,13 +32,17 @@ namespace Cysharp.Threading.Tasks.Linq
sealed class _SkipUntilCanceled : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
{
static readonly Action<object> CancelDelegate1 = OnCanceled1;
static readonly Action<object> CancelDelegate2 = OnCanceled2;
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
readonly IUniTaskAsyncEnumerable<TSource> source;
CancellationToken cancellationToken1;
CancellationToken cancellationToken2;
CancellationTokenRegistration cancellationTokenRegistration1;
CancellationTokenRegistration cancellationTokenRegistration2;
bool isCanceled;
int isCanceled;
IUniTaskAsyncEnumerator<TSource> enumerator;
UniTask<bool>.Awaiter awaiter;
bool continueNext;
@@ -48,6 +52,14 @@ namespace Cysharp.Threading.Tasks.Linq
this.source = source;
this.cancellationToken1 = cancellationToken1;
this.cancellationToken2 = cancellationToken2;
if (cancellationToken1.CanBeCanceled)
{
this.cancellationTokenRegistration1 = cancellationToken1.RegisterWithoutCaptureExecutionContext(CancelDelegate1, this);
}
if (cancellationToken1 != cancellationToken2 && cancellationToken2.CanBeCanceled)
{
this.cancellationTokenRegistration2 = cancellationToken2.RegisterWithoutCaptureExecutionContext(CancelDelegate2, this);
}
TaskTracker.TrackActiveTask(this, 3);
}
@@ -55,15 +67,18 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask<bool> MoveNextAsync()
{
if (cancellationToken1.IsCancellationRequested) isCanceled = true;
if (cancellationToken2.IsCancellationRequested) isCanceled = true;
if (enumerator == null)
{
if (cancellationToken1.IsCancellationRequested) isCanceled = 1;
if (cancellationToken2.IsCancellationRequested) isCanceled = 1;
enumerator = source.GetAsyncEnumerator(cancellationToken2); // use only AsyncEnumerator provided token.
}
completionSource.Reset();
SourceMoveNext();
if (isCanceled != 0)
{
SourceMoveNext();
}
return new UniTask<bool>(this, completionSource.Version);
}
@@ -102,25 +117,11 @@ namespace Cysharp.Threading.Tasks.Linq
{
if (result)
{
AGAIN:
if (self.isCanceled)
self.Current = self.enumerator.Current;
self.completionSource.TrySetResult(true);
if (self.continueNext)
{
self.continueNext = false;
self.Current = self.enumerator.Current;
self.completionSource.TrySetResult(true);
}
else
{
if (self.cancellationToken1.IsCancellationRequested) self.isCanceled = true;
if (self.cancellationToken2.IsCancellationRequested) self.isCanceled = true;
if (self.isCanceled) goto AGAIN;
if (!self.continueNext)
{
self.SourceMoveNext();
}
self.SourceMoveNext();
}
}
else
@@ -130,9 +131,37 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
static void OnCanceled1(object state)
{
var self = (_SkipUntilCanceled)state;
if (self.isCanceled == 0)
{
if (Interlocked.Increment(ref self.isCanceled) == 1)
{
self.cancellationTokenRegistration2.Dispose();
self.SourceMoveNext();
}
}
}
static void OnCanceled2(object state)
{
var self = (_SkipUntilCanceled)state;
if (self.isCanceled == 0)
{
if (Interlocked.Increment(ref self.isCanceled) == 1)
{
self.cancellationTokenRegistration2.Dispose();
self.SourceMoveNext();
}
}
}
public UniTask DisposeAsync()
{
TaskTracker.RemoveTracking(this);
cancellationTokenRegistration1.Dispose();
cancellationTokenRegistration2.Dispose();
if (enumerator != null)
{
return enumerator.DisposeAsync();

View File

@@ -0,0 +1,313 @@
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Threading;
using Subscribes = Cysharp.Threading.Tasks.Linq.Subscribe;
namespace Cysharp.Threading.Tasks.Linq
{
public static partial class UniTaskAsyncEnumerable
{
// OnNext
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> action)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(action, nameof(action));
var cts = new CancellationTokenDisposable();
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
return cts;
}
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> action)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(action, nameof(action));
var cts = new CancellationTokenDisposable();
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
return cts;
}
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTaskVoid> action)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(action, nameof(action));
var cts = new CancellationTokenDisposable();
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
return cts;
}
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> action, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(action, nameof(action));
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
}
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> action, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(action, nameof(action));
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
}
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTaskVoid> action, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(action, nameof(action));
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
}
// OnNext, OnError
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> onError)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onError, nameof(onError));
var cts = new CancellationTokenDisposable();
Subscribes.SubscribeCore(source, onNext, onError, Subscribes.NopCompleted, cts.Token).Forget();
return cts;
}
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action<Exception> onError)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onError, nameof(onError));
var cts = new CancellationTokenDisposable();
Subscribes.SubscribeCore(source, onNext, onError, Subscribes.NopCompleted, cts.Token).Forget();
return cts;
}
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> onError, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onError, nameof(onError));
Subscribes.SubscribeCore(source, onNext, onError, Subscribes.NopCompleted, cancellationToken).Forget();
}
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action<Exception> onError, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onError, nameof(onError));
Subscribes.SubscribeCore(source, onNext, onError, Subscribes.NopCompleted, cancellationToken).Forget();
}
// OnNext, OnCompleted
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action onCompleted)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
var cts = new CancellationTokenDisposable();
Subscribes.SubscribeCore(source, onNext, Subscribes.NopError, onCompleted, cts.Token).Forget();
return cts;
}
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action onCompleted)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
var cts = new CancellationTokenDisposable();
Subscribes.SubscribeCore(source, onNext, Subscribes.NopError, onCompleted, cts.Token).Forget();
return cts;
}
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action onCompleted, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
Subscribes.SubscribeCore(source, onNext, Subscribes.NopError, onCompleted, cancellationToken).Forget();
}
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action onCompleted, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
Subscribes.SubscribeCore(source, onNext, Subscribes.NopError, onCompleted, cancellationToken).Forget();
}
// IObserver
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, IObserver<TSource> observer)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(observer, nameof(observer));
var cts = new CancellationTokenDisposable();
Subscribes.SubscribeCore(source, observer, cts.Token).Forget();
return cts;
}
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, IObserver<TSource> observer, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(observer, nameof(observer));
Subscribes.SubscribeCore(source, observer, cancellationToken).Forget();
}
}
internal sealed class CancellationTokenDisposable : IDisposable
{
readonly CancellationTokenSource cts = new CancellationTokenSource();
public CancellationToken Token => cts.Token;
public void Dispose()
{
if (!cts.IsCancellationRequested)
{
cts.Cancel();
}
}
}
internal static class Subscribe
{
public static readonly Action<Exception> NopError = _ => { };
public static readonly Action NopCompleted = () => { };
public static async UniTaskVoid SubscribeCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
{
while (await e.MoveNextAsync())
{
onNext(e.Current);
}
onCompleted();
}
catch (Exception ex)
{
if (onError == NopError)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
return;
}
if (ex is OperationCanceledException) return;
onError(ex);
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
public static async UniTaskVoid SubscribeCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
{
while (await e.MoveNextAsync())
{
onNext(e.Current).Forget();
}
onCompleted();
}
catch (Exception ex)
{
if (onError == NopError)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
return;
}
if (ex is OperationCanceledException) return;
onError(ex);
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
public static async UniTaskVoid SubscribeCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTaskVoid> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
{
while (await e.MoveNextAsync())
{
onNext(e.Current, cancellationToken).Forget();
}
onCompleted();
}
catch (Exception ex)
{
if (onError == NopError)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
return;
}
if (ex is OperationCanceledException) return;
onError(ex);
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
public static async UniTaskVoid SubscribeCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, IObserver<TSource> observer, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
{
while (await e.MoveNextAsync())
{
observer.OnNext(e.Current);
}
observer.OnCompleted();
}
catch (Exception ex)
{
if (ex is OperationCanceledException) return;
observer.OnError(ex);
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,190 @@
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Linq
{
public static partial class UniTaskAsyncEnumerable
{
public static IUniTaskAsyncEnumerable<TSource> TakeUntil<TSource>(this IUniTaskAsyncEnumerable<TSource> source, UniTask other)
{
Error.ThrowArgumentNullException(source, nameof(source));
return new TakeUntil<TSource>(source, other, null);
}
public static IUniTaskAsyncEnumerable<TSource> TakeUntil<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<CancellationToken, UniTask> other)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(source, nameof(other));
return new TakeUntil<TSource>(source, default, other);
}
}
internal sealed class TakeUntil<TSource> : IUniTaskAsyncEnumerable<TSource>
{
readonly IUniTaskAsyncEnumerable<TSource> source;
readonly UniTask other;
readonly Func<CancellationToken, UniTask> other2;
public TakeUntil(IUniTaskAsyncEnumerable<TSource> source, UniTask other, Func<CancellationToken, UniTask> other2)
{
this.source = source;
this.other = other;
this.other2 = other2;
}
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
if (other2 != null)
{
return new _TakeUntil(source, this.other2(cancellationToken), cancellationToken);
}
else
{
return new _TakeUntil(source, this.other, cancellationToken);
}
}
sealed class _TakeUntil : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
{
static readonly Action<object> CancelDelegate1 = OnCanceled1;
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
readonly IUniTaskAsyncEnumerable<TSource> source;
CancellationToken cancellationToken1;
CancellationTokenRegistration cancellationTokenRegistration1;
bool completed;
Exception exception;
IUniTaskAsyncEnumerator<TSource> enumerator;
UniTask<bool>.Awaiter awaiter;
public _TakeUntil(IUniTaskAsyncEnumerable<TSource> source, UniTask other, CancellationToken cancellationToken1)
{
this.source = source;
this.cancellationToken1 = cancellationToken1;
if (cancellationToken1.CanBeCanceled)
{
this.cancellationTokenRegistration1 = cancellationToken1.RegisterWithoutCaptureExecutionContext(CancelDelegate1, this);
}
TaskTracker.TrackActiveTask(this, 3);
RunOther(other).Forget();
}
public TSource Current { get; private set; }
public UniTask<bool> MoveNextAsync()
{
if (completed)
{
return CompletedTasks.False;
}
if (exception != null)
{
return UniTask.FromException<bool>(exception);
}
if (cancellationToken1.IsCancellationRequested)
{
return UniTask.FromCanceled<bool>(cancellationToken1);
}
if (enumerator == null)
{
enumerator = source.GetAsyncEnumerator(cancellationToken1);
}
completionSource.Reset();
SourceMoveNext();
return new UniTask<bool>(this, completionSource.Version);
}
void SourceMoveNext()
{
try
{
awaiter = enumerator.MoveNextAsync().GetAwaiter();
if (awaiter.IsCompleted)
{
MoveNextCore(this);
}
else
{
awaiter.SourceOnCompleted(MoveNextCoreDelegate, this);
}
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
}
}
static void MoveNextCore(object state)
{
var self = (_TakeUntil)state;
if (self.TryGetResult(self.awaiter, out var result))
{
if (result)
{
if (self.exception != null)
{
self.completionSource.TrySetException(self.exception);
}
else if (self.cancellationToken1.IsCancellationRequested)
{
self.completionSource.TrySetCanceled(self.cancellationToken1);
}
else
{
self.Current = self.enumerator.Current;
self.completionSource.TrySetResult(true);
}
}
else
{
self.completionSource.TrySetResult(false);
}
}
}
async UniTaskVoid RunOther(UniTask other)
{
try
{
await other;
completed = true;
completionSource.TrySetResult(false);
}
catch (Exception ex)
{
exception = ex;
completionSource.TrySetException(ex);
}
}
static void OnCanceled1(object state)
{
var self = (_TakeUntil)state;
self.completionSource.TrySetCanceled(self.cancellationToken1);
}
public UniTask DisposeAsync()
{
TaskTracker.RemoveTracking(this);
cancellationTokenRegistration1.Dispose();
if (enumerator != null)
{
return enumerator.DisposeAsync();
}
return default;
}
}
}
}

View File

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

View File

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

View File

@@ -248,15 +248,18 @@ namespace Cysharp.Threading.Tasks.Linq
return current;
}
if (queuedResult.Count != 0)
lock (queuedResult)
{
current = queuedResult.Dequeue();
useCachedCurrent = true;
return current;
}
else
{
return default; // undefined.
if (queuedResult.Count != 0)
{
current = queuedResult.Dequeue();
useCachedCurrent = true;
return current;
}
else
{
return default; // undefined.
}
}
}
}

View File

@@ -34,6 +34,7 @@ namespace Cysharp.Threading.Tasks.Linq
public _EveryUpdate(PlayerLoopTiming updateTiming, CancellationToken cancellationToken)
{
this.updateTiming = updateTiming;
this.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(this, 2);
PlayerLoopHelper.AddAction(updateTiming, this);

View File

@@ -1,5 +1,6 @@
using System;
using System.Threading;
using UnityEngine;
namespace Cysharp.Threading.Tasks.Linq
{
@@ -22,16 +23,34 @@ namespace Cysharp.Threading.Tasks.Linq
public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update)
{
if (dueTimeFrameCount < 0)
{
throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. dueTimeFrameCount:" + dueTimeFrameCount);
}
return new TimerFrame(dueTimeFrameCount, null, updateTiming);
}
public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, int periodFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update)
{
if (dueTimeFrameCount < 0)
{
throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. dueTimeFrameCount:" + dueTimeFrameCount);
}
if (periodFrameCount < 0)
{
throw new ArgumentOutOfRangeException("Delay does not allow minus periodFrameCount. periodFrameCount:" + dueTimeFrameCount);
}
return new TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming);
}
public static IUniTaskAsyncEnumerable<AsyncUnit> IntervalFrame(int intervalFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update)
{
if (intervalFrameCount < 0)
{
throw new ArgumentOutOfRangeException("Delay does not allow minus intervalFrameCount. intervalFrameCount:" + intervalFrameCount);
}
return new TimerFrame(intervalFrameCount, intervalFrameCount, updateTiming);
}
}
@@ -64,6 +83,7 @@ namespace Cysharp.Threading.Tasks.Linq
readonly bool ignoreTimeScale;
CancellationToken cancellationToken;
int initialFrame;
float elapsed;
bool dueTimePhase;
bool completed;
@@ -80,9 +100,11 @@ namespace Cysharp.Threading.Tasks.Linq
if (this.period <= 0) this.period = 1;
}
this.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
this.dueTimePhase = true;
this.updateTiming = updateTiming;
this.ignoreTimeScale = ignoreTimeScale;
this.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(this, 2);
PlayerLoopHelper.AddAction(updateTiming, this);
}
@@ -119,9 +141,19 @@ namespace Cysharp.Threading.Tasks.Linq
return false;
}
elapsed += (ignoreTimeScale) ? UnityEngine.Time.unscaledDeltaTime : UnityEngine.Time.deltaTime;
if (dueTimePhase)
{
if (elapsed == 0)
{
// skip in initial frame.
if (initialFrame == Time.frameCount)
{
return true;
}
}
elapsed += (ignoreTimeScale) ? UnityEngine.Time.unscaledDeltaTime : UnityEngine.Time.deltaTime;
if (elapsed >= dueTime)
{
dueTimePhase = false;
@@ -137,6 +169,8 @@ namespace Cysharp.Threading.Tasks.Linq
return false;
}
elapsed += (ignoreTimeScale) ? UnityEngine.Time.unscaledDeltaTime : UnityEngine.Time.deltaTime;
if (elapsed >= period)
{
completionSource.TrySetResult(true);
@@ -172,6 +206,7 @@ namespace Cysharp.Threading.Tasks.Linq
readonly int? periodFrameCount;
CancellationToken cancellationToken;
int initialFrame;
int currentFrame;
bool dueTimePhase;
bool completed;
@@ -185,9 +220,11 @@ namespace Cysharp.Threading.Tasks.Linq
if (periodFrameCount <= 0) periodFrameCount = 1;
}
this.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
this.dueTimePhase = true;
this.dueTimeFrameCount = dueTimeFrameCount;
this.periodFrameCount = periodFrameCount;
this.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(this, 2);
PlayerLoopHelper.AddAction(updateTiming, this);
@@ -228,11 +265,30 @@ namespace Cysharp.Threading.Tasks.Linq
if (dueTimePhase)
{
if (currentFrame++ >= dueTimeFrameCount)
if (currentFrame == 0)
{
if (dueTimeFrameCount == 0)
{
dueTimePhase = false;
completionSource.TrySetResult(true);
return true;
}
// skip in initial frame.
if (initialFrame == Time.frameCount)
{
return true;
}
}
if (++currentFrame >= dueTimeFrameCount)
{
dueTimePhase = false;
completionSource.TrySetResult(true);
}
else
{
}
}
else
{

View File

@@ -92,8 +92,12 @@ namespace Cysharp.Threading.Tasks
{
public static SynchronizationContext UnitySynchronizationContext => unitySynchronizationContetext;
public static int MainThreadId => mainThreadId;
internal static string ApplicationDataPath => applicationDataPath;
public static bool IsMainThread => Thread.CurrentThread.ManagedThreadId == mainThreadId;
static int mainThreadId;
static string applicationDataPath;
static SynchronizationContext unitySynchronizationContetext;
static ContinuationQueue[] yielders;
static PlayerLoopRunner[] runners;
@@ -177,6 +181,11 @@ namespace Cysharp.Threading.Tasks
// capture default(unity) sync-context.
unitySynchronizationContetext = SynchronizationContext.Current;
mainThreadId = Thread.CurrentThread.ManagedThreadId;
try
{
applicationDataPath = Application.dataPath;
}
catch { }
#if UNITY_EDITOR && UNITY_2019_3_OR_NEWER
// When domain reload is disabled, re-initialization is required when entering play mode;

View File

@@ -1,13 +1,23 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using Cysharp.Threading.Tasks.Internal;
using UnityEngine;
namespace Cysharp.Threading.Tasks
{
public enum DelayType
{
/// <summary>use Time.deltaTime.</summary>
DeltaTime,
/// <summary>Ignore timescale, use Time.unscaledDeltaTime.</summary>
UnscaledDeltaTime,
/// <summary>use Stopwatch.GetTimestamp().</summary>
Realtime
}
public partial struct UniTask
{
public static YieldAwaitable Yield(PlayerLoopTiming timing = PlayerLoopTiming.Update)
@@ -21,6 +31,46 @@ namespace Cysharp.Threading.Tasks
return new UniTask(YieldPromise.Create(timing, cancellationToken, out var token), token);
}
/// <summary>
/// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary>
public static UniTask NextFrame(PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default)
{
return new UniTask(NextFramePromise.Create(timing, cancellationToken, out var token), token);
}
/// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate).
/// </summary>
public static YieldAwaitable WaitForEndOfFrame()
{
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
}
/// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken).
/// </summary>
public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken)
{
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken);
}
/// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate).
/// </summary>
public static YieldAwaitable WaitForFixedUpdate()
{
return UniTask.Yield(PlayerLoopTiming.FixedUpdate);
}
/// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken).
/// </summary>
public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken)
{
return UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken);
}
public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
if (delayFrameCount < 0)
@@ -34,26 +84,44 @@ namespace Cysharp.Threading.Tasks
public static UniTask Delay(int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
if (delayTimeSpan < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException("Delay does not allow minus millisecondsDelay. millisecondsDelay:" + millisecondsDelay);
}
return (ignoreTimeScale)
? new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token)
: new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out token), token);
return Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cancellationToken);
}
public static UniTask Delay(TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
var delayType = ignoreTimeScale ? DelayType.UnscaledDeltaTime : DelayType.DeltaTime;
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken);
}
public static UniTask Delay(int millisecondsDelay, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken);
}
public static UniTask Delay(TimeSpan delayTimeSpan, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
if (delayTimeSpan < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException("Delay does not allow minus delayTimeSpan. delayTimeSpan:" + delayTimeSpan);
}
return (ignoreTimeScale)
? new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token)
: new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out token), token);
switch (delayType)
{
case DelayType.UnscaledDeltaTime:
{
return new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
}
case DelayType.Realtime:
{
return new UniTask(DelayRealtimePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
}
case DelayType.DeltaTime:
default:
{
return new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
}
}
}
sealed class YieldPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<YieldPromise>
@@ -142,13 +210,99 @@ namespace Cysharp.Threading.Tasks
cancellationToken = default;
return pool.TryPush(this);
}
}
~YieldPromise()
sealed class NextFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<NextFramePromise>
{
static TaskPool<NextFramePromise> pool;
public NextFramePromise NextNode { get; set; }
static NextFramePromise()
{
if (TryReturn())
TaskPool.RegisterSizeGetter(typeof(NextFramePromise), () => pool.Size);
}
int frameCount;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<AsyncUnit> core;
NextFramePromise()
{
}
public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
GC.ReRegisterForFinalize(this);
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new NextFramePromise();
}
result.frameCount = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
TryReturn();
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
if (frameCount == Time.frameCount)
{
return true;
}
core.TrySetResult(AsyncUnit.Default);
return false;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
cancellationToken = default;
return pool.TryPush(this);
}
}
@@ -162,11 +316,12 @@ namespace Cysharp.Threading.Tasks
TaskPool.RegisterSizeGetter(typeof(DelayFramePromise), () => pool.Size);
}
int initialFrame;
int delayFrameCount;
CancellationToken cancellationToken;
int currentFrameCount;
UniTaskCompletionSourceCore<object> core;
UniTaskCompletionSourceCore<AsyncUnit> core;
DelayFramePromise()
{
@@ -186,6 +341,7 @@ namespace Cysharp.Threading.Tasks
result.delayFrameCount = delayFrameCount;
result.cancellationToken = cancellationToken;
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
TaskTracker.TrackActiveTask(result, 3);
@@ -230,13 +386,27 @@ namespace Cysharp.Threading.Tasks
return false;
}
if (currentFrameCount == delayFrameCount)
if (currentFrameCount == 0)
{
core.TrySetResult(null);
if (delayFrameCount == 0) // same as Yield
{
core.TrySetResult(AsyncUnit.Default);
return false;
}
// skip in initial frame.
if (initialFrame == Time.frameCount)
{
return true;
}
}
if (++currentFrameCount >= delayFrameCount)
{
core.TrySetResult(AsyncUnit.Default);
return false;
}
currentFrameCount++;
return true;
}
@@ -249,14 +419,6 @@ namespace Cysharp.Threading.Tasks
cancellationToken = default;
return pool.TryPush(this);
}
~DelayFramePromise()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
sealed class DelayPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayPromise>
@@ -269,7 +431,8 @@ namespace Cysharp.Threading.Tasks
TaskPool.RegisterSizeGetter(typeof(DelayPromise), () => pool.Size);
}
float delayFrameTimeSpan;
int initialFrame;
float delayTimeSpan;
float elapsed;
CancellationToken cancellationToken;
@@ -279,7 +442,7 @@ namespace Cysharp.Threading.Tasks
{
}
public static IUniTaskSource Create(TimeSpan delayFrameTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
@@ -292,8 +455,9 @@ namespace Cysharp.Threading.Tasks
}
result.elapsed = 0.0f;
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
result.delayTimeSpan = (float)delayTimeSpan.TotalSeconds;
result.cancellationToken = cancellationToken;
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
TaskTracker.TrackActiveTask(result, 3);
@@ -338,8 +502,16 @@ namespace Cysharp.Threading.Tasks
return false;
}
if (elapsed == 0.0f)
{
if (initialFrame == Time.frameCount)
{
return true;
}
}
elapsed += Time.deltaTime;
if (elapsed >= delayFrameTimeSpan)
if (elapsed >= delayTimeSpan)
{
core.TrySetResult(null);
return false;
@@ -352,19 +524,11 @@ namespace Cysharp.Threading.Tasks
{
TaskTracker.RemoveTracking(this);
core.Reset();
delayFrameTimeSpan = default;
delayTimeSpan = default;
elapsed = default;
cancellationToken = default;
return pool.TryPush(this);
}
~DelayPromise()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
sealed class DelayIgnoreTimeScalePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayIgnoreTimeScalePromise>
@@ -379,6 +543,7 @@ namespace Cysharp.Threading.Tasks
float delayFrameTimeSpan;
float elapsed;
int initialFrame;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<object> core;
@@ -401,6 +566,7 @@ namespace Cysharp.Threading.Tasks
result.elapsed = 0.0f;
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
@@ -446,6 +612,14 @@ namespace Cysharp.Threading.Tasks
return false;
}
if (elapsed == 0.0f)
{
if (initialFrame == Time.frameCount)
{
return true;
}
}
elapsed += Time.unscaledDeltaTime;
if (elapsed >= delayFrameTimeSpan)
{
@@ -465,13 +639,103 @@ namespace Cysharp.Threading.Tasks
cancellationToken = default;
return pool.TryPush(this);
}
}
~DelayIgnoreTimeScalePromise()
sealed class DelayRealtimePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayRealtimePromise>
{
static TaskPool<DelayRealtimePromise> pool;
public DelayRealtimePromise NextNode { get; set; }
static DelayRealtimePromise()
{
if (TryReturn())
TaskPool.RegisterSizeGetter(typeof(DelayRealtimePromise), () => pool.Size);
}
long delayTimeSpanTicks;
ValueStopwatch stopwatch;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<AsyncUnit> core;
DelayRealtimePromise()
{
}
public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
GC.ReRegisterForFinalize(this);
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new DelayRealtimePromise();
}
result.stopwatch = ValueStopwatch.StartNew();
result.delayTimeSpanTicks = delayTimeSpan.Ticks;
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
TryReturn();
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
if (stopwatch.ElapsedTicks >= delayTimeSpanTicks)
{
core.TrySetResult(AsyncUnit.Default);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
stopwatch = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
}

View File

@@ -3,6 +3,7 @@
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Threading;
namespace Cysharp.Threading.Tasks
@@ -11,10 +12,7 @@ namespace Cysharp.Threading.Tasks
{
static readonly UniTask CanceledUniTask = new Func<UniTask>(() =>
{
var promise = new UniTaskCompletionSource();
promise.TrySetCanceled(CancellationToken.None);
promise.MarkHandled();
return promise.Task;
return new UniTask(new CanceledResultSource(CancellationToken.None), 0);
})();
static class CanceledUniTaskCache<T>
@@ -23,10 +21,7 @@ namespace Cysharp.Threading.Tasks
static CanceledUniTaskCache()
{
var promise = new UniTaskCompletionSource<T>();
promise.TrySetCanceled(CancellationToken.None);
promise.MarkHandled();
Task = promise.Task;
Task = new UniTask<T>(new CanceledResultSource<T>(CancellationToken.None), 0);
}
}
@@ -34,18 +29,22 @@ namespace Cysharp.Threading.Tasks
public static UniTask FromException(Exception ex)
{
var promise = new UniTaskCompletionSource();
promise.TrySetException(ex);
promise.MarkHandled();
return promise.Task;
if (ex is OperationCanceledException oce)
{
return FromCanceled(oce.CancellationToken);
}
return new UniTask(new ExceptionResultSource(ex), 0);
}
public static UniTask<T> FromException<T>(Exception ex)
{
var promise = new UniTaskCompletionSource<T>();
promise.TrySetException(ex);
promise.MarkHandled();
return promise.Task;
if (ex is OperationCanceledException oce)
{
return FromCanceled<T>(oce.CancellationToken);
}
return new UniTask<T>(new ExceptionResultSource<T>(ex), 0);
}
public static UniTask<T> FromResult<T>(T value)
@@ -61,10 +60,7 @@ namespace Cysharp.Threading.Tasks
}
else
{
var promise = new UniTaskCompletionSource();
promise.TrySetCanceled(cancellationToken);
promise.MarkHandled();
return promise.Task;
return new UniTask(new CanceledResultSource(cancellationToken), 0);
}
}
@@ -76,10 +72,7 @@ namespace Cysharp.Threading.Tasks
}
else
{
var promise = new UniTaskCompletionSource<T>();
promise.TrySetCanceled(cancellationToken);
promise.MarkHandled();
return promise.Task;
return new UniTask<T>(new CanceledResultSource<T>(cancellationToken), 0);
}
}
@@ -182,6 +175,137 @@ namespace Cysharp.Threading.Tasks
return new UniTask<T>(new DeferPromise<T>(factory), 0);
}
sealed class ExceptionResultSource : IUniTaskSource
{
readonly ExceptionDispatchInfo exception;
public ExceptionResultSource(Exception exception)
{
this.exception = ExceptionDispatchInfo.Capture(exception);
}
public void GetResult(short token)
{
exception.Throw();
}
public UniTaskStatus GetStatus(short token)
{
return UniTaskStatus.Faulted;
}
public UniTaskStatus UnsafeGetStatus()
{
return UniTaskStatus.Faulted;
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
continuation(state);
}
}
sealed class ExceptionResultSource<T> : IUniTaskSource<T>
{
readonly ExceptionDispatchInfo exception;
public ExceptionResultSource(Exception exception)
{
this.exception = ExceptionDispatchInfo.Capture(exception);
}
public T GetResult(short token)
{
exception.Throw();
return default;
}
void IUniTaskSource.GetResult(short token)
{
exception.Throw();
}
public UniTaskStatus GetStatus(short token)
{
return UniTaskStatus.Faulted;
}
public UniTaskStatus UnsafeGetStatus()
{
return UniTaskStatus.Faulted;
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
continuation(state);
}
}
sealed class CanceledResultSource : IUniTaskSource
{
readonly CancellationToken cancellationToken;
public CanceledResultSource(CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
}
public void GetResult(short token)
{
throw new OperationCanceledException(cancellationToken);
}
public UniTaskStatus GetStatus(short token)
{
return UniTaskStatus.Canceled;
}
public UniTaskStatus UnsafeGetStatus()
{
return UniTaskStatus.Canceled;
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
continuation(state);
}
}
sealed class CanceledResultSource<T> : IUniTaskSource<T>
{
readonly CancellationToken cancellationToken;
public CanceledResultSource(CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
}
public T GetResult(short token)
{
throw new OperationCanceledException(cancellationToken);
}
void IUniTaskSource.GetResult(short token)
{
throw new OperationCanceledException(cancellationToken);
}
public UniTaskStatus GetStatus(short token)
{
return UniTaskStatus.Canceled;
}
public UniTaskStatus UnsafeGetStatus()
{
return UniTaskStatus.Canceled;
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
continuation(state);
}
}
sealed class DeferPromise : IUniTaskSource
{
Func<UniTask> factory;
@@ -204,7 +328,7 @@ namespace Cysharp.Threading.Tasks
if (f == null) throw new InvalidOperationException("Can't call twice.");
task = f();
awaiter = f().GetAwaiter();
awaiter = task.GetAwaiter();
return task.Status;
}
@@ -246,7 +370,7 @@ namespace Cysharp.Threading.Tasks
if (f == null) throw new InvalidOperationException("Can't call twice.");
task = f();
awaiter = f().GetAwaiter();
awaiter = task.GetAwaiter();
return task.Status;
}

View File

@@ -50,6 +50,50 @@ namespace Cysharp.Threading.Tasks
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask Run(Func<UniTask> action, bool configureAwait = true)
{
await UniTask.SwitchToThreadPool();
if (configureAwait)
{
try
{
await action();
}
finally
{
await UniTask.Yield();
}
}
else
{
await action();
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask Run(Func<object, UniTask> action, object state, bool configureAwait = true)
{
await UniTask.SwitchToThreadPool();
if (configureAwait)
{
try
{
await action(state);
}
finally
{
await UniTask.Yield();
}
}
else
{
await action(state);
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> Run<T>(Func<T> func, bool configureAwait = true)
{
@@ -71,6 +115,27 @@ namespace Cysharp.Threading.Tasks
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> Run<T>(Func<UniTask<T>> func, bool configureAwait = true)
{
await UniTask.SwitchToThreadPool();
if (configureAwait)
{
try
{
return await func();
}
finally
{
await UniTask.Yield();
}
}
else
{
return await func();
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true)
{
@@ -92,6 +157,28 @@ namespace Cysharp.Threading.Tasks
return func(state);
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> Run<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true)
{
await UniTask.SwitchToThreadPool();
if (configureAwait)
{
try
{
return await func(state);
}
finally
{
await UniTask.Yield();
}
}
else
{
return await func(state);
}
}
}
}

View File

@@ -15,33 +15,41 @@ namespace Cysharp.Threading.Tasks
/// <summary>
/// If running on mainthread, do nothing. Otherwise, same as UniTask.Yield(PlayerLoopTiming.Update).
/// </summary>
public static SwitchToMainThreadAwaitable SwitchToMainThread()
public static SwitchToMainThreadAwaitable SwitchToMainThread(CancellationToken cancellationToken = default)
{
return new SwitchToMainThreadAwaitable(PlayerLoopTiming.Update);
return new SwitchToMainThreadAwaitable(PlayerLoopTiming.Update, cancellationToken);
}
/// <summary>
/// If running on mainthread, do nothing. Otherwise, same as UniTask.Yield(timing).
/// </summary>
public static SwitchToMainThreadAwaitable SwitchToMainThread(PlayerLoopTiming timing)
public static SwitchToMainThreadAwaitable SwitchToMainThread(PlayerLoopTiming timing, CancellationToken cancellationToken = default)
{
return new SwitchToMainThreadAwaitable(timing);
return new SwitchToMainThreadAwaitable(timing, cancellationToken);
}
/// <summary>
/// Return to mainthread(same as await SwitchToMainThread) after using scope is closed.
/// </summary>
public static ReturnToMainThread ReturnToMainThread()
public static ReturnToMainThread ReturnToMainThread(CancellationToken cancellationToken = default)
{
return new ReturnToMainThread(PlayerLoopTiming.Update);
return new ReturnToMainThread(PlayerLoopTiming.Update, cancellationToken);
}
/// <summary>
/// Return to mainthread(same as await SwitchToMainThread) after using scope is closed.
/// </summary>
public static ReturnToMainThread ReturnToMainThread(PlayerLoopTiming timing)
public static ReturnToMainThread ReturnToMainThread(PlayerLoopTiming timing, CancellationToken cancellationToken = default)
{
return new ReturnToMainThread(timing);
return new ReturnToMainThread(timing, cancellationToken);
}
/// <summary>
/// Queue the action to PlayerLoop.
/// </summary>
public static void Post(Action action, PlayerLoopTiming timing = PlayerLoopTiming.Update)
{
PlayerLoopHelper.AddContinuation(timing, action);
}
#endif
@@ -59,20 +67,20 @@ namespace Cysharp.Threading.Tasks
return new SwitchToTaskPoolAwaitable();
}
public static SwitchToSynchronizationContextAwaitable SwitchToSynchronizationContext(SynchronizationContext synchronizationContext)
public static SwitchToSynchronizationContextAwaitable SwitchToSynchronizationContext(SynchronizationContext synchronizationContext, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(synchronizationContext, nameof(synchronizationContext));
return new SwitchToSynchronizationContextAwaitable(synchronizationContext);
return new SwitchToSynchronizationContextAwaitable(synchronizationContext, cancellationToken);
}
public static ReturnToSynchronizationContext ReturnToSynchronizationContext(SynchronizationContext synchronizationContext)
public static ReturnToSynchronizationContext ReturnToSynchronizationContext(SynchronizationContext synchronizationContext, CancellationToken cancellationToken = default)
{
return new ReturnToSynchronizationContext(synchronizationContext, false);
return new ReturnToSynchronizationContext(synchronizationContext, false, cancellationToken);
}
public static ReturnToSynchronizationContext ReturnToCurrentSynchronizationContext(bool dontPostWhenSameContext = true)
public static ReturnToSynchronizationContext ReturnToCurrentSynchronizationContext(bool dontPostWhenSameContext = true, CancellationToken cancellationToken = default)
{
return new ReturnToSynchronizationContext(SynchronizationContext.Current, dontPostWhenSameContext);
return new ReturnToSynchronizationContext(SynchronizationContext.Current, dontPostWhenSameContext, cancellationToken);
}
}
@@ -81,21 +89,25 @@ namespace Cysharp.Threading.Tasks
public struct SwitchToMainThreadAwaitable
{
readonly PlayerLoopTiming playerLoopTiming;
readonly CancellationToken cancellationToken;
public SwitchToMainThreadAwaitable(PlayerLoopTiming playerLoopTiming)
public SwitchToMainThreadAwaitable(PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken)
{
this.playerLoopTiming = playerLoopTiming;
this.cancellationToken = cancellationToken;
}
public Awaiter GetAwaiter() => new Awaiter(playerLoopTiming);
public Awaiter GetAwaiter() => new Awaiter(playerLoopTiming, cancellationToken);
public struct Awaiter : ICriticalNotifyCompletion
{
readonly PlayerLoopTiming playerLoopTiming;
readonly CancellationToken cancellationToken;
public Awaiter(PlayerLoopTiming playerLoopTiming)
public Awaiter(PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken)
{
this.playerLoopTiming = playerLoopTiming;
this.cancellationToken = cancellationToken;
}
public bool IsCompleted
@@ -114,7 +126,7 @@ namespace Cysharp.Threading.Tasks
}
}
public void GetResult() { }
public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); }
public void OnCompleted(Action continuation)
{
@@ -131,31 +143,35 @@ namespace Cysharp.Threading.Tasks
public struct ReturnToMainThread
{
readonly PlayerLoopTiming playerLoopTiming;
readonly CancellationToken cancellationToken;
public ReturnToMainThread(PlayerLoopTiming playerLoopTiming)
public ReturnToMainThread(PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken)
{
this.playerLoopTiming = playerLoopTiming;
this.cancellationToken = cancellationToken;
}
public Awaiter DisposeAsync()
{
return new Awaiter(playerLoopTiming); // run immediate.
return new Awaiter(playerLoopTiming, cancellationToken); // run immediate.
}
public readonly struct Awaiter : ICriticalNotifyCompletion
{
readonly PlayerLoopTiming timing;
readonly CancellationToken cancellationToken;
public Awaiter(PlayerLoopTiming timing)
public Awaiter(PlayerLoopTiming timing, CancellationToken cancellationToken)
{
this.timing = timing;
this.cancellationToken = cancellationToken;
}
public Awaiter GetAwaiter() => this;
public bool IsCompleted => PlayerLoopHelper.MainThreadId == System.Threading.Thread.CurrentThread.ManagedThreadId;
public void GetResult() { }
public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); }
public void OnCompleted(Action continuation)
{
@@ -277,26 +293,30 @@ namespace Cysharp.Threading.Tasks
public struct SwitchToSynchronizationContextAwaitable
{
readonly SynchronizationContext synchronizationContext;
readonly CancellationToken cancellationToken;
public SwitchToSynchronizationContextAwaitable(SynchronizationContext synchronizationContext)
public SwitchToSynchronizationContextAwaitable(SynchronizationContext synchronizationContext, CancellationToken cancellationToken)
{
this.synchronizationContext = synchronizationContext;
this.cancellationToken = cancellationToken;
}
public Awaiter GetAwaiter() => new Awaiter(synchronizationContext);
public Awaiter GetAwaiter() => new Awaiter(synchronizationContext, cancellationToken);
public struct Awaiter : ICriticalNotifyCompletion
{
static readonly SendOrPostCallback switchToCallback = Callback;
readonly SynchronizationContext synchronizationContext;
readonly CancellationToken cancellationToken;
public Awaiter(SynchronizationContext synchronizationContext)
public Awaiter(SynchronizationContext synchronizationContext, CancellationToken cancellationToken)
{
this.synchronizationContext = synchronizationContext;
this.cancellationToken = cancellationToken;
}
public bool IsCompleted => false;
public void GetResult() { }
public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); }
public void OnCompleted(Action continuation)
{
@@ -320,16 +340,18 @@ namespace Cysharp.Threading.Tasks
{
readonly SynchronizationContext syncContext;
readonly bool dontPostWhenSameContext;
readonly CancellationToken cancellationToken;
public ReturnToSynchronizationContext(SynchronizationContext syncContext, bool dontPostWhenSameContext)
public ReturnToSynchronizationContext(SynchronizationContext syncContext, bool dontPostWhenSameContext, CancellationToken cancellationToken)
{
this.syncContext = syncContext;
this.dontPostWhenSameContext = dontPostWhenSameContext;
this.cancellationToken = cancellationToken;
}
public Awaiter DisposeAsync()
{
return new Awaiter(syncContext, dontPostWhenSameContext);
return new Awaiter(syncContext, dontPostWhenSameContext, cancellationToken);
}
public struct Awaiter : ICriticalNotifyCompletion
@@ -338,11 +360,13 @@ namespace Cysharp.Threading.Tasks
readonly SynchronizationContext synchronizationContext;
readonly bool dontPostWhenSameContext;
readonly CancellationToken cancellationToken;
public Awaiter(SynchronizationContext synchronizationContext, bool dontPostWhenSameContext)
public Awaiter(SynchronizationContext synchronizationContext, bool dontPostWhenSameContext, CancellationToken cancellationToken)
{
this.synchronizationContext = synchronizationContext;
this.dontPostWhenSameContext = dontPostWhenSameContext;
this.cancellationToken = cancellationToken;
}
public Awaiter GetAwaiter() => this;
@@ -351,6 +375,8 @@ namespace Cysharp.Threading.Tasks
{
get
{
if (!dontPostWhenSameContext) return false;
var current = SynchronizationContext.Current;
if (current == synchronizationContext)
{
@@ -363,7 +389,7 @@ namespace Cysharp.Threading.Tasks
}
}
public void GetResult() { }
public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); }
public void OnCompleted(Action continuation)
{

View File

@@ -137,14 +137,6 @@ namespace Cysharp.Threading.Tasks
cancellationToken = default;
return pool.TryPush(this);
}
~WaitUntilPromise()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
sealed class WaitWhilePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise>
@@ -249,14 +241,6 @@ namespace Cysharp.Threading.Tasks
cancellationToken = default;
return pool.TryPush(this);
}
~WaitWhilePromise()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
sealed class WaitUntilCanceledPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilCanceledPromise>
@@ -344,14 +328,6 @@ namespace Cysharp.Threading.Tasks
cancellationToken = default;
return pool.TryPush(this);
}
~WaitUntilCanceledPromise()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
// where T : UnityEngine.Object, can not add constraint
@@ -475,14 +451,6 @@ namespace Cysharp.Threading.Tasks
cancellationToken = default;
return pool.TryPush(this);
}
~WaitUntilValueChangedUnityObjectPromise()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
sealed class WaitUntilValueChangedStandardObjectPromise<T, U> : IUniTaskSource<U>, IPlayerLoopItem, ITaskPoolNode<WaitUntilValueChangedStandardObjectPromise<T, U>>
@@ -604,14 +572,6 @@ namespace Cysharp.Threading.Tasks
cancellationToken = default;
return pool.TryPush(this);
}
~WaitUntilValueChangedStandardObjectPromise()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
}
}

View File

@@ -131,11 +131,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
public static UniTask<(T1, T2, T3)> WhenAll<T1, T2, T3>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3)
@@ -295,11 +290,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
public static UniTask<(T1, T2, T3, T4)> WhenAll<T1, T2, T3, T4>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4)
@@ -495,11 +485,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
public static UniTask<(T1, T2, T3, T4, T5)> WhenAll<T1, T2, T3, T4, T5>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5)
@@ -731,11 +716,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
public static UniTask<(T1, T2, T3, T4, T5, T6)> WhenAll<T1, T2, T3, T4, T5, T6>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6)
@@ -1003,11 +983,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
public static UniTask<(T1, T2, T3, T4, T5, T6, T7)> WhenAll<T1, T2, T3, T4, T5, T6, T7>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7)
@@ -1311,11 +1286,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
public static UniTask<(T1, T2, T3, T4, T5, T6, T7, T8)> WhenAll<T1, T2, T3, T4, T5, T6, T7, T8>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8)
@@ -1655,11 +1625,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
public static UniTask<(T1, T2, T3, T4, T5, T6, T7, T8, T9)> WhenAll<T1, T2, T3, T4, T5, T6, T7, T8, T9>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8, UniTask<T9> task9)
@@ -2035,11 +2000,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
public static UniTask<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)> WhenAll<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8, UniTask<T9> task9, UniTask<T10> task10)
@@ -2451,11 +2411,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
public static UniTask<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)> WhenAll<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8, UniTask<T9> task9, UniTask<T10> task10, UniTask<T11> task11)
@@ -2903,11 +2858,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
public static UniTask<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)> WhenAll<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8, UniTask<T9> task9, UniTask<T10> task10, UniTask<T11> task11, UniTask<T12> task12)
@@ -3391,11 +3341,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
public static UniTask<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13)> WhenAll<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8, UniTask<T9> task9, UniTask<T10> task10, UniTask<T11> task11, UniTask<T12> task12, UniTask<T13> task13)
@@ -3915,11 +3860,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
public static UniTask<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14)> WhenAll<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8, UniTask<T9> task9, UniTask<T10> task10, UniTask<T11> task11, UniTask<T12> task12, UniTask<T13> task13, UniTask<T14> task14)
@@ -4475,11 +4415,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
public static UniTask<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15)> WhenAll<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8, UniTask<T9> task9, UniTask<T10> task10, UniTask<T11> task11, UniTask<T12> task12, UniTask<T13> task13, UniTask<T14> task14, UniTask<T15> task15)
@@ -5071,11 +5006,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
}
}

View File

@@ -45,7 +45,7 @@ namespace Cysharp.Threading.Tasks
public WhenAllPromise(<#= args #>)
{
TaskTracker2.TrackActiveTask(this, 3);
TaskTracker.TrackActiveTask(this, 3);
this.completedCount = 0;
<# for(var j = 1; j <= i; j++) { #>
@@ -92,7 +92,7 @@ namespace Cysharp.Threading.Tasks
public (<#= t #>) GetResult(short token)
{
TaskTracker2.RemoveTracking(this);
TaskTracker.RemoveTracking(this);
GC.SuppressFinalize(this);
return core.GetResult(token);
}
@@ -116,11 +116,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
<# } #>
}

View File

@@ -142,11 +142,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
sealed class WhenAllPromise : IUniTaskSource
@@ -237,11 +232,6 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
~WhenAllPromise()
{
core.Reset();
}
}
}
}

View File

@@ -127,11 +127,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
public static UniTask<(int winArgumentIndex, T1 result1, T2 result2, T3 result3)> WhenAny<T1, T2, T3>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3)
@@ -289,11 +284,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
public static UniTask<(int winArgumentIndex, T1 result1, T2 result2, T3 result3, T4 result4)> WhenAny<T1, T2, T3, T4>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4)
@@ -488,11 +478,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
public static UniTask<(int winArgumentIndex, T1 result1, T2 result2, T3 result3, T4 result4, T5 result5)> WhenAny<T1, T2, T3, T4, T5>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5)
@@ -724,11 +709,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
public static UniTask<(int winArgumentIndex, T1 result1, T2 result2, T3 result3, T4 result4, T5 result5, T6 result6)> WhenAny<T1, T2, T3, T4, T5, T6>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6)
@@ -997,11 +977,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
public static UniTask<(int winArgumentIndex, T1 result1, T2 result2, T3 result3, T4 result4, T5 result5, T6 result6, T7 result7)> WhenAny<T1, T2, T3, T4, T5, T6, T7>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7)
@@ -1307,11 +1282,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
public static UniTask<(int winArgumentIndex, T1 result1, T2 result2, T3 result3, T4 result4, T5 result5, T6 result6, T7 result7, T8 result8)> WhenAny<T1, T2, T3, T4, T5, T6, T7, T8>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8)
@@ -1654,11 +1624,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
public static UniTask<(int winArgumentIndex, T1 result1, T2 result2, T3 result3, T4 result4, T5 result5, T6 result6, T7 result7, T8 result8, T9 result9)> WhenAny<T1, T2, T3, T4, T5, T6, T7, T8, T9>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8, UniTask<T9> task9)
@@ -2038,11 +2003,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
public static UniTask<(int winArgumentIndex, T1 result1, T2 result2, T3 result3, T4 result4, T5 result5, T6 result6, T7 result7, T8 result8, T9 result9, T10 result10)> WhenAny<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8, UniTask<T9> task9, UniTask<T10> task10)
@@ -2459,11 +2419,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
public static UniTask<(int winArgumentIndex, T1 result1, T2 result2, T3 result3, T4 result4, T5 result5, T6 result6, T7 result7, T8 result8, T9 result9, T10 result10, T11 result11)> WhenAny<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8, UniTask<T9> task9, UniTask<T10> task10, UniTask<T11> task11)
@@ -2917,11 +2872,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
public static UniTask<(int winArgumentIndex, T1 result1, T2 result2, T3 result3, T4 result4, T5 result5, T6 result6, T7 result7, T8 result8, T9 result9, T10 result10, T11 result11, T12 result12)> WhenAny<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8, UniTask<T9> task9, UniTask<T10> task10, UniTask<T11> task11, UniTask<T12> task12)
@@ -3412,11 +3362,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
public static UniTask<(int winArgumentIndex, T1 result1, T2 result2, T3 result3, T4 result4, T5 result5, T6 result6, T7 result7, T8 result8, T9 result9, T10 result10, T11 result11, T12 result12, T13 result13)> WhenAny<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8, UniTask<T9> task9, UniTask<T10> task10, UniTask<T11> task11, UniTask<T12> task12, UniTask<T13> task13)
@@ -3944,11 +3889,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
public static UniTask<(int winArgumentIndex, T1 result1, T2 result2, T3 result3, T4 result4, T5 result5, T6 result6, T7 result7, T8 result8, T9 result9, T10 result10, T11 result11, T12 result12, T13 result13, T14 result14)> WhenAny<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8, UniTask<T9> task9, UniTask<T10> task10, UniTask<T11> task11, UniTask<T12> task12, UniTask<T13> task13, UniTask<T14> task14)
@@ -4513,11 +4453,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
public static UniTask<(int winArgumentIndex, T1 result1, T2 result2, T3 result3, T4 result4, T5 result5, T6 result6, T7 result7, T8 result8, T9 result9, T10 result10, T11 result11, T12 result12, T13 result13, T14 result14, T15 result15)> WhenAny<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(UniTask<T1> task1, UniTask<T2> task2, UniTask<T3> task3, UniTask<T4> task4, UniTask<T5> task5, UniTask<T6> task6, UniTask<T7> task7, UniTask<T8> task8, UniTask<T9> task9, UniTask<T10> task10, UniTask<T11> task11, UniTask<T12> task12, UniTask<T13> task13, UniTask<T14> task14, UniTask<T15> task15)
@@ -5119,11 +5054,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
}

View File

@@ -37,7 +37,7 @@ namespace Cysharp.Threading.Tasks
public WhenAnyPromise(<#= args #>)
{
TaskTracker2.TrackActiveTask(this, 3);
TaskTracker.TrackActiveTask(this, 3);
this.completedCount = 0;
<# for(var j = 1; j <= i; j++) { #>
@@ -86,7 +86,7 @@ namespace Cysharp.Threading.Tasks
public (int, <#= tBool #>) GetResult(short token)
{
TaskTracker2.RemoveTracking(this);
TaskTracker.RemoveTracking(this);
GC.SuppressFinalize(this);
return core.GetResult(token);
}
@@ -110,11 +110,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
<# } #>

View File

@@ -171,11 +171,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyLRPromise()
{
core.Reset();
}
}
@@ -268,11 +263,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
sealed class WhenAnyPromise : IUniTaskSource<int>
@@ -363,11 +353,6 @@ namespace Cysharp.Threading.Tasks
{
GetResult(token);
}
~WhenAnyPromise()
{
core.Reset();
}
}
}
}

View File

@@ -1,9 +1,6 @@
{
"name": "UniTask",
"references": [
"Unity.ResourceManager",
"Unity.TextMeshPro",
"DOTween.Modules"
],
"includePlatforms": [],
"excludePlatforms": [],
@@ -13,21 +10,6 @@
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.addressables",
"expression": "",
"define": "UNITASK_ADDRESSABLE_SUPPORT"
},
{
"name": "com.unity.textmeshpro",
"expression": "",
"define": "UNITASK_TEXTMESHPRO_SUPPORT"
},
{
"name": "com.demigiant.dotween",
"expression": "",
"define": "UNITASK_DOTWEEN_SUPPORT"
}
],
"noEngineReferences": false
}

View File

@@ -78,7 +78,11 @@ namespace Cysharp.Threading.Tasks
return default;
}
#if NETSTANDARD2_0
return self.AsValueTask();
#else
return new System.Threading.Tasks.ValueTask(self.source, self.token);
#endif
}
#endif
@@ -439,7 +443,11 @@ namespace Cysharp.Threading.Tasks
return new System.Threading.Tasks.ValueTask<T>(self.result);
}
#if NETSTANDARD2_0
return self.AsValueTask();
#else
return new System.Threading.Tasks.ValueTask<T>(self.source, self.token);
#endif
}
#endif

View File

@@ -1,6 +1,7 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
@@ -38,13 +39,42 @@ namespace Cysharp.Threading.Tasks
{
}
internal class ExceptionHolder
{
ExceptionDispatchInfo exception;
bool calledGet = false;
public ExceptionHolder(ExceptionDispatchInfo exception)
{
this.exception = exception;
}
public ExceptionDispatchInfo GetException()
{
if (!calledGet)
{
calledGet = true;
GC.SuppressFinalize(this);
}
return exception;
}
~ExceptionHolder()
{
if (!calledGet)
{
UniTaskScheduler.PublishUnobservedTaskException(exception.SourceException);
}
}
}
[StructLayout(LayoutKind.Auto)]
public struct UniTaskCompletionSourceCore<TResult>
{
// Struct Size: TResult + (8 + 2 + 1 + 1 + 8 + 8)
TResult result;
object error; // ExceptionDispatchInfo or OperationCanceledException
object error; // ExceptionHolder or OperationCanceledException
short version;
bool hasUnhandledError;
int completedCount; // 0: completed == false
@@ -78,9 +108,9 @@ namespace Cysharp.Threading.Tasks
{
UniTaskScheduler.PublishUnobservedTaskException(oc);
}
else if (error is ExceptionDispatchInfo ei)
else if (error is ExceptionHolder e)
{
UniTaskScheduler.PublishUnobservedTaskException(ei.SourceException);
UniTaskScheduler.PublishUnobservedTaskException(e.GetException().SourceException);
}
}
catch
@@ -129,7 +159,7 @@ namespace Cysharp.Threading.Tasks
}
else
{
this.error = ExceptionDispatchInfo.Capture(error);
this.error = new ExceptionHolder(ExceptionDispatchInfo.Capture(error));
}
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
@@ -209,9 +239,9 @@ namespace Cysharp.Threading.Tasks
{
throw oce;
}
else if (error is ExceptionDispatchInfo edi)
else if (error is ExceptionHolder eh)
{
edi.Throw();
eh.GetException().Throw();
}
throw new InvalidOperationException("Critical: invalid exception type was held.");
@@ -286,95 +316,6 @@ namespace Cysharp.Threading.Tasks
}
}
public class UniTaskCompletionSource : IUniTaskSource, IPromise
{
UniTaskCompletionSourceCore<AsyncUnit> core;
bool handled = false;
public UniTaskCompletionSource()
{
TaskTracker.TrackActiveTask(this, 2);
}
[DebuggerHidden]
internal void MarkHandled()
{
if (!handled)
{
handled = true;
core.MarkHandled();
TaskTracker.RemoveTracking(this);
}
}
public UniTask Task
{
[DebuggerHidden]
get
{
return new UniTask(this, core.Version);
}
}
[DebuggerHidden]
public void Reset()
{
// Reset, re-active tracker
handled = false;
TaskTracker.TrackActiveTask(this, 2);
core.Reset();
}
[DebuggerHidden]
public bool TrySetResult()
{
return core.TrySetResult(AsyncUnit.Default);
}
[DebuggerHidden]
public bool TrySetCanceled(CancellationToken cancellationToken = default)
{
return core.TrySetCanceled(cancellationToken);
}
[DebuggerHidden]
public bool TrySetException(Exception exception)
{
return core.TrySetException(exception);
}
[DebuggerHidden]
public void GetResult(short token)
{
MarkHandled();
core.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);
}
~UniTaskCompletionSource()
{
// clear error information.
core.Reset();
}
}
public class AutoResetUniTaskCompletionSource : IUniTaskSource, ITaskPoolNode<AutoResetUniTaskCompletionSource>, IPromise
{
static TaskPool<AutoResetUniTaskCompletionSource> pool;
@@ -495,110 +436,6 @@ namespace Cysharp.Threading.Tasks
core.Reset();
return pool.TryPush(this);
}
~AutoResetUniTaskCompletionSource()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
return;
}
}
}
public class UniTaskCompletionSource<T> : IUniTaskSource<T>, IPromise<T>
{
UniTaskCompletionSourceCore<T> core;
bool handled = false;
[DebuggerHidden]
public UniTaskCompletionSource()
{
TaskTracker.TrackActiveTask(this, 2);
}
[DebuggerHidden]
internal void MarkHandled()
{
if (!handled)
{
handled = true;
core.MarkHandled();
TaskTracker.RemoveTracking(this);
}
}
[DebuggerHidden]
public UniTask<T> Task
{
get
{
return new UniTask<T>(this, core.Version);
}
}
[DebuggerHidden]
public void Reset()
{
handled = false;
core.Reset();
TaskTracker.TrackActiveTask(this, 2);
}
[DebuggerHidden]
public bool TrySetResult(T result)
{
return core.TrySetResult(result);
}
[DebuggerHidden]
public bool TrySetCanceled(CancellationToken cancellationToken = default)
{
return core.TrySetCanceled(cancellationToken);
}
[DebuggerHidden]
public bool TrySetException(Exception exception)
{
return core.TrySetException(exception);
}
[DebuggerHidden]
public T GetResult(short token)
{
MarkHandled();
return core.GetResult(token);
}
[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);
}
~UniTaskCompletionSource()
{
// clear error information.
core.Reset();
}
}
public class AutoResetUniTaskCompletionSource<T> : IUniTaskSource<T>, ITaskPoolNode<AutoResetUniTaskCompletionSource<T>>, IPromise<T>
@@ -726,15 +563,377 @@ namespace Cysharp.Threading.Tasks
core.Reset();
return pool.TryPush(this);
}
}
public class UniTaskCompletionSource : IUniTaskSource, IPromise
{
CancellationToken cancellationToken;
ExceptionHolder exception;
object gate;
Action<object> singleContinuation;
object singleState;
List<(Action<object>, object)> secondaryContinuationList;
~AutoResetUniTaskCompletionSource()
int intStatus; // UniTaskStatus
bool handled = false;
public UniTaskCompletionSource()
{
if (TryReturn())
TaskTracker.TrackActiveTask(this, 2);
}
[DebuggerHidden]
internal void MarkHandled()
{
if (!handled)
{
GC.ReRegisterForFinalize(this);
handled = true;
TaskTracker.RemoveTracking(this);
}
}
}
}
public UniTask Task
{
[DebuggerHidden]
get
{
return new UniTask(this, 0);
}
}
[DebuggerHidden]
public bool TrySetResult()
{
return TrySignalCompletion(UniTaskStatus.Succeeded);
}
[DebuggerHidden]
public bool TrySetCanceled(CancellationToken cancellationToken = default)
{
if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
this.cancellationToken = cancellationToken;
return TrySignalCompletion(UniTaskStatus.Canceled);
}
[DebuggerHidden]
public bool TrySetException(Exception exception)
{
if (exception is OperationCanceledException oce)
{
return TrySetCanceled(oce.CancellationToken);
}
if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
this.exception = new ExceptionHolder(ExceptionDispatchInfo.Capture(exception));
return TrySignalCompletion(UniTaskStatus.Faulted);
}
[DebuggerHidden]
public void GetResult(short token)
{
MarkHandled();
var status = (UniTaskStatus)intStatus;
switch (status)
{
case UniTaskStatus.Succeeded:
return;
case UniTaskStatus.Faulted:
exception.GetException().Throw();
return;
case UniTaskStatus.Canceled:
throw new OperationCanceledException(cancellationToken);
default:
case UniTaskStatus.Pending:
throw new InvalidOperationException("not yet completed.");
}
}
[DebuggerHidden]
public UniTaskStatus GetStatus(short token)
{
return (UniTaskStatus)intStatus;
}
[DebuggerHidden]
public UniTaskStatus UnsafeGetStatus()
{
return (UniTaskStatus)intStatus;
}
[DebuggerHidden]
public void OnCompleted(Action<object> continuation, object state, short token)
{
if (gate == null)
{
Interlocked.CompareExchange(ref gate, new object(), null);
}
var lockGate = Thread.VolatileRead(ref gate);
lock (lockGate) // wait TrySignalCompletion, after status is not pending.
{
if ((UniTaskStatus)intStatus != UniTaskStatus.Pending)
{
continuation(state);
return;
}
if (singleContinuation == null)
{
singleContinuation = continuation;
singleState = state;
}
else
{
if (secondaryContinuationList == null)
{
secondaryContinuationList = new List<(Action<object>, object)>();
}
secondaryContinuationList.Add((continuation, state));
}
}
}
[DebuggerHidden]
bool TrySignalCompletion(UniTaskStatus status)
{
if (Interlocked.CompareExchange(ref intStatus, (int)status, (int)UniTaskStatus.Pending) == (int)UniTaskStatus.Pending)
{
if (gate == null)
{
Interlocked.CompareExchange(ref gate, new object(), null);
}
var lockGate = Thread.VolatileRead(ref gate);
lock (lockGate) // wait OnCompleted.
{
if (singleContinuation != null)
{
try
{
singleContinuation(singleState);
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
if (secondaryContinuationList != null)
{
foreach (var (c, state) in secondaryContinuationList)
{
try
{
c(state);
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
}
singleContinuation = null;
singleState = null;
secondaryContinuationList = null;
}
return true;
}
return false;
}
}
public class UniTaskCompletionSource<T> : IUniTaskSource<T>, IPromise<T>
{
CancellationToken cancellationToken;
T result;
ExceptionHolder exception;
object gate;
Action<object> singleContinuation;
object singleState;
List<(Action<object>, object)> secondaryContinuationList;
int intStatus; // UniTaskStatus
bool handled = false;
public UniTaskCompletionSource()
{
TaskTracker.TrackActiveTask(this, 2);
}
[DebuggerHidden]
internal void MarkHandled()
{
if (!handled)
{
handled = true;
TaskTracker.RemoveTracking(this);
}
}
public UniTask<T> Task
{
[DebuggerHidden]
get
{
return new UniTask<T>(this, 0);
}
}
[DebuggerHidden]
public bool TrySetResult(T result)
{
if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
this.result = result;
return TrySignalCompletion(UniTaskStatus.Succeeded);
}
[DebuggerHidden]
public bool TrySetCanceled(CancellationToken cancellationToken = default)
{
if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
this.cancellationToken = cancellationToken;
return TrySignalCompletion(UniTaskStatus.Canceled);
}
[DebuggerHidden]
public bool TrySetException(Exception exception)
{
if (exception is OperationCanceledException oce)
{
return TrySetCanceled(oce.CancellationToken);
}
if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
this.exception = new ExceptionHolder(ExceptionDispatchInfo.Capture(exception));
return TrySignalCompletion(UniTaskStatus.Faulted);
}
[DebuggerHidden]
public T GetResult(short token)
{
MarkHandled();
var status = (UniTaskStatus)intStatus;
switch (status)
{
case UniTaskStatus.Succeeded:
return result;
case UniTaskStatus.Faulted:
exception.GetException().Throw();
return default;
case UniTaskStatus.Canceled:
throw new OperationCanceledException(cancellationToken);
default:
case UniTaskStatus.Pending:
throw new InvalidOperationException("not yet completed.");
}
}
[DebuggerHidden]
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
[DebuggerHidden]
public UniTaskStatus GetStatus(short token)
{
return (UniTaskStatus)intStatus;
}
[DebuggerHidden]
public UniTaskStatus UnsafeGetStatus()
{
return (UniTaskStatus)intStatus;
}
[DebuggerHidden]
public void OnCompleted(Action<object> continuation, object state, short token)
{
if (gate == null)
{
Interlocked.CompareExchange(ref gate, new object(), null);
}
var lockGate = Thread.VolatileRead(ref gate);
lock (lockGate) // wait TrySignalCompletion, after status is not pending.
{
if ((UniTaskStatus)intStatus != UniTaskStatus.Pending)
{
continuation(state);
return;
}
if (singleContinuation == null)
{
singleContinuation = continuation;
singleState = state;
}
else
{
if (secondaryContinuationList == null)
{
secondaryContinuationList = new List<(Action<object>, object)>();
}
secondaryContinuationList.Add((continuation, state));
}
}
}
[DebuggerHidden]
bool TrySignalCompletion(UniTaskStatus status)
{
if (Interlocked.CompareExchange(ref intStatus, (int)status, (int)UniTaskStatus.Pending) == (int)UniTaskStatus.Pending)
{
if (gate == null)
{
Interlocked.CompareExchange(ref gate, new object(), null);
}
var lockGate = Thread.VolatileRead(ref gate);
lock (lockGate) // wait OnCompleted.
{
if (singleContinuation != null)
{
try
{
singleContinuation(singleState);
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
if (secondaryContinuationList != null)
{
foreach (var (c, state) in secondaryContinuationList)
{
try
{
c(state);
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
}
singleContinuation = null;
singleState = null;
secondaryContinuationList = null;
}
return true;
}
return false;
}
}
}

View File

@@ -181,12 +181,12 @@ namespace Cysharp.Threading.Tasks
public static AsyncLazy ToAsyncLazy(this UniTask task)
{
return new AsyncLazy(task.Preserve()); // require Preserve
return new AsyncLazy(task);
}
public static AsyncLazy<T> ToAsyncLazy<T>(this UniTask<T> task)
{
return new AsyncLazy<T>(task.Preserve()); // require Preserve
return new AsyncLazy<T>(task);
}
#if UNITY_2018_3_OR_NEWER
@@ -201,10 +201,10 @@ namespace Cysharp.Threading.Tasks
return new ToCoroutineEnumerator(task, exceptionHandler);
}
public static async UniTask Timeout(this UniTask task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
public static async UniTask Timeout(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
{
var delayCancellationTokenSource = new CancellationTokenSource();
var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
int winArgIndex;
bool taskResultIsCanceled;
@@ -242,10 +242,10 @@ namespace Cysharp.Threading.Tasks
}
}
public static async UniTask<T> Timeout<T>(this UniTask<T> task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
public static async UniTask<T> Timeout<T>(this UniTask<T> task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
{
var delayCancellationTokenSource = new CancellationTokenSource();
var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
int winArgIndex;
(bool IsCanceled, T Result) taskResult;
@@ -288,10 +288,10 @@ namespace Cysharp.Threading.Tasks
/// <summary>
/// Timeout with suppress OperationCanceledException. Returns (bool, IsCacneled).
/// </summary>
public static async UniTask<bool> TimeoutWithoutException(this UniTask task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
public static async UniTask<bool> TimeoutWithoutException(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
{
var delayCancellationTokenSource = new CancellationTokenSource();
var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
int winArgIndex;
bool taskResultIsCanceled;
@@ -315,7 +315,7 @@ namespace Cysharp.Threading.Tasks
taskCancellationTokenSource.Dispose();
}
throw new TimeoutException("Exceed Timeout:" + timeout);
return true;
}
else
{
@@ -325,7 +325,7 @@ namespace Cysharp.Threading.Tasks
if (taskResultIsCanceled)
{
Error.ThrowOperationCanceledException();
return true;
}
return false;
@@ -334,10 +334,10 @@ namespace Cysharp.Threading.Tasks
/// <summary>
/// Timeout with suppress OperationCanceledException. Returns (bool IsTimeout, T Result).
/// </summary>
public static async UniTask<(bool IsTimeout, T Result)> TimeoutWithoutException<T>(this UniTask<T> task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
public static async UniTask<(bool IsTimeout, T Result)> TimeoutWithoutException<T>(this UniTask<T> task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
{
var delayCancellationTokenSource = new CancellationTokenSource();
var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
int winArgIndex;
(bool IsCanceled, T Result) taskResult;
@@ -361,7 +361,7 @@ namespace Cysharp.Threading.Tasks
taskCancellationTokenSource.Dispose();
}
throw new TimeoutException("Exceed Timeout:" + timeout);
return (true, default);
}
else
{
@@ -371,7 +371,7 @@ namespace Cysharp.Threading.Tasks
if (taskResult.IsCanceled)
{
Error.ThrowOperationCanceledException();
return (true, default);
}
return (false, taskResult.Result);
@@ -621,9 +621,8 @@ namespace Cysharp.Threading.Tasks
if (exception != null)
{
// throw exception on iterator (main)thread.
// unfortunately unity test-runner can not handle throw exception on hand-write IEnumerator.MoveNext.
UnityEngine.Debug.LogException(exception.SourceException);
exception.Throw();
return false;
}
return !completed;
@@ -692,9 +691,8 @@ namespace Cysharp.Threading.Tasks
if (exception != null)
{
// throw exception on iterator (main)thread.
// unfortunately unity test-runner can not handle throw exception on hand-write IEnumerator.MoveNext.
UnityEngine.Debug.LogException(exception.SourceException);
exception.Throw();
return false;
}
return !completed;

View File

@@ -0,0 +1,139 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Threading;
using UnityEngine.Rendering;
namespace Cysharp.Threading.Tasks
{
public static partial class UnityAsyncExtensions
{
#region AsyncGPUReadbackRequest
public static UniTask<AsyncGPUReadbackRequest>.Awaiter GetAwaiter(this AsyncGPUReadbackRequest asyncOperation)
{
return ToUniTask(asyncOperation).GetAwaiter();
}
public static UniTask<AsyncGPUReadbackRequest> WithCancellation(this AsyncGPUReadbackRequest asyncOperation, CancellationToken cancellationToken)
{
return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
}
public static UniTask<AsyncGPUReadbackRequest> ToUniTask(this AsyncGPUReadbackRequest asyncOperation, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
if (asyncOperation.done) return UniTask.FromResult(asyncOperation);
return new UniTask<AsyncGPUReadbackRequest>(AsyncGPUReadbackRequestAwaiterConfiguredSource.Create(asyncOperation, timing, cancellationToken, out var token), token);
}
sealed class AsyncGPUReadbackRequestAwaiterConfiguredSource : IUniTaskSource<AsyncGPUReadbackRequest>, IPlayerLoopItem, ITaskPoolNode<AsyncGPUReadbackRequestAwaiterConfiguredSource>
{
static TaskPool<AsyncGPUReadbackRequestAwaiterConfiguredSource> pool;
public AsyncGPUReadbackRequestAwaiterConfiguredSource NextNode { get; set; }
static AsyncGPUReadbackRequestAwaiterConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncGPUReadbackRequestAwaiterConfiguredSource), () => pool.Size);
}
AsyncGPUReadbackRequest asyncOperation;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<AsyncGPUReadbackRequest> core;
AsyncGPUReadbackRequestAwaiterConfiguredSource()
{
}
public static IUniTaskSource<AsyncGPUReadbackRequest> Create(AsyncGPUReadbackRequest asyncOperation, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<AsyncGPUReadbackRequest>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AsyncGPUReadbackRequestAwaiterConfiguredSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public AsyncGPUReadbackRequest GetResult(short token)
{
try
{
return core.GetResult(token);
}
finally
{
TryReturn();
}
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
if (asyncOperation.hasError)
{
core.TrySetException(new Exception("AsyncGPUReadbackRequest.hasError = true"));
return false;
}
if (asyncOperation.done)
{
core.TrySetResult(asyncOperation);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
#endregion
}
}

View File

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

View File

@@ -25,7 +25,7 @@ namespace Cysharp.Threading.Tasks
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.CompletedTask;
return new UniTask(AsyncOperationConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
return new UniTask(AsyncOperationWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
}
public static UniTask ToUniTask(this AsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -75,7 +75,119 @@ namespace Cysharp.Threading.Tasks
}
}
class AsyncOperationConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationConfiguredSource>
sealed class AsyncOperationWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationWithCancellationSource>
{
static TaskPool<AsyncOperationWithCancellationSource> pool;
public AsyncOperationWithCancellationSource NextNode { get; set; }
static AsyncOperationWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
AsyncOperation asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<AsyncUnit> core;
AsyncOperationWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource Create(AsyncOperation asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AsyncOperationWithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
core.TrySetResult(AsyncUnit.Default);
}
}
public void GetResult(short token)
{
core.GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AsyncOperationConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationConfiguredSource>
{
static TaskPool<AsyncOperationConfiguredSource> pool;
public AsyncOperationConfiguredSource NextNode { get; set; }
@@ -124,7 +236,6 @@ namespace Cysharp.Threading.Tasks
{
try
{
core.GetResult(token);
}
finally
@@ -133,6 +244,7 @@ namespace Cysharp.Threading.Tasks
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
@@ -179,14 +291,6 @@ namespace Cysharp.Threading.Tasks
cancellationToken = default;
return pool.TryPush(this);
}
~AsyncOperationConfiguredSource()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
#endregion
@@ -203,7 +307,7 @@ namespace Cysharp.Threading.Tasks
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset);
return new UniTask<UnityEngine.Object>(ResourceRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
return new UniTask<UnityEngine.Object>(ResourceRequestWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
}
public static UniTask<UnityEngine.Object> ToUniTask(this ResourceRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -257,7 +361,123 @@ namespace Cysharp.Threading.Tasks
}
}
class ResourceRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<ResourceRequestConfiguredSource>
sealed class ResourceRequestWithCancellationSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<ResourceRequestWithCancellationSource>
{
static TaskPool<ResourceRequestWithCancellationSource> pool;
public ResourceRequestWithCancellationSource NextNode { get; set; }
static ResourceRequestWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(ResourceRequestWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
ResourceRequest asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<UnityEngine.Object> core;
ResourceRequestWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<UnityEngine.Object> Create(ResourceRequest asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<UnityEngine.Object>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new ResourceRequestWithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
core.TrySetResult(asyncOperation.asset);
}
}
public UnityEngine.Object GetResult(short token)
{
return core.GetResult(token);
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class ResourceRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<ResourceRequestConfiguredSource>
{
static TaskPool<ResourceRequestConfiguredSource> pool;
public ResourceRequestConfiguredSource NextNode { get; set; }
@@ -306,7 +526,6 @@ namespace Cysharp.Threading.Tasks
{
try
{
return core.GetResult(token);
}
finally
@@ -366,14 +585,6 @@ namespace Cysharp.Threading.Tasks
cancellationToken = default;
return pool.TryPush(this);
}
~ResourceRequestConfiguredSource()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
#endregion
@@ -390,7 +601,7 @@ namespace Cysharp.Threading.Tasks
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset);
return new UniTask<UnityEngine.Object>(AssetBundleRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
return new UniTask<UnityEngine.Object>(AssetBundleRequestWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
}
public static UniTask<UnityEngine.Object> ToUniTask(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -444,7 +655,123 @@ namespace Cysharp.Threading.Tasks
}
}
class AssetBundleRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestConfiguredSource>
sealed class AssetBundleRequestWithCancellationSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestWithCancellationSource>
{
static TaskPool<AssetBundleRequestWithCancellationSource> pool;
public AssetBundleRequestWithCancellationSource NextNode { get; set; }
static AssetBundleRequestWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(AssetBundleRequestWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
AssetBundleRequest asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<UnityEngine.Object> core;
AssetBundleRequestWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<UnityEngine.Object> Create(AssetBundleRequest asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<UnityEngine.Object>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AssetBundleRequestWithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
core.TrySetResult(asyncOperation.asset);
}
}
public UnityEngine.Object GetResult(short token)
{
return core.GetResult(token);
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AssetBundleRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestConfiguredSource>
{
static TaskPool<AssetBundleRequestConfiguredSource> pool;
public AssetBundleRequestConfiguredSource NextNode { get; set; }
@@ -545,21 +872,13 @@ namespace Cysharp.Threading.Tasks
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
progress = default;
cancellationToken = default;
TaskTracker.RemoveTracking(this);
return pool.TryPush(this);
}
~AssetBundleRequestConfiguredSource()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
#endregion
@@ -576,7 +895,7 @@ namespace Cysharp.Threading.Tasks
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.assetBundle);
return new UniTask<AssetBundle>(AssetBundleCreateRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
return new UniTask<AssetBundle>(AssetBundleCreateRequestWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
}
public static UniTask<AssetBundle> ToUniTask(this AssetBundleCreateRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -630,7 +949,123 @@ namespace Cysharp.Threading.Tasks
}
}
class AssetBundleCreateRequestConfiguredSource : IUniTaskSource<AssetBundle>, IPlayerLoopItem, ITaskPoolNode<AssetBundleCreateRequestConfiguredSource>
sealed class AssetBundleCreateRequestWithCancellationSource : IUniTaskSource<AssetBundle>, IPlayerLoopItem, ITaskPoolNode<AssetBundleCreateRequestWithCancellationSource>
{
static TaskPool<AssetBundleCreateRequestWithCancellationSource> pool;
public AssetBundleCreateRequestWithCancellationSource NextNode { get; set; }
static AssetBundleCreateRequestWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(AssetBundleCreateRequestWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
AssetBundleCreateRequest asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<AssetBundle> core;
AssetBundleCreateRequestWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<AssetBundle> Create(AssetBundleCreateRequest asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<AssetBundle>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AssetBundleCreateRequestWithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
core.TrySetResult(asyncOperation.assetBundle);
}
}
public AssetBundle GetResult(short token)
{
return core.GetResult(token);
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AssetBundleCreateRequestConfiguredSource : IUniTaskSource<AssetBundle>, IPlayerLoopItem, ITaskPoolNode<AssetBundleCreateRequestConfiguredSource>
{
static TaskPool<AssetBundleCreateRequestConfiguredSource> pool;
public AssetBundleCreateRequestConfiguredSource NextNode { get; set; }
@@ -731,21 +1166,13 @@ namespace Cysharp.Threading.Tasks
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
progress = default;
cancellationToken = default;
TaskTracker.RemoveTracking(this);
return pool.TryPush(this);
}
~AssetBundleCreateRequestConfiguredSource()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
#endregion
@@ -763,7 +1190,7 @@ namespace Cysharp.Threading.Tasks
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.webRequest);
return new UniTask<UnityWebRequest>(UnityWebRequestAsyncOperationConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
return new UniTask<UnityWebRequest>(UnityWebRequestAsyncOperationWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
}
public static UniTask<UnityWebRequest> ToUniTask(this UnityWebRequestAsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -794,12 +1221,20 @@ namespace Cysharp.Threading.Tasks
continuationAction = null;
var result = asyncOperation.webRequest;
asyncOperation = null;
if (result.IsError())
{
throw new UnityWebRequestException(result);
}
return result;
}
else
{
var result = asyncOperation.webRequest;
asyncOperation = null;
if (result.IsError())
{
throw new UnityWebRequestException(result);
}
return result;
}
}
@@ -817,7 +1252,132 @@ namespace Cysharp.Threading.Tasks
}
}
class UnityWebRequestAsyncOperationConfiguredSource : IUniTaskSource<UnityWebRequest>, IPlayerLoopItem, ITaskPoolNode<UnityWebRequestAsyncOperationConfiguredSource>
sealed class UnityWebRequestAsyncOperationWithCancellationSource : IUniTaskSource<UnityWebRequest>, IPlayerLoopItem, ITaskPoolNode<UnityWebRequestAsyncOperationWithCancellationSource>
{
static TaskPool<UnityWebRequestAsyncOperationWithCancellationSource> pool;
public UnityWebRequestAsyncOperationWithCancellationSource NextNode { get; set; }
static UnityWebRequestAsyncOperationWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(UnityWebRequestAsyncOperationWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
UnityWebRequestAsyncOperation asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<UnityWebRequest> core;
UnityWebRequestAsyncOperationWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<UnityWebRequest> Create(UnityWebRequestAsyncOperation asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<UnityWebRequest>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new UnityWebRequestAsyncOperationWithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
var result = asyncOperation.webRequest;
if (result.IsError())
{
core.TrySetException(new UnityWebRequestException(result));
}
else
{
core.TrySetResult(result);
}
}
}
public UnityWebRequest GetResult(short token)
{
return core.GetResult(token);
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
asyncOperation.webRequest.Abort();
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class UnityWebRequestAsyncOperationConfiguredSource : IUniTaskSource<UnityWebRequest>, IPlayerLoopItem, ITaskPoolNode<UnityWebRequestAsyncOperationConfiguredSource>
{
static TaskPool<UnityWebRequestAsyncOperationConfiguredSource> pool;
public UnityWebRequestAsyncOperationConfiguredSource NextNode { get; set; }
@@ -866,7 +1426,6 @@ namespace Cysharp.Threading.Tasks
{
try
{
return core.GetResult(token);
}
finally
@@ -911,7 +1470,14 @@ namespace Cysharp.Threading.Tasks
if (asyncOperation.isDone)
{
core.TrySetResult(asyncOperation.webRequest);
if (asyncOperation.webRequest.IsError())
{
core.TrySetException(new UnityWebRequestException(asyncOperation.webRequest));
}
else
{
core.TrySetResult(asyncOperation.webRequest);
}
return false;
}
@@ -920,21 +1486,13 @@ namespace Cysharp.Threading.Tasks
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
progress = default;
cancellationToken = default;
TaskTracker.RemoveTracking(this);
return pool.TryPush(this);
}
~UnityWebRequestAsyncOperationConfiguredSource()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
#endregion

View File

@@ -37,7 +37,7 @@ namespace Cysharp.Threading.Tasks
<# if(t.returnType == "UnityWebRequest") { #>
#if ENABLE_UNITYWEBREQUEST
<# } #>
#region <#= t.typeName #>
#region <#= t.typeName #>
public static <#= t.typeName #>Awaiter GetAwaiter(this <#= t.typeName #> asyncOperation)
{
@@ -45,18 +45,18 @@ namespace Cysharp.Threading.Tasks
return new <#= t.typeName #>Awaiter(asyncOperation);
}
public static <#= ToUniTaskReturnType(t.returnType) #> ToUniTask(this <#= t.typeName #> asyncOperation)
public static <#= ToUniTaskReturnType(t.returnType) #> WithCancellation(this <#= t.typeName #> asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new <#= ToUniTaskReturnType(t.returnType) #>(<#= t.typeName #>ConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token);
if (asyncOperation.isDone) return <#= IsVoid(t) ? "UniTask.CompletedTask" : $"UniTask.FromResult(asyncOperation.{t.returnField})" #>;
return new <#= ToUniTaskReturnType(t.returnType) #>(<#= t.typeName #>WithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
}
public static <#= ToUniTaskReturnType(t.returnType) #> ConfigureAwait(this <#= t.typeName #> asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
public static <#= ToUniTaskReturnType(t.returnType) #> ToUniTask(this <#= t.typeName #> asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new <#= ToUniTaskReturnType(t.returnType) #>(<#= t.typeName #>ConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token);
if (asyncOperation.isDone) return <#= IsVoid(t) ? "UniTask.CompletedTask" : $"UniTask.FromResult(asyncOperation.{t.returnField})" #>;
return new <#= ToUniTaskReturnType(t.returnType) #>(<#= t.typeName #>ConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
}
public struct <#= t.typeName #>Awaiter : ICriticalNotifyCompletion
@@ -81,6 +81,12 @@ namespace Cysharp.Threading.Tasks
<# if (!IsVoid(t)) { #>
var result = <#= $"asyncOperation.{t.returnField}" #>;
asyncOperation = null;
<# if(t.returnType == "UnityWebRequest") { #>
if (result.IsError())
{
throw new UnityWebRequestException(result);
}
<# } #>
return result;
<# } else { #>
asyncOperation = null;
@@ -91,6 +97,12 @@ namespace Cysharp.Threading.Tasks
<# if (!IsVoid(t)) { #>
var result = <#= $"asyncOperation.{t.returnField}" #>;
asyncOperation = null;
<# if(t.returnType == "UnityWebRequest") { #>
if (result.IsError())
{
throw new UnityWebRequestException(result);
}
<# } #>
return result;
<# } else { #>
asyncOperation = null;
@@ -106,14 +118,157 @@ namespace Cysharp.Threading.Tasks
public void UnsafeOnCompleted(Action continuation)
{
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
continuationAction = continuation.AsFuncOfT<AsyncOperation>(); // allocate delegate.
continuationAction = PooledDelegate<AsyncOperation>.Create(continuation);
asyncOperation.completed += continuationAction;
}
}
class <#= t.typeName #>ConfiguredSource : <#= ToIUniTaskSourceReturnType(t.returnType) #>, IPlayerLoopItem, IPromisePoolItem
sealed class <#= t.typeName #>WithCancellationSource : <#= ToIUniTaskSourceReturnType(t.returnType) #>, IPlayerLoopItem, ITaskPoolNode<<#= t.typeName #>WithCancellationSource>
{
static readonly PromisePool<<#= t.typeName #>ConfiguredSource> pool = new PromisePool<<#= t.typeName #>ConfiguredSource>();
static TaskPool<<#= t.typeName #>WithCancellationSource> pool;
public <#= t.typeName #>WithCancellationSource NextNode { get; set; }
static <#= t.typeName #>WithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(<#= t.typeName #>WithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
<#= t.typeName #> asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<<#= IsVoid(t) ? "AsyncUnit" : t.returnType #>> core;
<#= t.typeName #>WithCancellationSource()
{
continuationAction = Continuation;
}
public static <#= ToIUniTaskSourceReturnType(t.returnType) #> Create(<#= t.typeName #> asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<#= IsVoid(t) ? "" : $"<{t.returnType}>" #>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new <#= t.typeName #>WithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
<# if(t.returnType == "UnityWebRequest") { #>
var result = asyncOperation.webRequest;
if (result.IsError())
{
core.TrySetException(new UnityWebRequestException(result));
}
else
{
core.TrySetResult(result);
}
<# } else { #>
core.TrySetResult(<#= IsVoid(t) ? "AsyncUnit.Default" : $"asyncOperation.{t.returnField}" #>);
<# } #>
}
}
public <#= t.returnType #> GetResult(short token)
{
<# if (!IsVoid(t)) { #>
return core.GetResult(token);
<# } else { #>
core.GetResult(token);
<# } #>
}
<# if (!IsVoid(t)) { #>
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
<# } #>
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
<# if(t.returnType == "UnityWebRequest") { #>
asyncOperation.webRequest.Abort();
<# } #>
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class <#= t.typeName #>ConfiguredSource : <#= ToIUniTaskSourceReturnType(t.returnType) #>, IPlayerLoopItem, ITaskPoolNode<<#= t.typeName #>ConfiguredSource>
{
static TaskPool<<#= t.typeName #>ConfiguredSource> pool;
public <#= t.typeName #>ConfiguredSource NextNode { get; set; }
static <#= t.typeName #>ConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(<#= t.typeName #>ConfiguredSource), () => pool.Size);
}
<#= t.typeName #> asyncOperation;
IProgress<float> progress;
@@ -133,7 +288,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource<#= IsVoid(t) ? "" : $"<{t.returnType}>" #>.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new <#= t.typeName #>ConfiguredSource();
if (!pool.TryPop(out var result))
{
result = new <#= t.typeName #>ConfiguredSource();
}
result.asyncOperation = asyncOperation;
result.progress = progress;
@@ -151,8 +309,6 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
<# if (!IsVoid(t)) { #>
return core.GetResult(token);
<# } else { #>
@@ -161,7 +317,7 @@ namespace Cysharp.Threading.Tasks
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -191,6 +347,9 @@ namespace Cysharp.Threading.Tasks
{
if (cancellationToken.IsCancellationRequested)
{
<# if(t.returnType == "UnityWebRequest") { #>
asyncOperation.webRequest.Abort();
<# } #>
core.TrySetCanceled(cancellationToken);
return false;
}
@@ -202,31 +361,36 @@ namespace Cysharp.Threading.Tasks
if (asyncOperation.isDone)
{
<# if(t.returnType == "UnityWebRequest") { #>
if (asyncOperation.webRequest.IsError())
{
core.TrySetException(new UnityWebRequestException(asyncOperation.webRequest));
}
else
{
core.TrySetResult(asyncOperation.webRequest);
}
<# } else { #>
core.TrySetResult(<#= IsVoid(t) ? "AsyncUnit.Default" : $"asyncOperation.{t.returnField}" #>);
<# } #>
return false;
}
return true;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
progress = default;
cancellationToken = default;
}
~<#= t.typeName #>ConfiguredSource()
{
if (pool.TryReturn(this))
{
GC.ReRegisterForFinalize(this);
}
return pool.TryPush(this);
}
}
# endregion
#endregion
<# if(t.returnType == "UnityWebRequest") { #>
#endif
<# } #>

View File

@@ -296,6 +296,58 @@ namespace Cysharp.Threading.Tasks
UniTask<T> OnEndEditAsync();
}
// for TMP_PRO
public interface IAsyncEndTextSelectionEventHandler<T> : IDisposable
{
UniTask<T> OnEndTextSelectionAsync();
}
public interface IAsyncTextSelectionEventHandler<T> : IDisposable
{
UniTask<T> OnTextSelectionAsync();
}
public interface IAsyncDeselectEventHandler<T> : IDisposable
{
UniTask<T> OnDeselectAsync();
}
public interface IAsyncSelectEventHandler<T> : IDisposable
{
UniTask<T> OnSelectAsync();
}
public interface IAsyncSubmitEventHandler<T> : IDisposable
{
UniTask<T> OnSubmitAsync();
}
internal class TextSelectionEventConverter : UnityEvent<(string, int, int)>, IDisposable
{
readonly UnityEvent<string, int, int> innerEvent;
readonly UnityAction<string, int, int> invokeDelegate;
public TextSelectionEventConverter(UnityEvent<string, int, int> unityEvent)
{
this.innerEvent = unityEvent;
this.invokeDelegate = InvokeCore;
innerEvent.AddListener(invokeDelegate);
}
void InvokeCore(string item1, int item2, int item3)
{
innerEvent.Invoke(item1, item2, item3);
}
public void Dispose()
{
innerEvent.RemoveListener(invokeDelegate);
}
}
public class AsyncUnityEventHandler : IUniTaskSource, IDisposable, IAsyncClickEventHandler
{
static Action<object> cancellationCallback = CancellationCallback;
@@ -402,6 +454,7 @@ namespace Cysharp.Threading.Tasks
}
public class AsyncUnityEventHandler<T> : IUniTaskSource<T>, IDisposable, IAsyncValueChangedEventHandler<T>, IAsyncEndEditEventHandler<T>
, IAsyncEndTextSelectionEventHandler<T>, IAsyncTextSelectionEventHandler<T>, IAsyncDeselectEventHandler<T>, IAsyncSelectEventHandler<T>, IAsyncSubmitEventHandler<T>
{
static Action<object> cancellationCallback = CancellationCallback;
@@ -464,6 +517,12 @@ namespace Cysharp.Threading.Tasks
registration.Dispose();
if (unityEvent != null)
{
// Dispose inner delegate for TextSelectionEventConverter
if (unityEvent is IDisposable disp)
{
disp.Dispose();
}
unityEvent.RemoveListener(action);
}
@@ -481,6 +540,31 @@ namespace Cysharp.Threading.Tasks
return OnInvokeAsync();
}
UniTask<T> IAsyncEndTextSelectionEventHandler<T>.OnEndTextSelectionAsync()
{
return OnInvokeAsync();
}
UniTask<T> IAsyncTextSelectionEventHandler<T>.OnTextSelectionAsync()
{
return OnInvokeAsync();
}
UniTask<T> IAsyncDeselectEventHandler<T>.OnDeselectAsync()
{
return OnInvokeAsync();
}
UniTask<T> IAsyncSelectEventHandler<T>.OnSelectAsync()
{
return OnInvokeAsync();
}
UniTask<T> IAsyncSubmitEventHandler<T>.OnSubmitAsync()
{
return OnInvokeAsync();
}
T IUniTaskSource<T>.GetResult(short token)
{
try
@@ -718,6 +802,10 @@ namespace Cysharp.Threading.Tasks
TaskTracker.RemoveTracking(this);
registration1.Dispose();
registration2.Dispose();
if (unityEvent is IDisposable disp)
{
disp.Dispose();
}
unityEvent.RemoveListener(unityAction);
}

View File

@@ -237,65 +237,5 @@ namespace Cysharp.Threading.Tasks
}
}
}
// TMP
#if UNITASK_TEXTMESHPRO_SUPPORT
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, TMPro.TMP_Text text, bool rebindOnError = true)
{
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
}
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, TMPro.TMP_Text text, CancellationToken cancellationToken, bool rebindOnError = true)
{
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
}
static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<string> source, TMPro.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();
}
}
}
#endif
}
}

View File

@@ -0,0 +1,51 @@
#if ENABLE_UNITYWEBREQUEST
using System;
using UnityEngine.Networking;
namespace Cysharp.Threading.Tasks
{
public class UnityWebRequestException : Exception
{
public UnityWebRequest UnityWebRequest { get; }
#if UNITY_2020_2_OR_NEWER
public UnityWebRequest.Result Result { get; }
#else
public bool IsNetworkError { get; }
public bool IsHttpError { get; }
#endif
public string Error { get; }
public string Text { get; }
public long ResponseCode { get; }
string msg;
public UnityWebRequestException(UnityWebRequest unityWebRequest)
{
this.UnityWebRequest = unityWebRequest;
#if UNITY_2020_2_OR_NEWER
this.Result = unityWebRequest.result;
#else
this.IsNetworkError = unityWebRequest.isNetworkError;
this.IsHttpError = unityWebRequest.isHttpError;
#endif
this.Error = unityWebRequest.error;
this.ResponseCode = unityWebRequest.responseCode;
this.Text = unityWebRequest.downloadHandler.text;
}
public override string Message
{
get
{
if (msg == null)
{
msg = Error + Environment.NewLine + Text;
}
return msg;
}
}
}
}
#endif

View File

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

View File

@@ -1,3 +1,6 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("UniTask.Linq")]
[assembly: InternalsVisibleTo("UniTask.Linq")]
[assembly: InternalsVisibleTo("UniTask.Addressables")]
[assembly: InternalsVisibleTo("UniTask.DOTween")]
[assembly: InternalsVisibleTo("UniTask.TextMeshPro")]

View File

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

View File

@@ -1,75 +1,46 @@
// Provided from: https://github.com/Cysharp/UniTask/issues/40
using System;
using System;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Linq;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
/// <summary>
/// Example script for comparing how exceptions in unobserved tasks are handled between
/// UniTask and normal tasks. This helps in verifying that unobserved exceptions are
/// logged in a way that it useful to developers.
/// </summary>
public class ExceptionExamples : MonoBehaviour
{
[SerializeField] private LogType _unobservedExceptionLogType = LogType.Exception;
public Button ButtonTest;
private void Awake()
void Start()
{
UniTaskScheduler.UnobservedExceptionWriteLogType = _unobservedExceptionLogType;
ButtonTest.OnClickAsAsyncEnumerable()
.Subscribe(async _ =>
{
try
{
await new Foo().MethodFooAsync();
}
catch (Exception e)
{
Debug.Log(e.StackTrace);
}
}, this.GetCancellationTokenOnDestroy());
}
}
class Foo
{
public async UniTask MethodFooAsync()
{
await MethodBarAsync();
}
private void Start()
private async UniTask MethodBarAsync()
{
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
ThrowFromAsyncVoid();
_ = ThrowFromTask();
_ = ThrowFromUniTask();
ThrowFromNonAsync();
Throw();
}
private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
private void Throw()
{
UnityEngine.Debug.LogException(e.Exception);
}
private void ThrowFromNonAsync()
{
throw new Exception("Thrown from non-async function");
}
private async void ThrowFromAsyncVoid()
{
await ThrowInner();
async Task ThrowInner()
{
await UniTask.Yield();
throw new Exception("Thrown from `async void` function");
}
}
private async Task ThrowFromTask()
{
await ThrowInner();
async Task ThrowInner()
{
await UniTask.Yield();
throw new Exception("Thrown from `async Task` function");
}
}
private async UniTask ThrowFromUniTask()
{
await ThrowInner();
async UniTask ThrowInner()
{
await UniTask.Yield();
throw new Exception("Thrown from `async UniTask` function");
}
throw new Exception();
}
}

View File

@@ -121,6 +121,149 @@ NavMeshSettings:
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &542336983
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 542336986}
- component: {fileID: 542336985}
- component: {fileID: 542336984}
m_Layer: 0
m_Name: EventSystem
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &542336984
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 542336983}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
m_Name:
m_EditorClassIdentifier:
m_HorizontalAxis: Horizontal
m_VerticalAxis: Vertical
m_SubmitButton: Submit
m_CancelButton: Cancel
m_InputActionsPerSecond: 10
m_RepeatDelay: 0.5
m_ForceModuleActive: 0
--- !u!114 &542336985
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 542336983}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
m_Name:
m_EditorClassIdentifier:
m_FirstSelected: {fileID: 0}
m_sendNavigationEvents: 1
m_DragThreshold: 10
--- !u!4 &542336986
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 542336983}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &730559310
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 730559311}
- component: {fileID: 730559313}
- component: {fileID: 730559312}
m_Layer: 5
m_Name: Text
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &730559311
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 730559310}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 1587363385}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &730559312
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 730559310}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 14
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 10
m_MaxSize: 40
m_Alignment: 4
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: Button
--- !u!222 &730559313
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 730559310}
m_CullTransparentMesh: 0
--- !u!1 &735985614
GameObject:
m_ObjectHideFlags: 0
@@ -152,7 +295,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 329ff620b32b4ef4abdc99884242ee67, type: 3}
m_Name:
m_EditorClassIdentifier:
_unobservedExceptionLogType: 0
ButtonTest: {fileID: 1587363386}
--- !u!81 &735985616
AudioListener:
m_ObjectHideFlags: 0
@@ -214,7 +357,225 @@ Transform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Children:
- {fileID: 1323586517}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1323586516
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1323586517}
- component: {fileID: 1323586520}
- component: {fileID: 1323586519}
- component: {fileID: 1323586518}
m_Layer: 5
m_Name: Canvas
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1323586517
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1323586516}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_Children:
- {fileID: 1587363385}
m_Father: {fileID: 735985618}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 0}
--- !u!114 &1323586518
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1323586516}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
m_Name:
m_EditorClassIdentifier:
m_IgnoreReversedGraphics: 1
m_BlockingObjects: 0
m_BlockingMask:
serializedVersion: 2
m_Bits: 4294967295
--- !u!114 &1323586519
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1323586516}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UiScaleMode: 0
m_ReferencePixelsPerUnit: 100
m_ScaleFactor: 1
m_ReferenceResolution: {x: 800, y: 600}
m_ScreenMatchMode: 0
m_MatchWidthOrHeight: 0
m_PhysicalUnit: 3
m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1
--- !u!223 &1323586520
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1323586516}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 0
m_Camera: {fileID: 0}
m_PlaneDistance: 100
m_PixelPerfect: 0
m_ReceivesEvents: 1
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_AdditionalShaderChannelsFlag: 0
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0
--- !u!1 &1587363384
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1587363385}
- component: {fileID: 1587363388}
- component: {fileID: 1587363387}
- component: {fileID: 1587363386}
m_Layer: 5
m_Name: Button
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1587363385
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1587363384}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 730559311}
m_Father: {fileID: 1323586517}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 160, y: 30}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1587363386
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1587363384}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 1587363387}
m_OnClick:
m_PersistentCalls:
m_Calls: []
--- !u!114 &1587363387
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1587363384}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!222 &1587363388
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1587363384}
m_CullTransparentMesh: 0

View File

@@ -14,6 +14,10 @@ using UnityEngine;
using UnityEngine.LowLevel;
using UnityEngine.Networking;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using UnityEngine.Rendering;
using System.IO;
// using DG.Tweening;
@@ -115,6 +119,8 @@ public class AsyncMessageBroker<T> : IDisposable
public class SandboxMain : MonoBehaviour
{
public Camera mycamera;
public Button okButton;
public Button cancelButton;
@@ -159,6 +165,9 @@ public class SandboxMain : MonoBehaviour
//setHp = Hp.GetSetter();
}
@@ -176,10 +185,7 @@ public class SandboxMain : MonoBehaviour
public Button button;
void Start2()
{
}
async UniTask RunStandardDelayAsync()
@@ -264,11 +270,14 @@ public class SandboxMain : MonoBehaviour
//var r = UniAsync("https://bing.com/", cts.Token);
//cts.Cancel();
//await r;
_ = await UnityWebRequest.Get("https://bing.com/").SendWebRequest();
Debug.Log("UNIASYNC1 ");
Debug.Log("SendWebRequestDone:" + PlayerLoopInfo.CurrentLoopType);
_ = await UnityWebRequest.Get("https://bing.com/").SendWebRequest();
Debug.Log("UNIASYNC2");
// var foo = await UnityWebRequest.Get("https://bing.com/").SendWebRequest();
// foo.downloadHandler.text;
//
_ = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().WithCancellation(CancellationToken.None);
Debug.Log("SendWebRequestWithCancellationDone:" + PlayerLoopInfo.CurrentLoopType);
}
catch
{
@@ -301,10 +310,259 @@ public class SandboxMain : MonoBehaviour
return 10;
}
async UniTask<int> Ex()
{
await UniTask.Yield();
//throw new Exception();
await UniTask.Delay(TimeSpan.FromSeconds(15));
return 0;
}
IEnumerator CoroutineRun()
{
UnityEngine.Debug.Log("Before Coroutine yield return null," + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
yield return null;
UnityEngine.Debug.Log("After Coroutine yield return null," + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
}
IEnumerator CoroutineRun2()
{
UnityEngine.Debug.Log("Before Coroutine yield return WaitForEndOfFrame," + Time.frameCount);
yield return new WaitForEndOfFrame();
UnityEngine.Debug.Log("After Coroutine yield return WaitForEndOfFrame," + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
yield return new WaitForEndOfFrame();
UnityEngine.Debug.Log("Onemore After Coroutine yield return WaitForEndOfFrame," + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
}
async UniTaskVoid AsyncRun()
{
UnityEngine.Debug.Log("Before async Yield(default)," + Time.frameCount);
await UniTask.Yield();
UnityEngine.Debug.Log("After async Yield(default)," + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
}
async UniTaskVoid AsyncLastUpdate()
{
UnityEngine.Debug.Log("Before async Yield(LastUpdate)," + Time.frameCount);
await UniTask.Yield(PlayerLoopTiming.LastUpdate);
UnityEngine.Debug.Log("After async Yield(LastUpdate)," + Time.frameCount);
}
async UniTaskVoid AsyncLastLast()
{
UnityEngine.Debug.Log("Before async Yield(LastPostLateUpdate)," + Time.frameCount);
await UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
UnityEngine.Debug.Log("After async Yield(LastPostLateUpdate)," + Time.frameCount);
}
async UniTaskVoid Yieldding()
{
await UniTask.Yield(PlayerLoopTiming.PreUpdate);
StartCoroutine(CoroutineRun());
}
async UniTaskVoid AsyncFixedUpdate()
{
while (true)
{
await UniTask.WaitForFixedUpdate();
Debug.Log("Async:" + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
}
}
IEnumerator CoroutineFixedUpdate()
{
while (true)
{
yield return new WaitForFixedUpdate();
Debug.Log("Coroutine:" + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
}
}
private void FixedUpdate()
{
// Debug.Log("FixedUpdate:" + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
}
async UniTaskVoid DelayFrame3_Pre()
{
await UniTask.Yield(PlayerLoopTiming.PreUpdate);
Debug.Log("Before framecount:" + Time.frameCount);
await UniTask.DelayFrame(3);
Debug.Log("After framecount:" + Time.frameCount);
}
async UniTaskVoid DelayFrame3_Post()
{
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
Debug.Log("Before framecount:" + Time.frameCount);
await UniTask.DelayFrame(3);
Debug.Log("After framecount:" + Time.frameCount);
}
async UniTask TestCoroutine()
{
await UniTask.Yield();
throw new Exception("foobarbaz");
}
async UniTask DelayCheck()
{
await UniTask.Yield(PlayerLoopTiming.PreUpdate);
Debug.Log("before");
var t = UniTask.Delay(TimeSpan.FromSeconds(1), ignoreTimeScale: false);
await t;
Debug.Log("after");
}
private async UniTaskVoid ExecuteAsync()
{
var req = UnityWebRequest.Get("https://google.com/");
var v = await req.SendWebRequest().ToUniTask();
// req.Dispose();
Debug.Log($"{v.isDone} {v.isHttpError} {v.isNetworkError}");
Debug.Log(v.downloadHandler.text);
}
private async void Go()
{
await UniTask.DelayFrame(0);
}
async UniTask Foo()
{
await UniTask.DelayFrame(10);
throw new Exception("yeah");
}
async void Nanika()
{
await UniTask.Yield();
Debug.Log("Here");
throw new Exception();
}
private void Awake()
{
PlayerLoopInfo.Inject();
PrepareCamera();
}
async UniTaskVoid Start()
{
await UniTask.SwitchToMainThread();
okButton.onClick.AddListener(() =>
{
ShootAsync().Forget();
});
// Nanika();
await UniTask.Yield();
// this.GetCancellationTokenOnDestroy()
//PlayerLoopInfo.Inject();
//_ = AsyncFixedUpdate();
//StartCoroutine(CoroutineFixedUpdate());
//StartCoroutine(TestCoroutine().ToCoroutine());
// Application.logMessageReceived += Application_logMessageReceived;
// var rp = new AsyncReactiveProperty<int>();
// rp.AddTo(this.GetCancellationTokenOnDestroy());
//var cts = new CancellationTokenSource();
// UniTask.Post(
// CancellationToken.
//UniTask.Delay(TimeSpan.FromSeconds(3)).
//okButton.onClick.AddListener(UniTask.UnityAction(async () =>
//{
// _ = ExecuteAsync();
// await UniTask.Yield();
// //await DelayCheck();
// /*
// UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType);
// StartCoroutine(CoroutineRun());
// StartCoroutine(CoroutineRun2());
// _ = AsyncRun();
// _ = AsyncLastUpdate();
// _ = AsyncLastLast();
// */
// //await UniTask.Yield();
// //_ = Test2();
// // EarlyUpdate.ExecuteMainThreadJobs
// // _ = Test2();
// //var t = await Resources.LoadAsync<TextAsset>(Application.streamingAssetsPath + "test.txt");
// //Debug.Log("LoadEnd" + PlayerLoopInfo.CurrentLoopType + ", " + (t != null));
// //Debug.Log("LoadEnd" + PlayerLoopInfo.CurrentLoopType + ", " + ((TextAsset)t).text);
// //await UniTask.Yield(PlayerLoopTiming.LastUpdate);
// //UnityEngine.Debug.Log("after update:" + Time.frameCount);
// ////await UniTask.NextFrame();
// ////await UniTask.Yield();
// ////UnityEngine.Debug.Log("after update nextframe:" + Time.frameCount);
// //StartCoroutine(CoroutineRun2());
// ////StartCoroutine(CoroutineRun());
// //UnityEngine.Debug.Log("FOO?");
// //_ = DelayFrame3_Pre();
// //await UniTask.Yield();
//}));
//cancelButton.onClick.AddListener(UniTask.UnityAction(async () =>
//{
// _ = DelayFrame3_Post();
// await UniTask.Yield();
// //await UniTask.Yield(PlayerLoopTiming.LastPreUpdate);
// //UnityEngine.Debug.Log("before update:" + Time.frameCount);
// //await UniTask.NextFrame();
// //await UniTask.Yield();
// //UnityEngine.Debug.Log("before update nextframe:" + Time.frameCount);
// //StartCoroutine(CoroutineRun());
// //UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType);
// //_ = Yieldding();
// //var cts = new CancellationTokenSource();
// //UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType + ":" + Time.frameCount);
// //var la = SceneManager.LoadSceneAsync("Scenes/ExceptionExamples").WithCancellation(cts.Token);
// ////cts.Cancel();
// //await la;
// //UnityEngine.Debug.Log("End LoadSceneAsync" + PlayerLoopInfo.CurrentLoopType + ":" + Time.frameCount);
//}));
//return;
//await UniTask.SwitchToMainThread();
//UniTaskAsyncEnumerable.EveryValueChanged(mcc, x => x.MyProperty)
// .Do(_ => { }, () => Debug.Log("COMPLETED"))
@@ -315,10 +573,29 @@ public class SandboxMain : MonoBehaviour
// .Forget();
//_ = Test1();
Test2().Forget();
//Test2().Forget();
//StartCoroutine(Test3("https://bing.com/"));
//bool flip = false;
//var rect = cancelButton.GetComponent<RectTransform>();
//var cts = new CancellationTokenSource();
//var ct = cts.Token;
//okButton.onClick.AddListener(UniTask.UnityAction(async () =>
//{
// await rect.DOMoveX(10f * (flip ? -1 : 1), 3).OnUpdate(() => { Debug.Log("UPDATE YEAH"); }).WithCancellation(ct);
// flip = !flip;
// // ok.
//}));
//cancelButton.onClick.AddListener(() =>
//{
// cts.Cancel();
//});
// DG.Tweening.Core.TweenerCore<int>
//Debug.Log("GO MOVEX");
//await okButton.GetComponent<RectTransform>().DOMoveX(-10.2f, 3).WithCancellation(CancellationToken.None);
@@ -329,7 +606,7 @@ public class SandboxMain : MonoBehaviour
//await okButton.GetComponent<RectTransform>().DOMoveY(10.2f, 3).WithCancellation(CancellationToken.None);
//Debug.Log("AGAIN END MOVE");
Debug.Log(Test().GetType().FullName);
//Debug.Log(Test().GetType().FullName);
@@ -383,11 +660,10 @@ public class SandboxMain : MonoBehaviour
//{
okButton.onClick.AddListener(UniTask.UnityAction(async () =>
{
await UniTask.Yield();
Debug.Log("Yeha");
}));
//foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
//{
// Debug.Log(type + ":" + size);
//}
//}).Forget();
@@ -396,7 +672,7 @@ public class SandboxMain : MonoBehaviour
//okButton.onClick.AddListener(UniTask.UnityAction(async () => await UniTask.Yield()));
PlayerLoopInfo.Inject();
//UpdateUniTask().Forget();
@@ -405,15 +681,21 @@ public class SandboxMain : MonoBehaviour
//await UniTask.Delay(TimeSpan.FromSeconds(1));
_ = ReturnToMainThreadTest();
// _ = ReturnToMainThreadTest();
//GameObject.Destroy(this.gameObject);
SynchronizationContext.Current.Post(_ =>
{
//UnityEngine.Debug.Log("Post:" + PlayerLoopInfo.CurrentLoopType);
}, null);
}
private void Application_logMessageReceived2(string condition, string stackTrace, LogType type)
{
throw new NotImplementedException();
}
private void Application_logMessageReceived1(string condition, string stackTrace, LogType type)
{
throw new NotImplementedException();
}
async UniTaskVoid UpdateUniTask()
@@ -703,6 +985,62 @@ public class SandboxMain : MonoBehaviour
// e.SetObserved();
// or other custom write code.
UnityEngine.Debug.LogError("Unobserved:" + e.Exception.ToString());
}
// GPU Screenshot Sample
void PrepareCamera()
{
Debug.Log("Support AsyncGPUReadback:" + SystemInfo.supportsAsyncGPUReadback);
var width = 480;
var height = 240;
var depth = 24;
mycamera.targetTexture = new RenderTexture(width, height, depth, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default)
{
antiAliasing = 8
};
mycamera.enabled = true;
//myRenderTexture = new RenderTexture(width, height, depth, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default)
//{
// antiAliasing = 8
//};
}
RenderTexture myRenderTexture;
async UniTask ShootAsync()
{
var rt = mycamera.targetTexture;
var req = await AsyncGPUReadback.Request(rt, 0);
Debug.Log("GPU Readback done?:" + req.done);
var rawByteArray = req.GetData<byte>().ToArray();
var graphicsFormat = rt.graphicsFormat;
var width = (uint)rt.width;
var height = (uint)rt.height;
Debug.Log("BytesSize:" + rawByteArray.Length);
var imageBytes = ImageConversion.EncodeArrayToPNG(rawByteArray, graphicsFormat, width, height);
File.WriteAllBytes("my_screenshot.png", imageBytes); // test
}
}
@@ -740,6 +1078,7 @@ public class PlayerLoopInfo
public static Type CurrentLoopType { get; private set; }
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
public static void Inject()
{
var system = PlayerLoop.GetCurrentPlayerLoop();

View File

@@ -239,6 +239,80 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 16537670}
m_CullTransparentMesh: 0
--- !u!1 &518730348
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 518730350}
- component: {fileID: 518730349}
m_Layer: 0
m_Name: Camera
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!20 &518730349
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 518730348}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 1
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_FocalLength: 50
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 60
orthographic: 0
orthographic size: 5
m_Depth: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!4 &518730350
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 518730348}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 488, y: 418, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &519420028
GameObject:
m_ObjectHideFlags: 0
@@ -278,9 +352,11 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: f0bc6c75abb2e0b47a25aa49bfd488ed, type: 3}
m_Name:
m_EditorClassIdentifier:
camera: {fileID: 518730349}
okButton: {fileID: 16537672}
cancelButton: {fileID: 628393011}
text: {fileID: 2101290655}
button: {fileID: 0}
--- !u!20 &519420031
Camera:
m_ObjectHideFlags: 0
@@ -597,7 +673,7 @@ Transform:
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 1
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1556045504
GameObject:
@@ -693,7 +769,7 @@ RectTransform:
- {fileID: 628393010}
- {fileID: 2101290654}
m_Father: {fileID: 0}
m_RootOrder: 2
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}

View File

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

View File

@@ -0,0 +1,27 @@
using Cysharp.Threading.Tasks;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class FooMonoBehaviour : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
private async UniTask Download(UnityWebRequest req, string filePath)
{
var foo = req.SendWebRequest();
var aaa = await foo;
Debug.Log(aaa);
//File.WriteAllText(filePath, req.downloadHandler.text ?? string.Empty);
}
}

View File

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

View File

@@ -0,0 +1,16 @@
{
"name": "TempAsm",
"references": [
"UniTask",
""
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

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

View File

@@ -126,15 +126,15 @@ namespace Cysharp.Threading.TasksTests
await ToaruCoroutineEnumerator(); // wait 5 frame:)
});
[UnityTest]
public IEnumerator JobSystem() => UniTask.ToCoroutine(async () =>
{
var job = new MyJob() { loopCount = 999, inOut = new NativeArray<int>(1, Allocator.TempJob) };
JobHandle.ScheduleBatchedJobs();
await job.Schedule();
job.inOut[0].Should().Be(999);
job.inOut.Dispose();
});
//[UnityTest]
//public IEnumerator JobSystem() => UniTask.ToCoroutine(async () =>
//{
// var job = new MyJob() { loopCount = 999, inOut = new NativeArray<int>(1, Allocator.TempJob) };
// JobHandle.ScheduleBatchedJobs();
// await job.Schedule();
// job.inOut[0].Should().Be(999);
// job.inOut.Dispose();
//});
class MyMyClass
{
@@ -197,7 +197,7 @@ namespace Cysharp.Threading.TasksTests
//await UniTask.SwitchToThreadPool();
@@ -269,7 +269,8 @@ namespace Cysharp.Threading.TasksTests
var first = Time.frameCount;
var canceled = await UniTask.DelayFrame(100, cancellationToken: cts.Token).SuppressCancellationThrow();
(Time.frameCount - first).Should().Be(11); // 10 frame canceled
var r = (Time.frameCount - first);
(9 < r && r < 11).Should().BeTrue();
canceled.Should().Be(true);
});
@@ -369,6 +370,24 @@ namespace Cysharp.Threading.TasksTests
throw new Exception("MyException");
}
[UnityTest]
public IEnumerator NextFrame1() => UniTask.ToCoroutine(async () =>
{
await UniTask.Yield(PlayerLoopTiming.LastUpdate);
var frame = Time.frameCount;
await UniTask.NextFrame();
Time.frameCount.Should().Be(frame + 1);
});
[UnityTest]
public IEnumerator NextFrame2() => UniTask.ToCoroutine(async () =>
{
await UniTask.Yield(PlayerLoopTiming.PreUpdate);
var frame = Time.frameCount;
await UniTask.NextFrame();
Time.frameCount.Should().Be(frame + 1);
});
[UnityTest]
public IEnumerator NestedEnumerator() => UniTask.ToCoroutine(async () =>
{
@@ -424,6 +443,7 @@ namespace Cysharp.Threading.TasksTests
public void OnError(Exception error) => OnErrorCalled = true;
}
#endif
#endif
}

View File

@@ -0,0 +1,96 @@
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Linq;
using FluentAssertions;
using NUnit.Framework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.TestTools;
namespace Cysharp.Threading.TasksTests
{
public class Cachelike
{
[UnityTest]
public IEnumerator Check() => UniTask.ToCoroutine(async () =>
{
{
var v = await CachedCheck("foo", 10);
v.Should().Be(10);
var v2 = await CachedCheck("bar", 20);
v2.Should().Be(20);
var v3 = await CachedCheck("baz", 30);
v3.Should().Be(30);
}
{
var v = await CachedCheck("foo", 10);
v.Should().Be(10);
var v2 = await CachedCheck("bar", 20);
v2.Should().Be(20);
var v3 = await CachedCheck("baz", 30);
v3.Should().Be(30);
}
{
var v = CachedCheck("foo", 10);
var v2 = CachedCheck("bar", 20);
var v3 = CachedCheck("baz", 30);
(await v).Should().Be(10);
(await v2).Should().Be(20);
(await v3).Should().Be(30);
}
{
var v = CachedCheck("foo", 10, true);
var v2 = CachedCheck("bar", 20, true);
var v3 = CachedCheck("baz", 30, true);
(await v).Should().Be(10);
(await v2).Should().Be(20);
(await v3).Should().Be(30);
}
});
static Dictionary<string, int> cacheDict = new Dictionary<string, int>();
async UniTask<int> CachedCheck(string cache, int value, bool yield = false)
{
if (!cacheDict.ContainsKey(cache))
{
await UniTask.Yield();
}
if (yield)
{
await UniTask.Yield();
}
if (cacheDict.TryGetValue(cache, out var v))
{
return v;
}
cacheDict.Add(cache, value);
return value;
}
}
}

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