Compare commits

...

27 Commits

Author SHA1 Message Date
neuecc
37cd00d347 2.0.15? 2020-06-09 12:16:28 +09:00
neuecc
859c4d706f logging 2020-06-09 12:00:12 +09:00
neuecc
7289fe6e25 ToCoroutine throws exception when error detected 2020-06-09 11:26:03 +09:00
neuecc
0c33977f5a timer and delay skip current frame 2020-06-09 11:25:45 +09:00
neuecc
4d4466e801 Add UniTask.WaitForFixedUpdate 2020-06-08 02:22:10 +09:00
neuecc
79330d7cdb ReadMe 2020-06-08 01:50:20 +09:00
neuecc
680ce1098b ReadMe 2020-06-08 01:45:57 +09:00
neuecc
2337d705ec WithCancellation uses native timing of asyncOperation 2020-06-08 01:45:41 +09:00
neuecc
d2880a818f Add UniTask.NextFrame, UniTask.WaitForEndOfFrame 2020-06-08 01:44:31 +09:00
neuecc
86ea128bf4 t4gen 2020-06-08 00:30:27 +09:00
neuecc
a66f378622 // for CI 2020-06-07 00:27:33 +09:00
neuecc
265f88584b store application.datapath on initialize #86 2020-06-06 14:21:31 +09:00
neuecc
686394c861 2.0.14 pkg version 2020-06-05 17:16:34 +09:00
neuecc
8bad158ab4 fix ci 2020-06-05 17:05:36 +09:00
neuecc
5e59e7ec86 netstandard2.0 support #84 2020-06-05 16:45:40 +09:00
neuecc
8bb0a48720 workaround for struct enumerator await 2020-06-05 15:08:13 +09:00
neuecc
b4468b4eba improve DOTween Extensions 2020-06-05 12:32:16 +09:00
neuecc
0725bd1b30 Merge remote-tracking branch 'origin/master' 2020-06-05 00:44:16 +09:00
neuecc
ebd80243e0 Fix UniTask.ReturnToCurrentSynchronizationContext 2020-06-05 00:43:54 +09:00
neuecc
f1ac469058 Fix UniTask.Defer 2020-06-05 00:43:03 +09:00
neuecc
2e0b603d25 docs: update TOC 2020-06-04 08:54:24 +00:00
neuecc
1d90a40f66 more ReadMe 2020-06-04 17:53:57 +09:00
Mayuki Sawatari
1bec3f507e Merge pull request #75 from Cysharp/feature/docs
Generate and publish documentation to GitHub Pages
2020-06-04 16:16:03 +09:00
Mayuki Sawatari
1d5ecbb3ab Include sub-directory source codes 2020-06-04 15:46:21 +09:00
Mayuki Sawatari
e1a4aeb9da Update toc.yml 2020-06-04 15:46:21 +09:00
Mayuki Sawatari
43f1bb4d85 Generate and publish documentation to GitHub Pages 2020-06-04 15:46:21 +09:00
Yoshifumi Kawai
8b7a0e9b15 Update README.md 2020-06-04 15:31:42 +09:00
44 changed files with 3011 additions and 607 deletions

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

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

View File

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

46
.gitignore vendored
View File

@@ -159,3 +159,49 @@ src/UniTask/UniTask.Tests.Editor.csproj
src/UniTask/UniTask.*.unitypackage src/UniTask/UniTask.*.unitypackage
src/UniTask/UniTask.Linq.csproj 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

View File

