complete delay and factory
parent
11bc28b33f
commit
ca58784c4d
|
@ -8,6 +8,14 @@ namespace UniRx.Async
|
||||||
{
|
{
|
||||||
// UnityEngine Bridges.
|
// UnityEngine Bridges.
|
||||||
|
|
||||||
|
public partial struct UniTask2
|
||||||
|
{
|
||||||
|
public static IEnumerator ToCoroutine(Func<UniTask2> taskFactory)
|
||||||
|
{
|
||||||
|
return taskFactory().ToCoroutine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public partial struct UniTask
|
public partial struct UniTask
|
||||||
{
|
{
|
||||||
public static IEnumerator ToCoroutine(Func<UniTask> taskFactory)
|
public static IEnumerator ToCoroutine(Func<UniTask> taskFactory)
|
||||||
|
|
|
@ -9,6 +9,449 @@ using UnityEngine;
|
||||||
|
|
||||||
namespace UniRx.Async
|
namespace UniRx.Async
|
||||||
{
|
{
|
||||||
|
// TODO:rename
|
||||||
|
public partial struct UniTask2
|
||||||
|
{
|
||||||
|
public static YieldAwaitable2 Yield(PlayerLoopTiming timing = PlayerLoopTiming.Update)
|
||||||
|
{
|
||||||
|
// optimized for single continuation
|
||||||
|
return new YieldAwaitable2(timing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask2 Yield(PlayerLoopTiming timing, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return new UniTask2(YieldPromise.Create(timing, cancellationToken, out var token), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask2 DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
if (delayFrameCount < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. delayFrameCount:" + delayFrameCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new UniTask2(DelayFramePromise.Create(delayFrameCount, delayTiming, cancellationToken, out var token), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask2 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 UniTask2(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token)
|
||||||
|
: new UniTask2(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out token), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask2 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 UniTask2(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token)
|
||||||
|
: new UniTask2(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out token), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
class YieldPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
|
||||||
|
{
|
||||||
|
static readonly PromisePool<YieldPromise> pool = new PromisePool<YieldPromise>();
|
||||||
|
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
UniTaskCompletionSourceCore<object> core;
|
||||||
|
|
||||||
|
YieldPromise()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = pool.TryRent() ?? new YieldPromise();
|
||||||
|
|
||||||
|
result.cancellationToken = cancellationToken;
|
||||||
|
|
||||||
|
TaskTracker2.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
PlayerLoopHelper.AddAction(timing, result);
|
||||||
|
|
||||||
|
token = result.core.Version;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetResult(short token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TaskTracker2.RemoveTracking(this);
|
||||||
|
core.GetResult(token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
pool.TryReturn(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus 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.SetCanceled(cancellationToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
core.SetResult(null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
core.Reset();
|
||||||
|
cancellationToken = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
|
||||||
|
{
|
||||||
|
static readonly PromisePool<DelayFramePromise> pool = new PromisePool<DelayFramePromise>();
|
||||||
|
|
||||||
|
int delayFrameCount;
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
|
||||||
|
int currentFrameCount;
|
||||||
|
UniTaskCompletionSourceCore<object> core;
|
||||||
|
|
||||||
|
DelayFramePromise()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskSource Create(int delayFrameCount, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = pool.TryRent() ?? new DelayFramePromise();
|
||||||
|
|
||||||
|
result.delayFrameCount = delayFrameCount;
|
||||||
|
result.cancellationToken = cancellationToken;
|
||||||
|
|
||||||
|
TaskTracker2.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
PlayerLoopHelper.AddAction(timing, result);
|
||||||
|
|
||||||
|
token = result.core.Version;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetResult(short token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TaskTracker2.RemoveTracking(this);
|
||||||
|
core.GetResult(token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
pool.TryReturn(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus 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.SetCanceled(cancellationToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentFrameCount == delayFrameCount)
|
||||||
|
{
|
||||||
|
core.SetResult(null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentFrameCount++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
core.Reset();
|
||||||
|
currentFrameCount = default;
|
||||||
|
delayFrameCount = default;
|
||||||
|
cancellationToken = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DelayPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
|
||||||
|
{
|
||||||
|
static readonly PromisePool<DelayPromise> pool = new PromisePool<DelayPromise>();
|
||||||
|
|
||||||
|
float delayFrameTimeSpan;
|
||||||
|
float elapsed;
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
|
||||||
|
UniTaskCompletionSourceCore<object> core;
|
||||||
|
|
||||||
|
DelayPromise()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskSource Create(TimeSpan delayFrameTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = pool.TryRent() ?? new DelayPromise();
|
||||||
|
|
||||||
|
result.elapsed = 0.0f;
|
||||||
|
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
|
||||||
|
result.cancellationToken = cancellationToken;
|
||||||
|
|
||||||
|
TaskTracker2.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
PlayerLoopHelper.AddAction(timing, result);
|
||||||
|
|
||||||
|
token = result.core.Version;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetResult(short token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TaskTracker2.RemoveTracking(this);
|
||||||
|
core.GetResult(token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
pool.TryReturn(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus 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.SetCanceled(cancellationToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
elapsed += Time.deltaTime;
|
||||||
|
if (elapsed >= delayFrameTimeSpan)
|
||||||
|
{
|
||||||
|
core.SetResult(null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
core.Reset();
|
||||||
|
delayFrameTimeSpan = default;
|
||||||
|
elapsed = default;
|
||||||
|
cancellationToken = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DelayIgnoreTimeScalePromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
|
||||||
|
{
|
||||||
|
static readonly PromisePool<DelayIgnoreTimeScalePromise> pool = new PromisePool<DelayIgnoreTimeScalePromise>();
|
||||||
|
|
||||||
|
float delayFrameTimeSpan;
|
||||||
|
float elapsed;
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
|
||||||
|
UniTaskCompletionSourceCore<object> core;
|
||||||
|
|
||||||
|
DelayIgnoreTimeScalePromise()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskSource Create(TimeSpan delayFrameTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = pool.TryRent() ?? new DelayIgnoreTimeScalePromise();
|
||||||
|
|
||||||
|
result.elapsed = 0.0f;
|
||||||
|
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
|
||||||
|
result.cancellationToken = cancellationToken;
|
||||||
|
|
||||||
|
TaskTracker2.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
PlayerLoopHelper.AddAction(timing, result);
|
||||||
|
|
||||||
|
token = result.core.Version;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetResult(short token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TaskTracker2.RemoveTracking(this);
|
||||||
|
core.GetResult(token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
pool.TryReturn(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus 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.SetCanceled(cancellationToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
elapsed += Time.unscaledDeltaTime;
|
||||||
|
if (elapsed >= delayFrameTimeSpan)
|
||||||
|
{
|
||||||
|
core.SetResult(null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
core.Reset();
|
||||||
|
delayFrameTimeSpan = default;
|
||||||
|
elapsed = default;
|
||||||
|
cancellationToken = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:rename
|
||||||
|
public struct YieldAwaitable2
|
||||||
|
{
|
||||||
|
readonly PlayerLoopTiming timing;
|
||||||
|
|
||||||
|
public YieldAwaitable2(PlayerLoopTiming timing)
|
||||||
|
{
|
||||||
|
this.timing = timing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Awaiter GetAwaiter()
|
||||||
|
{
|
||||||
|
return new Awaiter(timing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask2 ToUniTask()
|
||||||
|
{
|
||||||
|
return UniTask2.Yield(timing, CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Awaiter : ICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
readonly PlayerLoopTiming timing;
|
||||||
|
|
||||||
|
public Awaiter(PlayerLoopTiming timing)
|
||||||
|
{
|
||||||
|
this.timing = timing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCompleted => false;
|
||||||
|
|
||||||
|
public void GetResult() { }
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
PlayerLoopHelper.AddContinuation(timing, continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
PlayerLoopHelper.AddContinuation(timing, continuation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO:remove
|
||||||
public partial struct UniTask
|
public partial struct UniTask
|
||||||
{
|
{
|
||||||
public static YieldAwaitable Yield(PlayerLoopTiming timing = PlayerLoopTiming.Update)
|
public static YieldAwaitable Yield(PlayerLoopTiming timing = PlayerLoopTiming.Update)
|
||||||
|
@ -199,6 +642,7 @@ namespace UniRx.Async
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO:remove
|
||||||
public struct YieldAwaitable
|
public struct YieldAwaitable
|
||||||
{
|
{
|
||||||
readonly PlayerLoopTiming timing;
|
readonly PlayerLoopTiming timing;
|
||||||
|
|
|
@ -7,6 +7,121 @@ using UnityEngine.Events;
|
||||||
|
|
||||||
namespace UniRx.Async
|
namespace UniRx.Async
|
||||||
{
|
{
|
||||||
|
public partial struct UniTask2
|
||||||
|
{
|
||||||
|
static readonly UniTask2 CanceledUniTask = new Func<UniTask2>(() =>
|
||||||
|
{
|
||||||
|
var promise = new UniTaskCompletionSource2();
|
||||||
|
promise.SetCanceled(CancellationToken.None);
|
||||||
|
promise.MarkHandled();
|
||||||
|
return promise.Task;
|
||||||
|
})();
|
||||||
|
|
||||||
|
static class CanceledUniTaskCache<T>
|
||||||
|
{
|
||||||
|
public static readonly UniTask2<T> Task;
|
||||||
|
|
||||||
|
static CanceledUniTaskCache()
|
||||||
|
{
|
||||||
|
var promise = new UniTaskCompletionSource2<T>();
|
||||||
|
promise.SetCanceled(CancellationToken.None);
|
||||||
|
promise.MarkHandled();
|
||||||
|
Task = promise.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly UniTask2 CompletedTask = new UniTask2();
|
||||||
|
|
||||||
|
public static UniTask2 FromException(Exception ex)
|
||||||
|
{
|
||||||
|
var promise = new UniTaskCompletionSource2();
|
||||||
|
promise.SetException(ex);
|
||||||
|
promise.MarkHandled();
|
||||||
|
return promise.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask2<T> FromException<T>(Exception ex)
|
||||||
|
{
|
||||||
|
var promise = new UniTaskCompletionSource2<T>();
|
||||||
|
promise.SetException(ex);
|
||||||
|
promise.MarkHandled();
|
||||||
|
return promise.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask2<T> FromResult<T>(T value)
|
||||||
|
{
|
||||||
|
return new UniTask2<T>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask2 FromCanceled(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (cancellationToken == CancellationToken.None)
|
||||||
|
{
|
||||||
|
return CanceledUniTask;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var promise = new UniTaskCompletionSource2();
|
||||||
|
promise.SetCanceled(cancellationToken);
|
||||||
|
promise.MarkHandled();
|
||||||
|
return promise.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask2<T> FromCanceled<T>(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (cancellationToken == CancellationToken.None)
|
||||||
|
{
|
||||||
|
return CanceledUniTaskCache<T>.Task;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var promise = new UniTaskCompletionSource2<T>();
|
||||||
|
promise.SetCanceled(cancellationToken);
|
||||||
|
promise.MarkHandled();
|
||||||
|
return promise.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:...
|
||||||
|
|
||||||
|
/// <summary>shorthand of new UniTask[T](Func[UniTask[T]] factory)</summary>
|
||||||
|
public static UniTask<T> Lazy<T>(Func<UniTask<T>> factory)
|
||||||
|
{
|
||||||
|
return new UniTask<T>(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// helper of create add UniTaskVoid to delegate.
|
||||||
|
/// For example: FooEvent += () => UniTask.Void(async () => { /* */ })
|
||||||
|
/// </summary>
|
||||||
|
public static void Void(Func<UniTask> asyncAction)
|
||||||
|
{
|
||||||
|
asyncAction().Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Action VoidAction(Func<UniTask> asyncAction)
|
||||||
|
{
|
||||||
|
return () => Void(asyncAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UnityAction VoidUnityAction(Func<UniTask> asyncAction)
|
||||||
|
{
|
||||||
|
return () => Void(asyncAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// helper of create add UniTaskVoid to delegate.
|
||||||
|
/// For example: FooEvent += (sender, e) => UniTask.Void(async arg => { /* */ }, (sender, e))
|
||||||
|
/// </summary>
|
||||||
|
public static void Void<T>(Func<T, UniTask> asyncAction, T state)
|
||||||
|
{
|
||||||
|
asyncAction(state).Forget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO:remove
|
||||||
public partial struct UniTask
|
public partial struct UniTask
|
||||||
{
|
{
|
||||||
static readonly UniTask CanceledUniTask = new Func<UniTask>(() =>
|
static readonly UniTask CanceledUniTask = new Func<UniTask>(() =>
|
||||||
|
@ -120,6 +235,8 @@ namespace UniRx.Async
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO:remove
|
||||||
internal static class CompletedTasks
|
internal static class CompletedTasks
|
||||||
{
|
{
|
||||||
public static readonly UniTask<bool> True = UniTask.FromResult(true);
|
public static readonly UniTask<bool> True = UniTask.FromResult(true);
|
||||||
|
@ -130,7 +247,7 @@ namespace UniRx.Async
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO:rename
|
||||||
internal static class CompletedTasks2
|
internal static class CompletedTasks2
|
||||||
{
|
{
|
||||||
public static readonly UniTask2 Completed = new UniTask2();
|
public static readonly UniTask2 Completed = new UniTask2();
|
||||||
|
|
|
@ -3,136 +3,15 @@
|
||||||
#pragma warning disable CS0436
|
#pragma warning disable CS0436
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.ExceptionServices;
|
using System.Runtime.ExceptionServices;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Threading.Tasks.Sources;
|
|
||||||
using UniRx.Async.CompilerServices;
|
using UniRx.Async.CompilerServices;
|
||||||
using UniRx.Async.Internal;
|
using UniRx.Async.Internal;
|
||||||
|
|
||||||
namespace UniRx.Async
|
namespace UniRx.Async
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
public partial struct UniTask2
|
|
||||||
{
|
|
||||||
public static UniTask2 DelayFrame(int frameCount, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
return new UniTask2(DelayPromiseCore2.Create(frameCount, timing, cancellationToken, out var token), token);
|
|
||||||
|
|
||||||
|
|
||||||
//return new ValueTask<int>(DelayPromiseCore2.Create(frameCount, timing, cancellationToken, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly UniTask2 CompletedTask = new UniTask2();
|
|
||||||
|
|
||||||
public static UniTask2<T> FromResult<T>(T result)
|
|
||||||
{
|
|
||||||
return new UniTask2<T>(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class DelayPromiseCore2 : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
|
|
||||||
{
|
|
||||||
static readonly PromisePool<DelayPromiseCore2> pool = new PromisePool<DelayPromiseCore2>();
|
|
||||||
|
|
||||||
int delayFrameCount;
|
|
||||||
CancellationToken cancellationToken;
|
|
||||||
|
|
||||||
int currentFrameCount;
|
|
||||||
UniTaskCompletionSourceCore<object> core;
|
|
||||||
|
|
||||||
DelayPromiseCore2()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskSource Create(int delayFrameCount, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = pool.TryRent() ?? new DelayPromiseCore2();
|
|
||||||
|
|
||||||
result.delayFrameCount = delayFrameCount;
|
|
||||||
result.cancellationToken = cancellationToken;
|
|
||||||
|
|
||||||
TaskTracker2.TrackActiveTask(result, 3);
|
|
||||||
|
|
||||||
PlayerLoopHelper.AddAction(timing, result);
|
|
||||||
|
|
||||||
token = result.core.Version;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetResult(short token)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
TaskTracker2.RemoveTracking(this);
|
|
||||||
core.GetResult(token);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
pool.TryReturn(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AwaiterStatus GetStatus(short token)
|
|
||||||
{
|
|
||||||
return core.GetStatus(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AwaiterStatus 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.SetCancellation(cancellationToken);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentFrameCount == delayFrameCount)
|
|
||||||
{
|
|
||||||
core.SetResult(null);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentFrameCount++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
core.Reset();
|
|
||||||
currentFrameCount = default;
|
|
||||||
delayFrameCount = default;
|
|
||||||
cancellationToken = default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
internal static class AwaiterActions
|
internal static class AwaiterActions
|
||||||
{
|
{
|
||||||
internal static readonly Action<object> InvokeActionDelegate = InvokeAction;
|
internal static readonly Action<object> InvokeActionDelegate = InvokeAction;
|
||||||
|
@ -195,7 +74,20 @@ namespace UniRx.Async
|
||||||
return "(" + source.UnsafeGetStatus() + ")";
|
return "(" + source.UnsafeGetStatus() + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO:AsTask???
|
/// <summary>
|
||||||
|
/// Memoizing inner IValueTaskSource. The result UniTask can await multiple.
|
||||||
|
/// </summary>
|
||||||
|
public UniTask2 Preserve()
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new UniTask2(new MemoizeSource(source), token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static implicit operator UniTask2<AsyncUnit>(UniTask2 task)
|
public static implicit operator UniTask2<AsyncUnit>(UniTask2 task)
|
||||||
{
|
{
|
||||||
|
@ -287,6 +179,86 @@ namespace UniRx.Async
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MemoizeSource : IUniTaskSource
|
||||||
|
{
|
||||||
|
IUniTaskSource source;
|
||||||
|
ExceptionDispatchInfo exception;
|
||||||
|
AwaiterStatus status;
|
||||||
|
|
||||||
|
public MemoizeSource(IUniTaskSource source)
|
||||||
|
{
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetResult(short token)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
if (exception != null)
|
||||||
|
{
|
||||||
|
exception.Throw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
source.GetResult(token);
|
||||||
|
status = AwaiterStatus.Succeeded;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
exception = ExceptionDispatchInfo.Capture(ex);
|
||||||
|
if (ex is OperationCanceledException)
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Canceled;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Faulted;
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
source = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return source.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
continuation(state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
source.OnCompleted(continuation, state, token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus UnsafeGetStatus()
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return source.UnsafeGetStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public readonly struct Awaiter : ICriticalNotifyCompletion
|
public readonly struct Awaiter : ICriticalNotifyCompletion
|
||||||
{
|
{
|
||||||
readonly UniTask2 task;
|
readonly UniTask2 task;
|
||||||
|
@ -343,6 +315,21 @@ namespace UniRx.Async
|
||||||
task.source.OnCompleted(AwaiterActions.InvokeActionDelegate, continuation, task.token);
|
task.source.OnCompleted(AwaiterActions.InvokeActionDelegate, continuation, task.token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If register manually continuation, you can use it instead of for compiler OnCompleted methods.
|
||||||
|
/// </summary>
|
||||||
|
public void SourceOnCompleted(Action<object> continuation, object state)
|
||||||
|
{
|
||||||
|
if (task.source == null)
|
||||||
|
{
|
||||||
|
continuation(state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
task.source.OnCompleted(continuation, state, task.token);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,7 +378,33 @@ namespace UniRx.Async
|
||||||
return new Awaiter(this);
|
return new Awaiter(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO:AsTask???
|
/// <summary>
|
||||||
|
/// Memoizing inner IValueTaskSource. The result UniTask can await multiple.
|
||||||
|
/// </summary>
|
||||||
|
public UniTask2<T> Preserve()
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new UniTask2<T>(new MemoizeSource(source), token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator UniTask2(UniTask2<T> task)
|
||||||
|
{
|
||||||
|
if (task.source == null) return UniTask2.CompletedTask;
|
||||||
|
|
||||||
|
var status = task.source.GetStatus(task.token);
|
||||||
|
if (status.IsCompletedSuccessfully())
|
||||||
|
{
|
||||||
|
return UniTask2.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new UniTask2(task.source, task.token);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// returns (bool IsCanceled, T Result) instead of throws OperationCanceledException.
|
/// returns (bool IsCanceled, T Result) instead of throws OperationCanceledException.
|
||||||
|
@ -465,6 +478,94 @@ namespace UniRx.Async
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MemoizeSource : IUniTaskSource<T>
|
||||||
|
{
|
||||||
|
IUniTaskSource<T> source;
|
||||||
|
T result;
|
||||||
|
ExceptionDispatchInfo exception;
|
||||||
|
AwaiterStatus status;
|
||||||
|
|
||||||
|
public MemoizeSource(IUniTaskSource<T> source)
|
||||||
|
{
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetResult(short token)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
if (exception != null)
|
||||||
|
{
|
||||||
|
exception.Throw();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = source.GetResult(token);
|
||||||
|
status = AwaiterStatus.Succeeded;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
exception = ExceptionDispatchInfo.Capture(ex);
|
||||||
|
if (ex is OperationCanceledException)
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Canceled;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Faulted;
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
source = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IUniTaskSource.GetResult(short token)
|
||||||
|
{
|
||||||
|
GetResult(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return source.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
continuation(state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
source.OnCompleted(continuation, state, token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus UnsafeGetStatus()
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return source.UnsafeGetStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public readonly struct Awaiter : ICriticalNotifyCompletion
|
public readonly struct Awaiter : ICriticalNotifyCompletion
|
||||||
{
|
{
|
||||||
readonly UniTask2<T> task;
|
readonly UniTask2<T> task;
|
||||||
|
@ -530,6 +631,22 @@ namespace UniRx.Async
|
||||||
s.OnCompleted(AwaiterActions.InvokeActionDelegate, continuation, task.token);
|
s.OnCompleted(AwaiterActions.InvokeActionDelegate, continuation, task.token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If register manually continuation, you can use it instead of for compiler OnCompleted methods.
|
||||||
|
/// </summary>
|
||||||
|
public void SourceOnCompleted(Action<object> continuation, object state)
|
||||||
|
{
|
||||||
|
var s = task.source;
|
||||||
|
if (s == null)
|
||||||
|
{
|
||||||
|
continuation(state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s.OnCompleted(continuation, state, task.token);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -480,7 +480,7 @@ namespace UniRx.Async
|
||||||
SignalCompletion();
|
SignalCompletion();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCancellation(CancellationToken cancellationToken)
|
public void SetCanceled(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
this.error = new OperationCanceledException(cancellationToken);
|
this.error = new OperationCanceledException(cancellationToken);
|
||||||
SignalCompletion();
|
SignalCompletion();
|
||||||
|
@ -613,7 +613,7 @@ namespace UniRx.Async
|
||||||
}
|
}
|
||||||
|
|
||||||
[Conditional("UNITY_EDITOR")]
|
[Conditional("UNITY_EDITOR")]
|
||||||
void MarkHandled()
|
internal void MarkHandled()
|
||||||
{
|
{
|
||||||
if (!handled)
|
if (!handled)
|
||||||
{
|
{
|
||||||
|
@ -643,9 +643,9 @@ namespace UniRx.Async
|
||||||
core.SetResult(AsyncUnit.Default);
|
core.SetResult(AsyncUnit.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCancellation(CancellationToken cancellationToken)
|
public void SetCanceled(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
core.SetCancellation(cancellationToken);
|
core.SetCanceled(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetException(Exception exception)
|
public void SetException(Exception exception)
|
||||||
|
@ -701,7 +701,7 @@ namespace UniRx.Async
|
||||||
public static AutoResetUniTaskCompletionSource CreateFromCanceled(CancellationToken cancellationToken, out short token)
|
public static AutoResetUniTaskCompletionSource CreateFromCanceled(CancellationToken cancellationToken, out short token)
|
||||||
{
|
{
|
||||||
var source = Create();
|
var source = Create();
|
||||||
source.SetCancellation(cancellationToken);
|
source.SetCanceled(cancellationToken);
|
||||||
token = source.core.Version;
|
token = source.core.Version;
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
@ -735,7 +735,7 @@ namespace UniRx.Async
|
||||||
core.SetResult(AsyncUnit.Default);
|
core.SetResult(AsyncUnit.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCancellation(CancellationToken cancellationToken)
|
public void SetCanceled(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
core.SetCancellation(cancellationToken);
|
core.SetCancellation(cancellationToken);
|
||||||
}
|
}
|
||||||
|
@ -800,7 +800,7 @@ namespace UniRx.Async
|
||||||
}
|
}
|
||||||
|
|
||||||
[Conditional("UNITY_EDITOR")]
|
[Conditional("UNITY_EDITOR")]
|
||||||
void MarkHandled()
|
internal void MarkHandled()
|
||||||
{
|
{
|
||||||
if (!handled)
|
if (!handled)
|
||||||
{
|
{
|
||||||
|
@ -829,7 +829,7 @@ namespace UniRx.Async
|
||||||
core.SetResult(result);
|
core.SetResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCancellation(CancellationToken cancellationToken)
|
public void SetCanceled(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
core.SetCancellation(cancellationToken);
|
core.SetCancellation(cancellationToken);
|
||||||
}
|
}
|
||||||
|
@ -926,7 +926,7 @@ namespace UniRx.Async
|
||||||
core.SetResult(result);
|
core.SetResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCancellation(CancellationToken cancellationToken)
|
public void SetCanceled(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
core.SetCancellation(cancellationToken);
|
core.SetCancellation(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,574 @@ using UniRx.Async.Internal;
|
||||||
|
|
||||||
namespace UniRx.Async
|
namespace UniRx.Async
|
||||||
{
|
{
|
||||||
|
public static partial class UniTaskExtensions2
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Convert UniTask -> UniTask[AsyncUnit].
|
||||||
|
/// </summary>
|
||||||
|
public static UniTask2<AsyncUnit> AsAsyncUnitUniTask(this UniTask2 task)
|
||||||
|
{
|
||||||
|
// use implicit conversion
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert UniTask[T] -> UniTask.
|
||||||
|
/// </summary>
|
||||||
|
public static UniTask2 AsUniTask<T>(this UniTask2<T> task)
|
||||||
|
{
|
||||||
|
// use implicit conversion
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert Task[T] -> UniTask[T].
|
||||||
|
/// </summary>
|
||||||
|
public static UniTask2<T> AsUniTask<T>(this Task<T> task, bool useCurrentSynchronizationContext = true)
|
||||||
|
{
|
||||||
|
var promise = new UniTaskCompletionSource2<T>();
|
||||||
|
|
||||||
|
task.ContinueWith((x, state) =>
|
||||||
|
{
|
||||||
|
var p = (UniTaskCompletionSource2<T>)state;
|
||||||
|
|
||||||
|
switch (x.Status)
|
||||||
|
{
|
||||||
|
case TaskStatus.Canceled:
|
||||||
|
p.SetCanceled();
|
||||||
|
break;
|
||||||
|
case TaskStatus.Faulted:
|
||||||
|
p.SetException(x.Exception);
|
||||||
|
break;
|
||||||
|
case TaskStatus.RanToCompletion:
|
||||||
|
p.SetResult(x.Result);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}, promise, useCurrentSynchronizationContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Current);
|
||||||
|
|
||||||
|
return promise.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert Task -> UniTask.
|
||||||
|
/// </summary>
|
||||||
|
public static UniTask2 AsUniTask(this Task task, bool useCurrentSynchronizationContext = true)
|
||||||
|
{
|
||||||
|
var promise = new UniTaskCompletionSource2();
|
||||||
|
|
||||||
|
task.ContinueWith((x, state) =>
|
||||||
|
{
|
||||||
|
var p = (UniTaskCompletionSource2)state;
|
||||||
|
|
||||||
|
switch (x.Status)
|
||||||
|
{
|
||||||
|
case TaskStatus.Canceled:
|
||||||
|
p.SetCanceled();
|
||||||
|
break;
|
||||||
|
case TaskStatus.Faulted:
|
||||||
|
p.SetException(x.Exception);
|
||||||
|
break;
|
||||||
|
case TaskStatus.RanToCompletion:
|
||||||
|
p.SetResult();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}, promise, useCurrentSynchronizationContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Current);
|
||||||
|
|
||||||
|
return promise.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<T> AsTask<T>(this UniTask2<T> task)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var awaiter = task.GetAwaiter();
|
||||||
|
if (awaiter.IsCompleted)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = awaiter.GetResult();
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Task.FromException<T>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource<T>();
|
||||||
|
|
||||||
|
awaiter.SourceOnCompleted(state =>
|
||||||
|
{
|
||||||
|
var (inTcs, inAwaiter) = ((TaskCompletionSource<T>, UniTask2<T>.Awaiter))state;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = inAwaiter.GetResult();
|
||||||
|
inTcs.SetResult(result);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
inTcs.SetException(ex);
|
||||||
|
}
|
||||||
|
}, (tcs, awaiter));
|
||||||
|
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Task.FromException<T>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task AsTask(this UniTask2 task)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var awaiter = task.GetAwaiter();
|
||||||
|
if (awaiter.IsCompleted)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
awaiter.GetResult(); // check token valid on Succeeded
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Task.FromException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource<object>();
|
||||||
|
|
||||||
|
awaiter.SourceOnCompleted(state =>
|
||||||
|
{
|
||||||
|
var (inTcs, inAwaiter) = ((TaskCompletionSource<object>, UniTask2.Awaiter))state;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
inAwaiter.GetResult();
|
||||||
|
inTcs.SetResult(null);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
inTcs.SetException(ex);
|
||||||
|
}
|
||||||
|
}, (tcs, awaiter));
|
||||||
|
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Task.FromException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerator ToCoroutine<T>(this UniTask2<T> task, Action<T> resultHandler = null, Action<Exception> exceptionHandler = null)
|
||||||
|
{
|
||||||
|
return new ToCoroutineEnumerator<T>(task, resultHandler, exceptionHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerator ToCoroutine(this UniTask2 task, Action<Exception> exceptionHandler = null)
|
||||||
|
{
|
||||||
|
return new ToCoroutineEnumerator(task, exceptionHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask Timeout(this UniTask2 task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||||
|
{
|
||||||
|
return Timeout(task.AsAsyncUnitUniTask(), timeout, ignoreTimeScale, timeoutCheckTiming, taskCancellationTokenSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: require UniTask2.Delay, WhenAny, etc...
|
||||||
|
|
||||||
|
public static async UniTask<T> Timeout<T>(this UniTask2<T> task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||||
|
{
|
||||||
|
// left, right both suppress operation canceled exception.
|
||||||
|
|
||||||
|
var delayCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
var timeoutTask = (UniTask)UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming).SuppressCancellationThrow();
|
||||||
|
|
||||||
|
var (hasValue, value) = await UniTask.WhenAny(task.SuppressCancellationThrow(), timeoutTask);
|
||||||
|
|
||||||
|
if (!hasValue)
|
||||||
|
{
|
||||||
|
if (taskCancellationTokenSource != null)
|
||||||
|
{
|
||||||
|
taskCancellationTokenSource.Cancel();
|
||||||
|
taskCancellationTokenSource.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new TimeoutException("Exceed Timeout:" + timeout);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delayCancellationTokenSource.Cancel();
|
||||||
|
delayCancellationTokenSource.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.IsCanceled)
|
||||||
|
{
|
||||||
|
Error.ThrowOperationCanceledException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Timeout with suppress OperationCanceledException. Returns (bool, IsCacneled).
|
||||||
|
/// </summary>
|
||||||
|
public static async UniTask2<bool> TimeoutWithoutException(this UniTask2 task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||||
|
{
|
||||||
|
var v = await TimeoutWithoutException(task.AsAsyncUnitUniTask(), timeout, ignoreTimeScale, timeoutCheckTiming, taskCancellationTokenSource);
|
||||||
|
return v.IsTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Timeout with suppress OperationCanceledException. Returns (bool IsTimeout, T Result).
|
||||||
|
/// </summary>
|
||||||
|
public static async UniTask2<(bool IsTimeout, T Result)> TimeoutWithoutException<T>(this UniTask2<T> task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||||
|
{
|
||||||
|
// left, right both suppress operation canceled exception.
|
||||||
|
|
||||||
|
var delayCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
var timeoutTask = (UniTask)UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming).SuppressCancellationThrow();
|
||||||
|
|
||||||
|
var (hasValue, value) = await UniTask.WhenAny(task.SuppressCancellationThrow(), timeoutTask);
|
||||||
|
|
||||||
|
if (!hasValue)
|
||||||
|
{
|
||||||
|
if (taskCancellationTokenSource != null)
|
||||||
|
{
|
||||||
|
taskCancellationTokenSource.Cancel();
|
||||||
|
taskCancellationTokenSource.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (true, default(T));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delayCancellationTokenSource.Cancel();
|
||||||
|
delayCancellationTokenSource.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.IsCanceled)
|
||||||
|
{
|
||||||
|
Error.ThrowOperationCanceledException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (false, value.Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Forget(this UniTask2 task)
|
||||||
|
{
|
||||||
|
ForgetCore(task).Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Forget(this UniTask2 task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread = true)
|
||||||
|
{
|
||||||
|
if (exceptionHandler == null)
|
||||||
|
{
|
||||||
|
ForgetCore(task).Forget();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ForgetCoreWithCatch(task, exceptionHandler, handleExceptionOnMainThread).Forget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniTask to UniTaskVoid
|
||||||
|
static async UniTaskVoid ForgetCore(UniTask2 task)
|
||||||
|
{
|
||||||
|
await task;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async UniTaskVoid ForgetCoreWithCatch(UniTask2 task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await task;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (handleExceptionOnMainThread)
|
||||||
|
{
|
||||||
|
await UniTask2.SwitchToMainThread();
|
||||||
|
}
|
||||||
|
exceptionHandler(ex);
|
||||||
|
}
|
||||||
|
catch (Exception ex2)
|
||||||
|
{
|
||||||
|
UniTaskScheduler.PublishUnobservedTaskException(ex2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Forget<T>(this UniTask2<T> task)
|
||||||
|
{
|
||||||
|
ForgetCore(task).Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Forget<T>(this UniTask2<T> task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread = true)
|
||||||
|
{
|
||||||
|
if (exceptionHandler == null)
|
||||||
|
{
|
||||||
|
ForgetCore(task).Forget();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ForgetCoreWithCatch(task, exceptionHandler, handleExceptionOnMainThread).Forget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniTask to UniTaskVoid
|
||||||
|
static async UniTaskVoid ForgetCore<T>(UniTask2<T> task)
|
||||||
|
{
|
||||||
|
await task;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async UniTaskVoid ForgetCoreWithCatch<T>(UniTask2<T> task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await task;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (handleExceptionOnMainThread)
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToMainThread();
|
||||||
|
}
|
||||||
|
exceptionHandler(ex);
|
||||||
|
}
|
||||||
|
catch (Exception ex2)
|
||||||
|
{
|
||||||
|
UniTaskScheduler.PublishUnobservedTaskException(ex2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async UniTask2 ContinueWith<T>(this UniTask2<T> task, Action<T> continuationFunction)
|
||||||
|
{
|
||||||
|
continuationFunction(await task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async UniTask2 ContinueWith<T>(this UniTask2<T> task, Func<T, UniTask2> continuationFunction)
|
||||||
|
{
|
||||||
|
await continuationFunction(await task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async UniTask2<TR> ContinueWith<T, TR>(this UniTask2<T> task, Func<T, TR> continuationFunction)
|
||||||
|
{
|
||||||
|
return continuationFunction(await task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async UniTask2<TR> ContinueWith<T, TR>(this UniTask2<T> task, Func<T, UniTask2<TR>> continuationFunction)
|
||||||
|
{
|
||||||
|
return await continuationFunction(await task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async UniTask2 ContinueWith(this UniTask2 task, Action continuationFunction)
|
||||||
|
{
|
||||||
|
await task;
|
||||||
|
continuationFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async UniTask2 ContinueWith(this UniTask2 task, Func<UniTask2> continuationFunction)
|
||||||
|
{
|
||||||
|
await task;
|
||||||
|
await continuationFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async UniTask2<T> ContinueWith<T>(this UniTask2 task, Func<T> continuationFunction)
|
||||||
|
{
|
||||||
|
await task;
|
||||||
|
return continuationFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async UniTask2<T> ContinueWith<T>(this UniTask2 task, Func<UniTask2<T>> continuationFunction)
|
||||||
|
{
|
||||||
|
await task;
|
||||||
|
return await continuationFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async UniTask2 ConfigureAwait(this Task task, PlayerLoopTiming timing)
|
||||||
|
{
|
||||||
|
await task.ConfigureAwait(false);
|
||||||
|
await UniTask2.Yield(timing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async UniTask2<T> ConfigureAwait<T>(this Task<T> task, PlayerLoopTiming timing)
|
||||||
|
{
|
||||||
|
var v = await task.ConfigureAwait(false);
|
||||||
|
await UniTask2.Yield(timing);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async UniTask2 ConfigureAwait(this UniTask2 task, PlayerLoopTiming timing)
|
||||||
|
{
|
||||||
|
await task;
|
||||||
|
await UniTask2.Yield(timing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async UniTask2<T> ConfigureAwait<T>(this UniTask2<T> task, PlayerLoopTiming timing)
|
||||||
|
{
|
||||||
|
var v = await task;
|
||||||
|
await UniTask2.Yield(timing);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async UniTask2<T> Unwrap<T>(this UniTask2<UniTask2<T>> task)
|
||||||
|
{
|
||||||
|
return await await task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async UniTask2 Unwrap<T>(this UniTask2<UniTask2> task)
|
||||||
|
{
|
||||||
|
await await task;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ToCoroutineEnumerator : IEnumerator
|
||||||
|
{
|
||||||
|
bool completed;
|
||||||
|
UniTask2 task;
|
||||||
|
Action<Exception> exceptionHandler = null;
|
||||||
|
bool isStarted = false;
|
||||||
|
ExceptionDispatchInfo exception;
|
||||||
|
|
||||||
|
public ToCoroutineEnumerator(UniTask2 task, Action<Exception> exceptionHandler)
|
||||||
|
{
|
||||||
|
completed = false;
|
||||||
|
this.exceptionHandler = exceptionHandler;
|
||||||
|
this.task = task;
|
||||||
|
}
|
||||||
|
|
||||||
|
async UniTaskVoid RunTask(UniTask2 task)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await task;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (exceptionHandler != null)
|
||||||
|
{
|
||||||
|
exceptionHandler(ex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.exception = ExceptionDispatchInfo.Capture(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
completed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Current => null;
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (!isStarted)
|
||||||
|
{
|
||||||
|
isStarted = true;
|
||||||
|
RunTask(task).Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception != null)
|
||||||
|
{
|
||||||
|
// throw exception on iterator (main)thread.
|
||||||
|
// unfortunately unity test-runner can not handle throw exception on hand-write IEnumerator.MoveNext.
|
||||||
|
UnityEngine.Debug.LogException(exception.SourceException);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ToCoroutineEnumerator<T> : IEnumerator
|
||||||
|
{
|
||||||
|
bool completed;
|
||||||
|
Action<T> resultHandler = null;
|
||||||
|
Action<Exception> exceptionHandler = null;
|
||||||
|
bool isStarted = false;
|
||||||
|
UniTask2<T> task;
|
||||||
|
object current = null;
|
||||||
|
ExceptionDispatchInfo exception;
|
||||||
|
|
||||||
|
public ToCoroutineEnumerator(UniTask2<T> task, Action<T> resultHandler, Action<Exception> exceptionHandler)
|
||||||
|
{
|
||||||
|
completed = false;
|
||||||
|
this.task = task;
|
||||||
|
this.resultHandler = resultHandler;
|
||||||
|
this.exceptionHandler = exceptionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
async UniTaskVoid RunTask(UniTask2<T> task)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var value = await task;
|
||||||
|
current = value; // boxed if T is struct...
|
||||||
|
if (resultHandler != null)
|
||||||
|
{
|
||||||
|
resultHandler(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (exceptionHandler != null)
|
||||||
|
{
|
||||||
|
exceptionHandler(ex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.exception = ExceptionDispatchInfo.Capture(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
completed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Current => current;
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (!isStarted)
|
||||||
|
{
|
||||||
|
isStarted = true;
|
||||||
|
RunTask(task).Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception != null)
|
||||||
|
{
|
||||||
|
// throw exception on iterator (main)thread.
|
||||||
|
// unfortunately unity test-runner can not handle throw exception on hand-write IEnumerator.MoveNext.
|
||||||
|
UnityEngine.Debug.LogException(exception.SourceException);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:remove
|
||||||
public static partial class UniTaskExtensions
|
public static partial class UniTaskExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -573,7 +573,7 @@ namespace UniRx.Async
|
||||||
{
|
{
|
||||||
// TODO:Remove Tracking
|
// TODO:Remove Tracking
|
||||||
// TaskTracker.RemoveTracking();
|
// TaskTracker.RemoveTracking();
|
||||||
core.SetCancellation(cancellationToken);
|
core.SetCanceled(cancellationToken);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue