Compare commits

...

15 Commits

Author SHA1 Message Date
neuecc
d935b226c0 2.0.22 2020-06-29 01:13:37 +09:00
neuecc
d9e20de8a5 Add UniTaskAsyncEnumerable.SkipUntil, TakeUntil. Fix SkipUntilCanceled behaviour. 2020-06-29 01:10:18 +09:00
neuecc
23997f0f93 SwitchToMainThread, ReturnToMainThread, SwitchToSynchronizationContext, ReturnToSynchronizationContext supports CancellationToken 2020-06-28 20:57:42 +09:00
neuecc
529272d11b more ignore 2020-06-28 18:01:01 +09:00
neuecc
1194c38568 Add AsyncGPUReadbackRequest await support 2020-06-28 17:59:03 +09:00
neuecc
f0d2ee2beb typo 2020-06-26 17:57:27 +09:00
neuecc
68cdda086a async UniTaskVoid Start 2020-06-26 17:53:07 +09:00
neuecc
54ceca6ceb R 2020-06-25 23:03:13 +09:00
neuecc
50bdf7460c 2.0.21 2020-06-25 23:02:40 +09:00
neuecc
c06e45d0bb ToCancellationToken(linkeToken) 2020-06-25 23:02:30 +09:00
neuecc
3ed6e28a00 Removed Timeout(bool ignoreTimeScale), changed to Timeout(DelayType delayType) 2020-06-25 22:35:55 +09:00
neuecc
ab76098895 Removed CancelAfterSlim(bool ignoreTimeScale), changed to CancelAfterSlim(DelayType delayType) 2020-06-25 22:35:35 +09:00
neuecc
0a447e43b0 Add UniTask.ToCancellationToken 2020-06-25 22:34:45 +09:00
neuecc
8df44f2768 Remove UniTask.DelayRealtime, Add UniTask.Delay(DelayType) overload 2020-06-25 22:15:44 +09:00
neuecc
a7ec64d644 more 20 2020-06-24 05:09:00 +09:00
19 changed files with 1002 additions and 105 deletions

View File

@@ -136,7 +136,7 @@ UniTask feature rely on C# 7.0([task-like custom async method builder feature](h
Why UniTask(custom task-like object) is required? Because Task is too heavy, not matched to Unity threading(single-thread). UniTask does not use thread and SynchronizationContext/ExecutionContext because almost Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It acquires more fast and more less allocation, completely integrated with Unity. Why UniTask(custom task-like object) is required? Because Task is too heavy, not matched to Unity threading(single-thread). UniTask does not use thread and SynchronizationContext/ExecutionContext because almost Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It acquires more fast and more less allocation, completely integrated with Unity.
You can await `AsyncOperation`, `ResourceRequest`, `AssetBundleRequest`, `AssetBundleCreateRequest`, `UnityWebRequestAsyncOperation`, `IEnumerator` and others when `using Cysharp.Threading.Tasks;`. You can await `AsyncOperation`, `ResourceRequest`, `AssetBundleRequest`, `AssetBundleCreateRequest`, `UnityWebRequestAsyncOperation`, `AsyncGPUReadbackRequest`, `IEnumerator` and others when `using Cysharp.Threading.Tasks;`.
UniTask provides three pattern of extension methods. UniTask provides three pattern of extension methods.
@@ -386,7 +386,7 @@ PlayerLoopHelper.Initialize(ref playerLoop);
async void vs async UniTaskVoid async void vs async UniTaskVoid
--- ---
`async void` is a standard C# taks system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is a lightweight version of `async UniTask` because it does not have awaitable completion and report error immediately to `UniTaskScheduler.UnobservedTaskException`. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`. `async void` is a standard C# task system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is a lightweight version of `async UniTask` because it does not have awaitable completion and report error immediately to `UniTaskScheduler.UnobservedTaskException`. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
```csharp ```csharp
public async UniTaskVoid FireAndForgetMethod() public async UniTaskVoid FireAndForgetMethod()
@@ -431,6 +431,18 @@ actEvent += UniTask.Action(async () => { await UniTask.Yield(); });
unityEvent += UniTask.UnityAction(async () => { await UniTask.Yield(); }); unityEvent += UniTask.UnityAction(async () => { await UniTask.Yield(); });
``` ```
`UniTaskVoid` can also use in MonoBehaviour's `Start` method.
```csharp
class Sample : MonoBehaviour
{
async UniTaskVoid Start()
{
// async init code.
}
}
```
UniTaskTracker UniTaskTracker
--- ---
useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTask Tracker`. useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTask Tracker`.
@@ -800,7 +812,7 @@ After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of gi
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"` to `Packages/manifest.json`. 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.19`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.0.19`. If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#2.0.22`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.0.22`.
### Install via OpenUPM ### Install via OpenUPM