@@ -6,7 +6,7 @@ Provides an efficient async/await integration to Unity.
* Struct based `UniTask<T>` and custom AsyncMethodBuilder to achive zero allocation * Struct based `UniTask<T>` and custom AsyncMethodBuilder to achive zero allocation
* All Unity AsyncOperations and Coroutine to awaitable * All Unity AsyncOperations and Coroutine to awaitable
* PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc...) that enable to replace all coroutine operation * PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc..) that enable to replace all coroutine operation
* MonoBehaviour Message Events and uGUI Events as awaitable/async-enumerable * 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. * Completely run on Unity's PlayerLoop so don't use thread and run on WebGL, wasm, etc.
* Asynchronous LINQ, with Channel and AsyncReactiveProperty * Asynchronous LINQ, with Channel and AsyncReactiveProperty
@@ -29,6 +29,7 @@ Provides an efficient async/await integration to Unity.
- [Awaitable Events](#awaitable-events) - [Awaitable Events](#awaitable-events)
- [Channel](#channel) - [Channel](#channel)
- [For Unit Testing](#for-unit-testing) - [For Unit Testing](#for-unit-testing)
- [Pooling Configuration](#pooling-configuration)
- [API References](#api-references) - [API References](#api-references)
- [UPM Package](#upm-package) - [UPM Package](#upm-package)
- [Install via git URL](#install-via-git-url) - [Install via git URL](#install-via-git-url)
@@ -52,21 +53,33 @@ async UniTask<string> DemoAsync()
{ {
// You can await Unity's AsyncObject // You can await Unity's AsyncObject
var asset = await Resources.LoadAsync<TextAsset>("foo"); var asset = await Resources.LoadAsync<TextAsset>("foo");
var txt = (await UnityWebRequest.Get("https://...").SendWebRequest()).downloadHandler.text;
await SceneManager.LoadSceneAsync("scene2");
// .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject // .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
var asset2 = await Resources.LoadAsync<TextAsset>("foo").WithCancellation(this.GetCancellationTokenOnDestroy()); var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy());
// .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T> // .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
await SceneManager.LoadSceneAsync("scene2").ToUniTask(Progress.Create<float>(x => Debug.Log(x))); var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x)));
// await frame-based operation like coroutine // await frame-based operation like coroutine
await UniTask.DelayFrame(100); await UniTask.DelayFrame(100);
// replacement of WaitForSeconds/WaitForSecondsRealtime // replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
await UniTask.Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false); await UniTask.Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false);
// replacement of WaitForEndOfFrame(or other timing like yield return null, yield return WaitForFixedUpdate) // yield any playerloop timing(PreUpdate, Update, LateUpdate, etc...)
await UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate); 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 // replacement of yield return WaitUntil
await UniTask.WaitUntil(() => isActive == false); await UniTask.WaitUntil(() => isActive == false);
@@ -80,9 +93,14 @@ async UniTask<string> DemoAsync()
// You can await standard task // You can await standard task
await Task.Run(() => 100); 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(); await UniTask.SwitchToThreadPool();
/* work on ThreadPool */
// return to MainThread(same as `ObserveOnMainThread` in UniRx)
await UniTask.SwitchToMainThread();
// get async webrequest // get async webrequest
async UniTask<string> GetTextAsync(UnityWebRequest req) async UniTask<string> GetTextAsync(UnityWebRequest req)
{ {
@@ -126,6 +144,8 @@ UniTask provides three pattern of extension methods.
`WithCancellation` is a simple version of `ToUniTask`, both returns `UniTask`. Details of cancellation, see: [Cancellation and Exception handling](#cancellation-and-exception-handling) section. `WithCancellation` is a simple version of `ToUniTask`, both returns `UniTask`. Details of cancellation, see: [Cancellation and Exception handling](#cancellation-and-exception-handling) section.
> Note: WithCancellation is returned from native timing of PlayerLoop but ToUniTask is returned from specified PlayerLoopTiming. Details of timing, see: [PlayerLoop](#playerloop) section.
The type of `UniTask` can use utility like `UniTask.WhenAll`, `UniTask.WhenAny`. It is like Task.WhenAll/WhenAny but return type is more useful, returns value tuple so can deconsrtuct each result and pass multiple type. 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 ```csharp
@@ -320,9 +340,19 @@ public enum PlayerLoopTiming
It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop. It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop.
`PlayerLoopTiming.Update` is similar as `yield return null` in coroutine, but it is called before Update(Update is called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). If change timing to `PlayerLoopTiming.LastUpdate`, called after these Unity's update methods. `PlayerLoopTiming.Update` is similar as `yield return null` in coroutine, but it is called before Update(Update and uGUI events(button.onClick, etc...) are called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). `PlayerLoopTiming.FixedUpdate` is similar as `WaitForFixedUpdate`, `PlayerLoopTiming.LastPostLateUpdate` is similar as `WaitForEndOfFrame` in coroutine.
`PlayerLoopTiming.FixedUpdate` is similar as `WaitForFixedUpdate`, `PlayerLoopTiming.LastPostLateUpdate` is similar as `WaitForEndOfFrame` in coroutine. `yield return null` and `UniTask.Yield` is similar but different. `yield return null` always return next frame but `UniTask.Yield` return next called, that is, call `UniTask.Yield(PlayerLoopTiming.Update)` on `PreUpdate`, it returns same frame. `UniTask.NextFrame()` gurantees return next frame, this would be expected to behave exactly the same as `yield return null`.
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and run on YieldRunner. It is most lightweight and faster.
AsyncOperation is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after called, loaded scene called from `EarlyUpdate.ScriptRunDelayedStartupFrame`. Also `await UnityWebRequest` is returned from `EarlyUpdate.ExecuteMainThreadJobs`.
In UniTask, await directly and `WithCancellation` use native timing, `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, causes different order of Start and continuation after await. so recommend not to use `LoadSceneAsync.ToUniTask`.
In stacktrace, you can check where is running in playerloop.
![image](https://user-images.githubusercontent.com/46207/83735571-83caea80-a68b-11ea-8d22-5e22864f0d24.png)
In default, UniTask's PlayerLoop is initialized at `[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]`. In default, UniTask's PlayerLoop is initialized at `[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]`.
@@ -413,7 +443,7 @@ For debug use, enable tracking and capture stacktrace is useful but it it declin
External Assets External Assets
--- ---
In default, UniTask supports DOTween and Addressable(`AsyncOperationHandle` and `AsyncOpereationHandle<T>` as awaitable). 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. 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.
@@ -649,6 +679,17 @@ public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
UniTask itself's unit test is written by Unity Test Runner and [Cysharp/RuntimeUnitTestToolkit](https://github.com/Cysharp/RuntimeUnitTestToolkit) to check on CI and IL2CPP working. UniTask 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
foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
{
Debug.Log(type + ":" + size);
}
```
API References API References
--- ---
UniTask's API References is hosted at [cysharp.github.io/UniTask](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.html) by [DocFX](https://dotnet.github.io/docfx/) and [Cysharp/DocfXTemplate](https://github.com/Cysharp/DocfxTemplate). UniTask's API References 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).
@@ -706,6 +747,16 @@ public class ZeroAllocAsyncAwaitInDotNetCore
} }
} }
} }
// UniTask does not return to original SynchronizationContext but you can use helper `ReturnToCurrentSynchronizationContext`.
public ValueTask TestAsync()
{
await using (UniTask.ReturnToCurrentSynchronizationContext())
{
await UniTask.SwitchToThreadPool();
// do anything..
}
}
``` ```
.NET Core version is intended to allow users to use UniTask as an interface when sharing code with Unity (such as [Cysharp/MagicOnion](https://github.com/Cysharp/MagicOnion/)). .NET Core version of UniTask enables smooth code sharing. .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.

10
docs/.gitignore vendored Normal file
View File

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

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

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

70
docs/docfx.json Normal file
View File

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

8
docs/index.md Normal file
View File

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

11
docs/toc.yml Normal file
View File

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

View File

@@ -1,6 +1,8 @@
#pragma warning disable 0649 #pragma warning disable 0649
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading.Tasks.Sources;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
{ {
@@ -8,12 +10,20 @@ namespace Cysharp.Threading.Tasks
{ {
public static ValueTask AsValueTask(this in UniTask task) public static ValueTask AsValueTask(this in UniTask task)
{ {
#if NETSTANDARD2_0
return new ValueTask(new UniTaskValueTaskSource(task), 0);
#else
return task; return task;
#endif
} }
public static ValueTask<T> AsValueTask<T>(this in UniTask<T> task) public static ValueTask<T> AsValueTask<T>(this in UniTask<T> task)
{ {
#if NETSTANDARD2_0
return new ValueTask<T>(new UniTaskValueTaskSource<T>(task), 0);
#else
return task; return task;
#endif
} }
public static UniTask<T> AsUniTask<T>(this ValueTask<T> task, bool useCurrentSynchronizationContext = true) 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); 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
} }
} }

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netcoreapp3.1;netstandard2.1</TargetFrameworks> <TargetFrameworks>netcoreapp3.1;netstandard2.1;netstandard2.0</TargetFrameworks>
<AssemblyName>UniTask</AssemblyName> <AssemblyName>UniTask</AssemblyName>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace> <RootNamespace>Cysharp.Threading.Tasks</RootNamespace>

View File

@@ -277,7 +277,11 @@ namespace NetCoreSandbox
#endif #endif
// await new AllocationCheck().ViaUniTaskVoid(); // 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(); await WhereSelect();
SynchronizationContext.SetSynchronizationContext(new MySyncContext()); SynchronizationContext.SetSynchronizationContext(new MySyncContext());

View File

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

View File

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

View File

@@ -2,6 +2,7 @@
using Cysharp.Threading.Tasks.Internal; using Cysharp.Threading.Tasks.Internal;
using System; using System;
using System.Linq;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -29,12 +30,22 @@ namespace Cysharp.Threading.Tasks.CompilerServices
void SetException(Exception exception); void SetException(Exception exception);
} }
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 internal sealed class AsyncUniTaskVoid<TStateMachine> : IStateMachineRunner, ITaskPoolNode<AsyncUniTaskVoid<TStateMachine>>, IUniTaskSource
where TStateMachine : IAsyncStateMachine where TStateMachine : IAsyncStateMachine
{ {
static TaskPool<AsyncUniTaskVoid<TStateMachine>> pool; static TaskPool<AsyncUniTaskVoid<TStateMachine>> pool;
TStateMachine stateMachine; IAsyncStateMachine stateMachine; // unfortunatelly boxed to fix IL2CPP issue.
public Action MoveNext { get; } public Action MoveNext { get; }
@@ -43,7 +54,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
MoveNext = Run; 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)) if (!pool.TryPop(out var result))
{ {
@@ -51,7 +62,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
} }
TaskTracker.TrackActiveTask(result, 3); 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). result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
} }
@@ -102,7 +113,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{ {
static TaskPool<AsyncUniTask<TStateMachine>> pool; static TaskPool<AsyncUniTask<TStateMachine>> pool;
TStateMachine stateMachine; IAsyncStateMachine stateMachine; // unfortunatelly boxed to fix IL2CPP issue.
public Action MoveNext { get; } public Action MoveNext { get; }
@@ -113,7 +124,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
MoveNext = Run; 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)) if (!pool.TryPop(out var result))
{ {
@@ -121,7 +132,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
} }
TaskTracker.TrackActiveTask(result, 3); 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). result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
} }
@@ -213,7 +224,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{ {
static TaskPool<AsyncUniTask<TStateMachine, T>> pool; static TaskPool<AsyncUniTask<TStateMachine, T>> pool;
TStateMachine stateMachine; IAsyncStateMachine stateMachine; // unfortunatelly boxed to fix IL2CPP issue.
public Action MoveNext { get; } public Action MoveNext { get; }
@@ -224,7 +235,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
MoveNext = Run; 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)) if (!pool.TryPop(out var result))
{ {
@@ -232,10 +243,13 @@ namespace Cysharp.Threading.Tasks.CompilerServices
} }
TaskTracker.TrackActiveTask(result, 3); 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). result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
// UnityEngine.Debug.Log($"SetStateMachine State:" + StateMachineUtility.GetState(stateMachine));
} }
public AsyncUniTask<TStateMachine, T> NextNode { get; set; } public AsyncUniTask<TStateMachine, T> NextNode { get; set; }
static AsyncUniTask() static AsyncUniTask()
@@ -255,6 +269,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
void Run() void Run()
{ {
// UnityEngine.Debug.Log($"MoveNext State:" + StateMachineUtility.GetState(stateMachine));
stateMachine.MoveNext(); stateMachine.MoveNext();
} }

View File

@@ -12,10 +12,12 @@ namespace Cysharp.Threading.Tasks
{ {
public static class EnumeratorAsyncExtensions 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)); var e = (IEnumerator)enumerator;
return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, CancellationToken.None, out var token), token).GetAwaiter(); 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) public static UniTask WithCancellation(this IEnumerator enumerator, CancellationToken cancellationToken)

View File

@@ -23,7 +23,7 @@ namespace Cysharp.Threading.Tasks
public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken) public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken)
{ {
if (handle.IsDone) return UniTask.CompletedTask; 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)) 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> sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
{ {
static TaskPool<AsyncOperationHandleConfiguredSource> pool; static TaskPool<AsyncOperationHandleConfiguredSource> pool;
@@ -210,7 +336,7 @@ namespace Cysharp.Threading.Tasks
public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken) public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken)
{ {
if (handle.IsDone) return UniTask.FromResult(handle.Result); 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)) 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>> sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
{ {
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool; static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;

View File

@@ -93,28 +93,30 @@ namespace Cysharp.Threading.Tasks
TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size); TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
} }
static readonly Action<object> CancellationCallbackDelegate = CancellationCallback;
static readonly TweenCallback EmptyTweenCallback = () => { }; static readonly TweenCallback EmptyTweenCallback = () => { };
readonly TweenCallback onKillDelegate; readonly TweenCallback onKillDelegate;
readonly TweenCallback onUpdateDelegate;
Tween tween; Tween tween;
TweenCancelBehaviour cancelBehaviour; TweenCancelBehaviour cancelBehaviour;
CancellationToken cancellationToken; CancellationToken cancellationToken;
bool canceled; bool canceled;
CancellationTokenRegistration cancellationTokenRegistration; TweenCallback originalUpdateAction;
UniTaskCompletionSourceCore<AsyncUnit> core; UniTaskCompletionSourceCore<AsyncUnit> core;
TweenConfiguredSource() TweenConfiguredSource()
{ {
onKillDelegate = OnKill; onKillDelegate = OnKill;
onUpdateDelegate = OnUpdate;
} }
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
DoCancelBeforeCreate(tween, cancelBehaviour);
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
} }
@@ -127,27 +129,25 @@ namespace Cysharp.Threading.Tasks
result.cancelBehaviour = cancelBehaviour; result.cancelBehaviour = cancelBehaviour;
result.cancellationToken = cancellationToken; 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; token = result.core.Version;
return result; return result;
} }
void RegisterEvent()
{
if (cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(CancellationCallbackDelegate, this);
}
tween.OnKill(onKillDelegate);
}
void OnKill() void OnKill()
{ {
cancellationTokenRegistration.Dispose();
if (canceled) if (canceled)
{ {
core.TrySetCanceled(cancellationToken); 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: case TweenCancelBehaviour.Kill:
default: default:
self.tween.Kill(false); this.tween.Kill(false);
break; break;
case TweenCancelBehaviour.KillAndCancelAwait: case TweenCancelBehaviour.KillAndCancelAwait:
self.canceled = true; this.canceled = true;
self.tween.Kill(false); this.tween.Kill(false);
break; break;
case TweenCancelBehaviour.KillWithCompleteCallback: case TweenCancelBehaviour.KillWithCompleteCallback:
self.tween.Kill(true); this.tween.Kill(true);
break; break;
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait: case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
self.canceled = true; this.canceled = true;
self.tween.Kill(true); this.tween.Kill(true);
break; break;
case TweenCancelBehaviour.Complete: case TweenCancelBehaviour.Complete:
self.tween.Complete(false); this.tween.Complete(false);
break; break;
case TweenCancelBehaviour.CompleteAndCancelAwait: case TweenCancelBehaviour.CompleteAndCancelAwait:
self.canceled = true; this.canceled = true;
self.tween.Complete(false); this.tween.Complete(false);
break; break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallback: case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
self.tween.Complete(true); this.tween.Complete(true);
break; break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait: case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
self.canceled = true; this.canceled = true;
self.tween.Complete(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; break;
case TweenCancelBehaviour.CancelAwait: 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; break;
} }
} }
@@ -212,7 +252,6 @@ namespace Cysharp.Threading.Tasks
} }
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
{ {
return core.GetStatus(token); return core.GetStatus(token);
@@ -232,8 +271,11 @@ namespace Cysharp.Threading.Tasks
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
tween.onUpdate = originalUpdateAction;
tween.onKill = null;
tween = default; tween = default;
cancellationToken = default; cancellationToken = default;
originalUpdateAction = default;
return pool.TryPush(this); return pool.TryPush(this);
} }

View File

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

View File

@@ -239,7 +239,7 @@ namespace Cysharp.Threading.Tasks.Internal
} }
else 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; var withAssetsPath = "Assets/" + fname;
return "<a href=\"" + withAssetsPath + "\" line=\"" + line + "\">" + withAssetsPath + ":" + line + "</a>"; return "<a href=\"" + withAssetsPath + "\" line=\"" + line + "\">" + withAssetsPath + ":" + line + "</a>";
} }

View File

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

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Threading; using System.Threading;
using UnityEngine;
namespace Cysharp.Threading.Tasks.Linq 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) 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); return new TimerFrame(dueTimeFrameCount, null, updateTiming);
} }
public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, int periodFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, int periodFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update)
{ {
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); return new TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming);
} }
public static IUniTaskAsyncEnumerable<AsyncUnit> IntervalFrame(int intervalFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) 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); return new TimerFrame(intervalFrameCount, intervalFrameCount, updateTiming);
} }
} }
@@ -64,6 +83,7 @@ namespace Cysharp.Threading.Tasks.Linq
readonly bool ignoreTimeScale; readonly bool ignoreTimeScale;
CancellationToken cancellationToken; CancellationToken cancellationToken;
int initialFrame;
float elapsed; float elapsed;
bool dueTimePhase; bool dueTimePhase;
bool completed; bool completed;
@@ -80,6 +100,7 @@ namespace Cysharp.Threading.Tasks.Linq
if (this.period <= 0) this.period = 1; if (this.period <= 0) this.period = 1;
} }
this.initialFrame = Time.frameCount;
this.dueTimePhase = true; this.dueTimePhase = true;
this.updateTiming = updateTiming; this.updateTiming = updateTiming;
this.ignoreTimeScale = ignoreTimeScale; this.ignoreTimeScale = ignoreTimeScale;
@@ -119,9 +140,19 @@ namespace Cysharp.Threading.Tasks.Linq
return false; return false;
} }
elapsed += (ignoreTimeScale) ? UnityEngine.Time.unscaledDeltaTime : UnityEngine.Time.deltaTime;
if (dueTimePhase) 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) if (elapsed >= dueTime)
{ {
dueTimePhase = false; dueTimePhase = false;
@@ -137,6 +168,8 @@ namespace Cysharp.Threading.Tasks.Linq
return false; return false;
} }
elapsed += (ignoreTimeScale) ? UnityEngine.Time.unscaledDeltaTime : UnityEngine.Time.deltaTime;
if (elapsed >= period) if (elapsed >= period)
{ {
completionSource.TrySetResult(true); completionSource.TrySetResult(true);
@@ -172,6 +205,7 @@ namespace Cysharp.Threading.Tasks.Linq
readonly int? periodFrameCount; readonly int? periodFrameCount;
CancellationToken cancellationToken; CancellationToken cancellationToken;
int initialFrame;
int currentFrame; int currentFrame;
bool dueTimePhase; bool dueTimePhase;
bool completed; bool completed;
@@ -185,6 +219,7 @@ namespace Cysharp.Threading.Tasks.Linq
if (periodFrameCount <= 0) periodFrameCount = 1; if (periodFrameCount <= 0) periodFrameCount = 1;
} }
this.initialFrame = Time.frameCount;
this.dueTimePhase = true; this.dueTimePhase = true;
this.dueTimeFrameCount = dueTimeFrameCount; this.dueTimeFrameCount = dueTimeFrameCount;
this.periodFrameCount = periodFrameCount; this.periodFrameCount = periodFrameCount;
@@ -228,10 +263,29 @@ namespace Cysharp.Threading.Tasks.Linq
if (dueTimePhase) if (dueTimePhase)
{ {
if (currentFrame++ >= dueTimeFrameCount) if (currentFrame == 0)
{
if (dueTimeFrameCount == 0)
{ {
dueTimePhase = false; dueTimePhase = false;
completionSource.TrySetResult(true); 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 else

View File

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

View File

@@ -3,7 +3,6 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using Cysharp.Threading.Tasks.Internal;
using UnityEngine; using UnityEngine;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
@@ -21,6 +20,46 @@ namespace Cysharp.Threading.Tasks
return new UniTask(YieldPromise.Create(timing, cancellationToken, out var token), token); 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)) public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{ {
if (delayFrameCount < 0) if (delayFrameCount < 0)
@@ -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; static TaskPool<NextFramePromise> pool;
public DelayFramePromise NextNode { get; set; } public NextFramePromise NextNode { get; set; }
static DelayFramePromise() static NextFramePromise()
{ {
TaskPool.RegisterSizeGetter(typeof(DelayFramePromise), () => pool.Size); TaskPool.RegisterSizeGetter(typeof(NextFramePromise), () => pool.Size);
} }
int delayFrameCount; int frameCount;
CancellationToken cancellationToken; CancellationToken cancellationToken;
UniTaskCompletionSourceCore<AsyncUnit> core;
int currentFrameCount; NextFramePromise()
UniTaskCompletionSourceCore<object> core;
DelayFramePromise()
{ {
} }
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) if (cancellationToken.IsCancellationRequested)
{ {
@@ -181,10 +218,10 @@ namespace Cysharp.Threading.Tasks
if (!pool.TryPop(out var result)) if (!pool.TryPop(out var result))
{ {
result = new DelayFramePromise(); result = new NextFramePromise();
} }
result.delayFrameCount = delayFrameCount; result.frameCount = Time.frameCount;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -230,13 +267,133 @@ namespace Cysharp.Threading.Tasks
return false; 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; return false;
} }
currentFrameCount++;
return true; return true;
} }
@@ -269,6 +426,7 @@ namespace Cysharp.Threading.Tasks
TaskPool.RegisterSizeGetter(typeof(DelayPromise), () => pool.Size); TaskPool.RegisterSizeGetter(typeof(DelayPromise), () => pool.Size);
} }
int initialFrame;
float delayFrameTimeSpan; float delayFrameTimeSpan;
float elapsed; float elapsed;
CancellationToken cancellationToken; CancellationToken cancellationToken;
@@ -294,6 +452,7 @@ namespace Cysharp.Threading.Tasks
result.elapsed = 0.0f; result.elapsed = 0.0f;
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds; result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.initialFrame = Time.frameCount;
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -338,6 +497,14 @@ namespace Cysharp.Threading.Tasks
return false; return false;
} }
if (elapsed == 0.0f)
{
if (initialFrame == Time.frameCount)
{
return true;
}
}
elapsed += Time.deltaTime; elapsed += Time.deltaTime;
if (elapsed >= delayFrameTimeSpan) if (elapsed >= delayFrameTimeSpan)
{ {
@@ -379,6 +546,7 @@ namespace Cysharp.Threading.Tasks
float delayFrameTimeSpan; float delayFrameTimeSpan;
float elapsed; float elapsed;
int initialFrame;
CancellationToken cancellationToken; CancellationToken cancellationToken;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
@@ -401,6 +569,7 @@ namespace Cysharp.Threading.Tasks
result.elapsed = 0.0f; result.elapsed = 0.0f;
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds; result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
result.initialFrame = Time.frameCount;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -446,6 +615,14 @@ namespace Cysharp.Threading.Tasks
return false; return false;
} }
if (elapsed == 0.0f)
{
if (initialFrame == Time.frameCount)
{
return true;
}
}
elapsed += Time.unscaledDeltaTime; elapsed += Time.unscaledDeltaTime;
if (elapsed >= delayFrameTimeSpan) if (elapsed >= delayFrameTimeSpan)
{ {

View File

@@ -204,7 +204,7 @@ namespace Cysharp.Threading.Tasks
if (f == null) throw new InvalidOperationException("Can't call twice."); if (f == null) throw new InvalidOperationException("Can't call twice.");
task = f(); task = f();
awaiter = f().GetAwaiter(); awaiter = task.GetAwaiter();
return task.Status; return task.Status;
} }
@@ -246,7 +246,7 @@ namespace Cysharp.Threading.Tasks
if (f == null) throw new InvalidOperationException("Can't call twice."); if (f == null) throw new InvalidOperationException("Can't call twice.");
task = f(); task = f();
awaiter = f().GetAwaiter(); awaiter = task.GetAwaiter();
return task.Status; return task.Status;
} }

View File

@@ -351,6 +351,8 @@ namespace Cysharp.Threading.Tasks
{ {
get get
{ {
if (!dontPostWhenSameContext) return false;
var current = SynchronizationContext.Current; var current = SynchronizationContext.Current;
if (current == synchronizationContext) if (current == synchronizationContext)
{ {

View File

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

View File

@@ -621,9 +621,8 @@ namespace Cysharp.Threading.Tasks
if (exception != null) if (exception != null)
{ {
// throw exception on iterator (main)thread. exception.Throw();
// unfortunately unity test-runner can not handle throw exception on hand-write IEnumerator.MoveNext. return false;
UnityEngine.Debug.LogException(exception.SourceException);
} }
return !completed; return !completed;
@@ -692,9 +691,8 @@ namespace Cysharp.Threading.Tasks
if (exception != null) if (exception != null)
{ {
// throw exception on iterator (main)thread. exception.Throw();
// unfortunately unity test-runner can not handle throw exception on hand-write IEnumerator.MoveNext. return false;
UnityEngine.Debug.LogException(exception.SourceException);
} }
return !completed; return !completed;

View File

@@ -25,7 +25,7 @@ namespace Cysharp.Threading.Tasks
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.CompletedTask; 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)) public static UniTask ToUniTask(this AsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -75,7 +75,127 @@ 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; static TaskPool<AsyncOperationConfiguredSource> pool;
public AsyncOperationConfiguredSource NextNode { get; set; } public AsyncOperationConfiguredSource NextNode { get; set; }
@@ -124,7 +244,6 @@ namespace Cysharp.Threading.Tasks
{ {
try try
{ {
core.GetResult(token); core.GetResult(token);
} }
finally finally
@@ -133,6 +252,7 @@ namespace Cysharp.Threading.Tasks
} }
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
{ {
return core.GetStatus(token); return core.GetStatus(token);
@@ -203,7 +323,7 @@ namespace Cysharp.Threading.Tasks
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset); 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)) public static UniTask<UnityEngine.Object> ToUniTask(this ResourceRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -257,7 +377,131 @@ 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; static TaskPool<ResourceRequestConfiguredSource> pool;
public ResourceRequestConfiguredSource NextNode { get; set; } public ResourceRequestConfiguredSource NextNode { get; set; }
@@ -306,7 +550,6 @@ namespace Cysharp.Threading.Tasks
{ {
try try
{ {
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
@@ -390,7 +633,7 @@ namespace Cysharp.Threading.Tasks
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset); 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)) public static UniTask<UnityEngine.Object> ToUniTask(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -444,7 +687,131 @@ 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; static TaskPool<AssetBundleRequestConfiguredSource> pool;
public AssetBundleRequestConfiguredSource NextNode { get; set; } public AssetBundleRequestConfiguredSource NextNode { get; set; }
@@ -545,11 +912,11 @@ namespace Cysharp.Threading.Tasks
bool TryReturn() bool TryReturn()
{ {
TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
asyncOperation = default; asyncOperation = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
TaskTracker.RemoveTracking(this);
return pool.TryPush(this); return pool.TryPush(this);
} }
@@ -576,7 +943,7 @@ namespace Cysharp.Threading.Tasks
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.assetBundle); 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)) public static UniTask<AssetBundle> ToUniTask(this AssetBundleCreateRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -630,7 +997,131 @@ 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; static TaskPool<AssetBundleCreateRequestConfiguredSource> pool;
public AssetBundleCreateRequestConfiguredSource NextNode { get; set; } public AssetBundleCreateRequestConfiguredSource NextNode { get; set; }
@@ -731,11 +1222,11 @@ namespace Cysharp.Threading.Tasks
bool TryReturn() bool TryReturn()
{ {
TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
asyncOperation = default; asyncOperation = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
TaskTracker.RemoveTracking(this);
return pool.TryPush(this); return pool.TryPush(this);
} }
@@ -763,7 +1254,7 @@ namespace Cysharp.Threading.Tasks
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.webRequest); 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)) public static UniTask<UnityWebRequest> ToUniTask(this UnityWebRequestAsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
@@ -817,7 +1308,132 @@ namespace Cysharp.Threading.Tasks
} }
} }
class UnityWebRequestAsyncOperationConfiguredSource : IUniTaskSource<UnityWebRequest>, IPlayerLoopItem, ITaskPoolNode<UnityWebRequestAsyncOperationConfiguredSource> sealed class UnityWebRequestAsyncOperationWithCancellationSource : IUniTaskSource<UnityWebRequest>, IPlayerLoopItem, ITaskPoolNode<UnityWebRequestAsyncOperationWithCancellationSource>
{
static TaskPool<UnityWebRequestAsyncOperationWithCancellationSource> pool;
public UnityWebRequestAsyncOperationWithCancellationSource NextNode { get; set; }
static UnityWebRequestAsyncOperationWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(UnityWebRequestAsyncOperationWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
UnityWebRequestAsyncOperation asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<UnityWebRequest> core;
UnityWebRequestAsyncOperationWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<UnityWebRequest> Create(UnityWebRequestAsyncOperation asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<UnityWebRequest>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new UnityWebRequestAsyncOperationWithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
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; static TaskPool<UnityWebRequestAsyncOperationConfiguredSource> pool;
public UnityWebRequestAsyncOperationConfiguredSource NextNode { get; set; } public UnityWebRequestAsyncOperationConfiguredSource NextNode { get; set; }
@@ -866,7 +1482,6 @@ namespace Cysharp.Threading.Tasks
{ {
try try
{ {
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
@@ -920,11 +1535,11 @@ namespace Cysharp.Threading.Tasks
bool TryReturn() bool TryReturn()
{ {
TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
asyncOperation = default; asyncOperation = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
TaskTracker.RemoveTracking(this);
return pool.TryPush(this); return pool.TryPush(this);
} }

View File

@@ -37,7 +37,7 @@ namespace Cysharp.Threading.Tasks
<# if(t.returnType == "UnityWebRequest") { #> <# if(t.returnType == "UnityWebRequest") { #>
#if ENABLE_UNITYWEBREQUEST #if ENABLE_UNITYWEBREQUEST
<# } #> <# } #>
#region <#= t.typeName #> #region <#= t.typeName #>
public static <#= t.typeName #>Awaiter GetAwaiter(this <#= t.typeName #> asyncOperation) public static <#= t.typeName #>Awaiter GetAwaiter(this <#= t.typeName #> asyncOperation)
{ {
@@ -45,18 +45,18 @@ namespace Cysharp.Threading.Tasks
return new <#= t.typeName #>Awaiter(asyncOperation); 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)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return <#= IsVoid(t) ? "UniTask.CompletedTask" : $"UniTask.FromResult(asyncOperation.{t.returnField})" #>;
return new <#= ToUniTaskReturnType(t.returnType) #>(<#= t.typeName #>ConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token); 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)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
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, cancellation, out var token), token); return new <#= ToUniTaskReturnType(t.returnType) #>(<#= t.typeName #>ConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
} }
public struct <#= t.typeName #>Awaiter : ICriticalNotifyCompletion public struct <#= t.typeName #>Awaiter : ICriticalNotifyCompletion
@@ -106,14 +106,153 @@ namespace Cysharp.Threading.Tasks
public void UnsafeOnCompleted(Action continuation) public void UnsafeOnCompleted(Action continuation)
{ {
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
continuationAction = continuation.AsFuncOfT<AsyncOperation>(); // allocate delegate. continuationAction = PooledDelegate<AsyncOperation>.Create(continuation);
asyncOperation.completed += continuationAction; 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; <#= t.typeName #> asyncOperation;
IProgress<float> progress; IProgress<float> progress;
@@ -133,7 +272,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource<#= IsVoid(t) ? "" : $"<{t.returnType}>" #>.CreateFromCanceled(cancellationToken, out token); 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.asyncOperation = asyncOperation;
result.progress = progress; result.progress = progress;
@@ -151,8 +293,6 @@ namespace Cysharp.Threading.Tasks
{ {
try try
{ {
TaskTracker.RemoveTracking(this);
<# if (!IsVoid(t)) { #> <# if (!IsVoid(t)) { #>
return core.GetResult(token); return core.GetResult(token);
<# } else { #> <# } else { #>
@@ -161,7 +301,7 @@ namespace Cysharp.Threading.Tasks
} }
finally finally
{ {
pool.TryReturn(this); TryReturn();
} }
} }
@@ -191,6 +331,9 @@ namespace Cysharp.Threading.Tasks
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
<# if(t.returnType == "UnityWebRequest") { #>
asyncOperation.webRequest.Abort();
<# } #>
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
return false; return false;
} }
@@ -209,24 +352,26 @@ namespace Cysharp.Threading.Tasks
return true; return true;
} }
public void Reset() bool TryReturn()
{ {
TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
asyncOperation = default; asyncOperation = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
return pool.TryPush(this);
} }
~<#= t.typeName #>ConfiguredSource() ~<#= t.typeName #>ConfiguredSource()
{ {
if (pool.TryReturn(this)) if (TryReturn())
{ {
GC.ReRegisterForFinalize(this); GC.ReRegisterForFinalize(this);
} }
} }
} }
# endregion #endregion
<# if(t.returnType == "UnityWebRequest") { #> <# if(t.returnType == "UnityWebRequest") { #>
#endif #endif
<# } #> <# } #>

View File

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

View File

@@ -21,13 +21,15 @@ public class ExceptionExamples : MonoBehaviour
private void Start() private void Start()
{ {
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; UnityEngine.Debug.Log("ExceptionScene, LoopType:" + PlayerLoopInfo.CurrentLoopType + ":" + Time.frameCount);
ThrowFromAsyncVoid(); //TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
_ = ThrowFromTask();
_ = ThrowFromUniTask();
ThrowFromNonAsync(); //ThrowFromAsyncVoid();
//_ = ThrowFromTask();
//_ = ThrowFromUniTask();
//ThrowFromNonAsync();
} }
private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)

View File

@@ -14,6 +14,8 @@ using UnityEngine;
using UnityEngine.LowLevel; using UnityEngine.LowLevel;
using UnityEngine.Networking; using UnityEngine.Networking;
using UnityEngine.UI; using UnityEngine.UI;
using UnityEngine.SceneManagement;
// using DG.Tweening; // using DG.Tweening;
@@ -264,11 +266,14 @@ public class SandboxMain : MonoBehaviour
//var r = UniAsync("https://bing.com/", cts.Token); //var r = UniAsync("https://bing.com/", cts.Token);
//cts.Cancel(); //cts.Cancel();
//await r; //await r;
_ = await UnityWebRequest.Get("https://bing.com/").SendWebRequest(); Debug.Log("SendWebRequestDone:" + PlayerLoopInfo.CurrentLoopType);
Debug.Log("UNIASYNC1 ");
_ = 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 catch
{ {
@@ -301,10 +306,210 @@ public class SandboxMain : MonoBehaviour
return 10; return 10;
} }
async UniTask<int> Ex()
async UniTaskVoid Start()
{ {
await UniTask.SwitchToMainThread(); await UniTask.Yield();
//throw new Exception();
await UniTask.Delay(TimeSpan.FromSeconds(15));
return 0;
}
IEnumerator CoroutineRun()
{
UnityEngine.Debug.Log("Before Coroutine yield return null," + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
yield return null;
UnityEngine.Debug.Log("After Coroutine yield return null," + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
}
IEnumerator CoroutineRun2()
{
UnityEngine.Debug.Log("Before Coroutine yield return WaitForEndOfFrame," + Time.frameCount);
yield return new WaitForEndOfFrame();
UnityEngine.Debug.Log("After Coroutine yield return WaitForEndOfFrame," + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
yield return new WaitForEndOfFrame();
UnityEngine.Debug.Log("Onemore After Coroutine yield return WaitForEndOfFrame," + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
}
async UniTaskVoid AsyncRun()
{
UnityEngine.Debug.Log("Before async Yield(default)," + Time.frameCount);
await UniTask.Yield();
UnityEngine.Debug.Log("After async Yield(default)," + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
}
async UniTaskVoid AsyncLastUpdate()
{
UnityEngine.Debug.Log("Before async Yield(LastUpdate)," + Time.frameCount);
await UniTask.Yield(PlayerLoopTiming.LastUpdate);
UnityEngine.Debug.Log("After async Yield(LastUpdate)," + Time.frameCount);
}
async UniTaskVoid AsyncLastLast()
{
UnityEngine.Debug.Log("Before async Yield(LastPostLateUpdate)," + Time.frameCount);
await UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
UnityEngine.Debug.Log("After async Yield(LastPostLateUpdate)," + Time.frameCount);
}
async UniTaskVoid Yieldding()
{
await UniTask.Yield(PlayerLoopTiming.PreUpdate);
StartCoroutine(CoroutineRun());
}
async UniTaskVoid AsyncFixedUpdate()
{
while (true)
{
await UniTask.WaitForFixedUpdate();
Debug.Log("Async:" + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
}
}
IEnumerator CoroutineFixedUpdate()
{
while (true)
{
yield return new WaitForFixedUpdate();
Debug.Log("Coroutine:" + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
}
}
private void FixedUpdate()
{
// Debug.Log("FixedUpdate:" + Time.frameCount + ", " + PlayerLoopInfo.CurrentLoopType);
}
async UniTaskVoid DelayFrame3_Pre()
{
await UniTask.Yield(PlayerLoopTiming.PreUpdate);
Debug.Log("Before framecount:" + Time.frameCount);
await UniTask.DelayFrame(3);
Debug.Log("After framecount:" + Time.frameCount);
}
async UniTaskVoid DelayFrame3_Post()
{
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
Debug.Log("Before framecount:" + Time.frameCount);
await UniTask.DelayFrame(3);
Debug.Log("After framecount:" + Time.frameCount);
}
async UniTask TestCoroutine()
{
await UniTask.Yield();
throw new Exception("foobarbaz");
}
async UniTask DelayCheck()
{
await UniTask.Yield(PlayerLoopTiming.PreUpdate);
Debug.Log("before");
var t = UniTask.Delay(TimeSpan.FromSeconds(1), ignoreTimeScale: false);
await t;
Debug.Log("after");
}
private async UniTaskVoid ExecuteAsync()
{
Debug.Log("1");
{
var xs = await UniTaskAsyncEnumerable.TimerFrame(1).ToArrayAsync();
}
Debug.Log("------------------");
{
var xs = await UniTaskAsyncEnumerable.TimerFrame(1).ToArrayAsync();
Debug.Log("2");
}
}
void Start()
{
PlayerLoopInfo.Inject();
//_ = AsyncFixedUpdate();
//StartCoroutine(CoroutineFixedUpdate());
//StartCoroutine(TestCoroutine().ToCoroutine());
// Application.logMessageReceived += Application_logMessageReceived;
okButton.onClick.AddListener(UniTask.UnityAction(async () =>
{
_ = ExecuteAsync();
await UniTask.Yield();
//await DelayCheck();
/*
UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType);
StartCoroutine(CoroutineRun());
StartCoroutine(CoroutineRun2());
_ = AsyncRun();
_ = AsyncLastUpdate();
_ = AsyncLastLast();
*/
//await UniTask.Yield();
//_ = Test2();
// EarlyUpdate.ExecuteMainThreadJobs
// _ = Test2();
//var t = await Resources.LoadAsync<TextAsset>(Application.streamingAssetsPath + "test.txt");
//Debug.Log("LoadEnd" + PlayerLoopInfo.CurrentLoopType + ", " + (t != null));
//Debug.Log("LoadEnd" + PlayerLoopInfo.CurrentLoopType + ", " + ((TextAsset)t).text);
//await UniTask.Yield(PlayerLoopTiming.LastUpdate);
//UnityEngine.Debug.Log("after update:" + Time.frameCount);
////await UniTask.NextFrame();
////await UniTask.Yield();
////UnityEngine.Debug.Log("after update nextframe:" + Time.frameCount);
//StartCoroutine(CoroutineRun2());
////StartCoroutine(CoroutineRun());
//UnityEngine.Debug.Log("FOO?");
//_ = DelayFrame3_Pre();
//await UniTask.Yield();
}));
cancelButton.onClick.AddListener(UniTask.UnityAction(async () =>
{
_ = DelayFrame3_Post();
await UniTask.Yield();
//await UniTask.Yield(PlayerLoopTiming.LastPreUpdate);
//UnityEngine.Debug.Log("before update:" + Time.frameCount);
//await UniTask.NextFrame();
//await UniTask.Yield();
//UnityEngine.Debug.Log("before update nextframe:" + Time.frameCount);
//StartCoroutine(CoroutineRun());
//UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType);
//_ = Yieldding();
//var cts = new CancellationTokenSource();
//UnityEngine.Debug.Log("click:" + PlayerLoopInfo.CurrentLoopType + ":" + Time.frameCount);
//var la = SceneManager.LoadSceneAsync("Scenes/ExceptionExamples").WithCancellation(cts.Token);
////cts.Cancel();
//await la;
//UnityEngine.Debug.Log("End LoadSceneAsync" + PlayerLoopInfo.CurrentLoopType + ":" + Time.frameCount);
}));
//return;
//await UniTask.SwitchToMainThread();
//UniTaskAsyncEnumerable.EveryValueChanged(mcc, x => x.MyProperty) //UniTaskAsyncEnumerable.EveryValueChanged(mcc, x => x.MyProperty)
// .Do(_ => { }, () => Debug.Log("COMPLETED")) // .Do(_ => { }, () => Debug.Log("COMPLETED"))
@@ -315,10 +520,29 @@ public class SandboxMain : MonoBehaviour
// .Forget(); // .Forget();
//_ = Test1(); //_ = Test1();
Test2().Forget(); //Test2().Forget();
//StartCoroutine(Test3("https://bing.com/")); //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> // DG.Tweening.Core.TweenerCore<int>
//Debug.Log("GO MOVEX"); //Debug.Log("GO MOVEX");
//await okButton.GetComponent<RectTransform>().DOMoveX(-10.2f, 3).WithCancellation(CancellationToken.None); //await okButton.GetComponent<RectTransform>().DOMoveX(-10.2f, 3).WithCancellation(CancellationToken.None);
@@ -329,7 +553,7 @@ public class SandboxMain : MonoBehaviour
//await okButton.GetComponent<RectTransform>().DOMoveY(10.2f, 3).WithCancellation(CancellationToken.None); //await okButton.GetComponent<RectTransform>().DOMoveY(10.2f, 3).WithCancellation(CancellationToken.None);
//Debug.Log("AGAIN END MOVE"); //Debug.Log("AGAIN END MOVE");
Debug.Log(Test().GetType().FullName); //Debug.Log(Test().GetType().FullName);
@@ -383,11 +607,10 @@ public class SandboxMain : MonoBehaviour
//{ //{
okButton.onClick.AddListener(UniTask.UnityAction(async () => //foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
{ //{
await UniTask.Yield(); // Debug.Log(type + ":" + size);
Debug.Log("Yeha"); //}
}));
//}).Forget(); //}).Forget();
@@ -396,7 +619,7 @@ public class SandboxMain : MonoBehaviour
//okButton.onClick.AddListener(UniTask.UnityAction(async () => await UniTask.Yield())); //okButton.onClick.AddListener(UniTask.UnityAction(async () => await UniTask.Yield()));
PlayerLoopInfo.Inject();
//UpdateUniTask().Forget(); //UpdateUniTask().Forget();
@@ -405,15 +628,21 @@ public class SandboxMain : MonoBehaviour
//await UniTask.Delay(TimeSpan.FromSeconds(1)); //await UniTask.Delay(TimeSpan.FromSeconds(1));
_ = ReturnToMainThreadTest(); // _ = ReturnToMainThreadTest();
//GameObject.Destroy(this.gameObject); //GameObject.Destroy(this.gameObject);
SynchronizationContext.Current.Post(_ => }
private void Application_logMessageReceived2(string condition, string stackTrace, LogType type)
{ {
//UnityEngine.Debug.Log("Post:" + PlayerLoopInfo.CurrentLoopType); throw new NotImplementedException();
}, null); }
private void Application_logMessageReceived1(string condition, string stackTrace, LogType type)
{
throw new NotImplementedException();
} }
async UniTaskVoid UpdateUniTask() async UniTaskVoid UpdateUniTask()

View File

@@ -126,15 +126,15 @@ namespace Cysharp.Threading.TasksTests
await ToaruCoroutineEnumerator(); // wait 5 frame:) await ToaruCoroutineEnumerator(); // wait 5 frame:)
}); });
[UnityTest] //[UnityTest]
public IEnumerator JobSystem() => UniTask.ToCoroutine(async () => //public IEnumerator JobSystem() => UniTask.ToCoroutine(async () =>
{ //{
var job = new MyJob() { loopCount = 999, inOut = new NativeArray<int>(1, Allocator.TempJob) }; // var job = new MyJob() { loopCount = 999, inOut = new NativeArray<int>(1, Allocator.TempJob) };
JobHandle.ScheduleBatchedJobs(); // JobHandle.ScheduleBatchedJobs();
await job.Schedule(); // await job.Schedule();
job.inOut[0].Should().Be(999); // job.inOut[0].Should().Be(999);
job.inOut.Dispose(); // job.inOut.Dispose();
}); //});
class MyMyClass class MyMyClass
{ {
@@ -269,7 +269,8 @@ namespace Cysharp.Threading.TasksTests
var first = Time.frameCount; var first = Time.frameCount;
var canceled = await UniTask.DelayFrame(100, cancellationToken: cts.Token).SuppressCancellationThrow(); 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); canceled.Should().Be(true);
}); });
@@ -369,6 +370,24 @@ namespace Cysharp.Threading.TasksTests
throw new Exception("MyException"); 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] [UnityTest]
public IEnumerator NestedEnumerator() => UniTask.ToCoroutine(async () => public IEnumerator NestedEnumerator() => UniTask.ToCoroutine(async () =>
{ {

View 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;
}
}
}

View File

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

View 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;
}
}
}
}

View File

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

View File

@@ -1,400 +1,400 @@
#if !(UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2) //#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 //#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using UnityEngine; //using UnityEngine;
using System; //using System;
using System.Collections; //using System.Collections;
using System.Collections.Generic; //using System.Collections.Generic;
using System.Linq; //using System.Linq;
using System.Text; //using System.Text;
using UnityEngine.UI; //using UnityEngine.UI;
using UnityEngine.Scripting; //using UnityEngine.Scripting;
using Cysharp.Threading.Tasks; //using Cysharp.Threading.Tasks;
using UnityEngine.SceneManagement; //using UnityEngine.SceneManagement;
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6)) //#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
using System.Threading.Tasks; //using System.Threading.Tasks;
#endif //#endif
using UnityEngine.Networking; //using UnityEngine.Networking;
#if !UNITY_2019_3_OR_NEWER //#if !UNITY_2019_3_OR_NEWER
using UnityEngine.Experimental.LowLevel; //using UnityEngine.Experimental.LowLevel;
#else //#else
using UnityEngine.LowLevel; //using UnityEngine.LowLevel;
#endif //#endif
#if !UNITY_WSA //#if !UNITY_WSA
using Unity.Jobs; //using Unity.Jobs;
#endif //#endif
using Unity.Collections; //using Unity.Collections;
using System.Threading; //using System.Threading;
using NUnit.Framework; //using NUnit.Framework;
using UnityEngine.TestTools; //using UnityEngine.TestTools;
using FluentAssertions; //using FluentAssertions;
namespace Cysharp.Threading.TasksTests //namespace Cysharp.Threading.TasksTests
{ //{
public class AsyncTest // public class AsyncTest
{ // {
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6)) //#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
#if !UNITY_WSA //#if !UNITY_WSA
public struct MyJob : IJob // public struct MyJob : IJob
{ // {
public int loopCount; // public int loopCount;
public NativeArray<int> inOut; // public NativeArray<int> inOut;
public int result; // public int result;
public void Execute() // public void Execute()
{ // {
result = 0; // result = 0;
for (int i = 0; i < loopCount; i++) // for (int i = 0; i < loopCount; i++)
{ // {
result++; // result++;
} // }
inOut[0] = result; // inOut[0] = result;
} // }
} // }
[UnityTest] // [UnityTest]
public IEnumerator DelayAnd() => UniTask.ToCoroutine(async () => // public IEnumerator DelayAnd() => UniTask.ToCoroutine(async () =>
{ // {
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate); // await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
var time = Time.realtimeSinceStartup; // var time = Time.realtimeSinceStartup;
Time.timeScale = 0.5f; // Time.timeScale = 0.5f;
try // try
{ // {
await UniTask.Delay(TimeSpan.FromSeconds(3)); // await UniTask.Delay(TimeSpan.FromSeconds(3));
var elapsed = Time.realtimeSinceStartup - time; // var elapsed = Time.realtimeSinceStartup - time;
((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(6); // ((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(6);
} // }
finally // finally
{ // {
Time.timeScale = 1.0f; // Time.timeScale = 1.0f;
} // }
}); // });
[UnityTest] // [UnityTest]
public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () => // public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
{ // {
var time = Time.realtimeSinceStartup; // var time = Time.realtimeSinceStartup;
Time.timeScale = 0.5f; // Time.timeScale = 0.5f;
try // try
{ // {
await UniTask.Delay(TimeSpan.FromSeconds(3), ignoreTimeScale: true); // await UniTask.Delay(TimeSpan.FromSeconds(3), ignoreTimeScale: true);
var elapsed = Time.realtimeSinceStartup - time; // var elapsed = Time.realtimeSinceStartup - time;
((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(3); // ((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(3);
} // }
finally // finally
{ // {
Time.timeScale = 1.0f; // Time.timeScale = 1.0f;
} // }
}); // });
[UnityTest] // [UnityTest]
public IEnumerator WhenAll() => UniTask.ToCoroutine(async () => // public IEnumerator WhenAll() => UniTask.ToCoroutine(async () =>
{ // {
var a = UniTask.FromResult(999); // var a = UniTask.FromResult(999);
var b = UniTask.Yield(PlayerLoopTiming.Update, CancellationToken.None).AsAsyncUnitUniTask(); // var b = UniTask.Yield(PlayerLoopTiming.Update, CancellationToken.None).AsAsyncUnitUniTask();
var c = UniTask.DelayFrame(99).AsAsyncUnitUniTask(); // var c = UniTask.DelayFrame(99).AsAsyncUnitUniTask();
var (a2, b2, c2) = await UniTask.WhenAll(a, b, c); // var (a2, b2, c2) = await UniTask.WhenAll(a, b, c);
a2.Should().Be(999); // a2.Should().Be(999);
b2.Should().Be(AsyncUnit.Default); // b2.Should().Be(AsyncUnit.Default);
c2.Should().Be(AsyncUnit.Default); // c2.Should().Be(AsyncUnit.Default);
}); // });
[UnityTest] // [UnityTest]
public IEnumerator WhenAny() => UniTask.ToCoroutine(async () => // public IEnumerator WhenAny() => UniTask.ToCoroutine(async () =>
{ // {
var a = UniTask.FromResult(999); // var a = UniTask.FromResult(999);
var b = UniTask.Yield(PlayerLoopTiming.Update, CancellationToken.None).AsAsyncUnitUniTask(); // var b = UniTask.Yield(PlayerLoopTiming.Update, CancellationToken.None).AsAsyncUnitUniTask();
var c = UniTask.DelayFrame(99).AsAsyncUnitUniTask(); // var c = UniTask.DelayFrame(99).AsAsyncUnitUniTask();
var (win, a2, b2, c2) = await UniTask.WhenAny(a, b, c); // var (win, a2, b2, c2) = await UniTask.WhenAny(a, b, c);
win.Should().Be(0); // win.Should().Be(0);
a2.Should().Be(999); // a2.Should().Be(999);
}); // });
[UnityTest] // [UnityTest]
public IEnumerator BothEnumeratorCheck() => UniTask.ToCoroutine(async () => // public IEnumerator BothEnumeratorCheck() => UniTask.ToCoroutine(async () =>
{ // {
await ToaruCoroutineEnumerator(); // wait 5 frame:) // await ToaruCoroutineEnumerator(); // wait 5 frame:)
}); // });
[UnityTest] // [UnityTest]
public IEnumerator JobSystem() => UniTask.ToCoroutine(async () => // public IEnumerator JobSystem() => UniTask.ToCoroutine(async () =>
{ // {
var job = new MyJob() { loopCount = 999, inOut = new NativeArray<int>(1, Allocator.TempJob) }; // var job = new MyJob() { loopCount = 999, inOut = new NativeArray<int>(1, Allocator.TempJob) };
JobHandle.ScheduleBatchedJobs(); // JobHandle.ScheduleBatchedJobs();
await job.Schedule(); // await job.Schedule();
job.inOut[0].Should().Be(999); // job.inOut[0].Should().Be(999);
job.inOut.Dispose(); // job.inOut.Dispose();
}); // });
class MyMyClass // class MyMyClass
{ // {
public int MyProperty { get; set; } // public int MyProperty { get; set; }
} // }
[UnityTest] // [UnityTest]
public IEnumerator WaitUntil() => UniTask.ToCoroutine(async () => // public IEnumerator WaitUntil() => UniTask.ToCoroutine(async () =>
{ // {
bool t = false; // 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; // var startFrame = Time.frameCount;
await UniTask.WaitUntil(() => t, PlayerLoopTiming.EarlyUpdate); // await UniTask.WaitUntil(() => t, PlayerLoopTiming.EarlyUpdate);
var diff = Time.frameCount - startFrame; // var diff = Time.frameCount - startFrame;
diff.Should().Be(11); // diff.Should().Be(11);
}); // });
[UnityTest] // [UnityTest]
public IEnumerator WaitWhile() => UniTask.ToCoroutine(async () => // public IEnumerator WaitWhile() => UniTask.ToCoroutine(async () =>
{ // {
bool t = true; // 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; // var startFrame = Time.frameCount;
await UniTask.WaitWhile(() => t, PlayerLoopTiming.EarlyUpdate); // await UniTask.WaitWhile(() => t, PlayerLoopTiming.EarlyUpdate);
var diff = Time.frameCount - startFrame; // var diff = Time.frameCount - startFrame;
diff.Should().Be(11); // diff.Should().Be(11);
}); // });
[UnityTest] // [UnityTest]
public IEnumerator WaitUntilValueChanged() => UniTask.ToCoroutine(async () => // public IEnumerator WaitUntilValueChanged() => UniTask.ToCoroutine(async () =>
{ // {
var v = new MyMyClass { MyProperty = 99 }; // 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; // var startFrame = Time.frameCount;
await UniTask.WaitUntilValueChanged(v, x => x.MyProperty, PlayerLoopTiming.EarlyUpdate); // await UniTask.WaitUntilValueChanged(v, x => x.MyProperty, PlayerLoopTiming.EarlyUpdate);
var diff = Time.frameCount - startFrame; // var diff = Time.frameCount - startFrame;
diff.Should().Be(11); // diff.Should().Be(11);
}); // });
[UnityTest] // [UnityTest]
public IEnumerator SwitchTo() => UniTask.ToCoroutine(async () => // public IEnumerator SwitchTo() => UniTask.ToCoroutine(async () =>
{ // {
await UniTask.Yield(); // 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] // //[UnityTest]
//public IEnumerator ObservableConversion() => UniTask.ToCoroutine(async () => // //public IEnumerator ObservableConversion() => UniTask.ToCoroutine(async () =>
//{ // //{
// var v = await Observable.Range(1, 10).ToUniTask(); // // var v = await Observable.Range(1, 10).ToUniTask();
// v.Is(10); // // v.Is(10);
// v = await Observable.Range(1, 10).ToUniTask(useFirstValue: true); // // v = await Observable.Range(1, 10).ToUniTask(useFirstValue: true);
// v.Is(1); // // v.Is(1);
// v = await UniTask.DelayFrame(10).ToObservable().ToTask(); // // v = await UniTask.DelayFrame(10).ToObservable().ToTask();
// v.Is(10); // // v.Is(10);
// v = await UniTask.FromResult(99).ToObservable(); // // v = await UniTask.FromResult(99).ToObservable();
// v.Is(99); // // v.Is(99);
//}); // //});
//[UnityTest] // //[UnityTest]
//public IEnumerator AwaitableReactiveProperty() => UniTask.ToCoroutine(async () => // //public IEnumerator AwaitableReactiveProperty() => UniTask.ToCoroutine(async () =>
//{ // //{
// var rp1 = new ReactiveProperty<int>(99); // // 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 delay2 = UniTask.DelayFrame(10);
// // var (a, b ) = await UniTask.WhenAll(rp1.WaitUntilValueChangedAsync(), delay2); // // // var (a, b ) = await UniTask.WhenAll(rp1.WaitUntilValueChangedAsync(), delay2);
//}); // //});
//[UnityTest] // //[UnityTest]
//public IEnumerator AwaitableReactiveCommand() => UniTask.ToCoroutine(async () => // //public IEnumerator AwaitableReactiveCommand() => UniTask.ToCoroutine(async () =>
//{ // //{
// var rc = new ReactiveCommand<int>(); // // 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] // [UnityTest]
public IEnumerator ExceptionlessCancellation() => UniTask.ToCoroutine(async () => // public IEnumerator ExceptionlessCancellation() => UniTask.ToCoroutine(async () =>
{ // {
var cts = new CancellationTokenSource(); // var cts = new CancellationTokenSource();
UniTask.DelayFrame(10).ContinueWith(() => cts.Cancel()).Forget(); // UniTask.DelayFrame(10).ContinueWith(() => cts.Cancel()).Forget();
var first = Time.frameCount; // var first = Time.frameCount;
var canceled = await UniTask.DelayFrame(100, cancellationToken: cts.Token).SuppressCancellationThrow(); // var canceled = await UniTask.DelayFrame(100, cancellationToken: cts.Token).SuppressCancellationThrow();
(Time.frameCount - first).Should().Be(11); // 10 frame canceled // (Time.frameCount - first).Should().Be(11); // 10 frame canceled
canceled.Should().Be(true); // canceled.Should().Be(true);
}); // });
[UnityTest] // [UnityTest]
public IEnumerator ExceptionCancellation() => UniTask.ToCoroutine(async () => // public IEnumerator ExceptionCancellation() => UniTask.ToCoroutine(async () =>
{ // {
var cts = new CancellationTokenSource(); // var cts = new CancellationTokenSource();
UniTask.DelayFrame(10).ContinueWith(() => cts.Cancel()).Forget(); // UniTask.DelayFrame(10).ContinueWith(() => cts.Cancel()).Forget();
bool occur = false; // bool occur = false;
try // try
{ // {
await UniTask.DelayFrame(100, cancellationToken: cts.Token); // await UniTask.DelayFrame(100, cancellationToken: cts.Token);
} // }
catch (OperationCanceledException) // catch (OperationCanceledException)
{ // {
occur = true; // occur = true;
} // }
occur.Should().BeTrue(); // occur.Should().BeTrue();
}); // });
IEnumerator ToaruCoroutineEnumerator() // IEnumerator ToaruCoroutineEnumerator()
{ // {
yield return null; // yield return null;
yield return null; // yield return null;
yield return null; // yield return null;
yield return null; // yield return null;
yield return null; // yield return null;
} // }
[UnityTest] // [UnityTest]
public IEnumerator ExceptionUnobserved1() => UniTask.ToCoroutine(async () => // public IEnumerator ExceptionUnobserved1() => UniTask.ToCoroutine(async () =>
{ // {
bool calledEx = false; // bool calledEx = false;
Action<Exception> action = exx => // Action<Exception> action = exx =>
{ // {
calledEx = true; // calledEx = true;
exx.Message.Should().Be("MyException"); // exx.Message.Should().Be("MyException");
}; // };
UniTaskScheduler.UnobservedTaskException += action; // UniTaskScheduler.UnobservedTaskException += action;
var ex = InException1(); // var ex = InException1();
ex = default(UniTask); // ex = default(UniTask);
await UniTask.DelayFrame(3); // await UniTask.DelayFrame(3);
GC.Collect(); // GC.Collect();
GC.WaitForPendingFinalizers(); // GC.WaitForPendingFinalizers();
GC.Collect(); // GC.Collect();
await UniTask.DelayFrame(1); // await UniTask.DelayFrame(1);
calledEx.Should().BeTrue(); // calledEx.Should().BeTrue();
UniTaskScheduler.UnobservedTaskException -= action; // UniTaskScheduler.UnobservedTaskException -= action;
}); // });
[UnityTest] // [UnityTest]
public IEnumerator ExceptionUnobserved2() => UniTask.ToCoroutine(async () => // public IEnumerator ExceptionUnobserved2() => UniTask.ToCoroutine(async () =>
{ // {
bool calledEx = false; // bool calledEx = false;
Action<Exception> action = exx => // Action<Exception> action = exx =>
{ // {
calledEx = true; // calledEx = true;
exx.Message.Should().Be("MyException"); // exx.Message.Should().Be("MyException");
}; // };
UniTaskScheduler.UnobservedTaskException += action; // UniTaskScheduler.UnobservedTaskException += action;
var ex = InException2(); // var ex = InException2();
ex = default(UniTask<int>); // ex = default(UniTask<int>);
await UniTask.DelayFrame(3); // await UniTask.DelayFrame(3);
GC.Collect(); // GC.Collect();
GC.WaitForPendingFinalizers(); // GC.WaitForPendingFinalizers();
GC.Collect(); // 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() // async UniTask InException1()
{ // {
await UniTask.Yield(); // await UniTask.Yield();
throw new Exception("MyException"); // throw new Exception("MyException");
} // }
async UniTask<int> InException2() // async UniTask<int> InException2()
{ // {
await UniTask.Yield(); // await UniTask.Yield();
throw new Exception("MyException"); // throw new Exception("MyException");
} // }
[UnityTest] // [UnityTest]
public IEnumerator NestedEnumerator() => UniTask.ToCoroutine(async () => // public IEnumerator NestedEnumerator() => UniTask.ToCoroutine(async () =>
{ // {
var time = Time.realtimeSinceStartup; // var time = Time.realtimeSinceStartup;
await ParentCoroutineEnumerator(); // await ParentCoroutineEnumerator();
var elapsed = Time.realtimeSinceStartup - time; // var elapsed = Time.realtimeSinceStartup - time;
((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(3); // ((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(3);
}); // });
IEnumerator ParentCoroutineEnumerator() // IEnumerator ParentCoroutineEnumerator()
{ // {
yield return ChildCoroutineEnumerator(); // yield return ChildCoroutineEnumerator();
} // }
IEnumerator ChildCoroutineEnumerator() // IEnumerator ChildCoroutineEnumerator()
{ // {
yield return new WaitForSeconds(3); // yield return new WaitForSeconds(3);
} // }
#endif //#endif
#endif //#endif
} // }
} //}
#endif //#endif

View File

@@ -1,95 +1,95 @@
#if !(UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2) //#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 //#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using UnityEngine; //using UnityEngine;
using System; //using System;
using System.Collections; //using System.Collections;
using System.Collections.Generic; //using System.Collections.Generic;
using System.Linq; //using System.Linq;
using System.Text; //using System.Text;
using UnityEngine.UI; //using UnityEngine.UI;
using UnityEngine.Scripting; //using UnityEngine.Scripting;
using Cysharp.Threading.Tasks; //using Cysharp.Threading.Tasks;
using UnityEngine.SceneManagement; //using UnityEngine.SceneManagement;
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6)) //#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
using System.Threading.Tasks; //using System.Threading.Tasks;
#endif //#endif
using UnityEngine.Networking; //using UnityEngine.Networking;
#if !UNITY_2019_3_OR_NEWER //#if !UNITY_2019_3_OR_NEWER
using UnityEngine.Experimental.LowLevel; //using UnityEngine.Experimental.LowLevel;
#else //#else
using UnityEngine.LowLevel; //using UnityEngine.LowLevel;
#endif //#endif
#if !UNITY_WSA //#if !UNITY_WSA
using Unity.Jobs; //using Unity.Jobs;
#endif //#endif
using Unity.Collections; //using Unity.Collections;
using System.Threading; //using System.Threading;
using NUnit.Framework; //using NUnit.Framework;
using UnityEngine.TestTools; //using UnityEngine.TestTools;
using FluentAssertions; //using FluentAssertions;
namespace Cysharp.Threading.TasksTests //namespace Cysharp.Threading.TasksTests
{ //{
public class RunTest // public class RunTest
{ // {
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6)) //#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
#if !UNITY_WSA //#if !UNITY_WSA
//[UnityTest] // //[UnityTest]
//public IEnumerator RunThread() => UniTask.ToCoroutine(async () => // //public IEnumerator RunThread() => UniTask.ToCoroutine(async () =>
//{ // //{
// var main = Thread.CurrentThread.ManagedThreadId; // // var main = Thread.CurrentThread.ManagedThreadId;
// var v = await UniTask.Run(() => { return System.Threading.Thread.CurrentThread.ManagedThreadId; }, false); // // var v = await UniTask.Run(() => { return System.Threading.Thread.CurrentThread.ManagedThreadId; }, false);
// UnityEngine.Debug.Log("Ret Value is:" + v); // // UnityEngine.Debug.Log("Ret Value is:" + v);
// UnityEngine.Debug.Log("Run Here and id:" + System.Threading.Thread.CurrentThread.ManagedThreadId); // // UnityEngine.Debug.Log("Run Here and id:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
// //v.Should().Be(3); // // //v.Should().Be(3);
// main.Should().NotBe(Thread.CurrentThread.ManagedThreadId); // // main.Should().NotBe(Thread.CurrentThread.ManagedThreadId);
//}); // //});
[UnityTest] // [UnityTest]
public IEnumerator RunThreadConfigure() => UniTask.ToCoroutine(async () => // public IEnumerator RunThreadConfigure() => UniTask.ToCoroutine(async () =>
{ // {
var main = Thread.CurrentThread.ManagedThreadId; // var main = Thread.CurrentThread.ManagedThreadId;
var v = await UniTask.Run(() => 3, true); // var v = await UniTask.Run(() => 3, true);
v.Should().Be(3); // v.Should().Be(3);
main.Should().Be(Thread.CurrentThread.ManagedThreadId); // main.Should().Be(Thread.CurrentThread.ManagedThreadId);
}); // });
//[UnityTest] // //[UnityTest]
//public IEnumerator RunThreadException() => UniTask.ToCoroutine(async () => // //public IEnumerator RunThreadException() => UniTask.ToCoroutine(async () =>
//{ // //{
// var main = Thread.CurrentThread.ManagedThreadId; // // var main = Thread.CurrentThread.ManagedThreadId;
// try // // try
// { // // {
// await UniTask.Run<int>(() => throw new Exception(), false); // // await UniTask.Run<int>(() => throw new Exception(), false);
// } // // }
// catch // // catch
// { // // {
// main.Should().NotBe(Thread.CurrentThread.ManagedThreadId); // // main.Should().NotBe(Thread.CurrentThread.ManagedThreadId);
// } // // }
//}); // //});
[UnityTest] // [UnityTest]
public IEnumerator RunThreadExceptionConfigure() => UniTask.ToCoroutine(async () => // public IEnumerator RunThreadExceptionConfigure() => UniTask.ToCoroutine(async () =>
{ // {
var main = Thread.CurrentThread.ManagedThreadId; // var main = Thread.CurrentThread.ManagedThreadId;
try // try
{ // {
await UniTask.Run<int>(() => throw new Exception(), true); // await UniTask.Run<int>(() => throw new Exception(), true);
} // }
catch // catch
{ // {
main.Should().Be(Thread.CurrentThread.ManagedThreadId); // main.Should().Be(Thread.CurrentThread.ManagedThreadId);
} // }
}); // });
#endif //#endif
#endif //#endif
} // }
} //}
#endif //#endif

View File

@@ -1,43 +1,43 @@
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6)) //#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 //#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using UnityEngine; //using UnityEngine;
using System; //using System;
using System.Collections; //using System.Collections;
using System.Collections.Generic; //using System.Collections.Generic;
using System.Linq; //using System.Linq;
using System.Text; //using System.Text;
using UnityEngine.UI; //using UnityEngine.UI;
using UnityEngine.Scripting; //using UnityEngine.Scripting;
using Cysharp.Threading.Tasks; //using Cysharp.Threading.Tasks;
using Unity.Collections; //using Unity.Collections;
using System.Threading; //using System.Threading;
using NUnit.Framework; //using NUnit.Framework;
using UnityEngine.TestTools; //using UnityEngine.TestTools;
using FluentAssertions; //using FluentAssertions;
namespace Cysharp.Threading.TasksTests //namespace Cysharp.Threading.TasksTests
{ //{
public class WhenAnyTest // public class WhenAnyTest
{ // {
[UnityTest] // [UnityTest]
public IEnumerator WhenAnyCanceled() => UniTask.ToCoroutine(async () => // public IEnumerator WhenAnyCanceled() => UniTask.ToCoroutine(async () =>
{ // {
var cts = new CancellationTokenSource(); // var cts = new CancellationTokenSource();
var successDelayTask = UniTask.Delay(TimeSpan.FromSeconds(1)); // var successDelayTask = UniTask.Delay(TimeSpan.FromSeconds(1));
var cancelTask = UniTask.Delay(TimeSpan.FromSeconds(1), cancellationToken: cts.Token); // var cancelTask = UniTask.Delay(TimeSpan.FromSeconds(1), cancellationToken: cts.Token);
cts.CancelAfterSlim(200); // cts.CancelAfterSlim(200);
try // try
{ // {
var r = await UniTask.WhenAny(new[] { successDelayTask, cancelTask }); // var r = await UniTask.WhenAny(new[] { successDelayTask, cancelTask });
} // }
catch (Exception ex) // catch (Exception ex)
{ // {
ex.Should().BeAssignableTo<OperationCanceledException>(); // ex.Should().BeAssignableTo<OperationCanceledException>();
} // }
}); // });
} // }
} //}
#endif //#endif

View File

@@ -6,13 +6,20 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.TestTools; using UnityEngine.TestTools;
namespace Cysharp.Threading.TasksTests namespace Cysharp.Threading.TasksTests
{ {
public class Preserve public class Preserve
{ {
public Preserve()
{
// TaskPool.SetMaxPoolSize(0);
}
[UnityTest] [UnityTest]
public IEnumerator AwaitTwice() => UniTask.ToCoroutine(async () => public IEnumerator AwaitTwice() => UniTask.ToCoroutine(async () =>
{ {
@@ -33,12 +40,17 @@ namespace Cysharp.Threading.TasksTests
[UnityTest] [UnityTest]
public IEnumerator PreserveAllowTwice() => UniTask.ToCoroutine(async () => public IEnumerator PreserveAllowTwice() => UniTask.ToCoroutine(async () =>
{ {
await UniTask.Yield(PlayerLoopTiming.Update);
var delay = UniTask.DelayFrame(5, PlayerLoopTiming.PostLateUpdate).Preserve(); var delay = UniTask.DelayFrame(5, PlayerLoopTiming.PostLateUpdate).Preserve();
var before = UnityEngine.Time.frameCount;
var before = UnityEngine.Time.frameCount; // 0
await delay; await delay;
var afterOne = UnityEngine.Time.frameCount; var afterOne = UnityEngine.Time.frameCount; // 5
await delay; await delay;
var afterTwo = UnityEngine.Time.frameCount; var afterTwo = UnityEngine.Time.frameCount; // 5
(afterOne - before).Should().Be(5); (afterOne - before).Should().Be(5);
afterOne.Should().Be(afterTwo); afterOne.Should().Be(afterTwo);

View File

@@ -5,7 +5,8 @@
"UnityEditor.TestRunner", "UnityEditor.TestRunner",
"UniTask", "UniTask",
"Unity.ResourceManager", "Unity.ResourceManager",
"DOTween.Modules" "DOTween.Modules",
"UniTask.Linq"
], ],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],

View File

@@ -8,7 +8,7 @@ EditorBuildSettings:
- enabled: 1 - enabled: 1
path: Assets/Scenes/SandboxMain.unity path: Assets/Scenes/SandboxMain.unity
guid: 2cda990e2423bbf4892e6590ba056729 guid: 2cda990e2423bbf4892e6590ba056729
- enabled: 0 - enabled: 1
path: path: Assets/Scenes/ExceptionExamples.unity
guid: 00000000000000000000000000000000 guid: b5fed17e3ece238439bc796d8747df5d
m_configObjects: {} m_configObjects: {}