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.
380 lines
10 KiB
380 lines
10 KiB
#pragma warning disable CS1591 |
|
|
|
using Cysharp.Threading.Tasks.Internal; |
|
using System; |
|
using System.Linq; |
|
using System.Diagnostics; |
|
using System.Runtime.CompilerServices; |
|
|
|
namespace Cysharp.Threading.Tasks.CompilerServices |
|
{ |
|
// #ENABLE_IL2CPP in this file is to avoid bug of IL2CPP VM. |
|
// Issue is tracked on https://issuetracker.unity3d.com/issues/il2cpp-incorrect-results-when-calling-a-method-from-outside-class-in-a-struct |
|
// but currently it is labeled `Won't Fix`. |
|
|
|
internal interface IStateMachineRunner |
|
{ |
|
Action MoveNext { get; } |
|
void Return(); |
|
|
|
#if ENABLE_IL2CPP |
|
Action ReturnAction { get; } |
|
#endif |
|
} |
|
|
|
internal interface IStateMachineRunnerPromise : IUniTaskSource |
|
{ |
|
Action MoveNext { get; } |
|
UniTask Task { get; } |
|
void SetResult(); |
|
void SetException(Exception exception); |
|
} |
|
|
|
internal interface IStateMachineRunnerPromise<T> : IUniTaskSource<T> |
|
{ |
|
Action MoveNext { get; } |
|
UniTask<T> Task { get; } |
|
void SetResult(T result); |
|
void SetException(Exception exception); |
|
} |
|
|
|
internal static class StateMachineUtility |
|
{ |
|
// Get AsyncStateMachine internal state to check IL2CPP bug |
|
public static int GetState(IAsyncStateMachine stateMachine) |
|
{ |
|
var info = stateMachine.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) |
|
.First(x => x.Name.EndsWith("__state")); |
|
return (int)info.GetValue(stateMachine); |
|
} |
|
} |
|
|
|
internal sealed class AsyncUniTaskVoid<TStateMachine> : IStateMachineRunner, ITaskPoolNode<AsyncUniTaskVoid<TStateMachine>>, IUniTaskSource |
|
where TStateMachine : IAsyncStateMachine |
|
{ |
|
static TaskPool<AsyncUniTaskVoid<TStateMachine>> pool; |
|
|
|
#if ENABLE_IL2CPP |
|
public Action ReturnAction { get; } |
|
#endif |
|
|
|
TStateMachine stateMachine; |
|
|
|
public Action MoveNext { get; } |
|
|
|
public AsyncUniTaskVoid() |
|
{ |
|
MoveNext = Run; |
|
#if ENABLE_IL2CPP |
|
ReturnAction = Return; |
|
#endif |
|
} |
|
|
|
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunner runnerFieldRef) |
|
{ |
|
if (!pool.TryPop(out var result)) |
|
{ |
|
result = new AsyncUniTaskVoid<TStateMachine>(); |
|
} |
|
TaskTracker.TrackActiveTask(result, 3); |
|
|
|
runnerFieldRef = result; // set runner before copied. |
|
result.stateMachine = stateMachine; // copy struct StateMachine(in release build). |
|
} |
|
|
|
static AsyncUniTaskVoid() |
|
{ |
|
TaskPool.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size); |
|
} |
|
|
|
AsyncUniTaskVoid<TStateMachine> nextNode; |
|
public ref AsyncUniTaskVoid<TStateMachine> NextNode => ref nextNode; |
|
|
|
public void Return() |
|
{ |
|
TaskTracker.RemoveTracking(this); |
|
stateMachine = default; |
|
pool.TryPush(this); |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
void Run() |
|
{ |
|
stateMachine.MoveNext(); |
|
} |
|
|
|
// dummy interface implementation for TaskTracker. |
|
|
|
UniTaskStatus IUniTaskSource.GetStatus(short token) |
|
{ |
|
return UniTaskStatus.Pending; |
|
} |
|
|
|
UniTaskStatus IUniTaskSource.UnsafeGetStatus() |
|
{ |
|
return UniTaskStatus.Pending; |
|
} |
|
|
|
void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token) |
|
{ |
|
} |
|
|
|
void IUniTaskSource.GetResult(short token) |
|
{ |
|
} |
|
} |
|
|
|
internal sealed class AsyncUniTask<TStateMachine> : IStateMachineRunnerPromise, IUniTaskSource, ITaskPoolNode<AsyncUniTask<TStateMachine>> |
|
where TStateMachine : IAsyncStateMachine |
|
{ |
|
static TaskPool<AsyncUniTask<TStateMachine>> pool; |
|
|
|
#if ENABLE_IL2CPP |
|
readonly Action returnDelegate; |
|
#endif |
|
public Action MoveNext { get; } |
|
|
|
TStateMachine stateMachine; |
|
UniTaskCompletionSourceCore<AsyncUnit> core; |
|
|
|
AsyncUniTask() |
|
{ |
|
MoveNext = Run; |
|
#if ENABLE_IL2CPP |
|
returnDelegate = Return; |
|
#endif |
|
} |
|
|
|
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise runnerPromiseFieldRef) |
|
{ |
|
if (!pool.TryPop(out var result)) |
|
{ |
|
result = new AsyncUniTask<TStateMachine>(); |
|
} |
|
TaskTracker.TrackActiveTask(result, 3); |
|
|
|
runnerPromiseFieldRef = result; // set runner before copied. |
|
result.stateMachine = stateMachine; // copy struct StateMachine(in release build). |
|
} |
|
|
|
AsyncUniTask<TStateMachine> nextNode; |
|
public ref AsyncUniTask<TStateMachine> NextNode => ref nextNode; |
|
|
|
static AsyncUniTask() |
|
{ |
|
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine>), () => pool.Size); |
|
} |
|
|
|
void Return() |
|
{ |
|
TaskTracker.RemoveTracking(this); |
|
core.Reset(); |
|
stateMachine = default; |
|
pool.TryPush(this); |
|
} |
|
|
|
bool TryReturn() |
|
{ |
|
TaskTracker.RemoveTracking(this); |
|
core.Reset(); |
|
stateMachine = default; |
|
return pool.TryPush(this); |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
void Run() |
|
{ |
|
stateMachine.MoveNext(); |
|
} |
|
|
|
public UniTask Task |
|
{ |
|
[DebuggerHidden] |
|
get |
|
{ |
|
return new UniTask(this, core.Version); |
|
} |
|
} |
|
|
|
[DebuggerHidden] |
|
public void SetResult() |
|
{ |
|
core.TrySetResult(AsyncUnit.Default); |
|
} |
|
|
|
[DebuggerHidden] |
|
public void SetException(Exception exception) |
|
{ |
|
core.TrySetException(exception); |
|
} |
|
|
|
[DebuggerHidden] |
|
public void GetResult(short token) |
|
{ |
|
try |
|
{ |
|
core.GetResult(token); |
|
} |
|
finally |
|
{ |
|
#if ENABLE_IL2CPP |
|
// workaround for IL2CPP bug. |
|
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate); |
|
#else |
|
TryReturn(); |
|
#endif |
|
} |
|
} |
|
|
|
[DebuggerHidden] |
|
public UniTaskStatus GetStatus(short token) |
|
{ |
|
return core.GetStatus(token); |
|
} |
|
|
|
[DebuggerHidden] |
|
public UniTaskStatus UnsafeGetStatus() |
|
{ |
|
return core.UnsafeGetStatus(); |
|
} |
|
|
|
[DebuggerHidden] |
|
public void OnCompleted(Action<object> continuation, object state, short token) |
|
{ |
|
core.OnCompleted(continuation, state, token); |
|
} |
|
} |
|
|
|
internal sealed class AsyncUniTask<TStateMachine, T> : IStateMachineRunnerPromise<T>, IUniTaskSource<T>, ITaskPoolNode<AsyncUniTask<TStateMachine, T>> |
|
where TStateMachine : IAsyncStateMachine |
|
{ |
|
static TaskPool<AsyncUniTask<TStateMachine, T>> pool; |
|
|
|
#if ENABLE_IL2CPP |
|
readonly Action returnDelegate; |
|
#endif |
|
|
|
public Action MoveNext { get; } |
|
|
|
TStateMachine stateMachine; |
|
UniTaskCompletionSourceCore<T> core; |
|
|
|
AsyncUniTask() |
|
{ |
|
MoveNext = Run; |
|
#if ENABLE_IL2CPP |
|
returnDelegate = Return; |
|
#endif |
|
} |
|
|
|
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise<T> runnerPromiseFieldRef) |
|
{ |
|
if (!pool.TryPop(out var result)) |
|
{ |
|
result = new AsyncUniTask<TStateMachine, T>(); |
|
} |
|
TaskTracker.TrackActiveTask(result, 3); |
|
|
|
runnerPromiseFieldRef = result; // set runner before copied. |
|
result.stateMachine = stateMachine; // copy struct StateMachine(in release build). |
|
} |
|
|
|
AsyncUniTask<TStateMachine, T> nextNode; |
|
public ref AsyncUniTask<TStateMachine, T> NextNode => ref nextNode; |
|
|
|
static AsyncUniTask() |
|
{ |
|
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine, T>), () => pool.Size); |
|
} |
|
|
|
void Return() |
|
{ |
|
TaskTracker.RemoveTracking(this); |
|
core.Reset(); |
|
stateMachine = default; |
|
pool.TryPush(this); |
|
} |
|
|
|
bool TryReturn() |
|
{ |
|
TaskTracker.RemoveTracking(this); |
|
core.Reset(); |
|
stateMachine = default; |
|
return pool.TryPush(this); |
|
} |
|
|
|
[DebuggerHidden] |
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
void Run() |
|
{ |
|
// UnityEngine.Debug.Log($"MoveNext State:" + StateMachineUtility.GetState(stateMachine)); |
|
stateMachine.MoveNext(); |
|
} |
|
|
|
public UniTask<T> Task |
|
{ |
|
[DebuggerHidden] |
|
get |
|
{ |
|
return new UniTask<T>(this, core.Version); |
|
} |
|
} |
|
|
|
[DebuggerHidden] |
|
public void SetResult(T result) |
|
{ |
|
core.TrySetResult(result); |
|
} |
|
|
|
[DebuggerHidden] |
|
public void SetException(Exception exception) |
|
{ |
|
core.TrySetException(exception); |
|
} |
|
|
|
[DebuggerHidden] |
|
public T GetResult(short token) |
|
{ |
|
try |
|
{ |
|
return core.GetResult(token); |
|
} |
|
finally |
|
{ |
|
#if ENABLE_IL2CPP |
|
// workaround for IL2CPP bug. |
|
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate); |
|
#else |
|
TryReturn(); |
|
#endif |
|
} |
|
} |
|
|
|
[DebuggerHidden] |
|
void IUniTaskSource.GetResult(short token) |
|
{ |
|
GetResult(token); |
|
} |
|
|
|
[DebuggerHidden] |
|
public UniTaskStatus GetStatus(short token) |
|
{ |
|
return core.GetStatus(token); |
|
} |
|
|
|
[DebuggerHidden] |
|
public UniTaskStatus UnsafeGetStatus() |
|
{ |
|
return core.UnsafeGetStatus(); |
|
} |
|
|
|
[DebuggerHidden] |
|
public void OnCompleted(Action<object> continuation, object state, short token) |
|
{ |
|
core.OnCompleted(continuation, state, token); |
|
} |
|
} |
|
} |
|
|
|
|