mirror of
https://github.com/Cysharp/UniTask.git
synced 2026-05-15 11:30:09 +00:00
Compare commits
49 Commits
2.0.12-rc9
...
test_il2cp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
859c4d706f | ||
|
|
7289fe6e25 | ||
|
|
0c33977f5a | ||
|
|
4d4466e801 | ||
|
|
79330d7cdb | ||
|
|
680ce1098b | ||
|
|
2337d705ec | ||
|
|
d2880a818f | ||
|
|
86ea128bf4 | ||
|
|
a66f378622 | ||
|
|
265f88584b | ||
|
|
686394c861 | ||
|
|
8bad158ab4 | ||
|
|
5e59e7ec86 | ||
|
|
8bb0a48720 | ||
|
|
b4468b4eba | ||
|
|
0725bd1b30 | ||
|
|
ebd80243e0 | ||
|
|
f1ac469058 | ||
|
|
2e0b603d25 | ||
|
|
1d90a40f66 | ||
|
|
1bec3f507e | ||
|
|
1d5ecbb3ab | ||
|
|
e1a4aeb9da | ||
|
|
43f1bb4d85 | ||
|
|
8b7a0e9b15 | ||
|
|
da329e19d1 | ||
|
|
b8260d4e91 | ||
|
|
345e32aaf0 | ||
|
|
d7bef8c5b5 | ||
|
|
b2f82df4d3 | ||
|
|
83596b3d1f | ||
|
|
0022598a1c | ||
|
|
f4294d3752 | ||
|
|
a1444c0b39 | ||
|
|
e1d5359d73 | ||
|
|
239bf749b6 | ||
|
|
2bf3b1e172 | ||
|
|
d225de201f | ||
|
|
c3b8a3852d | ||
|
|
c31dab888e | ||
|
|
d4cf59bd2f | ||
|
|
d5edc3acd3 | ||
|
|
130286e8c2 | ||
|
|
a9baa52309 | ||
|
|
3001996298 | ||
|
|
bfcd18aabb | ||
|
|
96aa299e7e | ||
|
|
24faa34418 |
31
.github/workflows/build-docs.yml
vendored
Normal file
31
.github/workflows/build-docs.yml
vendored
Normal 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
|
||||
27
.github/workflows/build-release.yml
vendored
27
.github/workflows/build-release.yml
vendored
@@ -20,18 +20,17 @@ jobs:
|
||||
# set release tag(*.*.*) to env.GIT_TAG
|
||||
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
|
||||
|
||||
# build CommandTools first (use dotnet run command in ZLogger.csproj)
|
||||
- run: dotnet build -c Release ./tools/CommandTools/CommandTools.csproj
|
||||
# build and pack
|
||||
- run: dotnet build -c Release -p:Version=${{ env.GIT_TAG }}
|
||||
- run: dotnet test -c Release --no-build
|
||||
- run: dotnet pack ./src/ZLogger/ZLogger.csproj -c Release --no-build -p:Version=${{ env.GIT_TAG }}
|
||||
- run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ env.GIT_TAG }}
|
||||
|
||||
# Store artifacts.
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: nuget
|
||||
path: ./src/ZLogger/bin/Release/ZLogger.${{ env.GIT_TAG }}.nupkg
|
||||
|
||||
path: ./src/UniTask.NetCore/bin/Release/UniTask.${{ env.GIT_TAG }}.nupkg
|
||||
|
||||
build-unity:
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -57,15 +56,13 @@ jobs:
|
||||
# Execute scripts: Export Package
|
||||
- name: Export unitypackage
|
||||
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||
working-directory: src/ZLogger.Unity
|
||||
env:
|
||||
UNITY_PACKAGE_VERSION: ${{ env.GIT_TAG }}
|
||||
working-directory: src/UniTask
|
||||
|
||||
# Store artifacts.
|
||||
- uses: actions/upload-artifact@v1
|
||||
# Store artifacts.
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ZLogger.Unity.${{ env.GIT_TAG }}.unitypackage
|
||||
path: ./src/ZLogger.Unity/ZLogger.Unity.${{ env.GIT_TAG }}.unitypackage
|
||||
name: UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
path: ./src/UniTask/UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
|
||||
create-release:
|
||||
needs: [build-dotnet, build-unity]
|
||||
@@ -91,7 +88,7 @@ jobs:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Ver.${{ github.ref }}
|
||||
|
||||
# Download (All) Artifacts to current directory
|
||||
# Download(All) Artifacts to current directory
|
||||
- uses: actions/download-artifact@v2-preview
|
||||
|
||||
# Upload to NuGet
|
||||
@@ -103,6 +100,6 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./ZLogger.Unity.${{ env.GIT_TAG }}.unitypackage/ZLogger.Unity.${{ env.GIT_TAG }}.unitypackage
|
||||
asset_name: ZLogger.Unity.${{ env.GIT_TAG }}.unitypackage
|
||||
asset_path: ./UniTask.${{ env.GIT_TAG }}.unitypackage/UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
asset_name: UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
asset_content_type: application/octet-stream
|
||||
48
.gitignore
vendored
48
.gitignore
vendored
@@ -157,3 +157,51 @@ src/UniTask/UniTask.Tests.csproj
|
||||
src/UniTask/UniTask.Tests.Editor.csproj
|
||||
|
||||
src/UniTask/UniTask.*.unitypackage
|
||||
|
||||
src/UniTask/UniTask.Linq.csproj
|
||||
|
||||
src/UniTask/DOTween.Modules.csproj
|
||||
|
||||
src/UniTask/Unity.Addressables.csproj
|
||||
|
||||
src/UniTask/Unity.Addressables.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.Analytics.DataPrivacy.csproj
|
||||
|
||||
src/UniTask/Unity.Recorder.csproj
|
||||
|
||||
src/UniTask/Unity.Recorder.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.ResourceManager.csproj
|
||||
|
||||
src/UniTask/Unity.Rider.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.ScriptableBuildPipeline.csproj
|
||||
|
||||
src/UniTask/Unity.ScriptableBuildPipeline.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.TextMeshPro.csproj
|
||||
|
||||
src/UniTask/Unity.TextMeshPro.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.Timeline.csproj
|
||||
|
||||
src/UniTask/Unity.Timeline.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.VisualStudio.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.VSCode.Editor.csproj
|
||||
|
||||
src/UniTask/UnityEditor.CacheServer.csproj
|
||||
|
||||
src/UniTask/UnityEditor.TestRunner.csproj
|
||||
|
||||
src/UniTask/UnityEditor.UI.csproj
|
||||
|
||||
src/UniTask/UnityEngine.Advertisements.csproj
|
||||
|
||||
src/UniTask/UnityEngine.Monetization.csproj
|
||||
|
||||
src/UniTask/UnityEngine.TestRunner.csproj
|
||||
|
||||
src/UniTask/UnityEngine.UI.csproj
|
||||
|
||||
579
README.md
579
README.md
@@ -2,68 +2,105 @@ UniTask
|
||||
===
|
||||
[](https://github.com/Cysharp/UniTask/actions) [](https://github.com/Cysharp/UniTask/releases)
|
||||
|
||||
Provides an efficient async/await integration to Unity.
|
||||
Provides an efficient async/await integration to Unity.
|
||||
|
||||
* Struct based `UniTask<T>` and custom AsyncMethodBuilder to achive zero allocation
|
||||
* All Unity AsyncOperations and Coroutine to awaitable
|
||||
* PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc..) that enable to replace all coroutine operation
|
||||
* MonoBehaviour Message Events and uGUI Events as awaitable/async-enumerable
|
||||
* Completely run on Unity's PlayerLoop so don't use thread and run on WebGL, wasm, etc.
|
||||
* Asynchronous LINQ, with Channel and AsyncReactiveProperty
|
||||
* TaskTracker window to prevent memory leak
|
||||
* Highly compatible behaviour with Task/ValueTask/IValueTaskSource
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
## Table of Contents
|
||||
|
||||
- [Getting started](#getting-started)
|
||||
- [`UniTask<T>`](#unitaskt)
|
||||
- [Basics of UniTask and AsyncOperation](#basics-of-unitask-and-asyncoperation)
|
||||
- [Cancellation and Exception handling](#cancellation-and-exception-handling)
|
||||
- [Progress](#progress)
|
||||
- [PlayerLoop](#playerloop)
|
||||
- [async void vs async UniTaskVoid](#async-void-vs-async-unitaskvoid)
|
||||
- [UniTaskTracker](#unitasktracker)
|
||||
- [Reusable Promises](#reusable-promises)
|
||||
- [awaitable Events](#awaitable-events)
|
||||
- [async void vs async UniTask/UniTaskVoid](#async-void-vs-async-unitaskunitaskvoid)
|
||||
- [External Assets](#external-assets)
|
||||
- [AsyncEnumerable and Async LINQ](#asyncenumerable-and-async-linq)
|
||||
- [Awaitable Events](#awaitable-events)
|
||||
- [Channel](#channel)
|
||||
- [For Unit Testing](#for-unit-testing)
|
||||
- [Method List](#method-list)
|
||||
- [Pooling Configuration](#pooling-configuration)
|
||||
- [API References](#api-references)
|
||||
- [UPM Package](#upm-package)
|
||||
- [ECS, PlayerLoop](#ecs-playerloop)
|
||||
- [Install via git URL](#install-via-git-url)
|
||||
- [Install via OpenUPM](#install-via-openupm)
|
||||
- [.NET Core](#net-core)
|
||||
- [License](#license)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
|
||||
Getting started
|
||||
---
|
||||
Install package(`UniRx.Async.unitypackage`) is available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases) page.
|
||||
Install via [UPM package](#upm-package) or asset package(`UniTask.*.*.*.unitypackage`) available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases) page.
|
||||
|
||||
```csharp
|
||||
// extension awaiter/methods can be used by this namespace
|
||||
using UniRx.Async;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
// You can return type as struct UniTask<T>(or UniTask), it is unity specialized lightweight alternative of Task<T>
|
||||
// no(or less) allocation and fast excution for zero overhead async/await integrate with Unity
|
||||
// zero allocation and fast excution for zero overhead async/await integrate with Unity
|
||||
async UniTask<string> DemoAsync()
|
||||
{
|
||||
// You can await Unity's AsyncObject
|
||||
var asset = await Resources.LoadAsync<TextAsset>("foo");
|
||||
|
||||
// .ConfigureAwait accepts progress callback
|
||||
await SceneManager.LoadSceneAsync("scene2").ConfigureAwait(Progress.Create<float>(x => Debug.Log(x)));
|
||||
|
||||
var txt = (await UnityWebRequest.Get("https://...").SendWebRequest()).downloadHandler.text;
|
||||
await SceneManager.LoadSceneAsync("scene2");
|
||||
|
||||
// .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
|
||||
var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy());
|
||||
|
||||
// .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
|
||||
var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x)));
|
||||
|
||||
// await frame-based operation like coroutine
|
||||
await UniTask.DelayFrame(100);
|
||||
|
||||
// replacement of WaitForSeconds/WaitForSecondsRealtime
|
||||
// replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false);
|
||||
|
||||
// replacement of WaitForEndOfFrame(or other timing like yield return null, yield return WaitForFixedUpdate)
|
||||
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||
// yield any playerloop timing(PreUpdate, Update, LateUpdate, etc...)
|
||||
await UniTask.Yield(PlayerLoopTiming.PreLateUpdate);
|
||||
|
||||
// replacement of yield return null
|
||||
await UniTask.Yield();
|
||||
await UniTask.NextFrame();
|
||||
|
||||
// replacement of WaitForEndOfFrame(same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate))
|
||||
await UniTask.WaitForEndOfFrame();
|
||||
|
||||
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
|
||||
await UniTask.WaitForFixedUpdate();
|
||||
|
||||
// replacement of yield return WaitUntil
|
||||
await UniTask.WaitUntil(() => isActive == false);
|
||||
|
||||
// special helper of WaitUntil
|
||||
await UniTask.WaitUntilValueChanged(this, x => x.isActive);
|
||||
|
||||
// You can await IEnumerator coroutine
|
||||
await FooCoroutineEnumerator();
|
||||
|
||||
// You can await standard task
|
||||
await Task.Run(() => 100);
|
||||
|
||||
// Multithreading, run on ThreadPool under this code(use SwitchToMainThread, same as `ObserveOnMainThreadDispatcher`)
|
||||
// Multithreading, run on ThreadPool under this code
|
||||
await UniTask.SwitchToThreadPool();
|
||||
|
||||
/* work on ThreadPool */
|
||||
|
||||
// return to MainThread(same as `ObserveOnMainThread` in UniRx)
|
||||
await UniTask.SwitchToMainThread();
|
||||
|
||||
// get async webrequest
|
||||
async UniTask<string> GetTextAsync(UnityWebRequest req)
|
||||
{
|
||||
@@ -77,6 +114,9 @@ async UniTask<string> DemoAsync()
|
||||
|
||||
// concurrent async-wait and get result easily by tuple syntax
|
||||
var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3);
|
||||
|
||||
// shorthand of WhenAll, tuple can await directly
|
||||
var (google2, bing2, yahoo2) = await (task1, task2, task3);
|
||||
|
||||
// You can handle timeout easily
|
||||
await GetTextAsync(UnityWebRequest.Get("http://unity.com")).Timeout(TimeSpan.FromMilliseconds(300));
|
||||
@@ -86,47 +126,38 @@ async UniTask<string> DemoAsync()
|
||||
}
|
||||
```
|
||||
|
||||
`UniTask<T>`
|
||||
Basics of UniTask and AsyncOperation
|
||||
---
|
||||
UniTask feature rely on C# 7.0([task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)) so required Unity version is after `Unity 2018.3`.
|
||||
UniTask feature rely on C# 7.0([task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)) so required Unity version is after `Unity 2018.3`, officialy lower support version is `Unity 2018.4.13f1`.
|
||||
|
||||
Why UniTask(custom task-like object) is required? Because Task is too heavy, not matched to Unity threading(single-thread). UniTask does not use thread and SynchronizationContext because almost Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It acquires more fast and more less allocation, completely integrated with Unity.
|
||||
Why UniTask(custom task-like object) is required? Because Task is too heavy, not matched to Unity threading(single-thread). UniTask does not use thread and SynchronizationContext/ExecutionContext because almost Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It acquires more fast and more less allocation, completely integrated with Unity.
|
||||
|
||||
> More details, please see this slide: [Deep Dive async/await in Unity with UniTask(EN)
|
||||
](https://www.slideshare.net/neuecc/deep-dive-asyncawait-in-unity-with-unitasken)
|
||||
You can await `AsyncOperation`, `ResourceRequest`, `AssetBundleRequest`, `AssetBundleCreateRequest`, `UnityWebRequestAsyncOperation`, `IEnumerator` and others when `using Cysharp.Threading.Tasks;`.
|
||||
|
||||
You can await `AsyncOperation`, `ResourceRequest`, `UnityWebRequestAsyncOperation`, `IEnumerator` and others when using `UniRx.Async`.
|
||||
|
||||
`UniTask.Delay`, `UniTask.Yield`, `UniTask.Timeout` that is frame-based timer operators(no uses thread so works on WebGL publish) driven by custom PlayerLoop(Unity 2018 experimental feature). In default, UniTask initialize automatically when application begin, but it is override all. If you want to append PlayerLoop, please call `PlayerLoopHelper.Initialize(ref yourCustomizedPlayerLoop)` manually.
|
||||
|
||||
> Before Unity 2019.3, Unity does not have `PlayerLooop.GetCurrentPlayerLoop` so you can't use with Unity ECS package in default. If you want to use with ECS and before Unity 2019.3, you can use this hack below.
|
||||
UniTask provides three pattern of extension methods.
|
||||
|
||||
```csharp
|
||||
// Get ECS Loop.
|
||||
var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
|
||||
|
||||
// Setup UniTask's PlayerLoop.
|
||||
PlayerLoopHelper.Initialize(ref playerLoop);
|
||||
* await asyncOperation;
|
||||
* .WithCancellation(CancellationToken);
|
||||
* .ToUniTask(IProgress, PlayerLoopTiming, CancellationToken);
|
||||
```
|
||||
|
||||
`UniTask.WhenAll`, `UniTask.WhenAny` is like Task.WhenAll/WhenAny but return type is more useful.
|
||||
`WithCancellation` is a simple version of `ToUniTask`, both returns `UniTask`. Details of cancellation, see: [Cancellation and Exception handling](#cancellation-and-exception-handling) section.
|
||||
|
||||
`UniTask.ctor(Func<UniTask>)` is like the embeded [`AsyncLazy<T>`](https://blogs.msdn.microsoft.com/pfxteam/2011/01/15/asynclazyt/)
|
||||
> Note: WithCancellation is returned from native timing of PlayerLoop but ToUniTask is returned from specified PlayerLoopTiming. Details of timing, see: [PlayerLoop](#playerloop) section.
|
||||
|
||||
The type of `UniTask` can use utility like `UniTask.WhenAll`, `UniTask.WhenAny`. It is like Task.WhenAll/WhenAny but return type is more useful, returns value tuple so can deconsrtuct each result and pass multiple type.
|
||||
|
||||
```csharp
|
||||
public class SceneAssets
|
||||
{
|
||||
public readonly UniTask<Sprite> Front;
|
||||
public readonly UniTask<Sprite> Background;
|
||||
public readonly UniTask<Sprite> Effect;
|
||||
|
||||
public SceneAssets()
|
||||
{
|
||||
// ctor(Func) overload is AsyncLazy, initialized once when await.
|
||||
// and after it, await returns zero-allocation value immediately.
|
||||
Front = new UniTask<Sprite>(() => LoadAsSprite("foo"));
|
||||
Background = new UniTask<Sprite>(() => LoadAsSprite("bar"));
|
||||
Effect = new UniTask<Sprite>(() => LoadAsSprite("baz"));
|
||||
// parallel load.
|
||||
var (a, b, c) = await UniTask.WhenAll(
|
||||
LoadAsSprite("foo"),
|
||||
LoadAsSprite("bar"),
|
||||
LoadAsSprite("baz"));
|
||||
}
|
||||
|
||||
async UniTask<Sprite> LoadAsSprite(string path)
|
||||
@@ -152,13 +183,32 @@ public UniTask<int> WrapByUniTaskCompletionSource()
|
||||
}
|
||||
```
|
||||
|
||||
You can convert Task -> UniTask: `AsUniTask`, `UniTask` -> `UniTask<AsyncUnit>`: `AsAsyncUnitUniTask`(this is useful to use WhenAll/WhenAny), `UniTask<T>` -> `UniTask`: `AsUniTask`.
|
||||
You can convert Task -> UniTask: `AsUniTask`, `UniTask` -> `UniTask<AsyncUnit>`: `AsAsyncUnitUniTask`, `UniTask<T>` -> `UniTask`: `AsUniTask`. `UniTask<T>` -> `UniTask`'s conversion cost is free.
|
||||
|
||||
If you want to convert async to coroutine, you can use `UniTask.ToCoroutine`, this is useful to use only allow coroutine system.
|
||||
If you want to convert async to coroutine, you can use `.ToCoroutine()`, this is useful to use only allow coroutine system.
|
||||
|
||||
UniTask can not await twice. This is a similar constraint to the [ValueTask/IValueTaskSource](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1?view=netcore-3.1) introduced in .NET Standard 2.1.
|
||||
|
||||
> The following operations should never be performed on a ValueTask<TResult> instance:
|
||||
>
|
||||
> * Awaiting the instance multiple times.
|
||||
> * Calling AsTask multiple times.
|
||||
> * Using .Result or .GetAwaiter().GetResult() when the operation hasn't yet completed, or using them multiple times.
|
||||
> * Using more than one of these techniques to consume the instance.
|
||||
>
|
||||
> If you do any of the above, the results are undefined.
|
||||
|
||||
```csharp
|
||||
var task = UniTask.DelayFrame(10);
|
||||
await task;
|
||||
await task; // NG, throws Exception
|
||||
```
|
||||
|
||||
Store to the class field, you can use `UniTask.Lazy` that gurantee call multipletimes. `.Prevent()` allows for multiple calls (internally cached results). This is useful when multiple calls in a function scope.
|
||||
|
||||
Cancellation and Exception handling
|
||||
---
|
||||
Some UniTask factory methods have `CancellationToken cancellation = default(CancellationToken)` parameter. Andalso some async operation for unity have `ConfigureAwait(..., CancellationToken cancellation = default(CancellationToken))` extension methods.
|
||||
Some UniTask factory methods have `CancellationToken cancellationToken = default` parameter. Andalso some async operation for unity have `WithCancellation(CancellationToken)` and `ToUniTask(..., CancellationToken cancellation = default)` extension methods.
|
||||
|
||||
You can pass `CancellationToken` to parameter by standard [`CancellationTokenSource`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource).
|
||||
|
||||
@@ -170,7 +220,7 @@ cancelButton.onClick.AddListener(() =>
|
||||
cts.Cancel();
|
||||
});
|
||||
|
||||
await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().ConfigureAwait(cancellation: cts.Token);
|
||||
await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().WithCancellation(cts.Token);
|
||||
|
||||
await UniTask.DelayFrame(1000, cancellationToken: cts.Token);
|
||||
```
|
||||
@@ -184,7 +234,7 @@ await UniTask.DelayFrame(1000, cancellationToken: this.GetCancellationTokenOnDes
|
||||
|
||||
When detect cancellation, all methods throws `OperationCanceledException` and propagate to upstream. `OperationCanceledException` is special exception, if not handled this exception, finally it is propagated to `UniTaskScheduler.UnobservedTaskException`.
|
||||
|
||||
Default behaviour of received unhandled exception is write log as warning. Log level can change by `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to change custom beavhiour, set action to `UniTaskScheduler.UnobservedTaskException.`
|
||||
Default behaviour of received unhandled exception is write log as exception. Log level can change by `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to change custom beavhiour, set action to `UniTaskScheduler.UnobservedTaskException.`
|
||||
|
||||
If you want to cancel behaviour in async UniTask method, throws `OperationCanceledException` manually.
|
||||
|
||||
@@ -223,23 +273,23 @@ if (isCanceled)
|
||||
}
|
||||
```
|
||||
|
||||
Note: Only suppress throws if you call it directly into the most source method.
|
||||
Note: Only suppress throws if you call it directly into the most source method. Otherwise, the return value will be converted, but the entire pipeline will not be suppressed throws.
|
||||
|
||||
Progress
|
||||
---
|
||||
Some async operation for unity have `ConfigureAwait(IProgress<float> progress = null, ...)` extension methods.
|
||||
Some async operation for unity have `ToUniTask(IProgress<float> progress = null, ...)` extension methods.
|
||||
|
||||
```csharp
|
||||
var progress = Progress.Create<float>(x => Debug.Log(x));
|
||||
|
||||
var request = await UnityWebRequest.Get("http://google.co.jp")
|
||||
.SendWebRequest()
|
||||
.ConfigureAwait(progress: progress);
|
||||
.ToUniTask(progress: progress);
|
||||
```
|
||||
|
||||
Should not use `new System.Progress<T>`, because it allocate every times. Use `UniRx.Async.Progress` instead. Progress factory has two methods, `Create` and `CreateOnlyValueChanged`. `CreateOnlyValueChanged` calls only when progress value changed. Should not use `new System.Progress<T>`, it allocate every times.
|
||||
You should not use standard `new System.Progress<T>`, because it causes allocation every times. Use `Cysharp.Threading.Tasks.Progress` instead. This progress factory has two methods, `Create` and `CreateOnlyValueChanged`. `CreateOnlyValueChanged` calls only when progress value changed.
|
||||
|
||||
Implements interface is more better.
|
||||
Implements IProgress interface to caller is more better, there is no allocation of lambda.
|
||||
|
||||
```csharp
|
||||
public class Foo : MonoBehaviour, IProgress<float>
|
||||
@@ -253,16 +303,135 @@ public class Foo : MonoBehaviour, IProgress<float>
|
||||
{
|
||||
var request = await UnityWebRequest.Get("http://google.co.jp")
|
||||
.SendWebRequest()
|
||||
.ConfigureAwait(progress: this); // pass this
|
||||
.ToUniTask(progress: this); // pass this
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
PlayerLoop
|
||||
---
|
||||
UniTask is run on custom [PlayerLoop](https://docs.unity3d.com/ScriptReference/LowLevel.PlayerLoop.html). UniTask's playerloop based method(such as `Delay`, `DelayFrame`, `asyncOperation.ToUniTask`, etc...) accepts this `PlayerLoopTiming`.
|
||||
|
||||
```csharp
|
||||
public enum PlayerLoopTiming
|
||||
{
|
||||
Initialization = 0,
|
||||
LastInitialization = 1,
|
||||
|
||||
EarlyUpdate = 2,
|
||||
LastEarlyUpdate = 3,
|
||||
|
||||
FixedUpdate = 4,
|
||||
LastFixedUpdate = 5,
|
||||
|
||||
PreUpdate = 6,
|
||||
LastPreUpdate = 7,
|
||||
|
||||
Update = 8,
|
||||
LastUpdate = 9,
|
||||
|
||||
PreLateUpdate = 10,
|
||||
LastPreLateUpdate = 11,
|
||||
|
||||
PostLateUpdate = 12,
|
||||
LastPostLateUpdate = 13
|
||||
}
|
||||
```
|
||||
|
||||
It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop.
|
||||
|
||||
`PlayerLoopTiming.Update` is similar as `yield return null` in coroutine, but it is called before Update(Update and uGUI events(button.onClick, etc...) are called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). `PlayerLoopTiming.FixedUpdate` is similar as `WaitForFixedUpdate`, `PlayerLoopTiming.LastPostLateUpdate` is similar as `WaitForEndOfFrame` in coroutine.
|
||||
|
||||
`yield return null` and `UniTask.Yield` is similar but different. `yield return null` always return next frame but `UniTask.Yield` return next called, that is, call `UniTask.Yield(PlayerLoopTiming.Update)` on `PreUpdate`, it returns same frame. `UniTask.NextFrame()` gurantees return next frame, this would be expected to behave exactly the same as `yield return null`.
|
||||
|
||||
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and run on YieldRunner. It is most lightweight and faster.
|
||||
|
||||
AsyncOperation is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after called, loaded scene 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.
|
||||
|
||||

|
||||
|
||||
In default, UniTask's PlayerLoop is initialized at `[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]`.
|
||||
|
||||
The order in which methods are called in BeforeSceneLoad is indeterminate, so if you want to use UniTask in other BeforeSceneLoad methods, you should try to initialize it before this.
|
||||
|
||||
```csharp
|
||||
// AfterAssembliesLoaded is called before BeforeSceneLoad
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
|
||||
public static void InitUniTaskLoop()
|
||||
{
|
||||
var loop = PlayerLoop.GetCurrentPlayerLoop();
|
||||
Cysharp.Threading.Tasks.PlayerLoopHelper.Initialize(ref loop);
|
||||
}
|
||||
```
|
||||
|
||||
If you import Unity's `Entities` package, that reset custom player loop to default at `BeforeSceneLoad` and inject ECS's loop. When Unity call ECS's inject method after UniTask's initialize method, UniTask will no longer work.
|
||||
|
||||
To solve this issue, you can re-initialize UniTask PlayerLoop after ECS initialized.
|
||||
|
||||
```csharp
|
||||
// Get ECS Loop.
|
||||
var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
|
||||
|
||||
// Setup UniTask's PlayerLoop.
|
||||
PlayerLoopHelper.Initialize(ref playerLoop);
|
||||
```
|
||||
|
||||
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()`.
|
||||
|
||||
```csharp
|
||||
public async UniTaskVoid FireAndForgetMethod()
|
||||
{
|
||||
// do anything...
|
||||
await UniTask.Yield();
|
||||
}
|
||||
|
||||
public void Caller()
|
||||
{
|
||||
FireAndForgetMethod().Forget();
|
||||
}
|
||||
```
|
||||
|
||||
Also UniTask have `Forget` method, it is similar with UniTaskVoid and same effects with it. However still UniTaskVoid is more efficient if completely do not use await。
|
||||
|
||||
```csharp
|
||||
public async UniTask DoAsync()
|
||||
{
|
||||
// do anything...
|
||||
await UniTask.Yield();
|
||||
}
|
||||
|
||||
public void Caller()
|
||||
{
|
||||
DoAsync().Forget();
|
||||
}
|
||||
```
|
||||
|
||||
Using async lambda in register event, it is used `async void`. To avoid it, you can use `UniTask.Action` or `UniTask.UnityAction` that creates delegate via `async UniTaskVoid` lambda.
|
||||
|
||||
```csharp
|
||||
Action actEvent;
|
||||
UnityAction unityEvent; // especially used in uGUI
|
||||
|
||||
// Bad: async void
|
||||
actEvent += async () => { };
|
||||
unityEvent += async () => { };
|
||||
|
||||
// Ok: create Action delegate by lambda
|
||||
actEvent += UniTask.Action(async () => { await UniTask.Yield(); });
|
||||
unityEvent += UniTask.UnityAction(async () => { await UniTask.Yield(); });
|
||||
```
|
||||
|
||||
UniTaskTracker
|
||||
---
|
||||
useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTask Tracker`.
|
||||
|
||||

|
||||

|
||||
|
||||
* Enable AutoReload(Toggle) - Reload automatically.
|
||||
* Reload - Reload view.
|
||||
@@ -272,38 +441,89 @@ useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTa
|
||||
|
||||
For debug use, enable tracking and capture stacktrace is useful but it it decline performance. Recommended usage is enable both to find task leak, and when done, finally disable both.
|
||||
|
||||
Reusable Promises
|
||||
External Assets
|
||||
---
|
||||
Some UniTask factory can reuse to reduce allocation. The list is `Yield`, `Delay`, `DelayFrame`, `WaitUntil`, `WaitWhile`, `WaitUntilValueChanged`.
|
||||
In default, UniTask supports DOTween 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.
|
||||
|
||||
```csharp
|
||||
var reusePromise = UniTask.DelayFrame(10);
|
||||
// sequential
|
||||
await transform.DOMoveX(2, 10);
|
||||
await transform.DOMoveZ(5, 20);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
// parallel with cancellation
|
||||
var ct = this.GetCancellationTokenOnDestroy();
|
||||
|
||||
await UniTask.WhenAll(
|
||||
transform.DOMoveX(10, 3).WithCancellation(ct),
|
||||
transform.DOScale(10, 3).WithCancellation(ct));
|
||||
```
|
||||
|
||||
AsyncEnumerable and Async LINQ
|
||||
---
|
||||
Unity 2020.2.0a12 supports C# 8.0 so you can use `await foreach`. This is the new Update notation in async era.
|
||||
|
||||
```csharp
|
||||
// Unity 2020.2.0a12, C# 8.0
|
||||
await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate(token))
|
||||
{
|
||||
await reusePromise;
|
||||
Debug.Log("Update() " + Time.frameCount);
|
||||
}
|
||||
```
|
||||
|
||||
awaitable Events
|
||||
---
|
||||
Unity events can await like `OnClickAsync`, `OnCollisionEnterAsync`. It can use by `UniRx.Async.Triggers`.
|
||||
In a C# 7.3 environment, you can use the `ForEachAsync` method to work in almost the same way.
|
||||
|
||||
```csharp
|
||||
using UniRx.Async.Triggers;
|
||||
|
||||
async UniTask TripleClick(CancellationToken token)
|
||||
// C# 7.3(Unity 2018.3~)
|
||||
await UniTaskAsyncEnumerable.EveryUpdate(token).ForEachAsync(_ =>
|
||||
{
|
||||
await button.OnClickAsync(token);
|
||||
await button.OnClickAsync(token);
|
||||
await button.OnClickAsync(token);
|
||||
Debug.Log("Update() " + Time.frameCount);
|
||||
});
|
||||
```
|
||||
|
||||
UniTaskAsyncEnumerable implements asynchronous LINQ, similar to LINQ in `IEnumerable<T>` or Rx in `IObservable<T>`. All standard LINQ query operators can be applied to asynchronous streams. For example, the following code shows how to apply a Where filter to a button-click asynchronous stream that runs once every two clicks.
|
||||
|
||||
```csharp
|
||||
await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsync(_ =>
|
||||
{
|
||||
});
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
The method with Func as an argument has three additional overloads, `***Await`, `***AwaitWithCancellation`.
|
||||
|
||||
```csharp
|
||||
Select(Func<T, TR> selector)
|
||||
SelectAwait(Func<T, UniTask<TR>> selector)
|
||||
SelectAwaitWithCancellation(Func<T, CancellationToken, UniTask<TR>> selector)
|
||||
```
|
||||
|
||||
If you want to use the `async` method inside the func, use the `***Await` or `***AwaitWithCancellation`.
|
||||
|
||||
Awaitable Events
|
||||
---
|
||||
All uGUI component implements `***AsAsyncEnumerable` to convert asynchronous streams of events.
|
||||
|
||||
```csharp
|
||||
async UniTask TripleClick()
|
||||
{
|
||||
// In default, used button.GetCancellationTokenOnDestroy to manage lieftime of async
|
||||
await button.OnClickAsync();
|
||||
await button.OnClickAsync();
|
||||
await button.OnClickAsync();
|
||||
Debug.Log("Three times clicked");
|
||||
}
|
||||
|
||||
// more efficient way
|
||||
async UniTask TripleClick(CancellationToken token)
|
||||
async UniTask TripleClick()
|
||||
{
|
||||
using (var handler = button.GetAsyncClickEventHandler(token))
|
||||
using (var handler = button.GetAsyncClickEventHandler())
|
||||
{
|
||||
await handler.OnClickAsync();
|
||||
await handler.OnClickAsync();
|
||||
@@ -311,11 +531,126 @@ async UniTask TripleClick(CancellationToken token)
|
||||
Debug.Log("Three times clicked");
|
||||
}
|
||||
}
|
||||
|
||||
// use async LINQ
|
||||
async UniTask TripleClick(CancellationToken token)
|
||||
{
|
||||
await button.OnClickAsAsyncEnumerable().Take(3).Last();
|
||||
Debug.Log("Three times clicked");
|
||||
}
|
||||
|
||||
// use async LINQ2
|
||||
async UniTask TripleClick(CancellationToken token)
|
||||
{
|
||||
await button.OnClickAsAsyncEnumerable().Take(3).ForEachAsync(_ =>
|
||||
{
|
||||
Debug.Log("Every clicked");
|
||||
});
|
||||
Debug.Log("Three times clicked, complete.");
|
||||
}
|
||||
```
|
||||
|
||||
async void vs async UniTask/UniTaskVoid
|
||||
All MonoBehaviour message events can convert async-streams by `AsyncTriggers` that can enable by `using Cysharp.Threading.Tasks.Triggers;`.
|
||||
|
||||
```csharp
|
||||
using Cysharp.Threading.Tasks.Triggers;
|
||||
|
||||
async UniTaskVoid MonitorCollision()
|
||||
{
|
||||
await gameObject.OnCollisionEnterAsync();
|
||||
Debug.Log("Collision Enter");
|
||||
/* do anything */
|
||||
|
||||
await gameObject.OnCollisionExitAsync();
|
||||
Debug.Log("Collision Exit");
|
||||
}
|
||||
```
|
||||
|
||||
Similar as uGUI event, AsyncTrigger can get by `GetAsync***Trigger` and trigger it self is UniTaskAsyncEnumerable.
|
||||
|
||||
```csharp
|
||||
// use await multiple times, get AsyncTriggerHandler is more efficient.
|
||||
using(var trigger = this.GetOnCollisionEnterAsyncHandler())
|
||||
{
|
||||
await OnCollisionEnterAsync();
|
||||
await OnCollisionEnterAsync();
|
||||
await OnCollisionEnterAsync();
|
||||
}
|
||||
|
||||
// every moves.
|
||||
await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
|
||||
{
|
||||
});
|
||||
```
|
||||
|
||||
`AsyncReactiveProperty`, `AsyncReadOnlyReactiveProperty` is UniTask version of UniTask's ReactiveProperty.
|
||||
|
||||
`BindTo` extension method of `IUniTaskAsyncEnumerable<T>` for binding asynchronous stream values to Unity components(Text/Selectable/TMP/Text).
|
||||
|
||||
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 UniTask.Delay(TimeSpan.FromSeconds(3));
|
||||
}
|
||||
```
|
||||
|
||||
It is useful(prevent double-click) but not useful in sometimes.
|
||||
|
||||
Using `Queue()` method, which will also queue events during asynchronous processing.
|
||||
|
||||
```csharp
|
||||
// queued message in asynchronous processing
|
||||
await button.OnClickAsAsyncEnumerable().Queue().ForEachAwaitAsync()
|
||||
{
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(3));
|
||||
}
|
||||
```
|
||||
|
||||
Channel
|
||||
---
|
||||
`async void` is standard C# system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is lightweight version of `async UniTask` because it does not have awaitable completion. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
|
||||
`Channel` is same as [System.Threading.Tasks.Channels](https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.channels?view=netcore-3.1) that is similar as GoLang Channel.
|
||||
|
||||
Currently only supports multiple-producer, single-consumer unbounded channel. It can create by `Channel.CreateSingleConsumerUnbounded<T>()`.
|
||||
|
||||
For producer(`.Writer`), `TryWrite` to push value and `TryComplete` to complete channel. For consumer(`.Reader`), `TryRead`, `WaitToReadAsync`, `ReadAsync`, `Completion` and `ReadAllAsync` to read queued messages.
|
||||
|
||||
`ReadAllAsync` returns `IUniTaskAsyncEnumerable<T>` so query LINQ operators. Reader only allows single-consumer but use `.Publish()` query operator to enable multicast message. For example, make pub/sub utility.
|
||||
|
||||
```csharp
|
||||
public class AsyncMessageBroker<T> : IDisposable
|
||||
{
|
||||
Channel<T> channel;
|
||||
|
||||
IConnectableUniTaskAsyncEnumerable<T> multicastSource;
|
||||
IDisposable connection;
|
||||
|
||||
public AsyncMessageBroker()
|
||||
{
|
||||
channel = Channel.CreateSingleConsumerUnbounded<T>();
|
||||
multicastSource = channel.Reader.ReadAllAsync().Publish();
|
||||
connection = multicastSource.Connect(); // Publish returns IConnectableUniTaskAsyncEnumerable.
|
||||
}
|
||||
|
||||
public void Publish(T value)
|
||||
{
|
||||
channel.Writer.TryWrite(value);
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerable<T> Subscribe()
|
||||
{
|
||||
return multicastSource;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
channel.Writer.TryComplete();
|
||||
connection.Dispose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For Unit Testing
|
||||
---
|
||||
@@ -342,46 +677,92 @@ public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
|
||||
});
|
||||
```
|
||||
|
||||
Method List
|
||||
UniTask itself's unit test is written by Unity Test Runner and [Cysharp/RuntimeUnitTestToolkit](https://github.com/Cysharp/RuntimeUnitTestToolkit) to check on CI and IL2CPP working.
|
||||
|
||||
Pooling Configuration
|
||||
---
|
||||
UniTask is aggressively caching async promise object to achive zero allocation. In default, cache all promises but you can configure `TaskPool.SetMaxPoolSize` to your value, the value indicates cache size per type. `TaskPool.GetCacheSizeInfo` returns current cached object in pool.
|
||||
|
||||
```csharp
|
||||
UniTask.WaitUntil
|
||||
UniTask.WaitWhile
|
||||
UniTask.WaitUntilValueChanged
|
||||
UniTask.SwitchToThreadPool
|
||||
UniTask.SwitchToTaskPool
|
||||
UniTask.SwitchToMainThread
|
||||
UniTask.SwitchToSynchronizationContext
|
||||
UniTask.Yield
|
||||
UniTask.Run
|
||||
UniTask.Lazy
|
||||
UniTask.Void
|
||||
UniTask.ConfigureAwait
|
||||
UniTask.DelayFrame
|
||||
UniTask.Delay(..., bool ignoreTimeScale = false, ...) parameter
|
||||
foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
|
||||
{
|
||||
Debug.Log(type + ":" + size);
|
||||
}
|
||||
```
|
||||
|
||||
API References
|
||||
---
|
||||
UniTask's API References is hosted at [cysharp.github.io/UniTask](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.html) by [DocFX](https://dotnet.github.io/docfx/) and [Cysharp/DocfXTemplate](https://github.com/Cysharp/DocfxTemplate).
|
||||
|
||||
For example, UniTask's factory methods can see at [UniTask#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.UniTask.html#methods-1). UniTaskAsyncEnumerable's factory/extension methods can see at [UniTaskAsyncEnumerable#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.Linq.UniTaskAsyncEnumerable.html#methods-1).
|
||||
|
||||
UPM Package
|
||||
---
|
||||
After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of git package. You can add `https://github.com/Cysharp/UniTask.git?path=Assets/UniRx.Async` to Package Manager
|
||||
### Install via git URL
|
||||
|
||||
After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of git package. You can add `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask` to Package Manager
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=Assets/UniRx.Async"` to `Packages/manifest.json`.
|
||||
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"` to `Packages/manifest.json`.
|
||||
|
||||
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#1.3.0`. For example `https://github.com/Cysharp/UniTask.git?path=Assets/UniRx.Async#1.3.1`.
|
||||
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#2.0.13`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.0.13`.
|
||||
|
||||
ECS, PlayerLoop
|
||||
### Install via OpenUPM
|
||||
|
||||
The package is available on the [openupm registry](https://openupm.com). It's recommended to install it via [openupm-cli](https://github.com/openupm/openupm-cli).
|
||||
|
||||
```
|
||||
openupm add com.cysharp.unitask
|
||||
```
|
||||
|
||||
.NET Core
|
||||
---
|
||||
TODO:
|
||||
For .NET Core, use NuGet.
|
||||
|
||||
> PM> Install-Package [UniTask](https://www.nuget.org/packages/UniTask)
|
||||
|
||||
UniTask of .NET Core version is a subset of Unity UniTask, removed PlayerLoop dependent methods.
|
||||
|
||||
It runs at higher performance than the standard Task/ValueTask, but you should be careful to ignore the ExecutionContext/SynchronizationContext when using it. `AysncLocal` also does not work because it ignores ExecutionContext.
|
||||
|
||||
If you use UniTask internally, but provide ValueTask as an external API, you can write like the following(Inspired by [PooledAwait](https://github.com/mgravell/PooledAwait)).
|
||||
|
||||
```csharp
|
||||
var loop = PlayerLoop.GetCurrentPlayerLoop();
|
||||
PlayerLoopHelper.Initialize(ref loop);
|
||||
public class ZeroAllocAsyncAwaitInDotNetCore
|
||||
{
|
||||
public ValueTask<int> DoAsync(int x, int y)
|
||||
{
|
||||
return Core(this, x, y);
|
||||
|
||||
static async UniTask<int> Core(ZeroAllocAsyncAwaitInDotNetCore self, int x, int y)
|
||||
{
|
||||
// do anything...
|
||||
await Task.Delay(TimeSpan.FromSeconds(x + y));
|
||||
await UniTask.Yield();
|
||||
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UniTask does not return to original SynchronizationContext but you can use helper `ReturnToCurrentSynchronizationContext`.
|
||||
public ValueTask TestAsync()
|
||||
{
|
||||
await using (UniTask.ReturnToCurrentSynchronizationContext())
|
||||
{
|
||||
await UniTask.SwitchToThreadPool();
|
||||
// do anything..
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
.NET Core version is intended to allow users to use UniTask as an interface when sharing code with Unity (such as [Cysharp/MagicOnion](https://github.com/Cysharp/MagicOnion/)). .NET Core version of UniTask enables smooth code sharing.
|
||||
|
||||
Utility methods such as WhenAll which is equivalent to UniTask are provided as [Cysharp/ValueTaskSupplement](https://github.com/Cysharp/ValueTaskSupplement).
|
||||
|
||||
License
|
||||
---
|
||||
This library is under the MIT License.
|
||||
This library is under the MIT License.
|
||||
|
||||
10
docs/.gitignore
vendored
Normal file
10
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
###############
|
||||
# folder #
|
||||
###############
|
||||
/**/DROP/
|
||||
/**/TEMP/
|
||||
/**/packages/
|
||||
/**/bin/
|
||||
/**/obj/
|
||||
_site
|
||||
_DocfxTemplate
|
||||
5
docs/api/.gitignore
vendored
Normal file
5
docs/api/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
###############
|
||||
# temp file #
|
||||
###############
|
||||
*.yml
|
||||
.manifest
|
||||
70
docs/docfx.json
Normal file
70
docs/docfx.json
Normal 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
8
docs/index.md
Normal 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
11
docs/toc.yml
Normal 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
|
||||
BIN
src/UniTask.NetCore/Icon.png
Normal file
BIN
src/UniTask.NetCore/Icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
@@ -1,6 +1,8 @@
|
||||
#pragma warning disable 0649
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks.Sources;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
@@ -8,12 +10,20 @@ 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)
|
||||
@@ -26,5 +36,63 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
return task.AsTask().AsUniTask(useCurrentSynchronizationContext);
|
||||
}
|
||||
|
||||
#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
|
||||
}
|
||||
}
|
||||
|
||||
39
src/UniTask.NetCore/NetCore/UniTask.Delay.cs
Normal file
39
src/UniTask.NetCore/NetCore/UniTask.Delay.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
//using Cysharp.Threading.Tasks.Internal;
|
||||
//using System;
|
||||
//using System.Collections.Concurrent;
|
||||
//using System.Runtime.CompilerServices;
|
||||
//using System.Threading;
|
||||
|
||||
//namespace Cysharp.Threading.Tasks
|
||||
//{
|
||||
// public partial struct UniTask
|
||||
// {
|
||||
// public static UniTask Delay()
|
||||
// {
|
||||
// return default;
|
||||
// }
|
||||
|
||||
// sealed class DelayPromise : IUniTaskSource
|
||||
// {
|
||||
// public void GetResult(short token)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public UniTaskStatus GetStatus(short token)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public UniTaskStatus UnsafeGetStatus()
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -66,7 +66,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static ThreadPoolWorkItem()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(ThreadPoolWorkItem), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(ThreadPoolWorkItem), () => pool.Size);
|
||||
}
|
||||
|
||||
Action continuation;
|
||||
|
||||
@@ -1,12 +1,30 @@
|
||||
<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>
|
||||
|
||||
<!-- NuGet Packaging -->
|
||||
<Id>UniTask</Id>
|
||||
<PackageVersion>$(Version)</PackageVersion>
|
||||
<Company>Cysharp</Company>
|
||||
<Authors>Cysharp</Authors>
|
||||
<Copyright>© Cysharp, Inc.</Copyright>
|
||||
<PackageTags>task;async</PackageTags>
|
||||
<Description>Provides an efficient async/await integration to Unity and .NET Core.</Description>
|
||||
<PackageProjectUrl>https://github.com/Cysharp/UniTask</PackageProjectUrl>
|
||||
<RepositoryUrl>$(PackageProjectUrl)</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageIcon>Icon.png</PackageIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Icon.png" Pack="true" PackagePath="/" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\UniTask\Assets\Plugins\UniTask\Runtime\**\*.cs"
|
||||
Exclude="
|
||||
|
||||
@@ -15,6 +15,19 @@ using System.Reactive.Concurrency;
|
||||
|
||||
namespace NetCoreSandbox
|
||||
{
|
||||
public class MySyncContext : SynchronizationContext
|
||||
{
|
||||
public MySyncContext()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Post(SendOrPostCallback d, object state)
|
||||
{
|
||||
Console.WriteLine("Called SyncContext Post!");
|
||||
base.Post(d, state);
|
||||
}
|
||||
}
|
||||
|
||||
public class Text
|
||||
{
|
||||
|
||||
@@ -193,12 +206,68 @@ namespace NetCoreSandbox
|
||||
await Task.Delay(10, cancellationToken);
|
||||
}
|
||||
|
||||
public class MyDisposable : IDisposable
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void Test()
|
||||
{
|
||||
var disp = new MyDisposable();
|
||||
|
||||
using var _ = new MyDisposable();
|
||||
|
||||
Console.WriteLine("tako");
|
||||
}
|
||||
|
||||
|
||||
static async UniTask FooBarAsync()
|
||||
{
|
||||
await using (UniTask.ReturnToCurrentSynchronizationContext())
|
||||
{
|
||||
await UniTask.SwitchToThreadPool();
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static async UniTask Aaa()
|
||||
{
|
||||
await FooBarAsync();
|
||||
|
||||
Console.WriteLine("FooBarAsync End");
|
||||
}
|
||||
|
||||
static async UniTask WhereSelect()
|
||||
{
|
||||
await foreach (var item in UniTaskAsyncEnumerable.Range(1, 10)
|
||||
.SelectAwait(async x =>
|
||||
{
|
||||
await UniTask.Yield();
|
||||
return x;
|
||||
})
|
||||
.Where(x => x % 2 == 0))
|
||||
{
|
||||
Console.WriteLine(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
#if !DEBUG
|
||||
|
||||
|
||||
|
||||
|
||||
//await new AllocationCheck().ViaUniTaskVoid();
|
||||
//Console.ReadLine();
|
||||
BenchmarkDotNet.Running.BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
|
||||
@@ -208,7 +277,18 @@ namespace NetCoreSandbox
|
||||
#endif
|
||||
// await new AllocationCheck().ViaUniTaskVoid();
|
||||
|
||||
// AsyncTest().Forget();
|
||||
// AsyncTest().Forge
|
||||
|
||||
Console.WriteLine("A?");
|
||||
var a = await new ZeroAllocAsyncAwaitInDotNetCore().NanikaAsync(1, 2);
|
||||
Console.WriteLine("RET:" + a);
|
||||
await WhereSelect();
|
||||
|
||||
SynchronizationContext.SetSynchronizationContext(new MySyncContext());
|
||||
|
||||
await Aaa();
|
||||
|
||||
|
||||
|
||||
|
||||
//AsyncTest().Forget();
|
||||
@@ -236,10 +316,10 @@ namespace NetCoreSandbox
|
||||
//await c;
|
||||
|
||||
|
||||
foreach (var item in Cysharp.Threading.Tasks.Internal.TaskPoolMonitor.GetCacheSizeInfo())
|
||||
{
|
||||
Console.WriteLine(item);
|
||||
}
|
||||
//foreach (var item in Cysharp.Threading.Tasks.Internal.TaskPool.GetCacheSizeInfo())
|
||||
//{
|
||||
// Console.WriteLine(item);
|
||||
//}
|
||||
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
396
src/UniTask.NetCoreSandbox/QueueCheck.cs
Normal file
396
src/UniTask.NetCoreSandbox/QueueCheck.cs
Normal file
@@ -0,0 +1,396 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
[Config(typeof(BenchmarkConfig))]
|
||||
public class QueueCheck
|
||||
{
|
||||
Node node1 = new Node();
|
||||
Node node2 = new Node();
|
||||
Queue<Node> q1 = new Queue<Node>();
|
||||
Stack<Node> s1 = new Stack<Node>();
|
||||
ConcurrentQueue<Node> cq = new ConcurrentQueue<Node>();
|
||||
ConcurrentStack<Node> cs = new ConcurrentStack<Node>();
|
||||
static TaskPool<Node> pool;
|
||||
static TaskPoolEqualNull<Node> poolEqualNull;
|
||||
static TaskPoolClass<Node> poolClass = new TaskPoolClass<Node>();
|
||||
static TaskPoolWithoutSize<Node> poolWithoutSize;
|
||||
static TaskPoolWithoutLock<Node> poolWithoutLock;
|
||||
|
||||
[Benchmark]
|
||||
public void Queue()
|
||||
{
|
||||
q1.Enqueue(node1);
|
||||
q1.Enqueue(node1);
|
||||
q1.TryDequeue(out _);
|
||||
q1.TryDequeue(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void QueueLock()
|
||||
{
|
||||
lock (q1) { q1.Enqueue(node1); }
|
||||
lock (q1) { q1.Enqueue(node1); }
|
||||
lock (q1) { q1.TryDequeue(out _); }
|
||||
lock (q1) { q1.TryDequeue(out _); }
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Stack()
|
||||
{
|
||||
s1.Push(node1);
|
||||
s1.Push(node2);
|
||||
s1.TryPop(out _);
|
||||
s1.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void StackLock()
|
||||
{
|
||||
lock (s1) { s1.Push(node1); }
|
||||
lock (s1) { s1.Push(node2); }
|
||||
lock (s1) { s1.TryPop(out _); }
|
||||
lock (s1) { s1.TryPop(out _); }
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ConcurrentQueue()
|
||||
{
|
||||
cq.Enqueue(node1);
|
||||
cq.Enqueue(node1);
|
||||
cq.TryDequeue(out _);
|
||||
cq.TryDequeue(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ConcurrentStack()
|
||||
{
|
||||
cs.Push(node1);
|
||||
cs.Push(node2);
|
||||
cs.TryPop(out _);
|
||||
cs.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPool()
|
||||
{
|
||||
pool.TryPush(node1);
|
||||
pool.TryPush(node2);
|
||||
pool.TryPop(out _);
|
||||
pool.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPoolEqualNull()
|
||||
{
|
||||
poolEqualNull.TryPush(node1);
|
||||
poolEqualNull.TryPush(node2);
|
||||
poolEqualNull.TryPop(out _);
|
||||
poolEqualNull.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPoolClass()
|
||||
{
|
||||
poolClass.TryPush(node1);
|
||||
poolClass.TryPush(node2);
|
||||
poolClass.TryPop(out _);
|
||||
poolClass.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPoolWithoutSize()
|
||||
{
|
||||
poolWithoutSize.TryPush(node1);
|
||||
poolWithoutSize.TryPush(node2);
|
||||
poolWithoutSize.TryPop(out _);
|
||||
poolWithoutSize.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPoolWithoutLock()
|
||||
{
|
||||
poolWithoutLock.TryPush(node1);
|
||||
poolWithoutLock.TryPush(node2);
|
||||
poolWithoutLock.TryPop(out _);
|
||||
poolWithoutLock.TryPop(out _);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Node : ITaskPoolNode<Node>
|
||||
{
|
||||
public Node NextNode { get; set; }
|
||||
}
|
||||
|
||||
public interface ITaskPoolNode<T>
|
||||
{
|
||||
T NextNode { get; set; }
|
||||
}
|
||||
|
||||
// mutable struct, don't mark readonly.
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TaskPoolWithoutLock<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int size;
|
||||
T root;
|
||||
|
||||
public int Size => size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
// Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
size++;
|
||||
// Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
//return false;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TaskPool<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int gate;
|
||||
int size;
|
||||
T root;
|
||||
|
||||
public int Size => size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
size++;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TaskPoolEqualNull<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int gate;
|
||||
int size;
|
||||
T root;
|
||||
|
||||
public int Size => size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (v != null)
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
size++;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class TaskPoolClass<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int gate;
|
||||
int size;
|
||||
T root;
|
||||
|
||||
public int Size => size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
size++;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TaskPoolWithoutSize<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int gate;
|
||||
T root;
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
637
src/UniTask.NetCoreTests/TriggerEventTest.cs
Normal file
637
src/UniTask.NetCoreTests/TriggerEventTest.cs
Normal file
@@ -0,0 +1,637 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests
|
||||
{
|
||||
public class TriggerEventTest
|
||||
{
|
||||
[Fact]
|
||||
public void SimpleAdd()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
|
||||
ev.Add(one);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
// after removed, onemore
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
|
||||
ev.Add(one);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddFour()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
var four = new TestEvent(4);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(four);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
two.CompletedCalled.Count.Should().Be(1);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
|
||||
// after removed, onemore.
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
var four = new TestEvent(4);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(four);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
ev.Add(four);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
two.CompletedCalled.Count.Should().Be(1);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void OneRemove()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.Remove(one);
|
||||
|
||||
ev.SetResult(40);
|
||||
ev.SetResult(50);
|
||||
ev.SetResult(60);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public void TwoRemove()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.Remove(two);
|
||||
|
||||
ev.SetResult(40);
|
||||
ev.SetResult(50);
|
||||
ev.SetResult(60);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public void ThreeRemove()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.Remove(three);
|
||||
|
||||
ev.SetResult(40);
|
||||
ev.SetResult(50);
|
||||
ev.SetResult(60);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveSelf()
|
||||
{
|
||||
new RemoveMe().Run1();
|
||||
new RemoveMe().Run2();
|
||||
new RemoveMe().Run3();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveNextInIterating()
|
||||
{
|
||||
new RemoveNext().Run1();
|
||||
new RemoveNext().Run2();
|
||||
new RemoveNext().Run3();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveNextNextTest()
|
||||
{
|
||||
new RemoveNextNext().Run1();
|
||||
new RemoveNextNext().Run2();
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void AddTest()
|
||||
{
|
||||
new AddMe().Run1();
|
||||
new AddMe().Run2();
|
||||
}
|
||||
|
||||
public class RemoveMe
|
||||
{
|
||||
TriggerEvent<int> ev;
|
||||
|
||||
public void Run1()
|
||||
{
|
||||
TestEvent one = default;
|
||||
one = new TestEvent(1, () => ev.Remove(one));
|
||||
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
{
|
||||
TestEvent one = default;
|
||||
one = new TestEvent(1, () => ev.Remove(one));
|
||||
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(two);
|
||||
ev.Add(one); // add second.
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run3()
|
||||
{
|
||||
TestEvent one = default;
|
||||
one = new TestEvent(1, () => ev.Remove(one));
|
||||
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(one); // add thired.
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
}
|
||||
|
||||
public class RemoveNext
|
||||
{
|
||||
TriggerEvent<int> ev;
|
||||
|
||||
public void Run1()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
one = new TestEvent(1, () => ev.Remove(two));
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Count.Should().Be(0);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
one = new TestEvent(1, () => ev.Remove(two));
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
|
||||
ev.Add(two);
|
||||
ev.Add(one); // add second
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run3()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
one = new TestEvent(1, () => ev.Remove(two));
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(one); // add thired.
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
}
|
||||
|
||||
public class RemoveNextNext
|
||||
{
|
||||
TriggerEvent<int> ev;
|
||||
|
||||
public void Run1()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
TestEvent four = default;
|
||||
one = new TestEvent(1, () => { ev.Remove(two); ev.Remove(three); });
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
four = new TestEvent(4);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(four);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Count.Should().Be(0);
|
||||
three.NextCalled.Count.Should().Be(0);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
TestEvent four = default;
|
||||
one = new TestEvent(1, () => { ev.Remove(one); ev.Remove(two); ev.Remove(three); });
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
four = new TestEvent(4);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(four);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Count.Should().Be(0);
|
||||
three.NextCalled.Count.Should().Be(0);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class AddMe
|
||||
{
|
||||
TriggerEvent<int> ev;
|
||||
|
||||
public void Run1()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
TestEvent four = default;
|
||||
|
||||
one = new TestEvent(1, () =>
|
||||
{
|
||||
if (two == null)
|
||||
{
|
||||
ev.Add(two = new TestEvent(2));
|
||||
}
|
||||
else if (three == null)
|
||||
{
|
||||
ev.Add(three = new TestEvent(3));
|
||||
}
|
||||
else if (four == null)
|
||||
{
|
||||
ev.Add(four = new TestEvent(4));
|
||||
}
|
||||
});
|
||||
|
||||
ev.Add(one);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
ev.SetResult(40);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40);
|
||||
two.NextCalled.Should().BeEquivalentTo(20, 30, 40);
|
||||
three.NextCalled.Should().BeEquivalentTo(30, 40);
|
||||
four.NextCalled.Should().BeEquivalentTo(40);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
TestEvent four = default;
|
||||
|
||||
one = new TestEvent(1, () =>
|
||||
{
|
||||
if (two == null)
|
||||
{
|
||||
ev.Add(two = new TestEvent(2, () =>
|
||||
{
|
||||
if (three == null)
|
||||
{
|
||||
ev.Add(three = new TestEvent(3, () =>
|
||||
{
|
||||
if (four == null)
|
||||
{
|
||||
ev.Add(four = new TestEvent(4));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
ev.Add(one);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
ev.SetResult(40);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40);
|
||||
two.NextCalled.Should().BeEquivalentTo(20, 30, 40);
|
||||
three.NextCalled.Should().BeEquivalentTo(30, 40);
|
||||
four.NextCalled.Should().BeEquivalentTo(40);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TestEvent : ITriggerHandler<int>
|
||||
{
|
||||
public readonly int Id;
|
||||
readonly Action iteratingEvent;
|
||||
|
||||
public TestEvent(int id)
|
||||
{
|
||||
this.Id = id;
|
||||
}
|
||||
|
||||
public TestEvent(int id, Action iteratingEvent)
|
||||
{
|
||||
this.Id = id;
|
||||
this.iteratingEvent = iteratingEvent;
|
||||
}
|
||||
|
||||
public List<int> NextCalled = new List<int>();
|
||||
public List<Exception> ErrorCalled = new List<Exception>();
|
||||
public List<object> CompletedCalled = new List<object>();
|
||||
public List<CancellationToken> CancelCalled = new List<CancellationToken>();
|
||||
|
||||
public ITriggerHandler<int> Prev { get; set; }
|
||||
public ITriggerHandler<int> Next { get; set; }
|
||||
|
||||
public void OnCanceled(CancellationToken cancellationToken)
|
||||
{
|
||||
CancelCalled.Add(cancellationToken);
|
||||
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
CompletedCalled.Add(new object());
|
||||
}
|
||||
|
||||
public void OnError(Exception ex)
|
||||
{
|
||||
ErrorCalled.Add(ex);
|
||||
}
|
||||
|
||||
public void OnNext(int value)
|
||||
{
|
||||
NextCalled.Add(value);
|
||||
iteratingEvent?.Invoke();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Id.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
@@ -120,6 +119,9 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public T Current => value;
|
||||
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
// raise latest value on first call.
|
||||
@@ -300,6 +302,8 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
public T Current => value;
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct AsyncUniTaskMethodBuilder
|
||||
{
|
||||
internal IMoveNextRunnerPromise runnerPromise;
|
||||
IStateMachineRunnerPromise runnerPromise;
|
||||
Exception ex;
|
||||
|
||||
// 1. Static Create method.
|
||||
@@ -47,6 +47,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 3. SetException
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
if (runnerPromise == null)
|
||||
@@ -61,6 +62,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 4. SetResult
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult()
|
||||
{
|
||||
if (runnerPromise != null)
|
||||
@@ -71,13 +73,14 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 5. AwaitOnCompleted
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
AsyncUniTask<TStateMachine>.SetStateMachine(ref this, ref stateMachine);
|
||||
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runnerPromise.MoveNext);
|
||||
@@ -86,13 +89,14 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
// 6. AwaitUnsafeOnCompleted
|
||||
[DebuggerHidden]
|
||||
[SecuritySafeCritical]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
AsyncUniTask<TStateMachine>.SetStateMachine(ref this, ref stateMachine);
|
||||
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||
}
|
||||
|
||||
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
|
||||
@@ -100,6 +104,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 7. Start
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
@@ -133,7 +138,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct AsyncUniTaskMethodBuilder<T>
|
||||
{
|
||||
internal IMoveNextRunnerPromise<T> runnerPromise;
|
||||
IStateMachineRunnerPromise<T> runnerPromise;
|
||||
Exception ex;
|
||||
T result;
|
||||
|
||||
@@ -146,9 +151,10 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
}
|
||||
|
||||
// 2. TaskLike Task property.
|
||||
[DebuggerHidden]
|
||||
public UniTask<T> Task
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if (runnerPromise != null)
|
||||
@@ -168,6 +174,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 3. SetException
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
if (runnerPromise == null)
|
||||
@@ -182,6 +189,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 4. SetResult
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult(T result)
|
||||
{
|
||||
if (runnerPromise == null)
|
||||
@@ -196,13 +204,14 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 5. AwaitOnCompleted
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref this, ref stateMachine);
|
||||
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runnerPromise.MoveNext);
|
||||
@@ -211,13 +220,14 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
// 6. AwaitUnsafeOnCompleted
|
||||
[DebuggerHidden]
|
||||
[SecuritySafeCritical]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref this, ref stateMachine);
|
||||
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||
}
|
||||
|
||||
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
|
||||
@@ -225,6 +235,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 7. Start
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct AsyncUniTaskVoidMethodBuilder
|
||||
{
|
||||
internal IMoveNextRunner runner;
|
||||
IStateMachineRunner runner;
|
||||
|
||||
// 1. Static Create method.
|
||||
[DebuggerHidden]
|
||||
@@ -35,6 +35,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 3. SetException
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
// runner is finished, return first.
|
||||
@@ -49,6 +50,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 4. SetResult
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult()
|
||||
{
|
||||
// runner is finished, return.
|
||||
@@ -61,13 +63,14 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 5. AwaitOnCompleted
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (runner == null)
|
||||
{
|
||||
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref this, ref stateMachine);
|
||||
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runner.MoveNext);
|
||||
@@ -76,13 +79,14 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
// 6. AwaitUnsafeOnCompleted
|
||||
[DebuggerHidden]
|
||||
[SecuritySafeCritical]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (runner == null)
|
||||
{
|
||||
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref this, ref stateMachine);
|
||||
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner);
|
||||
}
|
||||
|
||||
awaiter.UnsafeOnCompleted(runner.MoveNext);
|
||||
|
||||
@@ -2,18 +2,19 @@
|
||||
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
{
|
||||
public interface IMoveNextRunner
|
||||
internal interface IStateMachineRunner
|
||||
{
|
||||
Action MoveNext { get; }
|
||||
void Return();
|
||||
}
|
||||
|
||||
internal interface IMoveNextRunnerPromise : IUniTaskSource
|
||||
internal interface IStateMachineRunnerPromise : IUniTaskSource
|
||||
{
|
||||
Action MoveNext { get; }
|
||||
UniTask Task { get; }
|
||||
@@ -21,7 +22,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
void SetException(Exception exception);
|
||||
}
|
||||
|
||||
internal interface IMoveNextRunnerPromise<T> : IUniTaskSource<T>
|
||||
internal interface IStateMachineRunnerPromise<T> : IUniTaskSource<T>
|
||||
{
|
||||
Action MoveNext { get; }
|
||||
UniTask<T> Task { get; }
|
||||
@@ -29,7 +30,17 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
void SetException(Exception exception);
|
||||
}
|
||||
|
||||
internal sealed class AsyncUniTaskVoid<TStateMachine> : IMoveNextRunner, ITaskPoolNode<AsyncUniTaskVoid<TStateMachine>>, IUniTaskSource
|
||||
internal static class StateMachineUtility
|
||||
{
|
||||
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;
|
||||
@@ -43,7 +54,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
MoveNext = Run;
|
||||
}
|
||||
|
||||
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,13 +62,13 @@ 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).
|
||||
}
|
||||
|
||||
static AsyncUniTaskVoid()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size);
|
||||
}
|
||||
|
||||
public AsyncUniTaskVoid<TStateMachine> NextNode { get; set; }
|
||||
@@ -97,7 +108,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class AsyncUniTask<TStateMachine> : IMoveNextRunnerPromise, IUniTaskSource, ITaskPoolNode<AsyncUniTask<TStateMachine>>
|
||||
internal sealed class AsyncUniTask<TStateMachine> : IStateMachineRunnerPromise, IUniTaskSource, ITaskPoolNode<AsyncUniTask<TStateMachine>>
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
static TaskPool<AsyncUniTask<TStateMachine>> pool;
|
||||
@@ -113,7 +124,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
MoveNext = Run;
|
||||
}
|
||||
|
||||
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 +132,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).
|
||||
}
|
||||
|
||||
@@ -129,7 +140,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
static AsyncUniTask()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine>), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine>), () => pool.Size);
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
@@ -208,7 +219,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class AsyncUniTask<TStateMachine, T> : IMoveNextRunnerPromise<T>, IUniTaskSource<T>, ITaskPoolNode<AsyncUniTask<TStateMachine, T>>
|
||||
internal sealed class AsyncUniTask<TStateMachine, T> : IStateMachineRunnerPromise<T>, IUniTaskSource<T>, ITaskPoolNode<AsyncUniTask<TStateMachine, T>>
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
static TaskPool<AsyncUniTask<TStateMachine, T>> pool;
|
||||
@@ -224,7 +235,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
MoveNext = Run;
|
||||
}
|
||||
|
||||
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,15 +243,17 @@ 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).
|
||||
UnityEngine.Debug.Log($"SetStateMachine State:" + StateMachineUtility.GetState(stateMachine));
|
||||
}
|
||||
|
||||
|
||||
public AsyncUniTask<TStateMachine, T> NextNode { get; set; }
|
||||
|
||||
static AsyncUniTask()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine, T>), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine, T>), () => pool.Size);
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
@@ -255,6 +268,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void Run()
|
||||
{
|
||||
UnityEngine.Debug.Log($"MoveNext State:" + StateMachineUtility.GetState(stateMachine));
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -37,7 +39,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static EnumeratorPromise()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(EnumeratorPromise), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(EnumeratorPromise), () => pool.Size);
|
||||
}
|
||||
|
||||
IEnumerator innerEnumerator;
|
||||
|
||||
@@ -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,132 @@ 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);
|
||||
}
|
||||
|
||||
~AsyncOperationHandleWithCancellationSource()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
|
||||
{
|
||||
static TaskPool<AsyncOperationHandleConfiguredSource> pool;
|
||||
@@ -84,7 +210,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
|
||||
}
|
||||
|
||||
AsyncOperationHandle handle;
|
||||
@@ -210,7 +336,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 +391,137 @@ 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);
|
||||
}
|
||||
|
||||
~AsyncOperationHandleWithCancellationSource()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
|
||||
{
|
||||
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
|
||||
@@ -272,7 +529,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
|
||||
}
|
||||
|
||||
AsyncOperationHandle<T> handle;
|
||||
|
||||
@@ -90,31 +90,33 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static TweenConfiguredSource()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
|
||||
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,8 +271,11 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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>";
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#if NET_4_6 || NET_STANDARD_2_0 || CSHARP_7_OR_LATER
|
||||
|
||||
using System;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Internal
|
||||
{
|
||||
internal static class FuncExtensions
|
||||
{
|
||||
// avoid lambda capture
|
||||
|
||||
internal static Action<T> AsFuncOfT<T>(this Action action)
|
||||
{
|
||||
return new Action<T>(action.Invoke);
|
||||
}
|
||||
|
||||
static void Invoke<T>(this Action action, T unused)
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -11,7 +11,7 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
|
||||
static PooledDelegate()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(PooledDelegate<T>), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(PooledDelegate<T>), () => pool.Size);
|
||||
}
|
||||
|
||||
readonly Action<T> runDelegate;
|
||||
|
||||
@@ -60,9 +60,9 @@ namespace Cysharp.Threading.Tasks
|
||||
#endif
|
||||
|
||||
|
||||
static List<KeyValuePair<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>> listPool = new List<KeyValuePair<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>>();
|
||||
static List<KeyValuePair<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>> listPool = new List<KeyValuePair<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>>();
|
||||
|
||||
static readonly WeakDictionary<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)> tracking = new WeakDictionary<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>();
|
||||
static readonly WeakDictionary<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)> tracking = new WeakDictionary<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>();
|
||||
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
public static void TrackActiveTask(IUniTaskSource task, int skipFrame)
|
||||
@@ -71,7 +71,19 @@ namespace Cysharp.Threading.Tasks
|
||||
dirty = true;
|
||||
if (!EditorEnableState.EnableTracking) return;
|
||||
var stackTrace = EditorEnableState.EnableStackTrace ? new StackTrace(skipFrame, true).CleanupAsyncStackTrace() : "";
|
||||
tracking.TryAdd(task, (Interlocked.Increment(ref trackingId), DateTime.UtcNow, stackTrace));
|
||||
|
||||
string typeName;
|
||||
if (EditorEnableState.EnableStackTrace)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
TypeBeautify(task.GetType(), sb);
|
||||
typeName = sb.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
typeName = task.GetType().Name;
|
||||
}
|
||||
tracking.TryAdd(task, (typeName, Interlocked.Increment(ref trackingId), DateTime.UtcNow, stackTrace));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -104,14 +116,8 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var keyType = listPool[i].Key.GetType();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
TypeBeautify(keyType, sb);
|
||||
var typeName = sb.ToString();
|
||||
|
||||
action(listPool[i].Value.trackingId, typeName, listPool[i].Key.UnsafeGetStatus(), listPool[i].Value.addTime, listPool[i].Value.stackTrace);
|
||||
listPool[i] = new KeyValuePair<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>(null, (0, default(DateTime), null)); // clear
|
||||
action(listPool[i].Value.trackingId, listPool[i].Value.formattedType, listPool[i].Key.UnsafeGetStatus(), listPool[i].Value.addTime, listPool[i].Value.stackTrace);
|
||||
listPool[i] = default;
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -126,17 +132,31 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
if (type.IsNested)
|
||||
{
|
||||
TypeBeautify(type.DeclaringType, sb);
|
||||
// TypeBeautify(type.DeclaringType, sb);
|
||||
sb.Append(type.DeclaringType.Name.ToString());
|
||||
sb.Append(".");
|
||||
}
|
||||
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var genericsStart = type.Name.IndexOf("`");
|
||||
sb.Append(type.Name.Substring(0, genericsStart));
|
||||
if (genericsStart != -1)
|
||||
{
|
||||
sb.Append(type.Name.Substring(0, genericsStart));
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(type.Name);
|
||||
}
|
||||
sb.Append("<");
|
||||
var first = true;
|
||||
foreach (var item in type.GetGenericArguments())
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
sb.Append(", ");
|
||||
}
|
||||
first = false;
|
||||
TypeBeautify(item, sb);
|
||||
}
|
||||
sb.Append(">");
|
||||
@@ -146,6 +166,13 @@ namespace Cysharp.Threading.Tasks
|
||||
sb.Append(type.Name);
|
||||
}
|
||||
}
|
||||
|
||||
//static string RemoveUniTaskNamespace(string str)
|
||||
//{
|
||||
// return str.Replace("Cysharp.Threading.Tasks.CompilerServices", "")
|
||||
// .Replace("Cysharp.Threading.Tasks.Linq", "")
|
||||
// .Replace("Cysharp.Threading.Tasks", "");
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,66 +3,9 @@ using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
public abstract class MoveNextSource : IUniTaskSource<bool>
|
||||
{
|
||||
protected UniTaskCompletionSourceCore<bool> completionSource;
|
||||
|
||||
public bool GetResult(short token)
|
||||
{
|
||||
return completionSource.GetResult(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return completionSource.GetStatus(token);
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
completionSource.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return completionSource.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
completionSource.GetResult(token);
|
||||
}
|
||||
|
||||
protected bool TryGetResult<T>(UniTask<T>.Awaiter awaiter, out T result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = awaiter.GetResult();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
completionSource.TrySetException(ex);
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool TryGetResult(UniTask.Awaiter awaiter)
|
||||
{
|
||||
try
|
||||
{
|
||||
awaiter.GetResult();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
completionSource.TrySetException(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class AsyncEnumeratorBase<TSource, TResult> : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
// note: refactor all inherit class and should remove this.
|
||||
// see Select and Where.
|
||||
internal abstract class AsyncEnumeratorBase<TSource, TResult> : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
static readonly Action<object> moveNextCallbackDelegate = MoveNextCallBack;
|
||||
|
||||
@@ -188,7 +131,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class AsyncEnumeratorAwaitSelectorBase<TSource, TResult, TAwait> : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
internal abstract class AsyncEnumeratorAwaitSelectorBase<TSource, TResult, TAwait> : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
static readonly Action<object> moveNextCallbackDelegate = MoveNextCallBack;
|
||||
static readonly Action<object> setCurrentCallbackDelegate = SetCurrentCallBack;
|
||||
@@ -410,5 +353,4 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -79,41 +79,123 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _DistinctUntilChanged(source, comparer, cancellationToken);
|
||||
}
|
||||
|
||||
class _DistinctUntilChanged : AsyncEnumeratorBase<TSource, TSource>
|
||||
sealed class _DistinctUntilChanged : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly IEqualityComparer<TSource> comparer;
|
||||
TSource prev;
|
||||
bool first;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
Action moveNextAction;
|
||||
|
||||
public _DistinctUntilChanged(IUniTaskAsyncEnumerable<TSource> source, IEqualityComparer<TSource> comparer, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.comparer = comparer;
|
||||
this.first = true;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (sourceHasCurrent)
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void MoveNext()
|
||||
{
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
var v = SourceCurrent;
|
||||
if (first || !comparer.Equals(prev, v))
|
||||
switch (state)
|
||||
{
|
||||
first = false;
|
||||
Current = prev = v;
|
||||
result = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case -3;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = -3;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case -3: // first
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 0: // normal
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
var v = enumerator.Current;
|
||||
if (!comparer.Equals(Current, v))
|
||||
{
|
||||
Current = v;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case -2:
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
result = false;
|
||||
return true;
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,45 +218,128 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _DistinctUntilChanged(source, keySelector, comparer, cancellationToken);
|
||||
}
|
||||
|
||||
class _DistinctUntilChanged : AsyncEnumeratorBase<TSource, TSource>
|
||||
sealed class _DistinctUntilChanged : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IEqualityComparer<TKey> comparer;
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, TKey> keySelector;
|
||||
readonly IEqualityComparer<TKey> comparer;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
Action moveNextAction;
|
||||
TKey prev;
|
||||
bool first;
|
||||
|
||||
public _DistinctUntilChanged(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.comparer = comparer;
|
||||
this.source = source;
|
||||
this.keySelector = keySelector;
|
||||
this.first = true;
|
||||
this.comparer = comparer;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (sourceHasCurrent)
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void MoveNext()
|
||||
{
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
var v = SourceCurrent;
|
||||
var key = keySelector(v);
|
||||
if (first || !comparer.Equals(prev, key))
|
||||
switch (state)
|
||||
{
|
||||
first = false;
|
||||
prev = key;
|
||||
Current = v;
|
||||
result = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case -3;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = -3;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case -3: // first
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 0: // normal
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
var v = enumerator.Current;
|
||||
var key = keySelector(v);
|
||||
if (!comparer.Equals(prev, key))
|
||||
{
|
||||
prev = key;
|
||||
Current = v;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case -2:
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
result = false;
|
||||
return true;
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,42 +362,142 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _DistinctUntilChangedAwait(source, keySelector, comparer, cancellationToken);
|
||||
}
|
||||
|
||||
class _DistinctUntilChangedAwait : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, TKey>
|
||||
sealed class _DistinctUntilChangedAwait : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IEqualityComparer<TKey> comparer;
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, UniTask<TKey>> keySelector;
|
||||
readonly IEqualityComparer<TKey> comparer;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<TKey>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
TSource enumeratorCurrent;
|
||||
TKey prev;
|
||||
bool first;
|
||||
|
||||
public _DistinctUntilChangedAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.comparer = comparer;
|
||||
this.source = source;
|
||||
this.keySelector = keySelector;
|
||||
this.first = true;
|
||||
this.comparer = comparer;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override UniTask<TKey> TransformAsync(TSource sourceCurrent)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return keySelector(sourceCurrent);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(TKey key, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
if (first || !comparer.Equals(prev, key))
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
first = false;
|
||||
prev = key;
|
||||
Current = SourceCurrent;
|
||||
terminateIteration = false;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case -3;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = -3;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case -3: // first
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 0: // normal
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
enumeratorCurrent = enumerator.Current;
|
||||
awaiter2 = keySelector(enumeratorCurrent).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
var key = awaiter2.GetResult();
|
||||
if (!comparer.Equals(prev, key))
|
||||
{
|
||||
prev = key;
|
||||
Current = enumeratorCurrent;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
case -2:
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
terminateIteration = false;
|
||||
return false;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,42 +520,142 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _DistinctUntilChangedAwaitWithCancellation(source, keySelector, comparer, cancellationToken);
|
||||
}
|
||||
|
||||
class _DistinctUntilChangedAwaitWithCancellation : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, TKey>
|
||||
sealed class _DistinctUntilChangedAwaitWithCancellation : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IEqualityComparer<TKey> comparer;
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, CancellationToken, UniTask<TKey>> keySelector;
|
||||
readonly IEqualityComparer<TKey> comparer;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<TKey>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
TSource enumeratorCurrent;
|
||||
TKey prev;
|
||||
bool first;
|
||||
|
||||
public _DistinctUntilChangedAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.comparer = comparer;
|
||||
this.source = source;
|
||||
this.keySelector = keySelector;
|
||||
this.first = true;
|
||||
this.comparer = comparer;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override UniTask<TKey> TransformAsync(TSource sourceCurrent)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return keySelector(sourceCurrent, cancellationToken);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(TKey key, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
if (first || !comparer.Equals(prev, key))
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
first = false;
|
||||
prev = key;
|
||||
Current = SourceCurrent;
|
||||
terminateIteration = false;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case -3;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = -3;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case -3: // first
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 0: // normal
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
enumeratorCurrent = enumerator.Current;
|
||||
awaiter2 = keySelector(enumeratorCurrent, cancellationToken).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
var key = awaiter2.GetResult();
|
||||
if (!comparer.Equals(prev, key))
|
||||
{
|
||||
prev = key;
|
||||
Current = enumeratorCurrent;
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
case -2:
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
terminateIteration = false;
|
||||
return false;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,8 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
}
|
||||
|
||||
public TSource Current { get; private set; }
|
||||
ITriggerHandler<TSource> ITriggerHandler<TSource>.Prev { get; set; }
|
||||
ITriggerHandler<TSource> ITriggerHandler<TSource>.Next { get; set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
|
||||
@@ -71,29 +71,92 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _Select(source, selector, cancellationToken);
|
||||
}
|
||||
|
||||
sealed class _Select : AsyncEnumeratorBase<TSource, TResult>
|
||||
sealed class _Select : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, TResult> selector;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
Action moveNextAction;
|
||||
|
||||
public _Select(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TResult> selector, CancellationToken cancellationToken)
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
|
||||
public TResult Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (sourceHasCurrent)
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void MoveNext()
|
||||
{
|
||||
try
|
||||
{
|
||||
Current = selector(SourceCurrent);
|
||||
result = true;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = selector(enumerator.Current);
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = false;
|
||||
return true;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,33 +174,96 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public IUniTaskAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _SelectInt(source, selector, cancellationToken);
|
||||
return new _Select(source, selector, cancellationToken);
|
||||
}
|
||||
|
||||
sealed class _SelectInt : AsyncEnumeratorBase<TSource, TResult>
|
||||
sealed class _Select : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, int, TResult> selector;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
Action moveNextAction;
|
||||
int index;
|
||||
|
||||
public _SelectInt(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, TResult> selector, CancellationToken cancellationToken)
|
||||
: base(source, cancellationToken)
|
||||
public _Select(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, TResult> selector, CancellationToken cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
|
||||
public TResult Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (sourceHasCurrent)
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void MoveNext()
|
||||
{
|
||||
try
|
||||
{
|
||||
Current = selector(SourceCurrent, checked(index++));
|
||||
result = true;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = selector(enumerator.Current, checked(index++));
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = false;
|
||||
return true;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,26 +284,105 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _SelectAwait(source, selector, cancellationToken);
|
||||
}
|
||||
|
||||
sealed class _SelectAwait : AsyncEnumeratorAwaitSelectorBase<TSource, TResult, TResult>
|
||||
sealed class _SelectAwait : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, UniTask<TResult>> selector;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<TResult>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
|
||||
public _SelectAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TResult>> selector, CancellationToken cancellationToken)
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override UniTask<TResult> TransformAsync(TSource sourceCurrent)
|
||||
public TResult Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return selector(sourceCurrent);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(TResult awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
Current = awaitResult;
|
||||
terminateIteration = false;
|
||||
return true;
|
||||
try
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
awaiter2 = selector(enumerator.Current).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
Current = awaiter2.GetResult();
|
||||
goto CONTINUE;
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,30 +400,109 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public IUniTaskAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _SelectIntAwait(source, selector, cancellationToken);
|
||||
return new _SelectAwait(source, selector, cancellationToken);
|
||||
}
|
||||
|
||||
sealed class _SelectIntAwait : AsyncEnumeratorAwaitSelectorBase<TSource, TResult, TResult>
|
||||
sealed class _SelectAwait : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, int, UniTask<TResult>> selector;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<TResult>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
int index;
|
||||
|
||||
public _SelectIntAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, UniTask<TResult>> selector, CancellationToken cancellationToken)
|
||||
: base(source, cancellationToken)
|
||||
public _SelectAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, UniTask<TResult>> selector, CancellationToken cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override UniTask<TResult> TransformAsync(TSource sourceCurrent)
|
||||
public TResult Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return selector(sourceCurrent, checked(index++));
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(TResult awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
Current = awaitResult;
|
||||
terminateIteration = false;
|
||||
return true;
|
||||
try
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
awaiter2 = selector(enumerator.Current, checked(index++)).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
Current = awaiter2.GetResult();
|
||||
goto CONTINUE;
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,26 +523,105 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _SelectAwaitWithCancellation(source, selector, cancellationToken);
|
||||
}
|
||||
|
||||
sealed class _SelectAwaitWithCancellation : AsyncEnumeratorAwaitSelectorBase<TSource, TResult, TResult>
|
||||
sealed class _SelectAwaitWithCancellation : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, CancellationToken, UniTask<TResult>> selector;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<TResult>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
|
||||
public _SelectAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TResult>> selector, CancellationToken cancellationToken)
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override UniTask<TResult> TransformAsync(TSource sourceCurrent)
|
||||
public TResult Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return selector(sourceCurrent, cancellationToken);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(TResult awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
Current = awaitResult;
|
||||
terminateIteration = false;
|
||||
return true;
|
||||
try
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
awaiter2 = selector(enumerator.Current, cancellationToken).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
Current = awaiter2.GetResult();
|
||||
goto CONTINUE;
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,32 +639,110 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public IUniTaskAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _SelectIntAwaitWithCancellation(source, selector, cancellationToken);
|
||||
return new _SelectAwaitWithCancellation(source, selector, cancellationToken);
|
||||
}
|
||||
|
||||
sealed class _SelectIntAwaitWithCancellation : AsyncEnumeratorAwaitSelectorBase<TSource, TResult, TResult>
|
||||
sealed class _SelectAwaitWithCancellation : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, int, CancellationToken, UniTask<TResult>> selector;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<TResult>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
int index;
|
||||
|
||||
public _SelectIntAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, UniTask<TResult>> selector, CancellationToken cancellationToken)
|
||||
: base(source, cancellationToken)
|
||||
public _SelectAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, UniTask<TResult>> selector, CancellationToken cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override UniTask<TResult> TransformAsync(TSource sourceCurrent)
|
||||
public TResult Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return selector(sourceCurrent, checked(index++), cancellationToken);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(TResult awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
Current = awaitResult;
|
||||
terminateIteration = false;
|
||||
return true;
|
||||
try
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
awaiter2 = selector(enumerator.Current, checked(index++), cancellationToken).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
Current = awaiter2.GetResult();
|
||||
goto CONTINUE;
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "UniTask.Linq",
|
||||
"references": [
|
||||
"UniTask"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c01796d064528144a599661eaab93a6
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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,6 +100,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
if (this.period <= 0) this.period = 1;
|
||||
}
|
||||
|
||||
this.initialFrame = Time.frameCount;
|
||||
this.dueTimePhase = true;
|
||||
this.updateTiming = updateTiming;
|
||||
this.ignoreTimeScale = ignoreTimeScale;
|
||||
@@ -119,9 +140,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 +168,8 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return false;
|
||||
}
|
||||
|
||||
elapsed += (ignoreTimeScale) ? UnityEngine.Time.unscaledDeltaTime : UnityEngine.Time.deltaTime;
|
||||
|
||||
if (elapsed >= period)
|
||||
{
|
||||
completionSource.TrySetResult(true);
|
||||
@@ -172,6 +205,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
readonly int? periodFrameCount;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
int initialFrame;
|
||||
int currentFrame;
|
||||
bool dueTimePhase;
|
||||
bool completed;
|
||||
@@ -185,6 +219,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
if (periodFrameCount <= 0) periodFrameCount = 1;
|
||||
}
|
||||
|
||||
this.initialFrame = Time.frameCount;
|
||||
this.dueTimePhase = true;
|
||||
this.dueTimeFrameCount = dueTimeFrameCount;
|
||||
this.periodFrameCount = periodFrameCount;
|
||||
@@ -228,11 +263,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
|
||||
{
|
||||
|
||||
@@ -71,36 +71,101 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _Where(source, predicate, cancellationToken);
|
||||
}
|
||||
|
||||
class _Where : AsyncEnumeratorBase<TSource, TSource>
|
||||
sealed class _Where : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, bool> predicate;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
Action moveNextAction;
|
||||
|
||||
public _Where(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, bool> predicate, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.predicate = predicate;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (sourceHasCurrent)
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void MoveNext()
|
||||
{
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
if (predicate(SourceCurrent))
|
||||
switch (state)
|
||||
{
|
||||
Current = SourceCurrent;
|
||||
result = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
if (predicate(Current))
|
||||
{
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
result = false;
|
||||
return true;
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,40 +183,105 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _WhereInt(source, predicate, cancellationToken);
|
||||
return new _Where(source, predicate, cancellationToken);
|
||||
}
|
||||
|
||||
class _WhereInt : AsyncEnumeratorBase<TSource, TSource>
|
||||
sealed class _Where : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, int, bool> predicate;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
Action moveNextAction;
|
||||
int index;
|
||||
|
||||
public _WhereInt(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, bool> predicate, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
public _Where(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, bool> predicate, CancellationToken cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.predicate = predicate;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (sourceHasCurrent)
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
void MoveNext()
|
||||
{
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
if (predicate(SourceCurrent, checked(index++)))
|
||||
switch (state)
|
||||
{
|
||||
Current = SourceCurrent;
|
||||
result = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
if (predicate(Current, checked(index++)))
|
||||
{
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
result = false;
|
||||
return true;
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,34 +302,115 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _WhereAwait(source, predicate, cancellationToken);
|
||||
}
|
||||
|
||||
class _WhereAwait : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, bool>
|
||||
sealed class _WhereAwait : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, UniTask<bool>> predicate;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<bool>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
|
||||
public _WhereAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<bool>> predicate, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.predicate = predicate;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override UniTask<bool> TransformAsync(TSource sourceCurrent)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return predicate(sourceCurrent);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
terminateIteration = false;
|
||||
if (awaitResult)
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
Current = SourceCurrent;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
|
||||
awaiter2 = predicate(Current).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
if (awaiter2.GetResult())
|
||||
{
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
return false;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,44 +428,123 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _WhereIntAwait(source, predicate, cancellationToken);
|
||||
return new _WhereAwait(source, predicate, cancellationToken);
|
||||
}
|
||||
|
||||
class _WhereIntAwait : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, bool>
|
||||
sealed class _WhereAwait : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, int, UniTask<bool>> predicate;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<bool>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
int index;
|
||||
|
||||
public _WhereIntAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, UniTask<bool>> predicate, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
public _WhereAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, UniTask<bool>> predicate, CancellationToken cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.predicate = predicate;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override UniTask<bool> TransformAsync(TSource sourceCurrent)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return predicate(sourceCurrent, checked(index++));
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
terminateIteration = false;
|
||||
if (awaitResult)
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
Current = SourceCurrent;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
|
||||
awaiter2 = predicate(Current, checked(index++)).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
if (awaiter2.GetResult())
|
||||
{
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
return false;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal sealed class WhereAwaitWithCancellation<TSource> : IUniTaskAsyncEnumerable<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
@@ -271,34 +561,115 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return new _WhereAwaitWithCancellation(source, predicate, cancellationToken);
|
||||
}
|
||||
|
||||
class _WhereAwaitWithCancellation : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, bool>
|
||||
sealed class _WhereAwaitWithCancellation : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, CancellationToken, UniTask<bool>> predicate;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<bool>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
|
||||
public _WhereAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<bool>> predicate, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.predicate = predicate;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override UniTask<bool> TransformAsync(TSource sourceCurrent)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return predicate(sourceCurrent, cancellationToken);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
terminateIteration = false;
|
||||
if (awaitResult)
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
Current = SourceCurrent;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
|
||||
awaiter2 = predicate(Current, cancellationToken).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
if (awaiter2.GetResult())
|
||||
{
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
return false;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -316,40 +687,120 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _WhereIntAwaitWithCancellation(source, predicate, cancellationToken);
|
||||
return new _WhereAwaitWithCancellation(source, predicate, cancellationToken);
|
||||
}
|
||||
|
||||
class _WhereIntAwaitWithCancellation : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, bool>
|
||||
sealed class _WhereAwaitWithCancellation : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
readonly Func<TSource, int, CancellationToken, UniTask<bool>> predicate;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
int state = -1;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
UniTask<bool>.Awaiter awaiter2;
|
||||
Action moveNextAction;
|
||||
int index;
|
||||
|
||||
public _WhereIntAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, UniTask<bool>> predicate, CancellationToken cancellationToken)
|
||||
|
||||
: base(source, cancellationToken)
|
||||
public _WhereAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, UniTask<bool>> predicate, CancellationToken cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.predicate = predicate;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.moveNextAction = MoveNext;
|
||||
}
|
||||
|
||||
protected override UniTask<bool> TransformAsync(TSource sourceCurrent)
|
||||
public TSource Current { get; private set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return predicate(sourceCurrent, checked(index++), cancellationToken);
|
||||
if (state == -2) return default;
|
||||
|
||||
completionSource.Reset();
|
||||
MoveNext();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration)
|
||||
void MoveNext()
|
||||
{
|
||||
terminateIteration = false;
|
||||
if (awaitResult)
|
||||
REPEAT:
|
||||
try
|
||||
{
|
||||
Current = SourceCurrent;
|
||||
return true;
|
||||
switch (state)
|
||||
{
|
||||
case -1: // init
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
goto case 0;
|
||||
case 0:
|
||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
||||
if (awaiter.IsCompleted)
|
||||
{
|
||||
goto case 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 1;
|
||||
awaiter.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
if (awaiter.GetResult())
|
||||
{
|
||||
Current = enumerator.Current;
|
||||
|
||||
awaiter2 = predicate(Current, checked(index++), cancellationToken).GetAwaiter();
|
||||
if (awaiter2.IsCompleted)
|
||||
{
|
||||
goto case 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
awaiter2.UnsafeOnCompleted(moveNextAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto DONE;
|
||||
}
|
||||
case 2:
|
||||
if (awaiter2.GetResult())
|
||||
{
|
||||
goto CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
goto REPEAT;
|
||||
}
|
||||
default:
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
return false;
|
||||
state = -2;
|
||||
completionSource.TrySetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
DONE:
|
||||
state = -2;
|
||||
completionSource.TrySetResult(false);
|
||||
return;
|
||||
|
||||
CONTINUE:
|
||||
state = 0;
|
||||
completionSource.TrySetResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
63
src/UniTask/Assets/Plugins/UniTask/Runtime/MoveNextSource.cs
Normal file
63
src/UniTask/Assets/Plugins/UniTask/Runtime/MoveNextSource.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public abstract class MoveNextSource : IUniTaskSource<bool>
|
||||
{
|
||||
protected UniTaskCompletionSourceCore<bool> completionSource;
|
||||
|
||||
public bool GetResult(short token)
|
||||
{
|
||||
return completionSource.GetResult(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return completionSource.GetStatus(token);
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
completionSource.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return completionSource.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
completionSource.GetResult(token);
|
||||
}
|
||||
|
||||
protected bool TryGetResult<T>(UniTask<T>.Awaiter awaiter, out T result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = awaiter.GetResult();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
completionSource.TrySetException(ex);
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool TryGetResult(UniTask.Awaiter awaiter)
|
||||
{
|
||||
try
|
||||
{
|
||||
awaiter.GetResult();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
completionSource.TrySetException(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d5a9a3e1f0f069478969f752fde29a9
|
||||
guid: dc4c5dc2a5f246e4f8df44cab735826c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -92,8 +92,10 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static SynchronizationContext UnitySynchronizationContext => unitySynchronizationContetext;
|
||||
public static int MainThreadId => mainThreadId;
|
||||
internal static string ApplicationDataPath => applicationDataPath;
|
||||
|
||||
static int mainThreadId;
|
||||
static string applicationDataPath;
|
||||
static SynchronizationContext unitySynchronizationContetext;
|
||||
static ContinuationQueue[] yielders;
|
||||
static PlayerLoopRunner[] runners;
|
||||
@@ -177,6 +179,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;
|
||||
|
||||
@@ -5,13 +5,14 @@ using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Internal
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
// internaly used but public, allow to user create custom operator with pooling.
|
||||
|
||||
public static class TaskPool
|
||||
{
|
||||
internal static int MaxPoolSize;
|
||||
static ConcurrentDictionary<Type, Func<int>> sizes = new ConcurrentDictionary<Type, Func<int>>();
|
||||
|
||||
static TaskPool()
|
||||
{
|
||||
@@ -36,6 +37,19 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
{
|
||||
MaxPoolSize = maxPoolSize;
|
||||
}
|
||||
|
||||
public static IEnumerable<(Type, int)> GetCacheSizeInfo()
|
||||
{
|
||||
foreach (var item in sizes)
|
||||
{
|
||||
yield return (item.Key, item.Value());
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterSizeGetter(Type type, Func<int> getSize)
|
||||
{
|
||||
sizes[type] = getSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,22 +112,4 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TaskPoolMonitor
|
||||
{
|
||||
static ConcurrentDictionary<Type, Func<int>> sizes = new ConcurrentDictionary<Type, Func<int>>();
|
||||
|
||||
public static IEnumerable<(Type, int)> GetCacheSizeInfo()
|
||||
{
|
||||
foreach (var item in sizes)
|
||||
{
|
||||
yield return (item.Key, item.Value());
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterSizeGetter(Type type, Func<int> getSize)
|
||||
{
|
||||
sizes[type] = getSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
@@ -10,341 +9,302 @@ namespace Cysharp.Threading.Tasks
|
||||
void OnError(Exception ex);
|
||||
void OnCompleted();
|
||||
void OnCanceled(CancellationToken cancellationToken);
|
||||
|
||||
// set/get from TriggerEvent<T>
|
||||
ITriggerHandler<T> Prev { get; set; }
|
||||
ITriggerHandler<T> Next { get; set; }
|
||||
}
|
||||
|
||||
// be careful to use, itself is struct.
|
||||
public struct TriggerEvent<T>
|
||||
{
|
||||
// optimize: many cases, handler is single.
|
||||
ITriggerHandler<T> singleHandler;
|
||||
ITriggerHandler<T> head; // head.prev is last
|
||||
ITriggerHandler<T> iteratingHead;
|
||||
|
||||
ITriggerHandler<T>[] handlers;
|
||||
bool preserveRemoveSelf;
|
||||
ITriggerHandler<T> iteratingNode;
|
||||
|
||||
// when running(in TrySetResult), does not add immediately(trampoline).
|
||||
bool isRunning;
|
||||
ITriggerHandler<T> waitHandler;
|
||||
MinimumQueue<ITriggerHandler<T>> waitQueue;
|
||||
void LogError(Exception ex)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void SetResult(T value)
|
||||
{
|
||||
isRunning = true;
|
||||
|
||||
if (singleHandler != null)
|
||||
if (iteratingNode != null)
|
||||
{
|
||||
throw new InvalidOperationException("Can not trigger itself in iterating.");
|
||||
}
|
||||
|
||||
var h = head;
|
||||
while (h != null)
|
||||
{
|
||||
iteratingNode = h;
|
||||
|
||||
try
|
||||
{
|
||||
singleHandler.OnNext(value);
|
||||
h.OnNext(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
LogError(ex);
|
||||
Remove(h);
|
||||
}
|
||||
}
|
||||
|
||||
if (handlers != null)
|
||||
{
|
||||
for (int i = 0; i < handlers.Length; i++)
|
||||
if (preserveRemoveSelf)
|
||||
{
|
||||
if (handlers[i] != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
handlers[i].OnNext(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
handlers[i] = null;
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
preserveRemoveSelf = false;
|
||||
iteratingNode = null;
|
||||
var next = h.Next;
|
||||
Remove(h);
|
||||
h = next;
|
||||
}
|
||||
}
|
||||
|
||||
isRunning = false;
|
||||
|
||||
if (waitHandler != null)
|
||||
{
|
||||
var h = waitHandler;
|
||||
waitHandler = null;
|
||||
Add(h);
|
||||
}
|
||||
|
||||
if (waitQueue != null)
|
||||
{
|
||||
while (waitQueue.Count != 0)
|
||||
else
|
||||
{
|
||||
Add(waitQueue.Dequeue());
|
||||
h = h.Next;
|
||||
}
|
||||
}
|
||||
|
||||
iteratingNode = null;
|
||||
if (iteratingHead != null)
|
||||
{
|
||||
Add(iteratingHead);
|
||||
iteratingHead = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCanceled(CancellationToken cancellationToken)
|
||||
{
|
||||
isRunning = true;
|
||||
|
||||
if (singleHandler != null)
|
||||
if (iteratingNode != null)
|
||||
{
|
||||
throw new InvalidOperationException("Can not trigger itself in iterating.");
|
||||
}
|
||||
|
||||
var h = head;
|
||||
while (h != null)
|
||||
{
|
||||
iteratingNode = h;
|
||||
try
|
||||
{
|
||||
(singleHandler).OnCanceled(cancellationToken);
|
||||
h.OnCanceled(cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
LogError(ex);
|
||||
}
|
||||
|
||||
preserveRemoveSelf = false;
|
||||
iteratingNode = null;
|
||||
var next = h.Next;
|
||||
Remove(h);
|
||||
h = next;
|
||||
}
|
||||
|
||||
if (handlers != null)
|
||||
iteratingNode = null;
|
||||
if (iteratingHead != null)
|
||||
{
|
||||
for (int i = 0; i < handlers.Length; i++)
|
||||
{
|
||||
if (handlers[i] != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
(handlers[i]).OnCanceled(cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
handlers[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isRunning = false;
|
||||
|
||||
if (waitHandler != null)
|
||||
{
|
||||
var h = waitHandler;
|
||||
waitHandler = null;
|
||||
Add(h);
|
||||
}
|
||||
|
||||
if (waitQueue != null)
|
||||
{
|
||||
while (waitQueue.Count != 0)
|
||||
{
|
||||
Add(waitQueue.Dequeue());
|
||||
}
|
||||
Add(iteratingHead);
|
||||
iteratingHead = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCompleted()
|
||||
{
|
||||
isRunning = true;
|
||||
|
||||
if (singleHandler != null)
|
||||
if (iteratingNode != null)
|
||||
{
|
||||
throw new InvalidOperationException("Can not trigger itself in iterating.");
|
||||
}
|
||||
|
||||
var h = head;
|
||||
while (h != null)
|
||||
{
|
||||
iteratingNode = h;
|
||||
try
|
||||
{
|
||||
(singleHandler).OnCompleted();
|
||||
h.OnCompleted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
LogError(ex);
|
||||
}
|
||||
|
||||
preserveRemoveSelf = false;
|
||||
iteratingNode = null;
|
||||
var next = h.Next;
|
||||
Remove(h);
|
||||
h = next;
|
||||
}
|
||||
|
||||
if (handlers != null)
|
||||
iteratingNode = null;
|
||||
if (iteratingHead != null)
|
||||
{
|
||||
for (int i = 0; i < handlers.Length; i++)
|
||||
{
|
||||
if (handlers[i] != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
(handlers[i]).OnCompleted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
handlers[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isRunning = false;
|
||||
|
||||
if (waitHandler != null)
|
||||
{
|
||||
var h = waitHandler;
|
||||
waitHandler = null;
|
||||
Add(h);
|
||||
}
|
||||
|
||||
if (waitQueue != null)
|
||||
{
|
||||
while (waitQueue.Count != 0)
|
||||
{
|
||||
Add(waitQueue.Dequeue());
|
||||
}
|
||||
Add(iteratingHead);
|
||||
iteratingHead = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetError(Exception exception)
|
||||
{
|
||||
isRunning = true;
|
||||
|
||||
if (singleHandler != null)
|
||||
if (iteratingNode != null)
|
||||
{
|
||||
throw new InvalidOperationException("Can not trigger itself in iterating.");
|
||||
}
|
||||
|
||||
var h = head;
|
||||
while (h != null)
|
||||
{
|
||||
iteratingNode = h;
|
||||
try
|
||||
{
|
||||
singleHandler.OnError(exception);
|
||||
h.OnError(exception);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
LogError(ex);
|
||||
}
|
||||
|
||||
preserveRemoveSelf = false;
|
||||
iteratingNode = null;
|
||||
var next = h.Next;
|
||||
Remove(h);
|
||||
h = next;
|
||||
}
|
||||
|
||||
if (handlers != null)
|
||||
iteratingNode = null;
|
||||
if (iteratingHead != null)
|
||||
{
|
||||
for (int i = 0; i < handlers.Length; i++)
|
||||
{
|
||||
if (handlers[i] != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
handlers[i].OnError(exception);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
handlers[i] = null;
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
#else
|
||||
Console.WriteLine(ex);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isRunning = false;
|
||||
|
||||
if (waitHandler != null)
|
||||
{
|
||||
var h = waitHandler;
|
||||
waitHandler = null;
|
||||
Add(h);
|
||||
}
|
||||
|
||||
if (waitQueue != null)
|
||||
{
|
||||
while (waitQueue.Count != 0)
|
||||
{
|
||||
Add(waitQueue.Dequeue());
|
||||
}
|
||||
Add(iteratingHead);
|
||||
iteratingHead = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(ITriggerHandler<T> handler)
|
||||
{
|
||||
if (isRunning)
|
||||
{
|
||||
if (waitHandler == null)
|
||||
{
|
||||
waitHandler = handler;
|
||||
return;
|
||||
}
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
if (waitQueue == null)
|
||||
{
|
||||
waitQueue = new MinimumQueue<ITriggerHandler<T>>(4);
|
||||
}
|
||||
waitQueue.Enqueue(handler);
|
||||
// zero node.
|
||||
if (head == null)
|
||||
{
|
||||
head = handler;
|
||||
return;
|
||||
}
|
||||
|
||||
if (singleHandler == null)
|
||||
if (iteratingNode != null)
|
||||
{
|
||||
singleHandler = handler;
|
||||
if (iteratingHead == null)
|
||||
{
|
||||
iteratingHead = handler;
|
||||
return;
|
||||
}
|
||||
|
||||
var last = iteratingHead.Prev;
|
||||
if (last == null)
|
||||
{
|
||||
// single node.
|
||||
iteratingHead.Prev = handler;
|
||||
iteratingHead.Next = handler;
|
||||
handler.Prev = iteratingHead;
|
||||
}
|
||||
else
|
||||
{
|
||||
// multi node
|
||||
iteratingHead.Prev = handler;
|
||||
last.Next = handler;
|
||||
handler.Prev = last;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (handlers == null)
|
||||
var last = head.Prev;
|
||||
if (last == null)
|
||||
{
|
||||
handlers = new ITriggerHandler<T>[4];
|
||||
// single node.
|
||||
head.Prev = handler;
|
||||
head.Next = handler;
|
||||
handler.Prev = head;
|
||||
}
|
||||
|
||||
// check empty
|
||||
for (int i = 0; i < handlers.Length; i++)
|
||||
else
|
||||
{
|
||||
if (handlers[i] == null)
|
||||
{
|
||||
handlers[i] = handler;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// full, ensure capacity
|
||||
var last = handlers.Length;
|
||||
{
|
||||
EnsureCapacity(ref handlers);
|
||||
handlers[last] = handler;
|
||||
// multi node
|
||||
head.Prev = handler;
|
||||
last.Next = handler;
|
||||
handler.Prev = last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void EnsureCapacity(ref ITriggerHandler<T>[] array)
|
||||
{
|
||||
var newSize = array.Length * 2;
|
||||
var newArray = new ITriggerHandler<T>[newSize];
|
||||
Array.Copy(array, 0, newArray, 0, array.Length);
|
||||
array = newArray;
|
||||
}
|
||||
|
||||
public void Remove(ITriggerHandler<T> handler)
|
||||
{
|
||||
if (singleHandler == handler)
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
if (iteratingNode != null && iteratingNode == handler)
|
||||
{
|
||||
singleHandler = null;
|
||||
// if remove self, reserve remove self after invoke completed.
|
||||
preserveRemoveSelf = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (handlers != null)
|
||||
var prev = handler.Prev;
|
||||
var next = handler.Next;
|
||||
|
||||
if (next != null)
|
||||
{
|
||||
for (int i = 0; i < handlers.Length; i++)
|
||||
next.Prev = prev;
|
||||
}
|
||||
|
||||
if (handler == head)
|
||||
{
|
||||
head = next;
|
||||
}
|
||||
else if (handler == iteratingHead)
|
||||
{
|
||||
iteratingHead = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// when handler is head, prev indicate last so don't use it.
|
||||
if (prev != null)
|
||||
{
|
||||
if (handlers[i] == handler)
|
||||
prev.Next = next;
|
||||
}
|
||||
}
|
||||
|
||||
if (head != null)
|
||||
{
|
||||
if (head.Prev == handler)
|
||||
{
|
||||
if (prev != head)
|
||||
{
|
||||
// fill null.
|
||||
handlers[i] = null;
|
||||
return;
|
||||
head.Prev = prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
head.Prev = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iteratingHead != null)
|
||||
{
|
||||
if (iteratingHead.Prev == handler)
|
||||
{
|
||||
if (prev != iteratingHead.Prev)
|
||||
{
|
||||
iteratingHead.Prev = prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
iteratingHead.Prev = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handler.Prev = null;
|
||||
handler.Next = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
@@ -103,6 +102,8 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
}
|
||||
|
||||
public T Current { get; private set; }
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
@@ -189,6 +190,9 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
|
||||
internal CancellationToken CancellationToken => cancellationToken;
|
||||
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||
|
||||
internal AsyncTriggerHandler(AsyncTriggerBase<T> trigger, bool callOnce)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
@@ -21,6 +20,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)
|
||||
@@ -63,7 +102,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static YieldPromise()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(YieldPromise), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(YieldPromise), () => pool.Size);
|
||||
}
|
||||
|
||||
CancellationToken cancellationToken;
|
||||
@@ -152,27 +191,25 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise>
|
||||
sealed class NextFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<NextFramePromise>
|
||||
{
|
||||
static TaskPool<DelayFramePromise> pool;
|
||||
public DelayFramePromise NextNode { get; set; }
|
||||
static TaskPool<NextFramePromise> pool;
|
||||
public NextFramePromise NextNode { get; set; }
|
||||
|
||||
static DelayFramePromise()
|
||||
static NextFramePromise()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(DelayFramePromise), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(NextFramePromise), () => pool.Size);
|
||||
}
|
||||
|
||||
int delayFrameCount;
|
||||
int frameCount;
|
||||
CancellationToken cancellationToken;
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
int currentFrameCount;
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
|
||||
DelayFramePromise()
|
||||
NextFramePromise()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(int delayFrameCount, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -181,10 +218,10 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new DelayFramePromise();
|
||||
result = new NextFramePromise();
|
||||
}
|
||||
|
||||
result.delayFrameCount = delayFrameCount;
|
||||
result.frameCount = Time.frameCount;
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
@@ -230,13 +267,133 @@ namespace Cysharp.Threading.Tasks
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentFrameCount == delayFrameCount)
|
||||
if (frameCount == Time.frameCount)
|
||||
{
|
||||
core.TrySetResult(null);
|
||||
return true;
|
||||
}
|
||||
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
cancellationToken = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
~NextFramePromise()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise>
|
||||
{
|
||||
static TaskPool<DelayFramePromise> pool;
|
||||
public DelayFramePromise NextNode { get; set; }
|
||||
|
||||
static DelayFramePromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(DelayFramePromise), () => pool.Size);
|
||||
}
|
||||
|
||||
int initialFrame;
|
||||
int delayFrameCount;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
int currentFrameCount;
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
DelayFramePromise()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(int delayFrameCount, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new DelayFramePromise();
|
||||
}
|
||||
|
||||
result.delayFrameCount = delayFrameCount;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.initialFrame = Time.frameCount;
|
||||
|
||||
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 (currentFrameCount == 0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -266,9 +423,10 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static DelayPromise()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(DelayPromise), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(DelayPromise), () => pool.Size);
|
||||
}
|
||||
|
||||
int initialFrame;
|
||||
float delayFrameTimeSpan;
|
||||
float elapsed;
|
||||
CancellationToken cancellationToken;
|
||||
@@ -294,6 +452,7 @@ namespace Cysharp.Threading.Tasks
|
||||
result.elapsed = 0.0f;
|
||||
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.initialFrame = Time.frameCount;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -338,6 +497,14 @@ namespace Cysharp.Threading.Tasks
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elapsed == 0.0f)
|
||||
{
|
||||
if (initialFrame == Time.frameCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
elapsed += Time.deltaTime;
|
||||
if (elapsed >= delayFrameTimeSpan)
|
||||
{
|
||||
@@ -374,11 +541,12 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static DelayIgnoreTimeScalePromise()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(DelayIgnoreTimeScalePromise), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(DelayIgnoreTimeScalePromise), () => pool.Size);
|
||||
}
|
||||
|
||||
float delayFrameTimeSpan;
|
||||
float elapsed;
|
||||
int initialFrame;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
@@ -401,6 +569,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
result.elapsed = 0.0f;
|
||||
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
|
||||
result.initialFrame = Time.frameCount;
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
@@ -446,6 +615,14 @@ namespace Cysharp.Threading.Tasks
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elapsed == 0.0f)
|
||||
{
|
||||
if (initialFrame == Time.frameCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
if (elapsed >= delayFrameTimeSpan)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#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;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
@@ -102,8 +104,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// helper of create add UniTaskVoid to delegate.
|
||||
/// For example: FooEvent += () => UniTask.Void(async () => { /* */ })
|
||||
/// helper of fire and forget void action.
|
||||
/// </summary>
|
||||
public static void Void(Func<UniTaskVoid> asyncAction)
|
||||
{
|
||||
@@ -111,7 +112,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// helper of create add UniTaskVoid to delegate.
|
||||
/// helper of fire and forget void action.
|
||||
/// </summary>
|
||||
public static void Void(Func<CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -119,8 +120,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// helper of create add UniTaskVoid to delegate.
|
||||
/// For example: FooEvent += (sender, e) => UniTask.Void(async arg => { /* */ }, (sender, e))
|
||||
/// helper of fire and forget void action.
|
||||
/// </summary>
|
||||
public static void Void<T>(Func<T, UniTaskVoid> asyncAction, T state)
|
||||
{
|
||||
@@ -204,7 +204,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 +246,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,31 @@ namespace Cysharp.Threading.Tasks
|
||||
/// </summary>
|
||||
public static SwitchToMainThreadAwaitable SwitchToMainThread()
|
||||
{
|
||||
return new SwitchToMainThreadAwaitable();
|
||||
return new SwitchToMainThreadAwaitable(PlayerLoopTiming.Update);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If running on mainthread, do nothing. Otherwise, same as UniTask.Yield(timing).
|
||||
/// </summary>
|
||||
public static SwitchToMainThreadAwaitable SwitchToMainThread(PlayerLoopTiming timing)
|
||||
{
|
||||
return new SwitchToMainThreadAwaitable(timing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return to mainthread(same as await SwitchToMainThread) after using scope is closed.
|
||||
/// </summary>
|
||||
public static ReturnToMainThread ReturnToMainThread()
|
||||
{
|
||||
return new ReturnToMainThread(PlayerLoopTiming.Update);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return to mainthread(same as await SwitchToMainThread) after using scope is closed.
|
||||
/// </summary>
|
||||
public static ReturnToMainThread ReturnToMainThread(PlayerLoopTiming timing)
|
||||
{
|
||||
return new ReturnToMainThread(timing);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -27,15 +51,28 @@ namespace Cysharp.Threading.Tasks
|
||||
return new SwitchToThreadPoolAwaitable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Note: use SwitchToThreadPool is recommended.
|
||||
/// </summary>
|
||||
public static SwitchToTaskPoolAwaitable SwitchToTaskPool()
|
||||
{
|
||||
return new SwitchToTaskPoolAwaitable();
|
||||
}
|
||||
|
||||
public static SwitchToSynchronizationContextAwaitable SwitchToSynchronizationContext(SynchronizationContext syncContext)
|
||||
public static SwitchToSynchronizationContextAwaitable SwitchToSynchronizationContext(SynchronizationContext synchronizationContext)
|
||||
{
|
||||
Error.ThrowArgumentNullException(syncContext, nameof(syncContext));
|
||||
return new SwitchToSynchronizationContextAwaitable(syncContext);
|
||||
Error.ThrowArgumentNullException(synchronizationContext, nameof(synchronizationContext));
|
||||
return new SwitchToSynchronizationContextAwaitable(synchronizationContext);
|
||||
}
|
||||
|
||||
public static ReturnToSynchronizationContext ReturnToSynchronizationContext(SynchronizationContext synchronizationContext)
|
||||
{
|
||||
return new ReturnToSynchronizationContext(synchronizationContext, false);
|
||||
}
|
||||
|
||||
public static ReturnToSynchronizationContext ReturnToCurrentSynchronizationContext(bool dontPostWhenSameContext = true)
|
||||
{
|
||||
return new ReturnToSynchronizationContext(SynchronizationContext.Current, dontPostWhenSameContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,10 +80,24 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public struct SwitchToMainThreadAwaitable
|
||||
{
|
||||
public Awaiter GetAwaiter() => new Awaiter();
|
||||
readonly PlayerLoopTiming playerLoopTiming;
|
||||
|
||||
public SwitchToMainThreadAwaitable(PlayerLoopTiming playerLoopTiming)
|
||||
{
|
||||
this.playerLoopTiming = playerLoopTiming;
|
||||
}
|
||||
|
||||
public Awaiter GetAwaiter() => new Awaiter(playerLoopTiming);
|
||||
|
||||
public struct Awaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
readonly PlayerLoopTiming playerLoopTiming;
|
||||
|
||||
public Awaiter(PlayerLoopTiming playerLoopTiming)
|
||||
{
|
||||
this.playerLoopTiming = playerLoopTiming;
|
||||
}
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
get
|
||||
@@ -67,12 +118,53 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.Update, continuation);
|
||||
PlayerLoopHelper.AddContinuation(playerLoopTiming, continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.Update, continuation);
|
||||
PlayerLoopHelper.AddContinuation(playerLoopTiming, continuation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct ReturnToMainThread
|
||||
{
|
||||
readonly PlayerLoopTiming playerLoopTiming;
|
||||
|
||||
public ReturnToMainThread(PlayerLoopTiming playerLoopTiming)
|
||||
{
|
||||
this.playerLoopTiming = playerLoopTiming;
|
||||
}
|
||||
|
||||
public Awaiter DisposeAsync()
|
||||
{
|
||||
return new Awaiter(playerLoopTiming); // run immediate.
|
||||
}
|
||||
|
||||
public readonly struct Awaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
readonly PlayerLoopTiming timing;
|
||||
|
||||
public Awaiter(PlayerLoopTiming timing)
|
||||
{
|
||||
this.timing = timing;
|
||||
}
|
||||
|
||||
public Awaiter GetAwaiter() => this;
|
||||
|
||||
public bool IsCompleted => PlayerLoopHelper.MainThreadId == System.Threading.Thread.CurrentThread.ManagedThreadId;
|
||||
|
||||
public void GetResult() { }
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
PlayerLoopHelper.AddContinuation(timing, continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
PlayerLoopHelper.AddContinuation(timing, continuation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,12 +184,16 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
ThreadPool.UnsafeQueueUserWorkItem(switchToCallback, continuation);
|
||||
ThreadPool.QueueUserWorkItem(switchToCallback, continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
#if NETCOREAPP3_1
|
||||
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
|
||||
#else
|
||||
ThreadPool.UnsafeQueueUserWorkItem(switchToCallback, continuation);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Callback(object state)
|
||||
@@ -106,6 +202,47 @@ namespace Cysharp.Threading.Tasks
|
||||
continuation();
|
||||
}
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_1
|
||||
|
||||
sealed class ThreadPoolWorkItem : IThreadPoolWorkItem, ITaskPoolNode<ThreadPoolWorkItem>
|
||||
{
|
||||
static TaskPool<ThreadPoolWorkItem> pool;
|
||||
public ThreadPoolWorkItem NextNode { get; set; }
|
||||
|
||||
static ThreadPoolWorkItem()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(ThreadPoolWorkItem), () => pool.Size);
|
||||
}
|
||||
|
||||
Action continuation;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ThreadPoolWorkItem Create(Action continuation)
|
||||
{
|
||||
if (!pool.TryPop(out var item))
|
||||
{
|
||||
item = new ThreadPoolWorkItem();
|
||||
}
|
||||
|
||||
item.continuation = continuation;
|
||||
return item;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Execute()
|
||||
{
|
||||
var call = continuation;
|
||||
continuation = null;
|
||||
if (call != null)
|
||||
{
|
||||
pool.TryPush(this);
|
||||
call.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
public struct SwitchToTaskPoolAwaitable
|
||||
@@ -178,5 +315,73 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct ReturnToSynchronizationContext
|
||||
{
|
||||
readonly SynchronizationContext syncContext;
|
||||
readonly bool dontPostWhenSameContext;
|
||||
|
||||
public ReturnToSynchronizationContext(SynchronizationContext syncContext, bool dontPostWhenSameContext)
|
||||
{
|
||||
this.syncContext = syncContext;
|
||||
this.dontPostWhenSameContext = dontPostWhenSameContext;
|
||||
}
|
||||
|
||||
public Awaiter DisposeAsync()
|
||||
{
|
||||
return new Awaiter(syncContext, dontPostWhenSameContext);
|
||||
}
|
||||
|
||||
public struct Awaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
static readonly SendOrPostCallback switchToCallback = Callback;
|
||||
|
||||
readonly SynchronizationContext synchronizationContext;
|
||||
readonly bool dontPostWhenSameContext;
|
||||
|
||||
public Awaiter(SynchronizationContext synchronizationContext, bool dontPostWhenSameContext)
|
||||
{
|
||||
this.synchronizationContext = synchronizationContext;
|
||||
this.dontPostWhenSameContext = dontPostWhenSameContext;
|
||||
}
|
||||
|
||||
public Awaiter GetAwaiter() => this;
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!dontPostWhenSameContext) return false;
|
||||
|
||||
var current = SynchronizationContext.Current;
|
||||
if (current == synchronizationContext)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GetResult() { }
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
synchronizationContext.Post(switchToCallback, continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
synchronizationContext.Post(switchToCallback, continuation);
|
||||
}
|
||||
|
||||
static void Callback(object state)
|
||||
{
|
||||
var continuation = (Action)state;
|
||||
continuation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static WaitUntilPromise()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(WaitUntilPromise), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitUntilPromise), () => pool.Size);
|
||||
}
|
||||
|
||||
Func<bool> predicate;
|
||||
@@ -154,7 +154,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static WaitWhilePromise()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(WaitWhilePromise), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise), () => pool.Size);
|
||||
}
|
||||
|
||||
Func<bool> predicate;
|
||||
@@ -266,7 +266,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static WaitUntilCanceledPromise()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(WaitUntilCanceledPromise), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitUntilCanceledPromise), () => pool.Size);
|
||||
}
|
||||
|
||||
CancellationToken cancellationToken;
|
||||
@@ -362,7 +362,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static WaitUntilValueChangedUnityObjectPromise()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(WaitUntilValueChangedUnityObjectPromise<T, U>), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitUntilValueChangedUnityObjectPromise<T, U>), () => pool.Size);
|
||||
}
|
||||
|
||||
T target;
|
||||
@@ -493,7 +493,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static WaitUntilValueChangedStandardObjectPromise()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(WaitUntilValueChangedStandardObjectPromise<T, U>), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitUntilValueChangedStandardObjectPromise<T, U>), () => pool.Size);
|
||||
}
|
||||
|
||||
WeakReference<T> target;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -382,7 +382,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static AutoResetUniTaskCompletionSource()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(AutoResetUniTaskCompletionSource), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(AutoResetUniTaskCompletionSource), () => pool.Size);
|
||||
}
|
||||
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
@@ -608,7 +608,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static AutoResetUniTaskCompletionSource()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(AutoResetUniTaskCompletionSource<T>), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(AutoResetUniTaskCompletionSource<T>), () => pool.Size);
|
||||
}
|
||||
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,14 +75,134 @@ 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);
|
||||
}
|
||||
|
||||
~AsyncOperationWithCancellationSource()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AsyncOperationConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationConfiguredSource>
|
||||
{
|
||||
static TaskPool<AsyncOperationConfiguredSource> pool;
|
||||
public AsyncOperationConfiguredSource NextNode { get; set; }
|
||||
|
||||
static AsyncOperationConfiguredSource()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(AsyncOperationConfiguredSource), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationConfiguredSource), () => pool.Size);
|
||||
}
|
||||
|
||||
AsyncOperation asyncOperation;
|
||||
@@ -124,7 +244,6 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
@@ -133,6 +252,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
@@ -203,7 +323,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,14 +377,138 @@ 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);
|
||||
}
|
||||
|
||||
~ResourceRequestWithCancellationSource()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ResourceRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<ResourceRequestConfiguredSource>
|
||||
{
|
||||
static TaskPool<ResourceRequestConfiguredSource> pool;
|
||||
public ResourceRequestConfiguredSource NextNode { get; set; }
|
||||
|
||||
static ResourceRequestConfiguredSource()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(ResourceRequestConfiguredSource), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(ResourceRequestConfiguredSource), () => pool.Size);
|
||||
}
|
||||
|
||||
ResourceRequest asyncOperation;
|
||||
@@ -306,7 +550,6 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
return core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
@@ -390,7 +633,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,14 +687,138 @@ 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);
|
||||
}
|
||||
|
||||
~AssetBundleRequestWithCancellationSource()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AssetBundleRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestConfiguredSource>
|
||||
{
|
||||
static TaskPool<AssetBundleRequestConfiguredSource> pool;
|
||||
public AssetBundleRequestConfiguredSource NextNode { get; set; }
|
||||
|
||||
static AssetBundleRequestConfiguredSource()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(AssetBundleRequestConfiguredSource), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(AssetBundleRequestConfiguredSource), () => pool.Size);
|
||||
}
|
||||
|
||||
AssetBundleRequest asyncOperation;
|
||||
@@ -545,11 +912,11 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
asyncOperation = default;
|
||||
progress = default;
|
||||
cancellationToken = default;
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
@@ -576,7 +943,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,14 +997,138 @@ 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);
|
||||
}
|
||||
|
||||
~AssetBundleCreateRequestWithCancellationSource()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AssetBundleCreateRequestConfiguredSource : IUniTaskSource<AssetBundle>, IPlayerLoopItem, ITaskPoolNode<AssetBundleCreateRequestConfiguredSource>
|
||||
{
|
||||
static TaskPool<AssetBundleCreateRequestConfiguredSource> pool;
|
||||
public AssetBundleCreateRequestConfiguredSource NextNode { get; set; }
|
||||
|
||||
static AssetBundleCreateRequestConfiguredSource()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(AssetBundleCreateRequestConfiguredSource), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(AssetBundleCreateRequestConfiguredSource), () => pool.Size);
|
||||
}
|
||||
|
||||
AssetBundleCreateRequest asyncOperation;
|
||||
@@ -731,11 +1222,11 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
asyncOperation = default;
|
||||
progress = default;
|
||||
cancellationToken = default;
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
@@ -763,7 +1254,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))
|
||||
@@ -817,14 +1308,139 @@ 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;
|
||||
core.TrySetResult(asyncOperation.webRequest);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
~UnityWebRequestAsyncOperationWithCancellationSource()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class UnityWebRequestAsyncOperationConfiguredSource : IUniTaskSource<UnityWebRequest>, IPlayerLoopItem, ITaskPoolNode<UnityWebRequestAsyncOperationConfiguredSource>
|
||||
{
|
||||
static TaskPool<UnityWebRequestAsyncOperationConfiguredSource> pool;
|
||||
public UnityWebRequestAsyncOperationConfiguredSource NextNode { get; set; }
|
||||
|
||||
static UnityWebRequestAsyncOperationConfiguredSource()
|
||||
{
|
||||
TaskPoolMonitor.RegisterSizeGetter(typeof(UnityWebRequestAsyncOperationConfiguredSource), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(UnityWebRequestAsyncOperationConfiguredSource), () => pool.Size);
|
||||
}
|
||||
|
||||
UnityWebRequestAsyncOperation asyncOperation;
|
||||
@@ -866,7 +1482,6 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
return core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
@@ -920,11 +1535,11 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
asyncOperation = default;
|
||||
progress = default;
|
||||
cancellationToken = default;
|
||||
TaskTracker.RemoveTracking(this);
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -106,14 +106,153 @@ 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;
|
||||
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);
|
||||
}
|
||||
|
||||
~<#= t.typeName #>WithCancellationSource()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(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 +272,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 +293,6 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
try
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
|
||||
<# if (!IsVoid(t)) { #>
|
||||
return core.GetResult(token);
|
||||
<# } else { #>
|
||||
@@ -161,7 +301,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +331,9 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
<# if(t.returnType == "UnityWebRequest") { #>
|
||||
asyncOperation.webRequest.Abort();
|
||||
<# } #>
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
@@ -209,24 +352,26 @@ namespace Cysharp.Threading.Tasks
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
asyncOperation = default;
|
||||
progress = default;
|
||||
cancellationToken = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
~<#= t.typeName #>ConfiguredSource()
|
||||
{
|
||||
if (pool.TryReturn(this))
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# endregion
|
||||
#endregion
|
||||
<# if(t.returnType == "UnityWebRequest") { #>
|
||||
#endif
|
||||
<# } #>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("UniTask.Linq")]
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fcb1f7467a3e2b64c8a016c8aee2f9b4
|
||||
guid: 8507e97eb606fad4b99c6edf92e19cb8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "com.cysharp.unitask",
|
||||
"displayName": "UniTask",
|
||||
"version": "2.0.12-rc9",
|
||||
"unity": "2019.1",
|
||||
"version": "2.0.14",
|
||||
"unity": "2018.4",
|
||||
"description": "Provides an efficient async/await integration to Unity.",
|
||||
"keywords": [ "async/await", "async", "Task", "UniTask" ],
|
||||
"license": "MIT",
|
||||
|
||||
@@ -21,13 +21,15 @@ public class ExceptionExamples : MonoBehaviour
|
||||
|
||||
private void Start()
|
||||
{
|
||||
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
|
||||
UnityEngine.Debug.Log("ExceptionScene, LoopType:" + PlayerLoopInfo.CurrentLoopType + ":" + Time.frameCount);
|
||||
|
||||
ThrowFromAsyncVoid();
|
||||
_ = ThrowFromTask();
|
||||
_ = ThrowFromUniTask();
|
||||
//TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
|
||||
|
||||
ThrowFromNonAsync();
|
||||
//ThrowFromAsyncVoid();
|
||||
//_ = ThrowFromTask();
|
||||
//_ = ThrowFromUniTask();
|
||||
|
||||
//ThrowFromNonAsync();
|
||||
}
|
||||
|
||||
private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using Cysharp.Threading.Tasks.Triggers;
|
||||
using System;
|
||||
@@ -10,8 +11,11 @@ using System.Threading.Tasks;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
using UnityEngine.LowLevel;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
|
||||
// using DG.Tweening;
|
||||
|
||||
@@ -262,11 +266,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
|
||||
{
|
||||
@@ -293,9 +300,208 @@ public class SandboxMain : MonoBehaviour
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
async UniTaskVoid Start()
|
||||
async Task<int> Test()
|
||||
{
|
||||
await Task.Yield();
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
void Start()
|
||||
{
|
||||
PlayerLoopInfo.Inject();
|
||||
|
||||
//_ = AsyncFixedUpdate();
|
||||
//StartCoroutine(CoroutineFixedUpdate());
|
||||
|
||||
//StartCoroutine(TestCoroutine().ToCoroutine());
|
||||
|
||||
Application.logMessageReceived += Application_logMessageReceived;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
okButton.onClick.AddListener(UniTask.UnityAction(async () =>
|
||||
{
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.TimerFrame(1).ToArrayAsync();
|
||||
}
|
||||
Debug.Log("------------------");
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.TimerFrame(1).ToArrayAsync();
|
||||
}
|
||||
|
||||
//await DelayCheck();
|
||||
/*
|
||||
UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType);
|
||||
StartCoroutine(CoroutineRun());
|
||||
StartCoroutine(CoroutineRun2());
|
||||
_ = AsyncRun();
|
||||
_ = AsyncLastUpdate();
|
||||
_ = AsyncLastLast();
|
||||
*/
|
||||
//await UniTask.Yield();
|
||||
//_ = Test2();
|
||||
// EarlyUpdate.ExecuteMainThreadJobs
|
||||
// _ = Test2();
|
||||
|
||||
//var t = await Resources.LoadAsync<TextAsset>(Application.streamingAssetsPath + "test.txt");
|
||||
//Debug.Log("LoadEnd" + PlayerLoopInfo.CurrentLoopType + ", " + (t != null));
|
||||
//Debug.Log("LoadEnd" + PlayerLoopInfo.CurrentLoopType + ", " + ((TextAsset)t).text);
|
||||
|
||||
|
||||
//await UniTask.Yield(PlayerLoopTiming.LastUpdate);
|
||||
//UnityEngine.Debug.Log("after update:" + Time.frameCount);
|
||||
////await UniTask.NextFrame();
|
||||
////await UniTask.Yield();
|
||||
////UnityEngine.Debug.Log("after update nextframe:" + Time.frameCount);
|
||||
|
||||
//StartCoroutine(CoroutineRun2());
|
||||
////StartCoroutine(CoroutineRun());
|
||||
//UnityEngine.Debug.Log("FOO?");
|
||||
|
||||
//_ = DelayFrame3_Pre();
|
||||
//await UniTask.Yield();
|
||||
|
||||
}));
|
||||
|
||||
cancelButton.onClick.AddListener(UniTask.UnityAction(async () =>
|
||||
{
|
||||
_ = DelayFrame3_Post();
|
||||
await UniTask.Yield();
|
||||
|
||||
//await UniTask.Yield(PlayerLoopTiming.LastPreUpdate);
|
||||
//UnityEngine.Debug.Log("before update:" + Time.frameCount);
|
||||
//await UniTask.NextFrame();
|
||||
//await UniTask.Yield();
|
||||
//UnityEngine.Debug.Log("before update nextframe:" + Time.frameCount);
|
||||
|
||||
//StartCoroutine(CoroutineRun());
|
||||
|
||||
//UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType);
|
||||
//_ = Yieldding();
|
||||
|
||||
//var cts = new CancellationTokenSource();
|
||||
|
||||
//UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType + ":" + Time.frameCount);
|
||||
//var la = SceneManager.LoadSceneAsync("Scenes/ExceptionExamples").WithCancellation(cts.Token);
|
||||
////cts.Cancel();
|
||||
//await la;
|
||||
//UnityEngine.Debug.Log("End LoadSceneAsync" + PlayerLoopInfo.CurrentLoopType + ":" + Time.frameCount);
|
||||
}));
|
||||
|
||||
//return;
|
||||
//await UniTask.SwitchToMainThread();
|
||||
|
||||
//UniTaskAsyncEnumerable.EveryValueChanged(mcc, x => x.MyProperty)
|
||||
// .Do(_ => { }, () => Debug.Log("COMPLETED"))
|
||||
// .ForEachAsync(x =>
|
||||
@@ -305,10 +511,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);
|
||||
@@ -319,44 +544,148 @@ 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);
|
||||
|
||||
await UniTask.Yield();
|
||||
// DOTween.To(
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
//var tween = okButton.GetComponent<RectTransform>().DOLocalMoveX(100, 5.0f);
|
||||
// check stacktrace
|
||||
// await UniTaskAsyncEnumerable.EveryUpdate().Where((x, i) => i % 2 == 0).Select(x => x).DistinctUntilChanged().ForEachAsync(x =>
|
||||
//{
|
||||
// Debug.Log("test");
|
||||
//});
|
||||
|
||||
cancelButton.OnClickAsAsyncEnumerable().ForEachAsync(_ =>
|
||||
|
||||
|
||||
|
||||
//// DOTween.To(
|
||||
|
||||
//var cts = new CancellationTokenSource();
|
||||
|
||||
////var tween = okButton.GetComponent<RectTransform>().DOLocalMoveX(100, 5.0f);
|
||||
|
||||
//cancelButton.OnClickAsAsyncEnumerable().ForEachAsync(_ =>
|
||||
//{
|
||||
// cts.Cancel();
|
||||
//}).Forget();
|
||||
|
||||
|
||||
//// await tween.ToUniTask(TweenCancelBehaviour.KillAndCancelAwait, cts.Token);
|
||||
|
||||
////tween.SetRecyclable(true);
|
||||
|
||||
//Debug.Log("END");
|
||||
|
||||
//// tween.Play();
|
||||
|
||||
//// DOTween.
|
||||
|
||||
//// DOVirtual.Float(0, 1, 1, x => { }).ToUniTask();
|
||||
|
||||
|
||||
//await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate())
|
||||
//{
|
||||
// Debug.Log("Update() " + Time.frameCount);
|
||||
//}
|
||||
|
||||
|
||||
|
||||
//await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsync(_ =>
|
||||
//{
|
||||
//});
|
||||
|
||||
|
||||
//okButton.OnClickAsAsyncEnumerable().ForEachAsync(_ =>
|
||||
//{
|
||||
|
||||
|
||||
//foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
|
||||
//{
|
||||
// Debug.Log(type + ":" + size);
|
||||
//}
|
||||
|
||||
|
||||
//}).Forget();
|
||||
|
||||
//CloseAsync(this.GetCancellationTokenOnDestroy()).Forget();
|
||||
|
||||
//okButton.onClick.AddListener(UniTask.UnityAction(async () => await UniTask.Yield()));
|
||||
|
||||
|
||||
|
||||
//UpdateUniTask().Forget();
|
||||
|
||||
//StartCoroutine(Coroutine());
|
||||
|
||||
//await UniTask.Delay(TimeSpan.FromSeconds(1));
|
||||
|
||||
|
||||
// _ = ReturnToMainThreadTest();
|
||||
|
||||
//GameObject.Destroy(this.gameObject);
|
||||
|
||||
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
cts.Cancel();
|
||||
}).Forget();
|
||||
await UniTask.Yield();
|
||||
UnityEngine.Debug.Log("UniTaskYield:" + PlayerLoopInfo.CurrentLoopType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// await tween.ToUniTask(TweenCancelBehaviour.KillAndCancelAwait, cts.Token);
|
||||
|
||||
//tween.SetRecyclable(true);
|
||||
|
||||
Debug.Log("END");
|
||||
|
||||
// tween.Play();
|
||||
|
||||
// DOTween.
|
||||
|
||||
// DOVirtual.Float(0, 1, 1, x => { }).ToUniTask();
|
||||
|
||||
okButton.OnClickAsAsyncEnumerable().ForEachAsync(_ =>
|
||||
async UniTaskVoid ReturnToMainThreadTest()
|
||||
{
|
||||
var d = UniTask.ReturnToCurrentSynchronizationContext();
|
||||
try
|
||||
{
|
||||
UnityEngine.Debug.Log("In MainThread?" + Thread.CurrentThread.ManagedThreadId);
|
||||
UnityEngine.Debug.Log("SyncContext is null?" + (SynchronizationContext.Current == null));
|
||||
await UniTask.SwitchToThreadPool();
|
||||
UnityEngine.Debug.Log("In ThreadPool?" + Thread.CurrentThread.ManagedThreadId);
|
||||
UnityEngine.Debug.Log("SyncContext is null?" + (SynchronizationContext.Current == null));
|
||||
}
|
||||
finally
|
||||
{
|
||||
await d.DisposeAsync();
|
||||
}
|
||||
|
||||
UnityEngine.Debug.Log("In ThreadPool?" + Thread.CurrentThread.ManagedThreadId);
|
||||
UnityEngine.Debug.Log("SyncContext is null2" + (SynchronizationContext.Current == null));
|
||||
}
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// UnityEngine.Debug.Log("Update:" + PlayerLoopInfo.CurrentLoopType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}).Forget();
|
||||
|
||||
CloseAsync(this.GetCancellationTokenOnDestroy()).Forget();
|
||||
|
||||
okButton.onClick.AddListener(UniTask.UnityAction(async () => await UniTask.Yield()));
|
||||
IEnumerator Coroutine()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
yield return null;
|
||||
//UnityEngine.Debug.Log("Coroutine null:" + PlayerLoopInfo.CurrentLoopType);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
UnityEngine.Debug.Log("Coroutine Finally");
|
||||
}
|
||||
}
|
||||
|
||||
async UniTaskVoid CloseAsync(CancellationToken cancellationToken = default)
|
||||
@@ -597,7 +926,7 @@ public class SandboxMain : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
public class ShowPlayerLoop
|
||||
public class PlayerLoopInfo
|
||||
{
|
||||
// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
static void Init()
|
||||
@@ -628,4 +957,45 @@ public class ShowPlayerLoop
|
||||
|
||||
UnityEngine.Debug.Log(sb.ToString());
|
||||
}
|
||||
|
||||
public static Type CurrentLoopType { get; private set; }
|
||||
|
||||
public static void Inject()
|
||||
{
|
||||
var system = PlayerLoop.GetCurrentPlayerLoop();
|
||||
|
||||
for (int i = 0; i < system.subSystemList.Length; i++)
|
||||
{
|
||||
var loop = system.subSystemList[i].subSystemList.SelectMany(x =>
|
||||
{
|
||||
var t = typeof(WrapLoop<>).MakeGenericType(x.type);
|
||||
var instance = (ILoopRunner)Activator.CreateInstance(t, x.type);
|
||||
return new[] { new PlayerLoopSystem { type = t, updateDelegate = instance.Run }, x };
|
||||
}).ToArray();
|
||||
|
||||
system.subSystemList[i].subSystemList = loop;
|
||||
}
|
||||
|
||||
PlayerLoop.SetPlayerLoop(system);
|
||||
}
|
||||
|
||||
interface ILoopRunner
|
||||
{
|
||||
void Run();
|
||||
}
|
||||
|
||||
class WrapLoop<T> : ILoopRunner
|
||||
{
|
||||
readonly Type type;
|
||||
|
||||
public WrapLoop(Type type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
CurrentLoopType = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
1457
src/UniTask/Assets/TempRuntimeUnitTestScene_1591671582.unity
Normal file
1457
src/UniTask/Assets/TempRuntimeUnitTestScene_1591671582.unity
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 370f60b2e4cd92d4faafda153c9c5f8a
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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 () =>
|
||||
{
|
||||
|
||||
97
src/UniTask/Assets/Tests/CachelikeTest.cs
Normal file
97
src/UniTask/Assets/Tests/CachelikeTest.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
11
src/UniTask/Assets/Tests/CachelikeTest.cs.meta
Normal file
11
src/UniTask/Assets/Tests/CachelikeTest.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 416e0e77b4408b0498792eb218ed2870
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
394
src/UniTask/Assets/Tests/DelayTest.cs
Normal file
394
src/UniTask/Assets/Tests/DelayTest.cs
Normal file
@@ -0,0 +1,394 @@
|
||||
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;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Cysharp.Threading.TasksTests
|
||||
{
|
||||
public class DelayTest
|
||||
{
|
||||
//[UnityTest]
|
||||
//public IEnumerator DelayFrame() => UniTask.ToCoroutine(async () =>
|
||||
//{
|
||||
// for (int i = 1; i < 5; i++)
|
||||
// {
|
||||
// await UniTask.Yield(PlayerLoopTiming.PreUpdate);
|
||||
// var frameCount = Time.frameCount;
|
||||
// await UniTask.DelayFrame(i);
|
||||
// Time.frameCount.Should().Be(frameCount + i);
|
||||
// }
|
||||
|
||||
// for (int i = 1; i < 5; i++)
|
||||
// {
|
||||
// await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||
// var frameCount = Time.frameCount;
|
||||
// await UniTask.DelayFrame(i);
|
||||
// Time.frameCount.Should().Be(frameCount + i);
|
||||
// }
|
||||
//});
|
||||
|
||||
//[UnityTest]
|
||||
//public IEnumerator DelayFrameZero() => UniTask.ToCoroutine(async () =>
|
||||
//{
|
||||
// {
|
||||
// await UniTask.Yield(PlayerLoopTiming.PreUpdate);
|
||||
// var frameCount = Time.frameCount;
|
||||
// await UniTask.DelayFrame(0);
|
||||
// Time.frameCount.Should().Be(frameCount); // same frame
|
||||
// }
|
||||
// {
|
||||
// await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||
// var frameCount = Time.frameCount;
|
||||
// await UniTask.DelayFrame(0);
|
||||
// Time.frameCount.Should().Be(frameCount + 1); // next frame
|
||||
// }
|
||||
//});
|
||||
|
||||
|
||||
|
||||
//[UnityTest]
|
||||
//public IEnumerator TimerFramePre() => UniTask.ToCoroutine(async () =>
|
||||
//{
|
||||
// await UniTask.Yield(PlayerLoopTiming.PreUpdate);
|
||||
|
||||
// var initialFrame = Time.frameCount;
|
||||
// var xs = await UniTaskAsyncEnumerable.TimerFrame(2, 3).Take(5).Select(_ => Time.frameCount).ToArrayAsync();
|
||||
|
||||
// xs[0].Should().Be(initialFrame + 2);
|
||||
// xs[1].Should().Be(initialFrame + 2 + (3 * 1));
|
||||
// xs[2].Should().Be(initialFrame + 2 + (3 * 2));
|
||||
// xs[3].Should().Be(initialFrame + 2 + (3 * 3));
|
||||
// xs[4].Should().Be(initialFrame + 2 + (3 * 4));
|
||||
//});
|
||||
|
||||
|
||||
//[UnityTest]
|
||||
//public IEnumerator TimerFramePost() => UniTask.ToCoroutine(async () =>
|
||||
//{
|
||||
// await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||
|
||||
// var initialFrame = Time.frameCount;
|
||||
// var xs = await UniTaskAsyncEnumerable.TimerFrame(2, 3).Take(5).Select(_ => Time.frameCount).ToArrayAsync();
|
||||
|
||||
// xs[0].Should().Be(initialFrame + 2);
|
||||
// xs[1].Should().Be(initialFrame + 2 + (3 * 1));
|
||||
// xs[2].Should().Be(initialFrame + 2 + (3 * 2));
|
||||
// xs[3].Should().Be(initialFrame + 2 + (3 * 3));
|
||||
// xs[4].Should().Be(initialFrame + 2 + (3 * 4));
|
||||
//});
|
||||
|
||||
|
||||
//[UnityTest]
|
||||
//public IEnumerator TimerFrameTest() => UniTask.ToCoroutine(async () =>
|
||||
//{
|
||||
// await UniTask.Yield(PlayerLoopTiming.PreUpdate);
|
||||
|
||||
// var initialFrame = Time.frameCount;
|
||||
// var xs = await UniTaskAsyncEnumerable.TimerFrame(0, 0).Take(5).Select(_ => Time.frameCount).ToArrayAsync();
|
||||
|
||||
// xs[0].Should().Be(initialFrame);
|
||||
// xs[1].Should().Be(initialFrame + 1);
|
||||
// xs[2].Should().Be(initialFrame + 2);
|
||||
// xs[3].Should().Be(initialFrame + 3);
|
||||
// xs[4].Should().Be(initialFrame + 4);
|
||||
//});
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator TimerFrameSinglePre2() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.TimerFrame(1).ToArrayAsync();
|
||||
}
|
||||
//Debug.Log("------------------");
|
||||
//{
|
||||
// var xs = await UniTaskAsyncEnumerable.TimerFrame(1).ToArrayAsync();
|
||||
//}
|
||||
});
|
||||
|
||||
|
||||
//[UnityTest]
|
||||
//public IEnumerator TimerFrameSinglePre2() => UniTask.ToCoroutine(async () =>
|
||||
//{
|
||||
// {
|
||||
// var initialFrame = Time.frameCount;
|
||||
// var xs = await new MyTimerFrame(0, null)/*.Select(_ => Time.frameCount)*/.ToArrayAsync();
|
||||
// Debug.Log("OK 0 ------------------");
|
||||
// }
|
||||
// {
|
||||
// var xs = await new MyTimerFrame(1, null)/*.Select(_ =>
|
||||
// {
|
||||
// var t = Time.frameCount;
|
||||
// UnityEngine.Debug.Log("store frameCount:" + t);
|
||||
// return t;
|
||||
// })*/.ToArrayAsync();
|
||||
// }
|
||||
//});
|
||||
|
||||
//[UnityTest]
|
||||
//public IEnumerator TimerFrameSinglePre() => UniTask.ToCoroutine(async () =>
|
||||
//{
|
||||
// {
|
||||
// await UniTask.Yield(PlayerLoopTiming.PreUpdate);
|
||||
// var initialFrame = Time.frameCount;
|
||||
// var xs = await UniTaskAsyncEnumerable.Return(UniTask.Yield(PlayerLoopTiming.Update, CancellationToken.None))/*.Select(_ => Time.frameCount)*/.ToArrayAsync();
|
||||
// xs[0].Should().Be(initialFrame);
|
||||
// Debug.Log("OK 0 ------------------");
|
||||
// }
|
||||
// {
|
||||
// await UniTask.Yield(PlayerLoopTiming.PreUpdate);
|
||||
// var initialFrame = Time.frameCount;
|
||||
// Debug.Log("initialFrame:" + initialFrame);
|
||||
// var xs = await UniTaskAsyncEnumerable.Return(UniTask.Yield(PlayerLoopTiming.Update, CancellationToken.None))/*.Select(_ =>
|
||||
// {
|
||||
// var t = Time.frameCount;
|
||||
// UnityEngine.Debug.Log("store frameCount:" + t);
|
||||
// return t;
|
||||
// })*/.ToArrayAsync();
|
||||
// Debug.Log("xs len:" + xs.Length);
|
||||
// Debug.Log("xs[0]:" + xs[0]);
|
||||
|
||||
// xs[0].Should().Be(initialFrame + 1);
|
||||
// Debug.Log("OK 1");
|
||||
// }
|
||||
// {
|
||||
// //await UniTask.Yield(PlayerLoopTiming.PreUpdate);
|
||||
// var initialFrame = Time.frameCount;
|
||||
// var xs = await UniTaskAsyncEnumerable.TimerFrame(2).Select(_ => Time.frameCount).ToArrayAsync();
|
||||
// xs[0].Should().Be(initialFrame + 2);
|
||||
// Debug.Log("OK 2");
|
||||
// }
|
||||
//});
|
||||
|
||||
|
||||
//[UnityTest]
|
||||
//public IEnumerator TimerFrameSinglePost() => UniTask.ToCoroutine(async () =>
|
||||
//{
|
||||
// {
|
||||
// //await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||
// //var initialFrame = Time.frameCount;
|
||||
// //var xs = await UniTaskAsyncEnumerable.TimerFrame(0).Select(_ => Time.frameCount).ToArrayAsync();
|
||||
// //xs[0].Should().Be(initialFrame);
|
||||
// }
|
||||
// {
|
||||
// //await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||
// var initialFrame = Time.frameCount;
|
||||
// var xs = await UniTaskAsyncEnumerable.TimerFrame(1).Select(_ => Time.frameCount).ToArrayAsync();
|
||||
// xs[0].Should().Be(initialFrame + 1);
|
||||
// }
|
||||
// {
|
||||
// //await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||
// var initialFrame = Time.frameCount;
|
||||
// var xs = await UniTaskAsyncEnumerable.TimerFrame(2).Select(_ => Time.frameCount).ToArrayAsync();
|
||||
// xs[0].Should().Be(initialFrame + 2);
|
||||
// }
|
||||
//});
|
||||
|
||||
|
||||
|
||||
//[UnityTest]
|
||||
//public IEnumerator Timer() => UniTask.ToCoroutine(async () =>
|
||||
//{
|
||||
// await UniTask.Yield(PlayerLoopTiming.PreUpdate);
|
||||
|
||||
// {
|
||||
// var initialSeconds = Time.realtimeSinceStartup;
|
||||
// var xs = await UniTaskAsyncEnumerable.Timer(TimeSpan.FromSeconds(2)).Select(_ => Time.realtimeSinceStartup).ToArrayAsync();
|
||||
|
||||
// Mathf.Approximately(initialSeconds, xs[0]).Should().BeFalse();
|
||||
// Debug.Log("Init:" + initialSeconds);
|
||||
// Debug.Log("After:" + xs[0]);
|
||||
// }
|
||||
//});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class DelayTest2
|
||||
{
|
||||
[UnityTest]
|
||||
public IEnumerator TimerFrameSinglePre2() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.TimerFrame(1).ToArrayAsync();
|
||||
}
|
||||
Debug.Log("------------------");
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.TimerFrame(1).ToArrayAsync();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public class ThreadRunner
|
||||
{
|
||||
Thread thread;
|
||||
|
||||
public void Start(IPlayerLoopItem runner)
|
||||
{
|
||||
thread = new Thread(() =>
|
||||
{
|
||||
Thread.Sleep(30);
|
||||
while (runner.MoveNext())
|
||||
{
|
||||
Thread.Sleep(30);
|
||||
}
|
||||
});
|
||||
|
||||
thread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
internal class MyTimerFrame : IUniTaskAsyncEnumerable<AsyncUnit>
|
||||
{
|
||||
//readonly PlayerLoopTiming updateTiming;
|
||||
readonly int dueTimeFrameCount;
|
||||
readonly int? periodFrameCount;
|
||||
|
||||
public MyTimerFrame(int dueTimeFrameCount, int? periodFrameCount)
|
||||
{
|
||||
//this.updateTiming = updateTiming;
|
||||
this.dueTimeFrameCount = dueTimeFrameCount;
|
||||
this.periodFrameCount = periodFrameCount;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _TimerFrame(dueTimeFrameCount, periodFrameCount, cancellationToken);
|
||||
}
|
||||
|
||||
class _TimerFrame : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem
|
||||
{
|
||||
readonly int dueTimeFrameCount;
|
||||
readonly int? periodFrameCount;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
int initialFrame;
|
||||
int currentFrame;
|
||||
bool dueTimePhase;
|
||||
bool completed;
|
||||
bool disposed;
|
||||
ThreadRunner runner;
|
||||
|
||||
public _TimerFrame(int dueTimeFrameCount, int? periodFrameCount, CancellationToken cancellationToken)
|
||||
{
|
||||
if (dueTimeFrameCount <= 0) dueTimeFrameCount = 0;
|
||||
if (periodFrameCount != null)
|
||||
{
|
||||
if (periodFrameCount <= 0) periodFrameCount = 1;
|
||||
}
|
||||
|
||||
//this.initialFrame = Time.frameCount;
|
||||
this.dueTimePhase = true;
|
||||
this.dueTimeFrameCount = dueTimeFrameCount;
|
||||
this.periodFrameCount = periodFrameCount;
|
||||
|
||||
//TaskTracker.TrackActiveTask(this, 2);
|
||||
//PlayerLoopHelper.AddAction(updateTiming, this);
|
||||
|
||||
runner = new ThreadRunner();
|
||||
runner.Start(this);
|
||||
}
|
||||
|
||||
public AsyncUnit Current => default;
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
// return false instead of throw
|
||||
if (disposed || cancellationToken.IsCancellationRequested || completed) return default;
|
||||
|
||||
|
||||
// reset value here.
|
||||
this.currentFrame = 0;
|
||||
|
||||
completionSource.Reset();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
disposed = true;
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
UnityEngine.Debug.Log("Called MoveNext");
|
||||
if (disposed || cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
UnityEngine.Debug.Log("Disposing");
|
||||
completionSource.TrySetResult(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dueTimePhase)
|
||||
{
|
||||
UnityEngine.Debug.Log("In DueTime Phase");
|
||||
if (currentFrame == 0)
|
||||
{
|
||||
if (dueTimeFrameCount == 0)
|
||||
{
|
||||
dueTimePhase = false;
|
||||
completionSource.TrySetResult(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// skip in initial frame.
|
||||
/*
|
||||
UnityEngine.Debug.Log("(Init, frameConut)" + (initialFrame, Time.frameCount));
|
||||
if (initialFrame == Time.frameCount)
|
||||
{
|
||||
UnityEngine.Debug.Log("Skip Here");
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
UnityEngine.Debug.Log("Which Go?");
|
||||
if (++currentFrame >= dueTimeFrameCount)
|
||||
{
|
||||
UnityEngine.Debug.Log("END Go?");
|
||||
dueTimePhase = false;
|
||||
completionSource.TrySetResult(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.Log("NG Go?");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (periodFrameCount == null)
|
||||
{
|
||||
UnityEngine.Debug.Log("PERIOD");
|
||||
completed = true;
|
||||
completionSource.TrySetResult(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (++currentFrame >= periodFrameCount)
|
||||
{
|
||||
completionSource.TrySetResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
src/UniTask/Assets/Tests/DelayTest.cs.meta
Normal file
11
src/UniTask/Assets/Tests/DelayTest.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1376b93d9e1083a4a9b4081e08f3a7b7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,400 +1,400 @@
|
||||
#if !(UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2)
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
//#if !(UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2)
|
||||
//#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Scripting;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine.SceneManagement;
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using UnityEngine.Networking;
|
||||
//using UnityEngine;
|
||||
//using System;
|
||||
//using System.Collections;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Linq;
|
||||
//using System.Text;
|
||||
//using UnityEngine.UI;
|
||||
//using UnityEngine.Scripting;
|
||||
//using Cysharp.Threading.Tasks;
|
||||
//using UnityEngine.SceneManagement;
|
||||
//#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
//using System.Threading.Tasks;
|
||||
//#endif
|
||||
//using UnityEngine.Networking;
|
||||
|
||||
#if !UNITY_2019_3_OR_NEWER
|
||||
using UnityEngine.Experimental.LowLevel;
|
||||
#else
|
||||
using UnityEngine.LowLevel;
|
||||
#endif
|
||||
//#if !UNITY_2019_3_OR_NEWER
|
||||
//using UnityEngine.Experimental.LowLevel;
|
||||
//#else
|
||||
//using UnityEngine.LowLevel;
|
||||
//#endif
|
||||
|
||||
#if !UNITY_WSA
|
||||
using Unity.Jobs;
|
||||
#endif
|
||||
using Unity.Collections;
|
||||
using System.Threading;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.TestTools;
|
||||
using FluentAssertions;
|
||||
//#if !UNITY_WSA
|
||||
//using Unity.Jobs;
|
||||
//#endif
|
||||
//using Unity.Collections;
|
||||
//using System.Threading;
|
||||
//using NUnit.Framework;
|
||||
//using UnityEngine.TestTools;
|
||||
//using FluentAssertions;
|
||||
|
||||
namespace Cysharp.Threading.TasksTests
|
||||
{
|
||||
public class AsyncTest
|
||||
{
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#if !UNITY_WSA
|
||||
//namespace Cysharp.Threading.TasksTests
|
||||
//{
|
||||
// public class AsyncTest
|
||||
// {
|
||||
//#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
//#if !UNITY_WSA
|
||||
|
||||
public struct MyJob : IJob
|
||||
{
|
||||
public int loopCount;
|
||||
public NativeArray<int> inOut;
|
||||
public int result;
|
||||
// public struct MyJob : IJob
|
||||
// {
|
||||
// public int loopCount;
|
||||
// public NativeArray<int> inOut;
|
||||
// public int result;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
result = 0;
|
||||
for (int i = 0; i < loopCount; i++)
|
||||
{
|
||||
result++;
|
||||
}
|
||||
inOut[0] = result;
|
||||
}
|
||||
}
|
||||
// public void Execute()
|
||||
// {
|
||||
// result = 0;
|
||||
// for (int i = 0; i < loopCount; i++)
|
||||
// {
|
||||
// result++;
|
||||
// }
|
||||
// inOut[0] = result;
|
||||
// }
|
||||
// }
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator DelayAnd() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||
// [UnityTest]
|
||||
// public IEnumerator DelayAnd() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||
|
||||
var time = Time.realtimeSinceStartup;
|
||||
// var time = Time.realtimeSinceStartup;
|
||||
|
||||
Time.timeScale = 0.5f;
|
||||
try
|
||||
{
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(3));
|
||||
// Time.timeScale = 0.5f;
|
||||
// try
|
||||
// {
|
||||
// await UniTask.Delay(TimeSpan.FromSeconds(3));
|
||||
|
||||
var elapsed = Time.realtimeSinceStartup - time;
|
||||
((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(6);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Time.timeScale = 1.0f;
|
||||
}
|
||||
});
|
||||
// var elapsed = Time.realtimeSinceStartup - time;
|
||||
// ((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(6);
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// Time.timeScale = 1.0f;
|
||||
// }
|
||||
// });
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
var time = Time.realtimeSinceStartup;
|
||||
// [UnityTest]
|
||||
// public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// var time = Time.realtimeSinceStartup;
|
||||
|
||||
Time.timeScale = 0.5f;
|
||||
try
|
||||
{
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(3), ignoreTimeScale: true);
|
||||
// Time.timeScale = 0.5f;
|
||||
// try
|
||||
// {
|
||||
// await UniTask.Delay(TimeSpan.FromSeconds(3), ignoreTimeScale: true);
|
||||
|
||||
var elapsed = Time.realtimeSinceStartup - time;
|
||||
((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(3);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Time.timeScale = 1.0f;
|
||||
}
|
||||
});
|
||||
// var elapsed = Time.realtimeSinceStartup - time;
|
||||
// ((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(3);
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// Time.timeScale = 1.0f;
|
||||
// }
|
||||
// });
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator WhenAll() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
var a = UniTask.FromResult(999);
|
||||
var b = UniTask.Yield(PlayerLoopTiming.Update, CancellationToken.None).AsAsyncUnitUniTask();
|
||||
var c = UniTask.DelayFrame(99).AsAsyncUnitUniTask();
|
||||
// [UnityTest]
|
||||
// public IEnumerator WhenAll() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// var a = UniTask.FromResult(999);
|
||||
// var b = UniTask.Yield(PlayerLoopTiming.Update, CancellationToken.None).AsAsyncUnitUniTask();
|
||||
// var c = UniTask.DelayFrame(99).AsAsyncUnitUniTask();
|
||||
|
||||
var (a2, b2, c2) = await UniTask.WhenAll(a, b, c);
|
||||
a2.Should().Be(999);
|
||||
b2.Should().Be(AsyncUnit.Default);
|
||||
c2.Should().Be(AsyncUnit.Default);
|
||||
});
|
||||
// var (a2, b2, c2) = await UniTask.WhenAll(a, b, c);
|
||||
// a2.Should().Be(999);
|
||||
// b2.Should().Be(AsyncUnit.Default);
|
||||
// c2.Should().Be(AsyncUnit.Default);
|
||||
// });
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator WhenAny() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
var a = UniTask.FromResult(999);
|
||||
var b = UniTask.Yield(PlayerLoopTiming.Update, CancellationToken.None).AsAsyncUnitUniTask();
|
||||
var c = UniTask.DelayFrame(99).AsAsyncUnitUniTask();
|
||||
// [UnityTest]
|
||||
// public IEnumerator WhenAny() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// var a = UniTask.FromResult(999);
|
||||
// var b = UniTask.Yield(PlayerLoopTiming.Update, CancellationToken.None).AsAsyncUnitUniTask();
|
||||
// var c = UniTask.DelayFrame(99).AsAsyncUnitUniTask();
|
||||
|
||||
var (win, a2, b2, c2) = await UniTask.WhenAny(a, b, c);
|
||||
win.Should().Be(0);
|
||||
a2.Should().Be(999);
|
||||
});
|
||||
// var (win, a2, b2, c2) = await UniTask.WhenAny(a, b, c);
|
||||
// win.Should().Be(0);
|
||||
// a2.Should().Be(999);
|
||||
// });
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator BothEnumeratorCheck() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
await ToaruCoroutineEnumerator(); // wait 5 frame:)
|
||||
});
|
||||
// [UnityTest]
|
||||
// public IEnumerator BothEnumeratorCheck() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// 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
|
||||
{
|
||||
public int MyProperty { get; set; }
|
||||
}
|
||||
// class MyMyClass
|
||||
// {
|
||||
// public int MyProperty { get; set; }
|
||||
// }
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator WaitUntil() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
bool t = false;
|
||||
// [UnityTest]
|
||||
// public IEnumerator WaitUntil() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// bool t = false;
|
||||
|
||||
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||
// await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||
|
||||
UniTask.DelayFrame(10,PlayerLoopTiming.PostLateUpdate).ContinueWith(() => t = true).Forget();
|
||||
// UniTask.DelayFrame(10,PlayerLoopTiming.PostLateUpdate).ContinueWith(() => t = true).Forget();
|
||||
|
||||
var startFrame = Time.frameCount;
|
||||
await UniTask.WaitUntil(() => t, PlayerLoopTiming.EarlyUpdate);
|
||||
// var startFrame = Time.frameCount;
|
||||
// await UniTask.WaitUntil(() => t, PlayerLoopTiming.EarlyUpdate);
|
||||
|
||||
var diff = Time.frameCount - startFrame;
|
||||
diff.Should().Be(11);
|
||||
});
|
||||
// var diff = Time.frameCount - startFrame;
|
||||
// diff.Should().Be(11);
|
||||
// });
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator WaitWhile() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
bool t = true;
|
||||
// [UnityTest]
|
||||
// public IEnumerator WaitWhile() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// bool t = true;
|
||||
|
||||
UniTask.DelayFrame(10, PlayerLoopTiming.PostLateUpdate).ContinueWith(() => t = false).Forget();
|
||||
// UniTask.DelayFrame(10, PlayerLoopTiming.PostLateUpdate).ContinueWith(() => t = false).Forget();
|
||||
|
||||
var startFrame = Time.frameCount;
|
||||
await UniTask.WaitWhile(() => t, PlayerLoopTiming.EarlyUpdate);
|
||||
// var startFrame = Time.frameCount;
|
||||
// await UniTask.WaitWhile(() => t, PlayerLoopTiming.EarlyUpdate);
|
||||
|
||||
var diff = Time.frameCount - startFrame;
|
||||
diff.Should().Be(11);
|
||||
});
|
||||
// var diff = Time.frameCount - startFrame;
|
||||
// diff.Should().Be(11);
|
||||
// });
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator WaitUntilValueChanged() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
var v = new MyMyClass { MyProperty = 99 };
|
||||
// [UnityTest]
|
||||
// public IEnumerator WaitUntilValueChanged() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// var v = new MyMyClass { MyProperty = 99 };
|
||||
|
||||
UniTask.DelayFrame(10, PlayerLoopTiming.PostLateUpdate).ContinueWith(() => v.MyProperty = 1000).Forget();
|
||||
// UniTask.DelayFrame(10, PlayerLoopTiming.PostLateUpdate).ContinueWith(() => v.MyProperty = 1000).Forget();
|
||||
|
||||
var startFrame = Time.frameCount;
|
||||
await UniTask.WaitUntilValueChanged(v, x => x.MyProperty, PlayerLoopTiming.EarlyUpdate);
|
||||
// var startFrame = Time.frameCount;
|
||||
// await UniTask.WaitUntilValueChanged(v, x => x.MyProperty, PlayerLoopTiming.EarlyUpdate);
|
||||
|
||||
var diff = Time.frameCount - startFrame;
|
||||
diff.Should().Be(11);
|
||||
});
|
||||
// var diff = Time.frameCount - startFrame;
|
||||
// diff.Should().Be(11);
|
||||
// });
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator SwitchTo() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
await UniTask.Yield();
|
||||
// [UnityTest]
|
||||
// public IEnumerator SwitchTo() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// await UniTask.Yield();
|
||||
|
||||
var currentThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
// var currentThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
|
||||
|
||||
|
||||
await UniTask.SwitchToThreadPool();
|
||||
//await UniTask.SwitchToThreadPool();
|
||||
//await UniTask.SwitchToThreadPool();
|
||||
// await UniTask.SwitchToThreadPool();
|
||||
// //await UniTask.SwitchToThreadPool();
|
||||
// //await UniTask.SwitchToThreadPool();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var switchedThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
// var switchedThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
|
||||
|
||||
|
||||
currentThreadId.Should().NotBe(switchedThreadId);
|
||||
// currentThreadId.Should().NotBe(switchedThreadId);
|
||||
|
||||
|
||||
await UniTask.Yield();
|
||||
// await UniTask.Yield();
|
||||
|
||||
var switchedThreadId2 = Thread.CurrentThread.ManagedThreadId;
|
||||
// var switchedThreadId2 = Thread.CurrentThread.ManagedThreadId;
|
||||
|
||||
currentThreadId.Should().Be(switchedThreadId2);
|
||||
});
|
||||
// currentThreadId.Should().Be(switchedThreadId2);
|
||||
// });
|
||||
|
||||
//[UnityTest]
|
||||
//public IEnumerator ObservableConversion() => UniTask.ToCoroutine(async () =>
|
||||
//{
|
||||
// var v = await Observable.Range(1, 10).ToUniTask();
|
||||
// v.Is(10);
|
||||
// //[UnityTest]
|
||||
// //public IEnumerator ObservableConversion() => UniTask.ToCoroutine(async () =>
|
||||
// //{
|
||||
// // var v = await Observable.Range(1, 10).ToUniTask();
|
||||
// // v.Is(10);
|
||||
|
||||
// v = await Observable.Range(1, 10).ToUniTask(useFirstValue: true);
|
||||
// v.Is(1);
|
||||
// // v = await Observable.Range(1, 10).ToUniTask(useFirstValue: true);
|
||||
// // v.Is(1);
|
||||
|
||||
// v = await UniTask.DelayFrame(10).ToObservable().ToTask();
|
||||
// v.Is(10);
|
||||
// // v = await UniTask.DelayFrame(10).ToObservable().ToTask();
|
||||
// // v.Is(10);
|
||||
|
||||
// v = await UniTask.FromResult(99).ToObservable();
|
||||
// v.Is(99);
|
||||
//});
|
||||
// // v = await UniTask.FromResult(99).ToObservable();
|
||||
// // v.Is(99);
|
||||
// //});
|
||||
|
||||
//[UnityTest]
|
||||
//public IEnumerator AwaitableReactiveProperty() => UniTask.ToCoroutine(async () =>
|
||||
//{
|
||||
// var rp1 = new ReactiveProperty<int>(99);
|
||||
// //[UnityTest]
|
||||
// //public IEnumerator AwaitableReactiveProperty() => UniTask.ToCoroutine(async () =>
|
||||
// //{
|
||||
// // var rp1 = new ReactiveProperty<int>(99);
|
||||
|
||||
// UniTask.DelayFrame(100).ContinueWith(x => rp1.Value = x).Forget();
|
||||
// // UniTask.DelayFrame(100).ContinueWith(x => rp1.Value = x).Forget();
|
||||
|
||||
// await rp1;
|
||||
// // await rp1;
|
||||
|
||||
// rp1.Value.Is(100);
|
||||
// // rp1.Value.Is(100);
|
||||
|
||||
// // var delay2 = UniTask.DelayFrame(10);
|
||||
// // var (a, b ) = await UniTask.WhenAll(rp1.WaitUntilValueChangedAsync(), delay2);
|
||||
// // // var delay2 = UniTask.DelayFrame(10);
|
||||
// // // var (a, b ) = await UniTask.WhenAll(rp1.WaitUntilValueChangedAsync(), delay2);
|
||||
|
||||
//});
|
||||
// //});
|
||||
|
||||
//[UnityTest]
|
||||
//public IEnumerator AwaitableReactiveCommand() => UniTask.ToCoroutine(async () =>
|
||||
//{
|
||||
// var rc = new ReactiveCommand<int>();
|
||||
// //[UnityTest]
|
||||
// //public IEnumerator AwaitableReactiveCommand() => UniTask.ToCoroutine(async () =>
|
||||
// //{
|
||||
// // var rc = new ReactiveCommand<int>();
|
||||
|
||||
// UniTask.DelayFrame(100).ContinueWith(x => rc.Execute(x)).Forget();
|
||||
// // UniTask.DelayFrame(100).ContinueWith(x => rc.Execute(x)).Forget();
|
||||
|
||||
// var v = await rc;
|
||||
// // var v = await rc;
|
||||
|
||||
// v.Is(100);
|
||||
//});
|
||||
// // v.Is(100);
|
||||
// //});
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ExceptionlessCancellation() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
// [UnityTest]
|
||||
// public IEnumerator ExceptionlessCancellation() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// var cts = new CancellationTokenSource();
|
||||
|
||||
UniTask.DelayFrame(10).ContinueWith(() => cts.Cancel()).Forget();
|
||||
// UniTask.DelayFrame(10).ContinueWith(() => cts.Cancel()).Forget();
|
||||
|
||||
var first = Time.frameCount;
|
||||
var canceled = await UniTask.DelayFrame(100, cancellationToken: cts.Token).SuppressCancellationThrow();
|
||||
// var first = Time.frameCount;
|
||||
// var canceled = await UniTask.DelayFrame(100, cancellationToken: cts.Token).SuppressCancellationThrow();
|
||||
|
||||
(Time.frameCount - first).Should().Be(11); // 10 frame canceled
|
||||
canceled.Should().Be(true);
|
||||
});
|
||||
// (Time.frameCount - first).Should().Be(11); // 10 frame canceled
|
||||
// canceled.Should().Be(true);
|
||||
// });
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ExceptionCancellation() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
// [UnityTest]
|
||||
// public IEnumerator ExceptionCancellation() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// var cts = new CancellationTokenSource();
|
||||
|
||||
UniTask.DelayFrame(10).ContinueWith(() => cts.Cancel()).Forget();
|
||||
// UniTask.DelayFrame(10).ContinueWith(() => cts.Cancel()).Forget();
|
||||
|
||||
bool occur = false;
|
||||
try
|
||||
{
|
||||
await UniTask.DelayFrame(100, cancellationToken: cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
occur = true;
|
||||
}
|
||||
occur.Should().BeTrue();
|
||||
});
|
||||
// bool occur = false;
|
||||
// try
|
||||
// {
|
||||
// await UniTask.DelayFrame(100, cancellationToken: cts.Token);
|
||||
// }
|
||||
// catch (OperationCanceledException)
|
||||
// {
|
||||
// occur = true;
|
||||
// }
|
||||
// occur.Should().BeTrue();
|
||||
// });
|
||||
|
||||
IEnumerator ToaruCoroutineEnumerator()
|
||||
{
|
||||
yield return null;
|
||||
yield return null;
|
||||
yield return null;
|
||||
yield return null;
|
||||
yield return null;
|
||||
}
|
||||
// IEnumerator ToaruCoroutineEnumerator()
|
||||
// {
|
||||
// yield return null;
|
||||
// yield return null;
|
||||
// yield return null;
|
||||
// yield return null;
|
||||
// yield return null;
|
||||
// }
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ExceptionUnobserved1() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
bool calledEx = false;
|
||||
Action<Exception> action = exx =>
|
||||
{
|
||||
calledEx = true;
|
||||
exx.Message.Should().Be("MyException");
|
||||
};
|
||||
// [UnityTest]
|
||||
// public IEnumerator ExceptionUnobserved1() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// bool calledEx = false;
|
||||
// Action<Exception> action = exx =>
|
||||
// {
|
||||
// calledEx = true;
|
||||
// exx.Message.Should().Be("MyException");
|
||||
// };
|
||||
|
||||
UniTaskScheduler.UnobservedTaskException += action;
|
||||
// UniTaskScheduler.UnobservedTaskException += action;
|
||||
|
||||
var ex = InException1();
|
||||
ex = default(UniTask);
|
||||
// var ex = InException1();
|
||||
// ex = default(UniTask);
|
||||
|
||||
await UniTask.DelayFrame(3);
|
||||
// await UniTask.DelayFrame(3);
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
// GC.Collect();
|
||||
// GC.WaitForPendingFinalizers();
|
||||
// GC.Collect();
|
||||
|
||||
await UniTask.DelayFrame(1);
|
||||
// await UniTask.DelayFrame(1);
|
||||
|
||||
calledEx.Should().BeTrue();
|
||||
// calledEx.Should().BeTrue();
|
||||
|
||||
UniTaskScheduler.UnobservedTaskException -= action;
|
||||
});
|
||||
// UniTaskScheduler.UnobservedTaskException -= action;
|
||||
// });
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ExceptionUnobserved2() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
bool calledEx = false;
|
||||
Action<Exception> action = exx =>
|
||||
{
|
||||
calledEx = true;
|
||||
exx.Message.Should().Be("MyException");
|
||||
};
|
||||
// [UnityTest]
|
||||
// public IEnumerator ExceptionUnobserved2() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// bool calledEx = false;
|
||||
// Action<Exception> action = exx =>
|
||||
// {
|
||||
// calledEx = true;
|
||||
// exx.Message.Should().Be("MyException");
|
||||
// };
|
||||
|
||||
UniTaskScheduler.UnobservedTaskException += action;
|
||||
// UniTaskScheduler.UnobservedTaskException += action;
|
||||
|
||||
var ex = InException2();
|
||||
ex = default(UniTask<int>);
|
||||
// var ex = InException2();
|
||||
// ex = default(UniTask<int>);
|
||||
|
||||
await UniTask.DelayFrame(3);
|
||||
// await UniTask.DelayFrame(3);
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
// GC.Collect();
|
||||
// GC.WaitForPendingFinalizers();
|
||||
// GC.Collect();
|
||||
|
||||
await UniTask.DelayFrame(1);
|
||||
// await UniTask.DelayFrame(1);
|
||||
|
||||
calledEx.Should().BeTrue();
|
||||
// calledEx.Should().BeTrue();
|
||||
|
||||
UniTaskScheduler.UnobservedTaskException -= action;
|
||||
});
|
||||
// UniTaskScheduler.UnobservedTaskException -= action;
|
||||
// });
|
||||
|
||||
async UniTask InException1()
|
||||
{
|
||||
await UniTask.Yield();
|
||||
throw new Exception("MyException");
|
||||
}
|
||||
// async UniTask InException1()
|
||||
// {
|
||||
// await UniTask.Yield();
|
||||
// throw new Exception("MyException");
|
||||
// }
|
||||
|
||||
async UniTask<int> InException2()
|
||||
{
|
||||
await UniTask.Yield();
|
||||
throw new Exception("MyException");
|
||||
}
|
||||
// async UniTask<int> InException2()
|
||||
// {
|
||||
// await UniTask.Yield();
|
||||
// throw new Exception("MyException");
|
||||
// }
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator NestedEnumerator() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
var time = Time.realtimeSinceStartup;
|
||||
// [UnityTest]
|
||||
// public IEnumerator NestedEnumerator() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// var time = Time.realtimeSinceStartup;
|
||||
|
||||
await ParentCoroutineEnumerator();
|
||||
// await ParentCoroutineEnumerator();
|
||||
|
||||
var elapsed = Time.realtimeSinceStartup - time;
|
||||
((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(3);
|
||||
});
|
||||
// var elapsed = Time.realtimeSinceStartup - time;
|
||||
// ((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(3);
|
||||
// });
|
||||
|
||||
IEnumerator ParentCoroutineEnumerator()
|
||||
{
|
||||
yield return ChildCoroutineEnumerator();
|
||||
}
|
||||
// IEnumerator ParentCoroutineEnumerator()
|
||||
// {
|
||||
// yield return ChildCoroutineEnumerator();
|
||||
// }
|
||||
|
||||
IEnumerator ChildCoroutineEnumerator()
|
||||
{
|
||||
yield return new WaitForSeconds(3);
|
||||
}
|
||||
// IEnumerator ChildCoroutineEnumerator()
|
||||
// {
|
||||
// yield return new WaitForSeconds(3);
|
||||
// }
|
||||
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
//#endif
|
||||
//#endif
|
||||
// }
|
||||
//}
|
||||
|
||||
#endif
|
||||
//#endif
|
||||
@@ -1,95 +1,95 @@
|
||||
#if !(UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2)
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
//#if !(UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2)
|
||||
//#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Scripting;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine.SceneManagement;
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using UnityEngine.Networking;
|
||||
//using UnityEngine;
|
||||
//using System;
|
||||
//using System.Collections;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Linq;
|
||||
//using System.Text;
|
||||
//using UnityEngine.UI;
|
||||
//using UnityEngine.Scripting;
|
||||
//using Cysharp.Threading.Tasks;
|
||||
//using UnityEngine.SceneManagement;
|
||||
//#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
//using System.Threading.Tasks;
|
||||
//#endif
|
||||
//using UnityEngine.Networking;
|
||||
|
||||
#if !UNITY_2019_3_OR_NEWER
|
||||
using UnityEngine.Experimental.LowLevel;
|
||||
#else
|
||||
using UnityEngine.LowLevel;
|
||||
#endif
|
||||
//#if !UNITY_2019_3_OR_NEWER
|
||||
//using UnityEngine.Experimental.LowLevel;
|
||||
//#else
|
||||
//using UnityEngine.LowLevel;
|
||||
//#endif
|
||||
|
||||
#if !UNITY_WSA
|
||||
using Unity.Jobs;
|
||||
#endif
|
||||
using Unity.Collections;
|
||||
using System.Threading;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.TestTools;
|
||||
using FluentAssertions;
|
||||
//#if !UNITY_WSA
|
||||
//using Unity.Jobs;
|
||||
//#endif
|
||||
//using Unity.Collections;
|
||||
//using System.Threading;
|
||||
//using NUnit.Framework;
|
||||
//using UnityEngine.TestTools;
|
||||
//using FluentAssertions;
|
||||
|
||||
namespace Cysharp.Threading.TasksTests
|
||||
{
|
||||
public class RunTest
|
||||
{
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#if !UNITY_WSA
|
||||
//namespace Cysharp.Threading.TasksTests
|
||||
//{
|
||||
// public class RunTest
|
||||
// {
|
||||
//#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
//#if !UNITY_WSA
|
||||
|
||||
//[UnityTest]
|
||||
//public IEnumerator RunThread() => UniTask.ToCoroutine(async () =>
|
||||
//{
|
||||
// var main = Thread.CurrentThread.ManagedThreadId;
|
||||
// var v = await UniTask.Run(() => { return System.Threading.Thread.CurrentThread.ManagedThreadId; }, false);
|
||||
// UnityEngine.Debug.Log("Ret Value is:" + v);
|
||||
// UnityEngine.Debug.Log("Run Here and id:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
|
||||
// //v.Should().Be(3);
|
||||
// main.Should().NotBe(Thread.CurrentThread.ManagedThreadId);
|
||||
//});
|
||||
// //[UnityTest]
|
||||
// //public IEnumerator RunThread() => UniTask.ToCoroutine(async () =>
|
||||
// //{
|
||||
// // var main = Thread.CurrentThread.ManagedThreadId;
|
||||
// // var v = await UniTask.Run(() => { return System.Threading.Thread.CurrentThread.ManagedThreadId; }, false);
|
||||
// // UnityEngine.Debug.Log("Ret Value is:" + v);
|
||||
// // UnityEngine.Debug.Log("Run Here and id:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
|
||||
// // //v.Should().Be(3);
|
||||
// // main.Should().NotBe(Thread.CurrentThread.ManagedThreadId);
|
||||
// //});
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator RunThreadConfigure() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
var main = Thread.CurrentThread.ManagedThreadId;
|
||||
var v = await UniTask.Run(() => 3, true);
|
||||
v.Should().Be(3);
|
||||
main.Should().Be(Thread.CurrentThread.ManagedThreadId);
|
||||
});
|
||||
// [UnityTest]
|
||||
// public IEnumerator RunThreadConfigure() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// var main = Thread.CurrentThread.ManagedThreadId;
|
||||
// var v = await UniTask.Run(() => 3, true);
|
||||
// v.Should().Be(3);
|
||||
// main.Should().Be(Thread.CurrentThread.ManagedThreadId);
|
||||
// });
|
||||
|
||||
//[UnityTest]
|
||||
//public IEnumerator RunThreadException() => UniTask.ToCoroutine(async () =>
|
||||
//{
|
||||
// var main = Thread.CurrentThread.ManagedThreadId;
|
||||
// try
|
||||
// {
|
||||
// await UniTask.Run<int>(() => throw new Exception(), false);
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// main.Should().NotBe(Thread.CurrentThread.ManagedThreadId);
|
||||
// }
|
||||
//});
|
||||
// //[UnityTest]
|
||||
// //public IEnumerator RunThreadException() => UniTask.ToCoroutine(async () =>
|
||||
// //{
|
||||
// // var main = Thread.CurrentThread.ManagedThreadId;
|
||||
// // try
|
||||
// // {
|
||||
// // await UniTask.Run<int>(() => throw new Exception(), false);
|
||||
// // }
|
||||
// // catch
|
||||
// // {
|
||||
// // main.Should().NotBe(Thread.CurrentThread.ManagedThreadId);
|
||||
// // }
|
||||
// //});
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator RunThreadExceptionConfigure() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
var main = Thread.CurrentThread.ManagedThreadId;
|
||||
try
|
||||
{
|
||||
await UniTask.Run<int>(() => throw new Exception(), true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
main.Should().Be(Thread.CurrentThread.ManagedThreadId);
|
||||
}
|
||||
});
|
||||
// [UnityTest]
|
||||
// public IEnumerator RunThreadExceptionConfigure() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// var main = Thread.CurrentThread.ManagedThreadId;
|
||||
// try
|
||||
// {
|
||||
// await UniTask.Run<int>(() => throw new Exception(), true);
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// main.Should().Be(Thread.CurrentThread.ManagedThreadId);
|
||||
// }
|
||||
// });
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
//#endif
|
||||
//#endif
|
||||
// }
|
||||
//}
|
||||
|
||||
#endif
|
||||
//#endif
|
||||
@@ -1,43 +1,43 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
//#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
//#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Scripting;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Unity.Collections;
|
||||
using System.Threading;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.TestTools;
|
||||
using FluentAssertions;
|
||||
//using UnityEngine;
|
||||
//using System;
|
||||
//using System.Collections;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Linq;
|
||||
//using System.Text;
|
||||
//using UnityEngine.UI;
|
||||
//using UnityEngine.Scripting;
|
||||
//using Cysharp.Threading.Tasks;
|
||||
//using Unity.Collections;
|
||||
//using System.Threading;
|
||||
//using NUnit.Framework;
|
||||
//using UnityEngine.TestTools;
|
||||
//using FluentAssertions;
|
||||
|
||||
namespace Cysharp.Threading.TasksTests
|
||||
{
|
||||
public class WhenAnyTest
|
||||
{
|
||||
[UnityTest]
|
||||
public IEnumerator WhenAnyCanceled() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
var successDelayTask = UniTask.Delay(TimeSpan.FromSeconds(1));
|
||||
var cancelTask = UniTask.Delay(TimeSpan.FromSeconds(1), cancellationToken: cts.Token);
|
||||
cts.CancelAfterSlim(200);
|
||||
//namespace Cysharp.Threading.TasksTests
|
||||
//{
|
||||
// public class WhenAnyTest
|
||||
// {
|
||||
// [UnityTest]
|
||||
// public IEnumerator WhenAnyCanceled() => UniTask.ToCoroutine(async () =>
|
||||
// {
|
||||
// var cts = new CancellationTokenSource();
|
||||
// var successDelayTask = UniTask.Delay(TimeSpan.FromSeconds(1));
|
||||
// var cancelTask = UniTask.Delay(TimeSpan.FromSeconds(1), cancellationToken: cts.Token);
|
||||
// cts.CancelAfterSlim(200);
|
||||
|
||||
try
|
||||
{
|
||||
var r = await UniTask.WhenAny(new[] { successDelayTask, cancelTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ex.Should().BeAssignableTo<OperationCanceledException>();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// try
|
||||
// {
|
||||
// var r = await UniTask.WhenAny(new[] { successDelayTask, cancelTask });
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// ex.Should().BeAssignableTo<OperationCanceledException>();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//}
|
||||
|
||||
#endif
|
||||
//#endif
|
||||
@@ -6,13 +6,20 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Cysharp.Threading.TasksTests
|
||||
{
|
||||
public class Preserve
|
||||
{
|
||||
public Preserve()
|
||||
{
|
||||
// TaskPool.SetMaxPoolSize(0);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AwaitTwice() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
@@ -33,12 +40,17 @@ namespace Cysharp.Threading.TasksTests
|
||||
[UnityTest]
|
||||
public IEnumerator PreserveAllowTwice() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
await UniTask.Yield(PlayerLoopTiming.Update);
|
||||
|
||||
var delay = UniTask.DelayFrame(5, PlayerLoopTiming.PostLateUpdate).Preserve();
|
||||
var before = UnityEngine.Time.frameCount;
|
||||
|
||||
var before = UnityEngine.Time.frameCount; // 0
|
||||
|
||||
await delay;
|
||||
var afterOne = UnityEngine.Time.frameCount;
|
||||
var afterOne = UnityEngine.Time.frameCount; // 5
|
||||
|
||||
await delay;
|
||||
var afterTwo = UnityEngine.Time.frameCount;
|
||||
var afterTwo = UnityEngine.Time.frameCount; // 5
|
||||
|
||||
(afterOne - before).Should().Be(5);
|
||||
afterOne.Should().Be(afterTwo);
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
"UnityEditor.TestRunner",
|
||||
"UniTask",
|
||||
"Unity.ResourceManager",
|
||||
"DOTween.Modules"
|
||||
"DOTween.Modules",
|
||||
"UniTask.Linq"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
||||
@@ -8,7 +8,7 @@ EditorBuildSettings:
|
||||
- enabled: 1
|
||||
path: Assets/Scenes/SandboxMain.unity
|
||||
guid: 2cda990e2423bbf4892e6590ba056729
|
||||
- enabled: 0
|
||||
path:
|
||||
guid: 00000000000000000000000000000000
|
||||
- enabled: 1
|
||||
path: Assets/Scenes/ExceptionExamples.unity
|
||||
guid: b5fed17e3ece238439bc796d8747df5d
|
||||
m_configObjects: {}
|
||||
|
||||
Reference in New Issue
Block a user