mirror of
https://github.com/Cysharp/UniTask.git
synced 2026-05-15 11:30:09 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d935b226c0 | ||
|
|
d9e20de8a5 | ||
|
|
23997f0f93 | ||
|
|
529272d11b | ||
|
|
1194c38568 | ||
|
|
f0d2ee2beb | ||
|
|
68cdda086a | ||
|
|
54ceca6ceb | ||
|
|
50bdf7460c | ||
|
|
c06e45d0bb | ||
|
|
3ed6e28a00 | ||
|
|
ab76098895 | ||
|
|
0a447e43b0 | ||
|
|
8df44f2768 | ||
|
|
a7ec64d644 |
18
README.md
18
README.md
@@ -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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -386,7 +386,7 @@ PlayerLoopHelper.Initialize(ref playerLoop);
|
||||
|
||||
async void vs async UniTaskVoid
|
||||
---
|
||||
`async void` is a standard C# taks system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is a lightweight version of `async UniTask` because it does not have awaitable completion and report error immediately to `UniTaskScheduler.UnobservedTaskException`. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
|
||||
`async void` is a standard C# task system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is a lightweight version of `async UniTask` because it does not have awaitable completion and report error immediately to `UniTaskScheduler.UnobservedTaskException`. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
|
||||
|
||||
```csharp
|
||||
public async UniTaskVoid FireAndForgetMethod()
|
||||
@@ -431,6 +431,18 @@ actEvent += UniTask.Action(async () => { await UniTask.Yield(); });
|
||||
unityEvent += UniTask.UnityAction(async () => { await UniTask.Yield(); });
|
||||
```
|
||||
|
||||
`UniTaskVoid` can also use in MonoBehaviour's `Start` method.
|
||||
|
||||
```csharp
|
||||
class Sample : MonoBehaviour
|
||||
{
|
||||
async UniTaskVoid Start()
|
||||
{
|
||||
// async init code.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
UniTaskTracker
|
||||
---
|
||||
useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTask Tracker`.
|
||||
@@ -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`.
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -45,9 +45,7 @@
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.uGUI.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.MonoBehaviour.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.*;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;
|
||||
" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace NetCoreTests.Linq
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TakeUntil()
|
||||
public async Task TakeUntilCanceled()
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace NetCoreTests.Linq
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SkipUntil()
|
||||
public async Task SkipUntilCanceled()
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace NetCoreTests.Linq
|
||||
await c;
|
||||
var foo = await xs;
|
||||
|
||||
foo.Should().BeEquivalentTo(new[] { 30, 40 });
|
||||
foo.Should().BeEquivalentTo(new[] { 20, 30, 40 });
|
||||
|
||||
async Task CancelAsync()
|
||||
{
|
||||
@@ -102,5 +102,64 @@ namespace NetCoreTests.Linq
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TakeUntil()
|
||||
{
|
||||
var cts = new AsyncReactiveProperty<int>(0);
|
||||
|
||||
var rp = new AsyncReactiveProperty<int>(1);
|
||||
|
||||
var xs = rp.TakeUntil(cts.WaitAsync()).ToArrayAsync();
|
||||
|
||||
var c = CancelAsync();
|
||||
|
||||
await c;
|
||||
var foo = await xs;
|
||||
|
||||
foo.Should().BeEquivalentTo(new[] { 1, 10, 20 });
|
||||
|
||||
async Task CancelAsync()
|
||||
{
|
||||
rp.Value = 10;
|
||||
await Task.Yield();
|
||||
rp.Value = 20;
|
||||
await Task.Yield();
|
||||
cts.Value = 9999;
|
||||
rp.Value = 30;
|
||||
await Task.Yield();
|
||||
rp.Value = 40;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SkipUntil()
|
||||
{
|
||||
var cts = new AsyncReactiveProperty<int>(0);
|
||||
|
||||
var rp = new AsyncReactiveProperty<int>(1);
|
||||
|
||||
var xs = rp.SkipUntil(cts.WaitAsync()).ToArrayAsync();
|
||||
|
||||
var c = CancelAsync();
|
||||
|
||||
await c;
|
||||
var foo = await xs;
|
||||
|
||||
foo.Should().BeEquivalentTo(new[] { 20, 30, 40 });
|
||||
|
||||
async Task CancelAsync()
|
||||
{
|
||||
rp.Value = 10;
|
||||
await Task.Yield();
|
||||
rp.Value = 20;
|
||||
await Task.Yield();
|
||||
cts.Value = 9999;
|
||||
rp.Value = 30;
|
||||
await Task.Yield();
|
||||
rp.Value = 40;
|
||||
|
||||
rp.Dispose(); // complete.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,55 @@ namespace Cysharp.Threading.Tasks
|
||||
static readonly Action<object> cancellationTokenCallback = Callback;
|
||||
static readonly Action<object> disposeCallback = DisposeCallback;
|
||||
|
||||
public static CancellationToken ToCancellationToken(this UniTask task)
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
ToCancellationTokenCore(task, cts).Forget();
|
||||
return cts.Token;
|
||||
}
|
||||
|
||||
public static CancellationToken ToCancellationToken(this UniTask task, CancellationToken linkToken)
|
||||
{
|
||||
if (linkToken.IsCancellationRequested)
|
||||
{
|
||||
return linkToken;
|
||||
}
|
||||
|
||||
if (!linkToken.CanBeCanceled)
|
||||
{
|
||||
return ToCancellationToken(task);
|
||||
}
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
ToCancellationTokenCore(task, cts).Forget();
|
||||
|
||||
return CancellationTokenSource.CreateLinkedTokenSource(linkToken).Token;
|
||||
}
|
||||
|
||||
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task)
|
||||
{
|
||||
return ToCancellationToken(task.AsUniTask());
|
||||
}
|
||||
|
||||
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task, CancellationToken linkToken)
|
||||
{
|
||||
return ToCancellationToken(task.AsUniTask(), linkToken);
|
||||
}
|
||||
|
||||
static async UniTaskVoid ToCancellationTokenCore(UniTask task, CancellationTokenSource cts)
|
||||
{
|
||||
try
|
||||
{
|
||||
await task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
}
|
||||
cts.Cancel();
|
||||
cts.Dispose();
|
||||
}
|
||||
|
||||
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
|
||||
@@ -9,15 +9,15 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static class CancellationTokenSourceExtensions
|
||||
{
|
||||
public static void CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||
public static void CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||
{
|
||||
var delay = UniTask.Delay(millisecondsDelay, ignoreTimeScale, delayTiming, cts.Token);
|
||||
var delay = UniTask.Delay(millisecondsDelay, delayType, delayTiming, cts.Token);
|
||||
CancelAfterCore(cts, delay).Forget();
|
||||
}
|
||||
|
||||
public static void CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||
public static void CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||
{
|
||||
var delay = UniTask.Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cts.Token);
|
||||
var delay = UniTask.Delay(delayTimeSpan, delayType, delayTiming, cts.Token);
|
||||
CancelAfterCore(cts, delay).Forget();
|
||||
}
|
||||
|
||||
|
||||
187
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/SkipUntil.cs
Normal file
187
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/SkipUntil.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de932d79c8d9f3841a066d05ff29edc9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -32,13 +32,17 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
sealed class _SkipUntilCanceled : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||
{
|
||||
static readonly Action<object> CancelDelegate1 = OnCanceled1;
|
||||
static readonly Action<object> CancelDelegate2 = OnCanceled2;
|
||||
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
|
||||
|
||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||
CancellationToken cancellationToken1;
|
||||
CancellationToken cancellationToken2;
|
||||
CancellationTokenRegistration cancellationTokenRegistration1;
|
||||
CancellationTokenRegistration cancellationTokenRegistration2;
|
||||
|
||||
bool isCanceled;
|
||||
int isCanceled;
|
||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||
UniTask<bool>.Awaiter awaiter;
|
||||
bool continueNext;
|
||||
@@ -48,6 +52,14 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
this.source = source;
|
||||
this.cancellationToken1 = cancellationToken1;
|
||||
this.cancellationToken2 = cancellationToken2;
|
||||
if (cancellationToken1.CanBeCanceled)
|
||||
{
|
||||
this.cancellationTokenRegistration1 = cancellationToken1.RegisterWithoutCaptureExecutionContext(CancelDelegate1, this);
|
||||
}
|
||||
if (cancellationToken1 != cancellationToken2 && cancellationToken2.CanBeCanceled)
|
||||
{
|
||||
this.cancellationTokenRegistration2 = cancellationToken2.RegisterWithoutCaptureExecutionContext(CancelDelegate2, this);
|
||||
}
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
}
|
||||
|
||||
@@ -55,15 +67,18 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (cancellationToken1.IsCancellationRequested) isCanceled = true;
|
||||
if (cancellationToken2.IsCancellationRequested) isCanceled = true;
|
||||
|
||||
if (enumerator == null)
|
||||
{
|
||||
if (cancellationToken1.IsCancellationRequested) isCanceled = 1;
|
||||
if (cancellationToken2.IsCancellationRequested) isCanceled = 1;
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken2); // use only AsyncEnumerator provided token.
|
||||
}
|
||||
completionSource.Reset();
|
||||
SourceMoveNext();
|
||||
|
||||
if (isCanceled != 0)
|
||||
{
|
||||
SourceMoveNext();
|
||||
}
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
@@ -102,25 +117,11 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
if (result)
|
||||
{
|
||||
AGAIN:
|
||||
|
||||
if (self.isCanceled)
|
||||
self.Current = self.enumerator.Current;
|
||||
self.completionSource.TrySetResult(true);
|
||||
if (self.continueNext)
|
||||
{
|
||||
self.continueNext = false;
|
||||
self.Current = self.enumerator.Current;
|
||||
self.completionSource.TrySetResult(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self.cancellationToken1.IsCancellationRequested) self.isCanceled = true;
|
||||
if (self.cancellationToken2.IsCancellationRequested) self.isCanceled = true;
|
||||
|
||||
if (self.isCanceled) goto AGAIN;
|
||||
|
||||
if (!self.continueNext)
|
||||
{
|
||||
self.SourceMoveNext();
|
||||
}
|
||||
self.SourceMoveNext();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -130,9 +131,37 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
}
|
||||
}
|
||||
|
||||
static void OnCanceled1(object state)
|
||||
{
|
||||
var self = (_SkipUntilCanceled)state;
|
||||
if (self.isCanceled == 0)
|
||||
{
|
||||
if (Interlocked.Increment(ref self.isCanceled) == 1)
|
||||
{
|
||||
self.cancellationTokenRegistration2.Dispose();
|
||||
self.SourceMoveNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnCanceled2(object state)
|
||||
{
|
||||
var self = (_SkipUntilCanceled)state;
|
||||
if (self.isCanceled == 0)
|
||||
{
|
||||
if (Interlocked.Increment(ref self.isCanceled) == 1)
|
||||
{
|
||||
self.cancellationTokenRegistration2.Dispose();
|
||||
self.SourceMoveNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
cancellationTokenRegistration1.Dispose();
|
||||
cancellationTokenRegistration2.Dispose();
|
||||
if (enumerator != null)
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
|
||||
190
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/TakeUntil.cs
Normal file
190
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/TakeUntil.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12bda324162f15349afefc2c152ac07f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -8,6 +8,16 @@ using UnityEngine;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public enum DelayType
|
||||
{
|
||||
/// <summary>use Time.deltaTime.</summary>
|
||||
DeltaTime,
|
||||
/// <summary>Ignore timescale, use Time.unscaledDeltaTime.</summary>
|
||||
UnscaledDeltaTime,
|
||||
/// <summary>use Stopwatch.GetTimestamp().</summary>
|
||||
Realtime
|
||||
}
|
||||
|
||||
public partial struct UniTask
|
||||
{
|
||||
public static YieldAwaitable Yield(PlayerLoopTiming timing = PlayerLoopTiming.Update)
|
||||
@@ -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))
|
||||
{
|
||||
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
|
||||
if (delayTimeSpan < TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Delay does not allow minus millisecondsDelay. millisecondsDelay:" + millisecondsDelay);
|
||||
}
|
||||
|
||||
return (ignoreTimeScale)
|
||||
? new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token)
|
||||
: new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out token), token);
|
||||
return Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cancellationToken);
|
||||
}
|
||||
|
||||
public static UniTask Delay(TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (delayTimeSpan < TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Delay does not allow minus delayTimeSpan. delayTimeSpan:" + delayTimeSpan);
|
||||
}
|
||||
|
||||
return (ignoreTimeScale)
|
||||
? new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token)
|
||||
: new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out token), token);
|
||||
var delayType = ignoreTimeScale ? DelayType.UnscaledDeltaTime : DelayType.DeltaTime;
|
||||
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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>
|
||||
|
||||
@@ -15,33 +15,33 @@ namespace Cysharp.Threading.Tasks
|
||||
/// <summary>
|
||||
/// If running on mainthread, do nothing. Otherwise, same as UniTask.Yield(PlayerLoopTiming.Update).
|
||||
/// </summary>
|
||||
public static SwitchToMainThreadAwaitable SwitchToMainThread()
|
||||
public static SwitchToMainThreadAwaitable SwitchToMainThread(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new SwitchToMainThreadAwaitable(PlayerLoopTiming.Update);
|
||||
return new SwitchToMainThreadAwaitable(PlayerLoopTiming.Update, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If running on mainthread, do nothing. Otherwise, same as UniTask.Yield(timing).
|
||||
/// </summary>
|
||||
public static SwitchToMainThreadAwaitable SwitchToMainThread(PlayerLoopTiming timing)
|
||||
public static SwitchToMainThreadAwaitable SwitchToMainThread(PlayerLoopTiming timing, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new SwitchToMainThreadAwaitable(timing);
|
||||
return new SwitchToMainThreadAwaitable(timing, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return to mainthread(same as await SwitchToMainThread) after using scope is closed.
|
||||
/// </summary>
|
||||
public static ReturnToMainThread ReturnToMainThread()
|
||||
public static ReturnToMainThread ReturnToMainThread(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new ReturnToMainThread(PlayerLoopTiming.Update);
|
||||
return new ReturnToMainThread(PlayerLoopTiming.Update, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return to mainthread(same as await SwitchToMainThread) after using scope is closed.
|
||||
/// </summary>
|
||||
public static ReturnToMainThread ReturnToMainThread(PlayerLoopTiming timing)
|
||||
public static ReturnToMainThread ReturnToMainThread(PlayerLoopTiming timing, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new ReturnToMainThread(timing);
|
||||
return new ReturnToMainThread(timing, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -67,20 +67,20 @@ namespace Cysharp.Threading.Tasks
|
||||
return new SwitchToTaskPoolAwaitable();
|
||||
}
|
||||
|
||||
public static SwitchToSynchronizationContextAwaitable SwitchToSynchronizationContext(SynchronizationContext synchronizationContext)
|
||||
public static SwitchToSynchronizationContextAwaitable SwitchToSynchronizationContext(SynchronizationContext synchronizationContext, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Error.ThrowArgumentNullException(synchronizationContext, nameof(synchronizationContext));
|
||||
return new SwitchToSynchronizationContextAwaitable(synchronizationContext);
|
||||
return new SwitchToSynchronizationContextAwaitable(synchronizationContext, cancellationToken);
|
||||
}
|
||||
|
||||
public static ReturnToSynchronizationContext ReturnToSynchronizationContext(SynchronizationContext synchronizationContext)
|
||||
public static ReturnToSynchronizationContext ReturnToSynchronizationContext(SynchronizationContext synchronizationContext, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new ReturnToSynchronizationContext(synchronizationContext, false);
|
||||
return new ReturnToSynchronizationContext(synchronizationContext, false, cancellationToken);
|
||||
}
|
||||
|
||||
public static ReturnToSynchronizationContext ReturnToCurrentSynchronizationContext(bool dontPostWhenSameContext = true)
|
||||
public static ReturnToSynchronizationContext ReturnToCurrentSynchronizationContext(bool dontPostWhenSameContext = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new ReturnToSynchronizationContext(SynchronizationContext.Current, dontPostWhenSameContext);
|
||||
return new ReturnToSynchronizationContext(SynchronizationContext.Current, dontPostWhenSameContext, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,21 +89,25 @@ namespace Cysharp.Threading.Tasks
|
||||
public struct SwitchToMainThreadAwaitable
|
||||
{
|
||||
readonly PlayerLoopTiming playerLoopTiming;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
public SwitchToMainThreadAwaitable(PlayerLoopTiming playerLoopTiming)
|
||||
public SwitchToMainThreadAwaitable(PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken)
|
||||
{
|
||||
this.playerLoopTiming = playerLoopTiming;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public Awaiter GetAwaiter() => new Awaiter(playerLoopTiming);
|
||||
public Awaiter GetAwaiter() => new Awaiter(playerLoopTiming, cancellationToken);
|
||||
|
||||
public struct Awaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
readonly PlayerLoopTiming playerLoopTiming;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
public Awaiter(PlayerLoopTiming playerLoopTiming)
|
||||
public Awaiter(PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken)
|
||||
{
|
||||
this.playerLoopTiming = playerLoopTiming;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public bool IsCompleted
|
||||
@@ -122,7 +126,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
}
|
||||
|
||||
public void GetResult() { }
|
||||
public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); }
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
@@ -139,31 +143,35 @@ namespace Cysharp.Threading.Tasks
|
||||
public struct ReturnToMainThread
|
||||
{
|
||||
readonly PlayerLoopTiming playerLoopTiming;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
public ReturnToMainThread(PlayerLoopTiming playerLoopTiming)
|
||||
public ReturnToMainThread(PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken)
|
||||
{
|
||||
this.playerLoopTiming = playerLoopTiming;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public Awaiter DisposeAsync()
|
||||
{
|
||||
return new Awaiter(playerLoopTiming); // run immediate.
|
||||
return new Awaiter(playerLoopTiming, cancellationToken); // run immediate.
|
||||
}
|
||||
|
||||
public readonly struct Awaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
readonly PlayerLoopTiming timing;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
public Awaiter(PlayerLoopTiming timing)
|
||||
public Awaiter(PlayerLoopTiming timing, CancellationToken cancellationToken)
|
||||
{
|
||||
this.timing = timing;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public Awaiter GetAwaiter() => this;
|
||||
|
||||
public bool IsCompleted => PlayerLoopHelper.MainThreadId == System.Threading.Thread.CurrentThread.ManagedThreadId;
|
||||
|
||||
public void GetResult() { }
|
||||
public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); }
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
@@ -285,26 +293,30 @@ namespace Cysharp.Threading.Tasks
|
||||
public struct SwitchToSynchronizationContextAwaitable
|
||||
{
|
||||
readonly SynchronizationContext synchronizationContext;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
public SwitchToSynchronizationContextAwaitable(SynchronizationContext synchronizationContext)
|
||||
public SwitchToSynchronizationContextAwaitable(SynchronizationContext synchronizationContext, CancellationToken cancellationToken)
|
||||
{
|
||||
this.synchronizationContext = synchronizationContext;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public Awaiter GetAwaiter() => new Awaiter(synchronizationContext);
|
||||
public Awaiter GetAwaiter() => new Awaiter(synchronizationContext, cancellationToken);
|
||||
|
||||
public struct Awaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
static readonly SendOrPostCallback switchToCallback = Callback;
|
||||
readonly SynchronizationContext synchronizationContext;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
public Awaiter(SynchronizationContext synchronizationContext)
|
||||
public Awaiter(SynchronizationContext synchronizationContext, CancellationToken cancellationToken)
|
||||
{
|
||||
this.synchronizationContext = synchronizationContext;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public bool IsCompleted => false;
|
||||
public void GetResult() { }
|
||||
public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); }
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
@@ -328,16 +340,18 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
readonly SynchronizationContext syncContext;
|
||||
readonly bool dontPostWhenSameContext;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
public ReturnToSynchronizationContext(SynchronizationContext syncContext, bool dontPostWhenSameContext)
|
||||
public ReturnToSynchronizationContext(SynchronizationContext syncContext, bool dontPostWhenSameContext, CancellationToken cancellationToken)
|
||||
{
|
||||
this.syncContext = syncContext;
|
||||
this.dontPostWhenSameContext = dontPostWhenSameContext;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public Awaiter DisposeAsync()
|
||||
{
|
||||
return new Awaiter(syncContext, dontPostWhenSameContext);
|
||||
return new Awaiter(syncContext, dontPostWhenSameContext, cancellationToken);
|
||||
}
|
||||
|
||||
public struct Awaiter : ICriticalNotifyCompletion
|
||||
@@ -346,11 +360,13 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
readonly SynchronizationContext synchronizationContext;
|
||||
readonly bool dontPostWhenSameContext;
|
||||
readonly CancellationToken cancellationToken;
|
||||
|
||||
public Awaiter(SynchronizationContext synchronizationContext, bool dontPostWhenSameContext)
|
||||
public Awaiter(SynchronizationContext synchronizationContext, bool dontPostWhenSameContext, CancellationToken cancellationToken)
|
||||
{
|
||||
this.synchronizationContext = synchronizationContext;
|
||||
this.dontPostWhenSameContext = dontPostWhenSameContext;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public Awaiter GetAwaiter() => this;
|
||||
@@ -373,7 +389,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
}
|
||||
|
||||
public void GetResult() { }
|
||||
public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); }
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
|
||||
@@ -201,10 +201,10 @@ namespace Cysharp.Threading.Tasks
|
||||
return new ToCoroutineEnumerator(task, exceptionHandler);
|
||||
}
|
||||
|
||||
public static async UniTask Timeout(this UniTask task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||
public static async UniTask Timeout(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||
{
|
||||
var delayCancellationTokenSource = new CancellationTokenSource();
|
||||
var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
|
||||
var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
|
||||
|
||||
int winArgIndex;
|
||||
bool taskResultIsCanceled;
|
||||
@@ -242,10 +242,10 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
}
|
||||
|
||||
public static async UniTask<T> Timeout<T>(this UniTask<T> task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||
public static async UniTask<T> Timeout<T>(this UniTask<T> task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||
{
|
||||
var delayCancellationTokenSource = new CancellationTokenSource();
|
||||
var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
|
||||
var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
|
||||
|
||||
int winArgIndex;
|
||||
(bool IsCanceled, T Result) taskResult;
|
||||
@@ -288,10 +288,10 @@ namespace Cysharp.Threading.Tasks
|
||||
/// <summary>
|
||||
/// Timeout with suppress OperationCanceledException. Returns (bool, IsCacneled).
|
||||
/// </summary>
|
||||
public static async UniTask<bool> TimeoutWithoutException(this UniTask task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||
public static async UniTask<bool> TimeoutWithoutException(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||
{
|
||||
var delayCancellationTokenSource = new CancellationTokenSource();
|
||||
var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
|
||||
var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
|
||||
|
||||
int winArgIndex;
|
||||
bool taskResultIsCanceled;
|
||||
@@ -334,10 +334,10 @@ namespace Cysharp.Threading.Tasks
|
||||
/// <summary>
|
||||
/// Timeout with suppress OperationCanceledException. Returns (bool IsTimeout, T Result).
|
||||
/// </summary>
|
||||
public static async UniTask<(bool IsTimeout, T Result)> TimeoutWithoutException<T>(this UniTask<T> task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||
public static async UniTask<(bool IsTimeout, T Result)> TimeoutWithoutException<T>(this UniTask<T> task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||
{
|
||||
var delayCancellationTokenSource = new CancellationTokenSource();
|
||||
var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
|
||||
var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow();
|
||||
|
||||
int winArgIndex;
|
||||
(bool IsCanceled, T Result) taskResult;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98f5fedb44749ab4688674d79126b46a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "com.cysharp.unitask",
|
||||
"displayName": "UniTask",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.22",
|
||||
"unity": "2018.4",
|
||||
"description": "Provides an efficient async/await integration to Unity.",
|
||||
"keywords": [ "async/await", "async", "Task", "UniTask" ],
|
||||
|
||||
@@ -15,6 +15,8 @@ using UnityEngine.LowLevel;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.Rendering;
|
||||
using System.IO;
|
||||
|
||||
|
||||
// using DG.Tweening;
|
||||
@@ -117,6 +119,8 @@ public class AsyncMessageBroker<T> : IDisposable
|
||||
|
||||
public class SandboxMain : MonoBehaviour
|
||||
{
|
||||
public Camera mycamera;
|
||||
|
||||
public Button okButton;
|
||||
public Button cancelButton;
|
||||
|
||||
@@ -161,6 +165,9 @@ public class SandboxMain : MonoBehaviour
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//setHp = Hp.GetSetter();
|
||||
}
|
||||
|
||||
@@ -178,10 +185,6 @@ public class SandboxMain : MonoBehaviour
|
||||
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();
|
||||
|
||||
@@ -458,7 +489,11 @@ public class SandboxMain : MonoBehaviour
|
||||
//var cts = new CancellationTokenSource();
|
||||
|
||||
|
||||
// UniTask.Post(
|
||||
|
||||
// CancellationToken.
|
||||
|
||||
//UniTask.Delay(TimeSpan.FromSeconds(3)).
|
||||
|
||||
|
||||
//okButton.onClick.AddListener(UniTask.UnityAction(async () =>
|
||||
@@ -950,6 +985,62 @@ public class SandboxMain : MonoBehaviour
|
||||
// e.SetObserved();
|
||||
// or other custom write code.
|
||||
UnityEngine.Debug.LogError("Unobserved:" + e.Exception.ToString());
|
||||
}
|
||||
|
||||
|
||||
// GPU Screenshot Sample
|
||||
|
||||
void PrepareCamera()
|
||||
{
|
||||
Debug.Log("Support AsyncGPUReadback:" + SystemInfo.supportsAsyncGPUReadback);
|
||||
|
||||
var width = 480;
|
||||
var height = 240;
|
||||
var depth = 24;
|
||||
|
||||
mycamera.targetTexture = new RenderTexture(width, height, depth, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default)
|
||||
{
|
||||
antiAliasing = 8
|
||||
};
|
||||
mycamera.enabled = true;
|
||||
|
||||
//myRenderTexture = new RenderTexture(width, height, depth, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default)
|
||||
//{
|
||||
// antiAliasing = 8
|
||||
//};
|
||||
}
|
||||
|
||||
RenderTexture myRenderTexture;
|
||||
|
||||
async UniTask ShootAsync()
|
||||
{
|
||||
var rt = mycamera.targetTexture;
|
||||
|
||||
|
||||
|
||||
var req = await AsyncGPUReadback.Request(rt, 0);
|
||||
|
||||
Debug.Log("GPU Readback done?:" + req.done);
|
||||
|
||||
var rawByteArray = req.GetData<byte>().ToArray();
|
||||
var graphicsFormat = rt.graphicsFormat;
|
||||
var width = (uint)rt.width;
|
||||
var height = (uint)rt.height;
|
||||
|
||||
Debug.Log("BytesSize:" + rawByteArray.Length);
|
||||
|
||||
|
||||
var imageBytes = ImageConversion.EncodeArrayToPNG(rawByteArray, graphicsFormat, width, height);
|
||||
|
||||
|
||||
File.WriteAllBytes("my_screenshot.png", imageBytes); // test
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -239,6 +239,80 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 16537670}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!1 &518730348
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 518730350}
|
||||
- component: {fileID: 518730349}
|
||||
m_Layer: 0
|
||||
m_Name: Camera
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!20 &518730349
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 518730348}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 1
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_projectionMatrixMode: 1
|
||||
m_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_FocalLength: 50
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &518730350
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 518730348}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 488, y: 418, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 1
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &519420028
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -278,9 +352,11 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: f0bc6c75abb2e0b47a25aa49bfd488ed, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
camera: {fileID: 518730349}
|
||||
okButton: {fileID: 16537672}
|
||||
cancelButton: {fileID: 628393011}
|
||||
text: {fileID: 2101290655}
|
||||
button: {fileID: 0}
|
||||
--- !u!20 &519420031
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -597,7 +673,7 @@ Transform:
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 1
|
||||
m_RootOrder: 2
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1556045504
|
||||
GameObject:
|
||||
@@ -693,7 +769,7 @@ RectTransform:
|
||||
- {fileID: 628393010}
|
||||
- {fileID: 2101290654}
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 2
|
||||
m_RootOrder: 3
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
|
||||
@@ -190,7 +190,7 @@ namespace Cysharp.Threading.TasksTests
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
|
||||
await UniTask.DelayRealtime(TimeSpan.FromSeconds(2));
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(2), DelayType.Realtime);
|
||||
|
||||
var elapsed = DateTimeOffset.UtcNow - now;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user