You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
707 lines
20 KiB
707 lines
20 KiB
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member |
|
#pragma warning disable CS0436 |
|
|
|
using Cysharp.Threading.Tasks.CompilerServices; |
|
using System; |
|
using System.Diagnostics; |
|
using System.Runtime.CompilerServices; |
|
using System.Runtime.ExceptionServices; |
|
using System.Runtime.InteropServices; |
|
|
|
namespace Cysharp.Threading.Tasks |
|
{ |
|
internal static class AwaiterActions |
|
{ |
|
internal static readonly Action<object> InvokeContinuationDelegate = Continuation; |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
static void Continuation(object state) |
|
{ |
|
((Action)state).Invoke(); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Lightweight unity specified task-like object. |
|
/// </summary> |
|
[AsyncMethodBuilder(typeof(AsyncUniTaskMethodBuilder))] |
|
[StructLayout(LayoutKind.Auto)] |
|
public readonly partial struct UniTask |
|
{ |
|
readonly IUniTaskSource source; |
|
readonly short token; |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public UniTask(IUniTaskSource source, short token) |
|
{ |
|
this.source = source; |
|
this.token = token; |
|
} |
|
|
|
public UniTaskStatus Status |
|
{ |
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
get |
|
{ |
|
if (source == null) return UniTaskStatus.Succeeded; |
|
return source.GetStatus(token); |
|
} |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public Awaiter GetAwaiter() |
|
{ |
|
return new Awaiter(this); |
|
} |
|
|
|
/// <summary> |
|
/// returns (bool IsCanceled) instead of throws OperationCanceledException. |
|
/// </summary> |
|
public UniTask<bool> SuppressCancellationThrow() |
|
{ |
|
var status = Status; |
|
if (status == UniTaskStatus.Succeeded) return CompletedTasks.False; |
|
if (status == UniTaskStatus.Canceled) return CompletedTasks.True; |
|
return new UniTask<bool>(new IsCanceledSource(source), token); |
|
} |
|
|
|
#if !UNITY_2018_3_OR_NEWER |
|
|
|
public static implicit operator System.Threading.Tasks.ValueTask(in UniTask self) |
|
{ |
|
if (self.source == null) |
|
{ |
|
return default; |
|
} |
|
|
|
#if NETSTANDARD2_0 |
|
return self.AsValueTask(); |
|
#else |
|
return new System.Threading.Tasks.ValueTask(self.source, self.token); |
|
#endif |
|
} |
|
|
|
#endif |
|
|
|
public override string ToString() |
|
{ |
|
if (source == null) return "()"; |
|
return "(" + source.UnsafeGetStatus() + ")"; |
|
} |
|
|
|
/// <summary> |
|
/// Memoizing inner IValueTaskSource. The result UniTask can await multiple. |
|
/// </summary> |
|
public UniTask Preserve() |
|
{ |
|
if (source == null) |
|
{ |
|
return this; |
|
} |
|
else |
|
{ |
|
return new UniTask(new MemoizeSource(source), token); |
|
} |
|
} |
|
|
|
public UniTask<AsyncUnit> AsAsyncUnitUniTask() |
|
{ |
|
if (this.source == null) return CompletedTasks.AsyncUnit; |
|
|
|
var status = this.source.GetStatus(this.token); |
|
if (status.IsCompletedSuccessfully()) |
|
{ |
|
this.source.GetResult(this.token); |
|
return CompletedTasks.AsyncUnit; |
|
} |
|
else if(this.source is IUniTaskSource<AsyncUnit> asyncUnitSource) |
|
{ |
|
return new UniTask<AsyncUnit>(asyncUnitSource, this.token); |
|
} |
|
|
|
return new UniTask<AsyncUnit>(new AsyncUnitSource(this.source), this.token); |
|
} |
|
|
|
sealed class AsyncUnitSource : IUniTaskSource<AsyncUnit> |
|
{ |
|
readonly IUniTaskSource source; |
|
|
|
public AsyncUnitSource(IUniTaskSource source) |
|
{ |
|
this.source = source; |
|
} |
|
|
|
public AsyncUnit GetResult(short token) |
|
{ |
|
source.GetResult(token); |
|
return AsyncUnit.Default; |
|
} |
|
|
|
public UniTaskStatus GetStatus(short token) |
|
{ |
|
return source.GetStatus(token); |
|
} |
|
|
|
public void OnCompleted(Action<object> continuation, object state, short token) |
|
{ |
|
source.OnCompleted(continuation, state, token); |
|
} |
|
|
|
public UniTaskStatus UnsafeGetStatus() |
|
{ |
|
return source.UnsafeGetStatus(); |
|
} |
|
|
|
void IUniTaskSource.GetResult(short token) |
|
{ |
|
GetResult(token); |
|
} |
|
} |
|
|
|
sealed class IsCanceledSource : IUniTaskSource<bool> |
|
{ |
|
readonly IUniTaskSource source; |
|
|
|
public IsCanceledSource(IUniTaskSource source) |
|
{ |
|
this.source = source; |
|
} |
|
|
|
public bool GetResult(short token) |
|
{ |
|
if (source.GetStatus(token) == UniTaskStatus.Canceled) |
|
{ |
|
return true; |
|
} |
|
|
|
source.GetResult(token); |
|
return false; |
|
} |
|
|
|
void IUniTaskSource.GetResult(short token) |
|
{ |
|
GetResult(token); |
|
} |
|
|
|
public UniTaskStatus GetStatus(short token) |
|
{ |
|
return source.GetStatus(token); |
|
} |
|
|
|
public UniTaskStatus UnsafeGetStatus() |
|
{ |
|
return source.UnsafeGetStatus(); |
|
} |
|
|
|
public void OnCompleted(Action<object> continuation, object state, short token) |
|
{ |
|
source.OnCompleted(continuation, state, token); |
|
} |
|
} |
|
|
|
sealed class MemoizeSource : IUniTaskSource |
|
{ |
|
IUniTaskSource source; |
|
ExceptionDispatchInfo exception; |
|
UniTaskStatus 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 = UniTaskStatus.Succeeded; |
|
} |
|
catch (Exception ex) |
|
{ |
|
exception = ExceptionDispatchInfo.Capture(ex); |
|
if (ex is OperationCanceledException) |
|
{ |
|
status = UniTaskStatus.Canceled; |
|
} |
|
else |
|
{ |
|
status = UniTaskStatus.Faulted; |
|
} |
|
throw; |
|
} |
|
finally |
|
{ |
|
source = null; |
|
} |
|
} |
|
} |
|
|
|
public UniTaskStatus 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 UniTaskStatus UnsafeGetStatus() |
|
{ |
|
if (source == null) |
|
{ |
|
return status; |
|
} |
|
|
|
return source.UnsafeGetStatus(); |
|
} |
|
} |
|
|
|
public readonly struct Awaiter : ICriticalNotifyCompletion |
|
{ |
|
readonly UniTask task; |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public Awaiter(in UniTask task) |
|
{ |
|
this.task = task; |
|
} |
|
|
|
public bool IsCompleted |
|
{ |
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
get |
|
{ |
|
return task.Status.IsCompleted(); |
|
} |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public void GetResult() |
|
{ |
|
if (task.source == null) return; |
|
task.source.GetResult(task.token); |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public void OnCompleted(Action continuation) |
|
{ |
|
if (task.source == null) |
|
{ |
|
continuation(); |
|
} |
|
else |
|
{ |
|
task.source.OnCompleted(AwaiterActions.InvokeContinuationDelegate, continuation, task.token); |
|
} |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public void UnsafeOnCompleted(Action continuation) |
|
{ |
|
if (task.source == null) |
|
{ |
|
continuation(); |
|
} |
|
else |
|
{ |
|
task.source.OnCompleted(AwaiterActions.InvokeContinuationDelegate, continuation, task.token); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// If register manually continuation, you can use it instead of for compiler OnCompleted methods. |
|
/// </summary> |
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public void SourceOnCompleted(Action<object> continuation, object state) |
|
{ |
|
if (task.source == null) |
|
{ |
|
continuation(state); |
|
} |
|
else |
|
{ |
|
task.source.OnCompleted(continuation, state, task.token); |
|
} |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Lightweight unity specified task-like object. |
|
/// </summary> |
|
[AsyncMethodBuilder(typeof(AsyncUniTaskMethodBuilder<>))] |
|
[StructLayout(LayoutKind.Auto)] |
|
public readonly struct UniTask<T> |
|
{ |
|
readonly IUniTaskSource<T> source; |
|
readonly T result; |
|
readonly short token; |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public UniTask(T result) |
|
{ |
|
this.source = default; |
|
this.token = default; |
|
this.result = result; |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public UniTask(IUniTaskSource<T> source, short token) |
|
{ |
|
this.source = source; |
|
this.token = token; |
|
this.result = default; |
|
} |
|
|
|
public UniTaskStatus Status |
|
{ |
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
get |
|
{ |
|
return (source == null) ? UniTaskStatus.Succeeded : source.GetStatus(token); |
|
} |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public Awaiter GetAwaiter() |
|
{ |
|
return new Awaiter(this); |
|
} |
|
|
|
/// <summary> |
|
/// Memoizing inner IValueTaskSource. The result UniTask can await multiple. |
|
/// </summary> |
|
public UniTask<T> Preserve() |
|
{ |
|
if (source == null) |
|
{ |
|
return this; |
|
} |
|
else |
|
{ |
|
return new UniTask<T>(new MemoizeSource(source), token); |
|
} |
|
} |
|
|
|
public UniTask AsUniTask() |
|
{ |
|
if (this.source == null) return UniTask.CompletedTask; |
|
|
|
var status = this.source.GetStatus(this.token); |
|
if (status.IsCompletedSuccessfully()) |
|
{ |
|
this.source.GetResult(this.token); |
|
return UniTask.CompletedTask; |
|
} |
|
|
|
// Converting UniTask<T> -> UniTask is zero overhead. |
|
return new UniTask(this.source, this.token); |
|
} |
|
|
|
public static implicit operator UniTask(UniTask<T> self) |
|
{ |
|
return self.AsUniTask(); |
|
} |
|
|
|
#if !UNITY_2018_3_OR_NEWER |
|
|
|
public static implicit operator System.Threading.Tasks.ValueTask<T>(in UniTask<T> self) |
|
{ |
|
if (self.source == null) |
|
{ |
|
return new System.Threading.Tasks.ValueTask<T>(self.result); |
|
} |
|
|
|
#if NETSTANDARD2_0 |
|
return self.AsValueTask(); |
|
#else |
|
return new System.Threading.Tasks.ValueTask<T>(self.source, self.token); |
|
#endif |
|
} |
|
|
|
#endif |
|
|
|
/// <summary> |
|
/// returns (bool IsCanceled, T Result) instead of throws OperationCanceledException. |
|
/// </summary> |
|
public UniTask<(bool IsCanceled, T Result)> SuppressCancellationThrow() |
|
{ |
|
if (source == null) |
|
{ |
|
return new UniTask<(bool IsCanceled, T Result)>((false, result)); |
|
} |
|
|
|
return new UniTask<(bool, T)>(new IsCanceledSource(source), token); |
|
} |
|
|
|
public override string ToString() |
|
{ |
|
return (this.source == null) ? result?.ToString() |
|
: "(" + this.source.UnsafeGetStatus() + ")"; |
|
} |
|
|
|
sealed class IsCanceledSource : IUniTaskSource<(bool, T)> |
|
{ |
|
readonly IUniTaskSource<T> source; |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public IsCanceledSource(IUniTaskSource<T> source) |
|
{ |
|
this.source = source; |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public (bool, T) GetResult(short token) |
|
{ |
|
if (source.GetStatus(token) == UniTaskStatus.Canceled) |
|
{ |
|
return (true, default); |
|
} |
|
|
|
var result = source.GetResult(token); |
|
return (false, result); |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
void IUniTaskSource.GetResult(short token) |
|
{ |
|
GetResult(token); |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public UniTaskStatus GetStatus(short token) |
|
{ |
|
return source.GetStatus(token); |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public UniTaskStatus UnsafeGetStatus() |
|
{ |
|
return source.UnsafeGetStatus(); |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public void OnCompleted(Action<object> continuation, object state, short token) |
|
{ |
|
source.OnCompleted(continuation, state, token); |
|
} |
|
} |
|
|
|
sealed class MemoizeSource : IUniTaskSource<T> |
|
{ |
|
IUniTaskSource<T> source; |
|
T result; |
|
ExceptionDispatchInfo exception; |
|
UniTaskStatus 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 = UniTaskStatus.Succeeded; |
|
return result; |
|
} |
|
catch (Exception ex) |
|
{ |
|
exception = ExceptionDispatchInfo.Capture(ex); |
|
if (ex is OperationCanceledException) |
|
{ |
|
status = UniTaskStatus.Canceled; |
|
} |
|
else |
|
{ |
|
status = UniTaskStatus.Faulted; |
|
} |
|
throw; |
|
} |
|
finally |
|
{ |
|
source = null; |
|
} |
|
} |
|
} |
|
|
|
void IUniTaskSource.GetResult(short token) |
|
{ |
|
GetResult(token); |
|
} |
|
|
|
public UniTaskStatus 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 UniTaskStatus UnsafeGetStatus() |
|
{ |
|
if (source == null) |
|
{ |
|
return status; |
|
} |
|
|
|
return source.UnsafeGetStatus(); |
|
} |
|
} |
|
|
|
public readonly struct Awaiter : ICriticalNotifyCompletion |
|
{ |
|
readonly UniTask<T> task; |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public Awaiter(in UniTask<T> task) |
|
{ |
|
this.task = task; |
|
} |
|
|
|
public bool IsCompleted |
|
{ |
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
get |
|
{ |
|
return task.Status.IsCompleted(); |
|
} |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public T GetResult() |
|
{ |
|
var s = task.source; |
|
if (s == null) |
|
{ |
|
return task.result; |
|
} |
|
else |
|
{ |
|
return s.GetResult(task.token); |
|
} |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public void OnCompleted(Action continuation) |
|
{ |
|
var s = task.source; |
|
if (s == null) |
|
{ |
|
continuation(); |
|
} |
|
else |
|
{ |
|
s.OnCompleted(AwaiterActions.InvokeContinuationDelegate, continuation, task.token); |
|
} |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public void UnsafeOnCompleted(Action continuation) |
|
{ |
|
var s = task.source; |
|
if (s == null) |
|
{ |
|
continuation(); |
|
} |
|
else |
|
{ |
|
s.OnCompleted(AwaiterActions.InvokeContinuationDelegate, continuation, task.token); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// If register manually continuation, you can use it instead of for compiler OnCompleted methods. |
|
/// </summary> |
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
public void SourceOnCompleted(Action<object> continuation, object state) |
|
{ |
|
var s = task.source; |
|
if (s == null) |
|
{ |
|
continuation(state); |
|
} |
|
else |
|
{ |
|
s.OnCompleted(continuation, state, task.token); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|