complete delay and factory

master
Yoshifumi Kawai 2020-04-19 02:55:44 +09:00
parent 11bc28b33f
commit ca58784c4d
7 changed files with 1390 additions and 136 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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();

View File

@ -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);
}
}
} }
} }

View File

@ -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);
} }

View File

@ -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>

View File

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