Compare commits

..

29 Commits

Author SHA1 Message Date
neuecc
53907a3719 Merge remote-tracking branch 'origin/master' 2020-08-25 07:16:16 +09:00
neuecc
4937aeee3f 2.0.31 2020-08-25 07:16:08 +09:00
Yoshifumi Kawai
5e5b8aff89 Merge pull request #140 from hikarin522/patch-1
Fix ToCancellationToken
2020-08-25 07:13:56 +09:00
Yoshifumi Kawai
a2cbbd82d0 Merge pull request #139 from Cysharp/feature/SubscribeAwait
Add UniTaskAsyncEnumerable.SubscribeAwait
2020-08-25 07:12:00 +09:00
Yoshifumi Kawai
7eac5d8ba8 Merge pull request #138 from Cysharp/hotfix/ContinueOnErrorCallingOnNext
Continue to subscribe if an exception is raised when calling onNext
2020-08-25 07:11:43 +09:00
Yoshifumi Kawai
e2b1ed55ae Merge pull request #134 from SeungHuLee/Add-TMP-Generic-BindTo
Add TMP Generic BindTo Support
2020-08-25 07:11:26 +09:00
neuecc
727c7102d3 Add more DoTween support - AwaitForComplete, AwaitForPause, AwaitForPlay, AwaitForRewind, AwaitForStepComplete. 2020-08-25 07:10:39 +09:00
neuecc
1494ea6717 removelog spam on UniTaskAsyncEnumerable.EveryValueChanged #141 2020-08-25 06:43:31 +09:00
neuecc
f1ce64dbd3 middleware sample 2020-08-25 06:43:03 +09:00
hikari
3bad5cd2bf fixup! Fix ToCancellationToken linkToken 2020-08-25 02:55:09 +09:00
hikari
7432c0073a Fix ToCancellationToken linkToken 2020-08-24 17:10:26 +09:00
Mayuki Sawatari
f1813a7c94 Continue to subscribe if an exception is raised when calling onNext 2020-08-24 14:44:48 +09:00
Mayuki Sawatari
9e45c0a4d1 Add UniTaskAsyncEnumerable.SubscribeAwait 2020-08-24 14:35:20 +09:00
LSH
2c652cdde7 Add TMP Generic BindTo Support 2020-08-20 03:30:41 +09:00
neuecc
7718d345c8 2.0.30 2020-08-14 17:36:50 +09:00
neuecc
9f39708325 Merge remote-tracking branch 'origin/master' 2020-08-14 17:29:39 +09:00
neuecc
bb6dbfa920 Fix await IEnumerator + WaitForSeconds does not follow timescale(to behave same as StartCoroutine) #133 2020-08-14 17:29:30 +09:00
Yoshifumi Kawai
ba265005bb Merge pull request #132 from RamType-0/Disable-AutoReference-EditorAssembly
Disable "Auto Referenced" of UniTask.Editor
2020-08-14 06:25:21 +09:00
Ram.Type-0
4d7cc7ed61 Disable auto reference of UniTask.Editor 2020-08-14 04:56:58 +09:00
neuecc
b64f31eb0b 2.0.28 2020-08-13 17:44:15 +09:00
neuecc
38d159b69e IEnumerator.ToUniTask() behave same as StartCoroutine #120 2020-08-13 17:44:06 +09:00
neuecc
d5455f3716 Merge remote-tracking branch 'origin/master' 2020-08-13 16:33:41 +09:00
neuecc
a72ceeba11 UNITASK_ASSETBUNDLE_SUPPORT #131 2020-08-13 16:33:32 +09:00
Yoshifumi Kawai
c6b7d332b2 Update README.md 2020-08-12 11:41:21 +09:00
neuecc
f37278f2a6 docs: update TOC 2020-08-12 02:23:09 +00:00
Yoshifumi Kawai
3f3e03b83d Update README.md 2020-08-12 11:22:52 +09:00
Yoshifumi Kawai
c99d3eb3c3 Update README.md 2020-08-05 15:55:09 +09:00
Yoshifumi Kawai
08d5183e7e Merge pull request #121 from Cysharp/chore/unity_activation
chore: remove generate unity alf (activation license file)
2020-08-04 14:54:50 +09:00
Ikiru Yoshizaki
51769b2224 chore: remove generate unity alf (activation license file) 2020-08-04 13:20:33 +09:00
21 changed files with 1159 additions and 68 deletions

View File

@@ -42,13 +42,6 @@ jobs:
steps:
- run: apt update && apt install git -y
- uses: actions/checkout@v2
# create unity activation file and store to artifacts.
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -logFile -createManualActivationFile || exit 0
- uses: actions/upload-artifact@v1
with:
name: Unity_v${{ matrix.unity }}.alf
path: ./Unity_v${{ matrix.unity }}.alf
# activate Unity from manual license file(ulf)
- run: echo -n "$UNITY_LICENSE" >> .Unity.ulf
env:
UNITY_LICENSE: ${{ secrets[matrix.license] }}

View File

@@ -34,6 +34,7 @@ Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await f
- [For Unit Testing](#for-unit-testing)
- [Compare with Standard Task API](#compare-with-standard-task-api)
- [Pooling Configuration](#pooling-configuration)
- [Allocation on Profiler](#allocation-on-profiler)
- [UniTaskSynchronizationContext](#unitasksynchronizationcontext)
- [API References](#api-references)
- [UPM Package](#upm-package)
@@ -151,25 +152,24 @@ UniTask provides three pattern of extension methods.
> Note: WithCancellation is returned from native timing of PlayerLoop but ToUniTask is returned from specified PlayerLoopTiming. Details of timing, see: [PlayerLoop](#playerloop) section.
> Note: AssetBundleRequest has `asset` and `allAssets`, in default await returns `asset`. If you want to get `allAssets`, you can use `AwaitForAllAssets()` method.
The type of `UniTask` can use utility like `UniTask.WhenAll`, `UniTask.WhenAny`. It is like Task.WhenAll/WhenAny but return type is more useful, returns value tuple so can deconsrtuct each result and pass multiple type.
```csharp
public class SceneAssets
public async UniTaskVoid LoadManyAsync()
{
public SceneAssets()
{
// parallel load.
var (a, b, c) = await UniTask.WhenAll(
LoadAsSprite("foo"),
LoadAsSprite("bar"),
LoadAsSprite("baz"));
}
// parallel load.
var (a, b, c) = await UniTask.WhenAll(
LoadAsSprite("foo"),
LoadAsSprite("bar"),
LoadAsSprite("baz"));
}
async UniTask<Sprite> LoadAsSprite(string path)
{
var resource = await Resources.LoadAsync<Sprite>(path);
return (resource as Sprite);
}
async UniTask<Sprite> LoadAsSprite(string path)
{
var resource = await Resources.LoadAsync<Sprite>(path);
return (resource as Sprite);
}
```
@@ -489,6 +489,8 @@ await UniTask.WhenAll(
transform.DOScale(10, 3).WithCancellation(ct));
```
DOTween support's default behaviour(`await`, `WithCancellation`, `ToUniTask`) awaits tween is killed. It works both Complete(true/false) and Kill(true/false). But if you want to tween reuse(`SetAutoKill(false)`), it does not work you expected. Or, if you want to await for another timing, the following extension methods exist in Tween, `AwaitForComplete`, `AwaitForPause`, `AwaitForPlay`, `AwaitForRewind`, `AwaitForStepComplete`.
AsyncEnumerable and Async LINQ
---
Unity 2020.2.0a12 supports C# 8.0 so you can use `await foreach`. This is the new Update notation in async era.
@@ -835,7 +837,15 @@ foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
}
```
> In UnityEditor profiler shows allocation of compiler generated AsyncStateMachine but it only occurs in debug(development) build. C# Compiler generate AsyncStateMachine as class on Debug build and as struct on Release build.
Allocation on Profiler
---
In UnityEditor profiler shows allocation of compiler generated AsyncStateMachine but it only occurs in debug(development) build. C# Compiler generate AsyncStateMachine as class on Debug build and as struct on Release build.
After Unity 2020.1 supports Code Optimization option on UnityEditor(right, footer).
![](https://user-images.githubusercontent.com/46207/89967342-2f944600-dc8c-11ea-99fc-0b74527a16f6.png)
You can change C# compiler optimization to release, it removes AsyncStateMachine allocation. Andalso optimization option can set via `Compilation.CompilationPipeline-codeOptimization`, and `Compilation.CodeOptimization`.
UniTaskSynchronizationContext
---
@@ -872,7 +882,7 @@ After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of gi
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"` to `Packages/manifest.json`.
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#2.0.24`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.0.24`.
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#2.0.31`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.0.31`.
### Install via OpenUPM

View File

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

View File

@@ -30,10 +30,10 @@ namespace Cysharp.Threading.Tasks
return ToCancellationToken(task);
}
var cts = new CancellationTokenSource();
var cts = CancellationTokenSource.CreateLinkedTokenSource(linkToken);
ToCancellationTokenCore(task, cts).Forget();
return CancellationTokenSource.CreateLinkedTokenSource(linkToken).Token;
return cts.Token;
}
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task)

View File

@@ -44,6 +44,7 @@ namespace Cysharp.Threading.Tasks
IEnumerator innerEnumerator;
CancellationToken cancellationToken;
int initialFrame;
UniTaskCompletionSourceCore<object> core;
@@ -66,10 +67,13 @@ namespace Cysharp.Threading.Tasks
result.innerEnumerator = ConsumeEnumerator(innerEnumerator);
result.cancellationToken = cancellationToken;
result.initialFrame = -1;
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
result.MoveNext(); // run immediately.
return result;
}
@@ -108,6 +112,19 @@ namespace Cysharp.Threading.Tasks
return false;
}
if (initialFrame == -1)
{
// Time can not touch in threadpool.
if (PlayerLoopHelper.IsMainThread)
{
initialFrame = Time.frameCount;
}
}
else if (initialFrame == Time.frameCount)
{
return true; // already executed in first frame, skip.
}
try
{
if (innerEnumerator.MoveNext())
@@ -208,12 +225,12 @@ namespace Cysharp.Threading.Tasks
static IEnumerator UnwrapWaitForSeconds(WaitForSeconds waitForSeconds)
{
var second = (float)waitForSeconds_Seconds.GetValue(waitForSeconds);
var startTime = DateTimeOffset.UtcNow;
var elapsed = 0.0f;
while (true)
{
yield return null;
var elapsed = (DateTimeOffset.UtcNow - startTime).TotalSeconds;
elapsed += Time.deltaTime;
if (elapsed >= second)
{
break;

View File

@@ -11,7 +11,6 @@ using System.Threading;
namespace Cysharp.Threading.Tasks
{
// The idea of TweenCancelBehaviour is borrowed from https://www.shibuya24.info/entry/dotween_async_await
public enum TweenCancelBehaviour
{
Kill,
@@ -29,6 +28,16 @@ namespace Cysharp.Threading.Tasks
public static class DOTweenAsyncExtensions
{
enum CallbackType
{
Kill,
Complete,
Pause,
Play,
Rewind,
StepComplete
}
public static TweenAwaiter GetAwaiter(this Tween tween)
{
return new TweenAwaiter(tween);
@@ -39,7 +48,7 @@ namespace Cysharp.Threading.Tasks
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, TweenCancelBehaviour.Kill, cancellationToken, out var token), token);
return new UniTask(TweenConfiguredSource.Create(tween, TweenCancelBehaviour.Kill, cancellationToken, CallbackType.Kill, out var token), token);
}
public static UniTask ToUniTask(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
@@ -47,7 +56,47 @@ namespace Cysharp.Threading.Tasks
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, out var token), token);
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Kill, out var token), token);
}
public static UniTask AwaitForComplete(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Complete, out var token), token);
}
public static UniTask AwaitForPause(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Pause, out var token), token);
}
public static UniTask AwaitForPlay(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Play, out var token), token);
}
public static UniTask AwaitForRewind(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Rewind, out var token), token);
}
public static UniTask AwaitForStepComplete(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.StepComplete, out var token), token);
}
public struct TweenAwaiter : ICriticalNotifyCompletion
@@ -95,12 +144,13 @@ namespace Cysharp.Threading.Tasks
static readonly TweenCallback EmptyTweenCallback = () => { };
readonly TweenCallback onKillDelegate;
readonly TweenCallback onCompleteCallbackDelegate;
readonly TweenCallback onUpdateDelegate;
Tween tween;
TweenCancelBehaviour cancelBehaviour;
CancellationToken cancellationToken;
CallbackType callbackType;
bool canceled;
TweenCallback originalUpdateAction;
@@ -108,11 +158,11 @@ namespace Cysharp.Threading.Tasks
TweenConfiguredSource()
{
onKillDelegate = OnKill;
onCompleteCallbackDelegate = OnCompleteCallbackDelegate;
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, CallbackType callbackType, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
@@ -128,6 +178,7 @@ namespace Cysharp.Threading.Tasks
result.tween = tween;
result.cancelBehaviour = cancelBehaviour;
result.cancellationToken = cancellationToken;
result.callbackType = callbackType;
result.originalUpdateAction = tween.onUpdate;
result.canceled = false;
@@ -138,7 +189,30 @@ namespace Cysharp.Threading.Tasks
}
tween.onUpdate = result.onUpdateDelegate;
tween.onKill = result.onKillDelegate;
switch (callbackType)
{
case CallbackType.Kill:
tween.onKill = result.onCompleteCallbackDelegate;
break;
case CallbackType.Complete:
tween.onComplete = result.onCompleteCallbackDelegate;
break;
case CallbackType.Pause:
tween.onPause = result.onCompleteCallbackDelegate;
break;
case CallbackType.Play:
tween.onPlay = result.onCompleteCallbackDelegate;
break;
case CallbackType.Rewind:
tween.onRewind = result.onCompleteCallbackDelegate;
break;
case CallbackType.StepComplete:
tween.onStepComplete = result.onCompleteCallbackDelegate;
break;
default:
break;
}
TaskTracker.TrackActiveTask(result, 3);
@@ -146,7 +220,7 @@ namespace Cysharp.Threading.Tasks
return result;
}
void OnKill()
void OnCompleteCallbackDelegate()
{
if (canceled)
{
@@ -199,7 +273,31 @@ namespace Cysharp.Threading.Tasks
this.tween.Complete(true);
break;
case TweenCancelBehaviour.CancelAwait:
this.tween.onKill = EmptyTweenCallback; // replace to empty(avoid callback after Canceled(instance is returned to pool.)
// replace to empty(avoid callback after Canceled(instance is returned to pool.)
switch (callbackType)
{
case CallbackType.Kill:
tween.onKill = EmptyTweenCallback;
break;
case CallbackType.Complete:
tween.onComplete = EmptyTweenCallback;
break;
case CallbackType.Pause:
tween.onPause = EmptyTweenCallback;
break;
case CallbackType.Play:
tween.onPlay = EmptyTweenCallback;
break;
case CallbackType.Rewind:
tween.onRewind = EmptyTweenCallback;
break;
case CallbackType.StepComplete:
tween.onStepComplete = EmptyTweenCallback;
break;
default:
break;
}
this.core.TrySetCanceled(this.cancellationToken);
break;
}
@@ -272,7 +370,31 @@ namespace Cysharp.Threading.Tasks
TaskTracker.RemoveTracking(this);
core.Reset();
tween.onUpdate = originalUpdateAction;
tween.onKill = null;
switch (callbackType)
{
case CallbackType.Kill:
tween.onKill = null;
break;
case CallbackType.Complete:
tween.onComplete = null;
break;
case CallbackType.Pause:
tween.onPause = null;
break;
case CallbackType.Play:
tween.onPlay = null;
break;
case CallbackType.Rewind:
tween.onRewind = null;
break;
case CallbackType.StepComplete:
tween.onStepComplete = null;
break;
default:
break;
}
tween = default;
cancellationToken = default;
originalUpdateAction = default;

View File

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

View File

@@ -63,6 +63,42 @@ namespace Cysharp.Threading.Tasks.Linq
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
}
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
var cts = new CancellationTokenDisposable();
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
return cts;
}
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
}
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
var cts = new CancellationTokenDisposable();
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
return cts;
}
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
}
// OnNext, OnError
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> onError)
@@ -105,6 +141,46 @@ namespace Cysharp.Threading.Tasks.Linq
Subscribes.SubscribeCore(source, onNext, onError, Subscribes.NopCompleted, cancellationToken).Forget();
}
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action<Exception> onError)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onError, nameof(onError));
var cts = new CancellationTokenDisposable();
Subscribes.SubscribeAwaitCore(source, onNext, onError, Subscribes.NopCompleted, cts.Token).Forget();
return cts;
}
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action<Exception> onError, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onError, nameof(onError));
Subscribes.SubscribeAwaitCore(source, onNext, onError, Subscribes.NopCompleted, cancellationToken).Forget();
}
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action<Exception> onError)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onError, nameof(onError));
var cts = new CancellationTokenDisposable();
Subscribes.SubscribeAwaitCore(source, onNext, onError, Subscribes.NopCompleted, cts.Token).Forget();
return cts;
}
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action<Exception> onError, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onError, nameof(onError));
Subscribes.SubscribeAwaitCore(source, onNext, onError, Subscribes.NopCompleted, cancellationToken).Forget();
}
// OnNext, OnCompleted
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action onCompleted)
@@ -147,6 +223,46 @@ namespace Cysharp.Threading.Tasks.Linq
Subscribes.SubscribeCore(source, onNext, Subscribes.NopError, onCompleted, cancellationToken).Forget();
}
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action onCompleted)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
var cts = new CancellationTokenDisposable();
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, onCompleted, cts.Token).Forget();
return cts;
}
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action onCompleted, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, onCompleted, cancellationToken).Forget();
}
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action onCompleted)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
var cts = new CancellationTokenDisposable();
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, onCompleted, cts.Token).Forget();
return cts;
}
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action onCompleted, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(onNext, nameof(onNext));
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, onCompleted, cancellationToken).Forget();
}
// IObserver
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, IObserver<TSource> observer)
@@ -195,7 +311,14 @@ namespace Cysharp.Threading.Tasks.Linq
{
while (await e.MoveNextAsync())
{
onNext(e.Current);
try
{
onNext(e.Current);
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
onCompleted();
}
@@ -227,7 +350,14 @@ namespace Cysharp.Threading.Tasks.Linq
{
while (await e.MoveNextAsync())
{
onNext(e.Current).Forget();
try
{
onNext(e.Current).Forget();
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
onCompleted();
}
@@ -259,7 +389,14 @@ namespace Cysharp.Threading.Tasks.Linq
{
while (await e.MoveNextAsync())
{
onNext(e.Current, cancellationToken).Forget();
try
{
onNext(e.Current, cancellationToken).Forget();
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
onCompleted();
}
@@ -291,7 +428,14 @@ namespace Cysharp.Threading.Tasks.Linq
{
while (await e.MoveNextAsync())
{
observer.OnNext(e.Current);
try
{
observer.OnNext(e.Current);
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
observer.OnCompleted();
}
@@ -309,5 +453,84 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
}
public static async UniTaskVoid SubscribeAwaitCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
{
while (await e.MoveNextAsync())
{
try
{
await onNext(e.Current);
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
onCompleted();
}
catch (Exception ex)
{
if (onError == NopError)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
return;
}
if (ex is OperationCanceledException) return;
onError(ex);
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
public static async UniTaskVoid SubscribeAwaitCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
{
var e = source.GetAsyncEnumerator(cancellationToken);
try
{
while (await e.MoveNextAsync())
{
try
{
await onNext(e.Current, cancellationToken);
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
}
onCompleted();
}
catch (Exception ex)
{
if (onError == NopError)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
return;
}
if (ex is OperationCanceledException) return;
onError(ex);
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
}
}

View File

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

View File

@@ -208,7 +208,6 @@ namespace Cysharp.Threading.Tasks.Linq
public bool MoveNext()
{
UnityEngine.Debug.Log("TRY_RESULT:" + target.TryGetTarget(out var _));
if (disposed || cancellationToken.IsCancellationRequested || !target.TryGetTarget(out var t))
{
completionSource.TrySetResult(false);

View File

@@ -10,6 +10,11 @@
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.modules.assetbundle",
"expression": "",
"define": "UNITASK_ASSETBUNDLE_SUPPORT"
}
],
"noEngineReferences": false
}

View File

@@ -1,6 +1,7 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#if UNITY_2018_4 || UNITY_2019_4_OR_NEWER
#if UNITASK_ASSETBUNDLE_SUPPORT
using Cysharp.Threading.Tasks.Internal;
using System;
@@ -312,4 +313,5 @@ namespace Cysharp.Threading.Tasks
}
}
#endif
#endif

View File

@@ -605,6 +605,7 @@ namespace Cysharp.Threading.Tasks
#endregion
#if UNITASK_ASSETBUNDLE_SUPPORT
#region AssetBundleRequest
public static AssetBundleRequestAwaiter GetAwaiter(this AssetBundleRequest asyncOperation)
@@ -906,7 +907,9 @@ namespace Cysharp.Threading.Tasks
}
#endregion
#endif
#if UNITASK_ASSETBUNDLE_SUPPORT
#region AssetBundleCreateRequest
public static AssetBundleCreateRequestAwaiter GetAwaiter(this AssetBundleCreateRequest asyncOperation)
@@ -1208,6 +1211,7 @@ namespace Cysharp.Threading.Tasks
}
#endregion
#endif
#if ENABLE_UNITYWEBREQUEST
#region UnityWebRequestAsyncOperation

View File

@@ -17,6 +17,7 @@
Func<string, string> ToUniTaskReturnType = x => (x == "void") ? "UniTask" : $"UniTask<{x}>";
Func<string, string> ToIUniTaskSourceReturnType = x => (x == "void") ? "IUniTaskSource" : $"IUniTaskSource<{x}>";
Func<(string typeName, string returnType, string returnField), bool> IsUnityWebRequest = x => x.returnType == "UnityWebRequest";
Func<(string typeName, string returnType, string returnField), bool> IsAssetBundleModule = x => x.typeName == "AssetBundleRequest" || x.typeName == "AssetBundleCreateRequest";
Func<(string typeName, string returnType, string returnField), bool> IsVoid = x => x.returnType == "void";
#>
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
@@ -37,6 +38,8 @@ namespace Cysharp.Threading.Tasks
<# foreach(var t in types) { #>
<# if(IsUnityWebRequest(t)) { #>
#if ENABLE_UNITYWEBREQUEST
<# } else if(IsAssetBundleModule(t)) { #>
#if UNITASK_ASSETBUNDLE_SUPPORT
<# } #>
#region <#= t.typeName #>
@@ -422,7 +425,7 @@ namespace Cysharp.Threading.Tasks
}
#endregion
<# if(IsUnityWebRequest(t)) { #>
<# if(IsUnityWebRequest(t) || IsAssetBundleModule(t)) { #>
#endif
<# } #>

View File

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

View File

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

View File

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

View File

@@ -17,6 +17,8 @@ using UnityEngine.UI;
using UnityEngine.SceneManagement;
using UnityEngine.Rendering;
using System.IO;
using System.Linq.Expressions;
using Cysharp.Threading.Tasks.Sample;
// using DG.Tweening;
@@ -493,9 +495,11 @@ public class SandboxMain : MonoBehaviour
async UniTaskVoid Start()
{
RunStandardTaskAsync();
//Expression.Lambda<Func<int>>(null).Compile(true);
UnityEngine.Debug.Log("UniTaskPlayerLoop ready? " + PlayerLoopHelper.IsInjectedUniTaskPlayerLoop());
//RunStandardTaskAsync();
//UnityEngine.Debug.Log("UniTaskPlayerLoop ready? " + PlayerLoopHelper.IsInjectedUniTaskPlayerLoop());
//var url = "http://google.com/404";
//var webRequestAsyncOperation = UnityWebRequest.Get(url).SendWebRequest();
@@ -524,8 +528,23 @@ public class SandboxMain : MonoBehaviour
//UniTask.Delay(TimeSpan.FromSeconds(3)).
//okButton.onClick.AddListener(UniTask.UnityAction(async () =>
//{
okButton.onClick.AddListener(UniTask.UnityAction(async () =>
{
var client = new NetworkClient("http://localhost:5000", TimeSpan.FromSeconds(2),
new QueueRequestDecorator(),
new LoggingDecorator());
//new AppendTokenDecorator(),
//new SetupHeaderDecorator());
await client.PostAsync("", new { Id = 100 });
}));
// _ = ExecuteAsync();
// await UniTask.Yield();

View File

@@ -0,0 +1,153 @@
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine.UI;
using UnityEngine.Scripting;
using Cysharp.Threading.Tasks;
using Unity.Collections;
using System.Threading;
using NUnit.Framework;
using UnityEngine.TestTools;
using FluentAssertions;
namespace Cysharp.Threading.TasksTests
{
public class CoroutineToUniTaskTest
{
[UnityTest]
public IEnumerator EarlyUpdate() => UniTask.ToCoroutine(async () =>
{
await UniTask.Yield(PlayerLoopTiming.EarlyUpdate);
var l = new List<(int, int)>();
var currentFrame = Time.frameCount;
var t = Worker(l).ToUniTask();
l.Count.Should().Be(1);
l[0].Should().Be((0, currentFrame));
await t;
l[1].Should().Be((1, Time.frameCount));
l[1].Item2.Should().NotBe(currentFrame);
});
[UnityTest]
public IEnumerator LateUpdate() => UniTask.ToCoroutine(async () =>
{
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
var l = new List<(int, int)>();
var currentFrame = Time.frameCount;
var t = Worker(l).ToUniTask();
l.Count.Should().Be(1);
l[0].Should().Be((0, currentFrame));
await t;
l[1].Should().Be((1, Time.frameCount));
l[1].Item2.Should().NotBe(currentFrame);
});
//[UnityTest]
//public IEnumerator TestCoroutine()
//{
// yield return UniTask.Yield(PlayerLoopTiming.EarlyUpdate).ToUniTask().ToCoroutine();
// var nanika = (UnityEngine.MonoBehaviour)GameObject.FindObjectOfType(typeof(UnityEngine.MonoBehaviour));
// var l = new List<(int, int)>();
// var currentFrame = Time.frameCount;
// var t = nanika.StartCoroutine(Worker(l));
// l.Count.Should().Be(1);
// l[0].Should().Be((0, currentFrame));
// yield return t;
// l[1].Should().Be((1, Time.frameCount));
// l[1].Item2.Should().NotBe(currentFrame);
//}
//[UnityTest]
//public IEnumerator TestCoroutine2()
//{
// yield return UniTask.Yield(PlayerLoopTiming.PostLateUpdate).ToUniTask().ToCoroutine();
// var nanika = (UnityEngine.MonoBehaviour)GameObject.FindObjectOfType(typeof(UnityEngine.MonoBehaviour));
// var l = new List<(int, int)>();
// var currentFrame = Time.frameCount;
// var t = nanika.StartCoroutine(Worker(l));
// l.Count.Should().Be(1);
// l[0].Should().Be((0, currentFrame));
// yield return t;
// l[1].Should().Be((1, Time.frameCount));
// l[1].Item2.Should().NotBe(currentFrame);
//}
[UnityTest]
public IEnumerator WaitForSecondsTest() => UniTask.ToCoroutine(async () =>
{
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
Time.timeScale = 0.5f;
try
{
var now = DateTimeOffset.UtcNow;
await WaitFor();
var elapsed = DateTimeOffset.UtcNow - now;
(5.8f <= elapsed.TotalSeconds && elapsed.TotalSeconds <= 6.2f).Should().BeTrue();
}
finally
{
Time.timeScale = 1.0f;
}
});
IEnumerator WaitFor()
{
yield return new WaitForSeconds(3.0f);
}
IEnumerator Worker(List<(int, int)> l)
{
l.Add((0, Time.frameCount));
yield return null;
l.Add((1, Time.frameCount));
}
public async UniTask Foo()
{
var tasks = new List<UniTask>();
var t = Bar<int>();
tasks.Add(t);
t = Bar<int>();
tasks.Add(t);
await UniTask.WhenAll(tasks);
}
public async UniTask<T> Bar<T>()
{
await UniTask.Yield();
return default(T);
}
}
}
#endif

View File

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

View File

@@ -111,6 +111,8 @@ PlayerSettings:
switchNVNShaderPoolsGranularity: 33554432
switchNVNDefaultPoolsGranularity: 16777216
switchNVNOtherPoolsGranularity: 16777216
stadiaPresentMode: 0
stadiaTargetFramerate: 0
vulkanNumSwapchainBuffers: 3
vulkanEnableSetSRGBWrite: 0
m_SupportedAspectRatios:
@@ -191,22 +193,6 @@ PlayerSettings:
uIStatusBarHidden: 1
uIExitOnSuspend: 0
uIStatusBarStyle: 0
iPhoneSplashScreen: {fileID: 0}
iPhoneHighResSplashScreen: {fileID: 0}
iPhoneTallHighResSplashScreen: {fileID: 0}
iPhone47inSplashScreen: {fileID: 0}
iPhone55inPortraitSplashScreen: {fileID: 0}
iPhone55inLandscapeSplashScreen: {fileID: 0}
iPhone58inPortraitSplashScreen: {fileID: 0}
iPhone58inLandscapeSplashScreen: {fileID: 0}
iPadPortraitSplashScreen: {fileID: 0}
iPadHighResPortraitSplashScreen: {fileID: 0}
iPadLandscapeSplashScreen: {fileID: 0}
iPadHighResLandscapeSplashScreen: {fileID: 0}
iPhone65inPortraitSplashScreen: {fileID: 0}
iPhone65inLandscapeSplashScreen: {fileID: 0}
iPhone61inPortraitSplashScreen: {fileID: 0}
iPhone61inLandscapeSplashScreen: {fileID: 0}
appleTVSplashScreen: {fileID: 0}
appleTVSplashScreen2x: {fileID: 0}
tvOSSmallIconLayers: []
@@ -566,7 +552,8 @@ PlayerSettings:
scriptingRuntimeVersion: 1
gcIncremental: 0
gcWBarrierValidation: 0
apiCompatibilityLevelPerPlatform: {}
apiCompatibilityLevelPerPlatform:
Standalone: 6
m_RenderingPath: 1
m_MobileRenderingPath: 1
metroPackageName: Template_2D
@@ -621,6 +608,7 @@ PlayerSettings:
XboxOnePersistentLocalStorageSize: 0
XboxOneXTitleMemory: 8
XboxOneOverrideIdentityName:
XboxOneOverrideIdentityPublisher:
vrEditorSettings:
daydream:
daydreamIconForeground: {fileID: 0}