View File

@@ -45,9 +45,7 @@
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs; ..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs; ..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs; ..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.cs; ..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.*;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.uGUI.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.MonoBehaviour.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs; ..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;
" /> " />
</ItemGroup> </ItemGroup>

View File

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

View File

@@ -11,6 +11,55 @@ namespace Cysharp.Threading.Tasks
static readonly Action<object> cancellationTokenCallback = Callback; static readonly Action<object> cancellationTokenCallback = Callback;
static readonly Action<object> disposeCallback = DisposeCallback; static readonly Action<object> disposeCallback = DisposeCallback;
public static CancellationToken ToCancellationToken(this UniTask task)
{
var cts = new CancellationTokenSource();
ToCancellationTokenCore(task, cts).Forget();
return cts.Token;
}
public static CancellationToken ToCancellationToken(this UniTask task, CancellationToken linkToken)
{
if (linkToken.IsCancellationRequested)
{
return linkToken;
}
if (!linkToken.CanBeCanceled)
{
return ToCancellationToken(task);
}
var cts = new CancellationTokenSource();
ToCancellationTokenCore(task, cts).Forget();
return CancellationTokenSource.CreateLinkedTokenSource(linkToken).Token;
}
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task)
{
return ToCancellationToken(task.AsUniTask());
}
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task, CancellationToken linkToken)
{
return ToCancellationToken(task.AsUniTask(), linkToken);
}
static async UniTaskVoid ToCancellationTokenCore(UniTask task, CancellationTokenSource cts)
{
try
{
await task;
}
catch (Exception ex)
{
UniTaskScheduler.PublishUnobservedTaskException(ex);
}
cts.Cancel();
cts.Dispose();
}
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken) public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,16 @@ using UnityEngine;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
{ {
public enum DelayType
{
/// <summary>use Time.deltaTime.</summary>
DeltaTime,
/// <summary>Ignore timescale, use Time.unscaledDeltaTime.</summary>
UnscaledDeltaTime,
/// <summary>use Stopwatch.GetTimestamp().</summary>
Realtime
}
public partial struct UniTask public partial struct UniTask
{ {
public static YieldAwaitable Yield(PlayerLoopTiming timing = PlayerLoopTiming.Update) public static YieldAwaitable Yield(PlayerLoopTiming timing = PlayerLoopTiming.Update)
@@ -74,36 +84,44 @@ namespace Cysharp.Threading.Tasks
public static UniTask Delay(int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask Delay(int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{ {
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay); var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
if (delayTimeSpan < TimeSpan.Zero) return Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cancellationToken);
{
throw new ArgumentOutOfRangeException("Delay does not allow minus millisecondsDelay. millisecondsDelay:" + millisecondsDelay);
}
return (ignoreTimeScale)
? new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token)
: new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out token), token);
} }
public static UniTask Delay(TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask Delay(TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{ {
if (delayTimeSpan < TimeSpan.Zero) var delayType = ignoreTimeScale ? DelayType.UnscaledDeltaTime : DelayType.DeltaTime;
{ return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken);
throw new ArgumentOutOfRangeException("Delay does not allow minus delayTimeSpan. delayTimeSpan:" + delayTimeSpan);
}
return (ignoreTimeScale)
? new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token)
: new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out token), token);
} }
public static UniTask DelayRealtime(TimeSpan delayTimeSpan, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask Delay(int millisecondsDelay, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken);
}
public static UniTask Delay(TimeSpan delayTimeSpan, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{ {
if (delayTimeSpan < TimeSpan.Zero) if (delayTimeSpan < TimeSpan.Zero)
{ {
throw new ArgumentOutOfRangeException("Delay does not allow minus delayTimeSpan. delayTimeSpan:" + delayTimeSpan); throw new ArgumentOutOfRangeException("Delay does not allow minus delayTimeSpan. delayTimeSpan:" + delayTimeSpan);
} }
return new UniTask(DelayRealtimePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token); switch (delayType)
{
case DelayType.UnscaledDeltaTime:
{
return new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
}
case DelayType.Realtime:
{
return new UniTask(DelayRealtimePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
}
case DelayType.DeltaTime:
default:
{
return new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
}
}
} }
sealed class YieldPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<YieldPromise> sealed class YieldPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<YieldPromise>

View File

@@ -15,33 +15,33 @@ namespace Cysharp.Threading.Tasks
/// <summary> /// <summary>
/// If running on mainthread, do nothing. Otherwise, same as UniTask.Yield(PlayerLoopTiming.Update). /// If running on mainthread, do nothing. Otherwise, same as UniTask.Yield(PlayerLoopTiming.Update).
/// </summary> /// </summary>
public static SwitchToMainThreadAwaitable SwitchToMainThread() public static SwitchToMainThreadAwaitable SwitchToMainThread(CancellationToken cancellationToken = default)
{ {
return new SwitchToMainThreadAwaitable(PlayerLoopTiming.Update); return new SwitchToMainThreadAwaitable(PlayerLoopTiming.Update, cancellationToken);
} }
/// <summary> /// <summary>
/// If running on mainthread, do nothing. Otherwise, same as UniTask.Yield(timing). /// If running on mainthread, do nothing. Otherwise, same as UniTask.Yield(timing).
/// </summary> /// </summary>
public static SwitchToMainThreadAwaitable SwitchToMainThread(PlayerLoopTiming timing) public static SwitchToMainThreadAwaitable SwitchToMainThread(PlayerLoopTiming timing, CancellationToken cancellationToken = default)
{ {
return new SwitchToMainThreadAwaitable(timing); return new SwitchToMainThreadAwaitable(timing, cancellationToken);
} }
/// <summary> /// <summary>
/// Return to mainthread(same as await SwitchToMainThread) after using scope is closed. /// Return to mainthread(same as await SwitchToMainThread) after using scope is closed.
/// </summary> /// </summary>
public static ReturnToMainThread ReturnToMainThread() public static ReturnToMainThread ReturnToMainThread(CancellationToken cancellationToken = default)
{ {
return new ReturnToMainThread(PlayerLoopTiming.Update); return new ReturnToMainThread(PlayerLoopTiming.Update, cancellationToken);
} }
/// <summary> /// <summary>
/// Return to mainthread(same as await SwitchToMainThread) after using scope is closed. /// Return to mainthread(same as await SwitchToMainThread) after using scope is closed.
/// </summary> /// </summary>
public static ReturnToMainThread ReturnToMainThread(PlayerLoopTiming timing) public static ReturnToMainThread ReturnToMainThread(PlayerLoopTiming timing, CancellationToken cancellationToken = default)
{ {
return new ReturnToMainThread(timing); return new ReturnToMainThread(timing, cancellationToken);
} }
/// <summary> /// <summary>
@@ -67,20 +67,20 @@ namespace Cysharp.Threading.Tasks
return new SwitchToTaskPoolAwaitable(); return new SwitchToTaskPoolAwaitable();
} }
public static SwitchToSynchronizationContextAwaitable SwitchToSynchronizationContext(SynchronizationContext synchronizationContext) public static SwitchToSynchronizationContextAwaitable SwitchToSynchronizationContext(SynchronizationContext synchronizationContext, CancellationToken cancellationToken = default)
{ {
Error.ThrowArgumentNullException(synchronizationContext, nameof(synchronizationContext)); Error.ThrowArgumentNullException(synchronizationContext, nameof(synchronizationContext));
return new SwitchToSynchronizationContextAwaitable(synchronizationContext); return new SwitchToSynchronizationContextAwaitable(synchronizationContext, cancellationToken);
} }
public static ReturnToSynchronizationContext ReturnToSynchronizationContext(SynchronizationContext synchronizationContext) public static ReturnToSynchronizationContext ReturnToSynchronizationContext(SynchronizationContext synchronizationContext, CancellationToken cancellationToken = default)
{ {
return new ReturnToSynchronizationContext(synchronizationContext, false); return new ReturnToSynchronizationContext(synchronizationContext, false, cancellationToken);
} }
public static ReturnToSynchronizationContext ReturnToCurrentSynchronizationContext(bool dontPostWhenSameContext = true) public static ReturnToSynchronizationContext ReturnToCurrentSynchronizationContext(bool dontPostWhenSameContext = true, CancellationToken cancellationToken = default)
{ {
return new ReturnToSynchronizationContext(SynchronizationContext.Current, dontPostWhenSameContext); return new ReturnToSynchronizationContext(SynchronizationContext.Current, dontPostWhenSameContext, cancellationToken);
} }
} }
@@ -89,21 +89,25 @@ namespace Cysharp.Threading.Tasks
public struct SwitchToMainThreadAwaitable public struct SwitchToMainThreadAwaitable
{ {
readonly PlayerLoopTiming playerLoopTiming; readonly PlayerLoopTiming playerLoopTiming;
readonly CancellationToken cancellationToken;
public SwitchToMainThreadAwaitable(PlayerLoopTiming playerLoopTiming) public SwitchToMainThreadAwaitable(PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken)
{ {
this.playerLoopTiming = playerLoopTiming; this.playerLoopTiming = playerLoopTiming;
this.cancellationToken = cancellationToken;
} }
public Awaiter GetAwaiter() => new Awaiter(playerLoopTiming); public Awaiter GetAwaiter() => new Awaiter(playerLoopTiming, cancellationToken);
public struct Awaiter : ICriticalNotifyCompletion public struct Awaiter : ICriticalNotifyCompletion
{ {
readonly PlayerLoopTiming playerLoopTiming; readonly PlayerLoopTiming playerLoopTiming;
readonly CancellationToken cancellationToken;
public Awaiter(PlayerLoopTiming playerLoopTiming) public Awaiter(PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken)
{ {
this.playerLoopTiming = playerLoopTiming; this.playerLoopTiming = playerLoopTiming;
this.cancellationToken = cancellationToken;
} }
public bool IsCompleted public bool IsCompleted
@@ -122,7 +126,7 @@ namespace Cysharp.Threading.Tasks
} }
} }
public void GetResult() { } public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); }
public void OnCompleted(Action continuation) public void OnCompleted(Action continuation)
{ {
@@ -139,31 +143,35 @@ namespace Cysharp.Threading.Tasks
public struct ReturnToMainThread public struct ReturnToMainThread
{ {
readonly PlayerLoopTiming playerLoopTiming; readonly PlayerLoopTiming playerLoopTiming;
readonly CancellationToken cancellationToken;
public ReturnToMainThread(PlayerLoopTiming playerLoopTiming) public ReturnToMainThread(PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken)
{ {
this.playerLoopTiming = playerLoopTiming; this.playerLoopTiming = playerLoopTiming;
this.cancellationToken = cancellationToken;
} }
public Awaiter DisposeAsync() public Awaiter DisposeAsync()
{ {
return new Awaiter(playerLoopTiming); // run immediate. return new Awaiter(playerLoopTiming, cancellationToken); // run immediate.
} }
public readonly struct Awaiter : ICriticalNotifyCompletion public readonly struct Awaiter : ICriticalNotifyCompletion
{ {
readonly PlayerLoopTiming timing; readonly PlayerLoopTiming timing;
readonly CancellationToken cancellationToken;
public Awaiter(PlayerLoopTiming timing) public Awaiter(PlayerLoopTiming timing, CancellationToken cancellationToken)
{ {
this.timing = timing; this.timing = timing;
this.cancellationToken = cancellationToken;
} }
public Awaiter GetAwaiter() => this; public Awaiter GetAwaiter() => this;
public bool IsCompleted => PlayerLoopHelper.MainThreadId == System.Threading.Thread.CurrentThread.ManagedThreadId; public bool IsCompleted => PlayerLoopHelper.MainThreadId == System.Threading.Thread.CurrentThread.ManagedThreadId;
public void GetResult() { } public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); }
public void OnCompleted(Action continuation) public void OnCompleted(Action continuation)
{ {
@@ -285,26 +293,30 @@ namespace Cysharp.Threading.Tasks
public struct SwitchToSynchronizationContextAwaitable public struct SwitchToSynchronizationContextAwaitable
{ {
readonly SynchronizationContext synchronizationContext; readonly SynchronizationContext synchronizationContext;
readonly CancellationToken cancellationToken;
public SwitchToSynchronizationContextAwaitable(SynchronizationContext synchronizationContext) public SwitchToSynchronizationContextAwaitable(SynchronizationContext synchronizationContext, CancellationToken cancellationToken)
{ {
this.synchronizationContext = synchronizationContext; this.synchronizationContext = synchronizationContext;
this.cancellationToken = cancellationToken;
} }
public Awaiter GetAwaiter() => new Awaiter(synchronizationContext); public Awaiter GetAwaiter() => new Awaiter(synchronizationContext, cancellationToken);
public struct Awaiter : ICriticalNotifyCompletion public struct Awaiter : ICriticalNotifyCompletion
{ {
static readonly SendOrPostCallback switchToCallback = Callback; static readonly SendOrPostCallback switchToCallback = Callback;
readonly SynchronizationContext synchronizationContext; readonly SynchronizationContext synchronizationContext;
readonly CancellationToken cancellationToken;
public Awaiter(SynchronizationContext synchronizationContext) public Awaiter(SynchronizationContext synchronizationContext, CancellationToken cancellationToken)
{ {
this.synchronizationContext = synchronizationContext; this.synchronizationContext = synchronizationContext;
this.cancellationToken = cancellationToken;
} }
public bool IsCompleted => false; public bool IsCompleted => false;
public void GetResult() { } public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); }
public void OnCompleted(Action continuation) public void OnCompleted(Action continuation)
{ {
@@ -328,16 +340,18 @@ namespace Cysharp.Threading.Tasks
{ {
readonly SynchronizationContext syncContext; readonly SynchronizationContext syncContext;
readonly bool dontPostWhenSameContext; readonly bool dontPostWhenSameContext;
readonly CancellationToken cancellationToken;
public ReturnToSynchronizationContext(SynchronizationContext syncContext, bool dontPostWhenSameContext) public ReturnToSynchronizationContext(SynchronizationContext syncContext, bool dontPostWhenSameContext, CancellationToken cancellationToken)
{ {
this.syncContext = syncContext; this.syncContext = syncContext;
this.dontPostWhenSameContext = dontPostWhenSameContext; this.dontPostWhenSameContext = dontPostWhenSameContext;
this.cancellationToken = cancellationToken;
} }
public Awaiter DisposeAsync() public Awaiter DisposeAsync()
{ {
return new Awaiter(syncContext, dontPostWhenSameContext); return new Awaiter(syncContext, dontPostWhenSameContext, cancellationToken);
} }
public struct Awaiter : ICriticalNotifyCompletion public struct Awaiter : ICriticalNotifyCompletion
@@ -346,11 +360,13 @@ namespace Cysharp.Threading.Tasks
readonly SynchronizationContext synchronizationContext; readonly SynchronizationContext synchronizationContext;
readonly bool dontPostWhenSameContext; readonly bool dontPostWhenSameContext;
readonly CancellationToken cancellationToken;
public Awaiter(SynchronizationContext synchronizationContext, bool dontPostWhenSameContext) public Awaiter(SynchronizationContext synchronizationContext, bool dontPostWhenSameContext, CancellationToken cancellationToken)
{ {
this.synchronizationContext = synchronizationContext; this.synchronizationContext = synchronizationContext;
this.dontPostWhenSameContext = dontPostWhenSameContext; this.dontPostWhenSameContext = dontPostWhenSameContext;
this.cancellationToken = cancellationToken;
} }
public Awaiter GetAwaiter() => this; public Awaiter GetAwaiter() => this;
@@ -373,7 +389,7 @@ namespace Cysharp.Threading.Tasks
} }
} }
public void GetResult() { } public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); }
public void OnCompleted(Action continuation) public void OnCompleted(Action continuation)
{ {

View File

@@ -201,10 +201,10 @@ namespace Cysharp.Threading.Tasks
return new ToCoroutineEnumerator(task, exceptionHandler); return new ToCoroutineEnumerator(task, exceptionHandler);
} }
public static async UniTask Timeout(this UniTask task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) public static async UniTask Timeout(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
{ {
var delayCancellationTokenSource = new CancellationTokenSource(); var delayCancellationTokenSource = new CancellationTokenSource();
var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow(); var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
int winArgIndex; int winArgIndex;
bool taskResultIsCanceled; bool taskResultIsCanceled;
@@ -242,10 +242,10 @@ namespace Cysharp.Threading.Tasks
} }
} }
public static async UniTask<T> Timeout<T>(this UniTask<T> task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) public static async UniTask<T> Timeout<T>(this UniTask<T> task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
{ {
var delayCancellationTokenSource = new CancellationTokenSource(); var delayCancellationTokenSource = new CancellationTokenSource();
var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow(); var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
int winArgIndex; int winArgIndex;
(bool IsCanceled, T Result) taskResult; (bool IsCanceled, T Result) taskResult;
@@ -288,10 +288,10 @@ namespace Cysharp.Threading.Tasks
/// <summary> /// <summary>
/// Timeout with suppress OperationCanceledException. Returns (bool, IsCacneled). /// Timeout with suppress OperationCanceledException. Returns (bool, IsCacneled).
/// </summary> /// </summary>
public static async UniTask<bool> TimeoutWithoutException(this UniTask task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) public static async UniTask<bool> TimeoutWithoutException(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
{ {
var delayCancellationTokenSource = new CancellationTokenSource(); var delayCancellationTokenSource = new CancellationTokenSource();
var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow(); var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
int winArgIndex; int winArgIndex;
bool taskResultIsCanceled; bool taskResultIsCanceled;
@@ -334,10 +334,10 @@ namespace Cysharp.Threading.Tasks
/// <summary> /// <summary>
/// Timeout with suppress OperationCanceledException. Returns (bool IsTimeout, T Result). /// Timeout with suppress OperationCanceledException. Returns (bool IsTimeout, T Result).
/// </summary> /// </summary>
public static async UniTask<(bool IsTimeout, T Result)> TimeoutWithoutException<T>(this UniTask<T> task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) public static async UniTask<(bool IsTimeout, T Result)> TimeoutWithoutException<T>(this UniTask<T> task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
{ {
var delayCancellationTokenSource = new CancellationTokenSource(); var delayCancellationTokenSource = new CancellationTokenSource();
var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow(); var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
int winArgIndex; int winArgIndex;
(bool IsCanceled, T Result) taskResult; (bool IsCanceled, T Result) taskResult;

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
{ {
"name": "com.cysharp.unitask", "name": "com.cysharp.unitask",
"displayName": "UniTask", "displayName": "UniTask",
"version": "2.0.20", "version": "2.0.22",
"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

@@ -15,6 +15,8 @@ using UnityEngine.LowLevel;
using UnityEngine.Networking; using UnityEngine.Networking;
using UnityEngine.UI; using UnityEngine.UI;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using UnityEngine.Rendering;
using System.IO;
// using DG.Tweening; // using DG.Tweening;
@@ -117,6 +119,8 @@ public class AsyncMessageBroker<T> : IDisposable
public class SandboxMain : MonoBehaviour public class SandboxMain : MonoBehaviour
{ {
public Camera mycamera;
public Button okButton; public Button okButton;
public Button cancelButton; public Button cancelButton;
@@ -161,6 +165,9 @@ public class SandboxMain : MonoBehaviour
//setHp = Hp.GetSetter(); //setHp = Hp.GetSetter();
} }
@@ -178,10 +185,6 @@ public class SandboxMain : MonoBehaviour
public Button button; public Button button;
void Start2()
{
}
@@ -435,12 +438,40 @@ public class SandboxMain : MonoBehaviour
} }
void Start()
{
_ = Foo(); // unhandled.
Go();
UnityEngine.Debug.Log("Start:" + PlayerLoopInfo.CurrentLoopType);
async void Nanika()
{
await UniTask.Yield();
Debug.Log("Here");
throw new Exception();
}
private void Awake()
{
PlayerLoopInfo.Inject();
PrepareCamera();
}
async UniTaskVoid Start()
{
okButton.onClick.AddListener(() =>
{
ShootAsync().Forget();
});
// Nanika();
await UniTask.Yield();
// this.GetCancellationTokenOnDestroy()
//PlayerLoopInfo.Inject(); //PlayerLoopInfo.Inject();
@@ -458,7 +489,11 @@ public class SandboxMain : MonoBehaviour
//var cts = new CancellationTokenSource(); //var cts = new CancellationTokenSource();
// UniTask.Post(
// CancellationToken.
//UniTask.Delay(TimeSpan.FromSeconds(3)).
//okButton.onClick.AddListener(UniTask.UnityAction(async () => //okButton.onClick.AddListener(UniTask.UnityAction(async () =>
@@ -950,6 +985,62 @@ public class SandboxMain : MonoBehaviour
// e.SetObserved(); // e.SetObserved();
// or other custom write code. // or other custom write code.
UnityEngine.Debug.LogError("Unobserved:" + e.Exception.ToString()); UnityEngine.Debug.LogError("Unobserved:" + e.Exception.ToString());
}
// GPU Screenshot Sample
void PrepareCamera()
{
Debug.Log("Support AsyncGPUReadback:" + SystemInfo.supportsAsyncGPUReadback);
var width = 480;
var height = 240;
var depth = 24;
mycamera.targetTexture = new RenderTexture(width, height, depth, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default)
{
antiAliasing = 8
};
mycamera.enabled = true;
//myRenderTexture = new RenderTexture(width, height, depth, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default)
//{
// antiAliasing = 8
//};
}
RenderTexture myRenderTexture;
async UniTask ShootAsync()
{
var rt = mycamera.targetTexture;
var req = await AsyncGPUReadback.Request(rt, 0);
Debug.Log("GPU Readback done?:" + req.done);
var rawByteArray = req.GetData<byte>().ToArray();
var graphicsFormat = rt.graphicsFormat;
var width = (uint)rt.width;
var height = (uint)rt.height;
Debug.Log("BytesSize:" + rawByteArray.Length);
var imageBytes = ImageConversion.EncodeArrayToPNG(rawByteArray, graphicsFormat, width, height);
File.WriteAllBytes("my_screenshot.png", imageBytes); // test
} }
} }

View File

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

View File

@@ -190,7 +190,7 @@ namespace Cysharp.Threading.TasksTests
{ {
var now = DateTimeOffset.UtcNow; var now = DateTimeOffset.UtcNow;
await UniTask.DelayRealtime(TimeSpan.FromSeconds(2)); await UniTask.Delay(TimeSpan.FromSeconds(2), DelayType.Realtime);
var elapsed = DateTimeOffset.UtcNow - now; var elapsed = DateTimeOffset.UtcNow - now;