mirror of
https://github.com/Cysharp/UniTask.git
synced 2026-05-18 21:20:10 +00:00
complete infrastructure
This commit is contained in:
@@ -5,10 +5,12 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace UniRx.Async.CompilerServices
|
||||
{
|
||||
// TODO:Remove
|
||||
public struct AsyncUniTaskMethodBuilder
|
||||
{
|
||||
UniTaskCompletionSource promise;
|
||||
@@ -138,7 +140,7 @@ namespace UniRx.Async.CompilerServices
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO:Remove
|
||||
public struct AsyncUniTaskMethodBuilder<T>
|
||||
{
|
||||
T result;
|
||||
@@ -269,6 +271,247 @@ namespace UniRx.Async.CompilerServices
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct AsyncUniTask2MethodBuilder
|
||||
{
|
||||
// cache items.
|
||||
AutoResetUniTaskCompletionSource promise;
|
||||
IMoveNextRunner runner;
|
||||
|
||||
// 1. Static Create method.
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static AsyncUniTask2MethodBuilder Create()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
// 2. TaskLike Task property.
|
||||
public UniTask2 Task
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if (promise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource.Create();
|
||||
}
|
||||
return promise.Task;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. SetException
|
||||
[DebuggerHidden]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
// runner is finished, return first.
|
||||
if (runner != null)
|
||||
{
|
||||
runner.Return();
|
||||
runner = null;
|
||||
}
|
||||
|
||||
if (promise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource.Create();
|
||||
}
|
||||
promise.SetException(exception);
|
||||
}
|
||||
|
||||
// 4. SetResult
|
||||
[DebuggerHidden]
|
||||
public void SetResult()
|
||||
{
|
||||
// runner is finished, return first.
|
||||
if (runner != null)
|
||||
{
|
||||
runner.Return();
|
||||
runner = null;
|
||||
}
|
||||
|
||||
if (promise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource.Create();
|
||||
}
|
||||
promise.SetResult();
|
||||
}
|
||||
|
||||
// 5. AwaitOnCompleted
|
||||
[DebuggerHidden]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (promise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource.Create();
|
||||
}
|
||||
if (runner == null)
|
||||
{
|
||||
runner = MoveNextRunner2<TStateMachine>.Create(ref stateMachine);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
}
|
||||
|
||||
// 6. AwaitUnsafeOnCompleted
|
||||
[DebuggerHidden]
|
||||
[SecuritySafeCritical]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (promise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource.Create();
|
||||
}
|
||||
if (runner == null)
|
||||
{
|
||||
runner = MoveNextRunner2<TStateMachine>.Create(ref stateMachine);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
}
|
||||
|
||||
// 7. Start
|
||||
[DebuggerHidden]
|
||||
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
// 8. SetStateMachine
|
||||
[DebuggerHidden]
|
||||
public void SetStateMachine(IAsyncStateMachine stateMachine)
|
||||
{
|
||||
// don't use boxed stateMachine.
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct AsyncUniTask2MethodBuilder<T>
|
||||
{
|
||||
// cache items.
|
||||
AutoResetUniTaskCompletionSource<T> promise;
|
||||
IMoveNextRunner runner;
|
||||
|
||||
// 1. Static Create method.
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static AsyncUniTask2MethodBuilder<T> Create()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
// 2. TaskLike Task property.
|
||||
[DebuggerHidden]
|
||||
public UniTask2<T> Task
|
||||
{
|
||||
get
|
||||
{
|
||||
if (promise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource<T>.Create();
|
||||
}
|
||||
return promise.Task;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. SetException
|
||||
[DebuggerHidden]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
// runner is finished, return first.
|
||||
if (runner != null)
|
||||
{
|
||||
runner.Return();
|
||||
runner = null;
|
||||
}
|
||||
|
||||
if (promise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource<T>.Create();
|
||||
}
|
||||
promise.SetException(exception);
|
||||
}
|
||||
|
||||
// 4. SetResult
|
||||
[DebuggerHidden]
|
||||
public void SetResult(T result)
|
||||
{
|
||||
// runner is finished, return first.
|
||||
if (runner != null)
|
||||
{
|
||||
runner.Return();
|
||||
runner = null;
|
||||
}
|
||||
|
||||
if (promise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource<T>.Create();
|
||||
}
|
||||
promise.SetResult(result);
|
||||
}
|
||||
|
||||
// 5. AwaitOnCompleted
|
||||
[DebuggerHidden]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (promise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource<T>.Create();
|
||||
}
|
||||
if (runner == null)
|
||||
{
|
||||
runner = MoveNextRunner2<TStateMachine>.Create(ref stateMachine);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
}
|
||||
|
||||
// 6. AwaitUnsafeOnCompleted
|
||||
[DebuggerHidden]
|
||||
[SecuritySafeCritical]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (promise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource<T>.Create();
|
||||
}
|
||||
if (runner == null)
|
||||
{
|
||||
runner = MoveNextRunner2<TStateMachine>.Create(ref stateMachine);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
}
|
||||
|
||||
// 7. Start
|
||||
[DebuggerHidden]
|
||||
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
// 8. SetStateMachine
|
||||
[DebuggerHidden]
|
||||
public void SetStateMachine(IAsyncStateMachine stateMachine)
|
||||
{
|
||||
// don't use boxed stateMachine.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -11,23 +11,38 @@ namespace UniRx.Async.CompilerServices
|
||||
{
|
||||
public struct AsyncUniTaskVoidMethodBuilder
|
||||
{
|
||||
Action moveNext;
|
||||
IMoveNextRunner runner;
|
||||
|
||||
// 1. Static Create method.
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static AsyncUniTaskVoidMethodBuilder Create()
|
||||
{
|
||||
var builder = new AsyncUniTaskVoidMethodBuilder();
|
||||
return builder;
|
||||
return default;
|
||||
}
|
||||
|
||||
// 2. TaskLike Task property(void)
|
||||
public UniTaskVoid Task => default(UniTaskVoid);
|
||||
public UniTaskVoid Task
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. SetException
|
||||
[DebuggerHidden]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
// runner is finished, return first.
|
||||
if (runner != null)
|
||||
{
|
||||
runner.Return();
|
||||
runner = null;
|
||||
}
|
||||
|
||||
UniTaskScheduler.PublishUnobservedTaskException(exception);
|
||||
}
|
||||
|
||||
@@ -35,7 +50,12 @@ namespace UniRx.Async.CompilerServices
|
||||
[DebuggerHidden]
|
||||
public void SetResult()
|
||||
{
|
||||
// do nothing
|
||||
// runner is finished, return.
|
||||
if (runner != null)
|
||||
{
|
||||
runner.Return();
|
||||
runner = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. AwaitOnCompleted
|
||||
@@ -44,14 +64,12 @@ namespace UniRx.Async.CompilerServices
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (moveNext == null)
|
||||
if (runner == null)
|
||||
{
|
||||
var runner = new MoveNextRunner<TStateMachine>();
|
||||
moveNext = runner.Run;
|
||||
runner.StateMachine = stateMachine; // set after create delegate.
|
||||
runner = MoveNextRunner2<TStateMachine>.Create(ref stateMachine);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(moveNext);
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
}
|
||||
|
||||
// 6. AwaitUnsafeOnCompleted
|
||||
@@ -61,14 +79,12 @@ namespace UniRx.Async.CompilerServices
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (moveNext == null)
|
||||
if (runner == null)
|
||||
{
|
||||
var runner = new MoveNextRunner<TStateMachine>();
|
||||
moveNext = runner.Run;
|
||||
runner.StateMachine = stateMachine; // set after create delegate.
|
||||
runner = MoveNextRunner2<TStateMachine>.Create(ref stateMachine);
|
||||
}
|
||||
|
||||
awaiter.UnsafeOnCompleted(moveNext);
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
}
|
||||
|
||||
// 7. Start
|
||||
@@ -83,6 +99,7 @@ namespace UniRx.Async.CompilerServices
|
||||
[DebuggerHidden]
|
||||
public void SetStateMachine(IAsyncStateMachine stateMachine)
|
||||
{
|
||||
// don't use boxed stateMachine.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UniRx.Async.Internal;
|
||||
|
||||
namespace UniRx.Async.CompilerServices
|
||||
{
|
||||
// TODO: Remove it.
|
||||
internal class MoveNextRunner<TStateMachine>
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
@@ -18,6 +21,52 @@ namespace UniRx.Async.CompilerServices
|
||||
StateMachine.MoveNext();
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IMoveNextRunner
|
||||
{
|
||||
Action CallMoveNext { get; }
|
||||
void Return();
|
||||
}
|
||||
|
||||
internal class MoveNextRunner2<TStateMachine> : IMoveNextRunner, IPromisePoolItem
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
static PromisePool<MoveNextRunner2<TStateMachine>> pool = new PromisePool<MoveNextRunner2<TStateMachine>>();
|
||||
|
||||
TStateMachine stateMachine;
|
||||
internal readonly Action callMoveNext;
|
||||
|
||||
public Action CallMoveNext => callMoveNext;
|
||||
|
||||
MoveNextRunner2()
|
||||
{
|
||||
callMoveNext = MoveNext;
|
||||
}
|
||||
|
||||
public static MoveNextRunner2<TStateMachine> Create(ref TStateMachine stateMachine)
|
||||
{
|
||||
var result = pool.TryRent() ?? new MoveNextRunner2<TStateMachine>();
|
||||
result.stateMachine = stateMachine;
|
||||
return result;
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void MoveNext()
|
||||
{
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
public void Return()
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
}
|
||||
|
||||
void IPromisePoolItem.Reset()
|
||||
{
|
||||
stateMachine = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -13,9 +13,11 @@ using System.Security;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniRx.Async
|
||||
{
|
||||
// TODO: Internal.
|
||||
public static class DiagnosticsExtensions
|
||||
{
|
||||
static bool displayFilenames = true;
|
||||
@@ -45,36 +47,6 @@ namespace UniRx.Async
|
||||
{ typeof(UniTaskVoid), "UniTaskVoid" }
|
||||
};
|
||||
|
||||
public static string ToStringWithCleanupAsyncStackTrace(this Exception exception)
|
||||
{
|
||||
if (exception == null) return "";
|
||||
|
||||
String message = exception.Message;
|
||||
String s;
|
||||
|
||||
if (message == null || message.Length <= 0)
|
||||
{
|
||||
s = exception.GetType().ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
s = exception.GetType().ToString() + ": " + message;
|
||||
}
|
||||
|
||||
if (exception.InnerException != null)
|
||||
{
|
||||
s = s + " ---> " + exception.InnerException.ToString() + Environment.NewLine + " Exception_EndOfInnerExceptionStack";
|
||||
}
|
||||
|
||||
string stackTrace = new StackTrace(exception).CleanupAsyncStackTrace();
|
||||
if (stackTrace != null)
|
||||
{
|
||||
s += Environment.NewLine + stackTrace;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public static string CleanupAsyncStackTrace(this StackTrace stackTrace)
|
||||
{
|
||||
if (stackTrace == null) return "";
|
||||
@@ -143,7 +115,7 @@ namespace UniRx.Async
|
||||
if (fileName != null)
|
||||
{
|
||||
sb.Append(' ');
|
||||
sb.AppendFormat(CultureInfo.InvariantCulture, "in {0}:{1}", SimplifyPath(fileName), sf.GetFileLineNumber());
|
||||
sb.AppendFormat(CultureInfo.InvariantCulture, "(at {0})", AppendHyperLink(fileName, sf.GetFileLineNumber().ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +184,7 @@ namespace UniRx.Async
|
||||
{
|
||||
return "(" + string.Join(", ", t.GetGenericArguments().Select(x => BeautifyType(x, true))) + ")";
|
||||
}
|
||||
if (!t.IsGenericType) return shortName ? t.Name : t.FullName ?? t.Name;
|
||||
if (!t.IsGenericType) return shortName ? t.Name : t.FullName.Replace("UniRx.Async.Triggers.", "").Replace("UniRx.Async.Internal.", "").Replace("UniRx.Async.", "") ?? t.Name;
|
||||
|
||||
var innerFormat = string.Join(", ", t.GetGenericArguments().Select(x => BeautifyType(x, true)));
|
||||
|
||||
@@ -222,7 +194,7 @@ namespace UniRx.Async
|
||||
genericType = "Task";
|
||||
}
|
||||
|
||||
return typeBeautifyRegex.Replace(genericType, "") + "<" + innerFormat + ">";
|
||||
return typeBeautifyRegex.Replace(genericType, "").Replace("UniRx.Async.Triggers.", "").Replace("UniRx.Async.Internal.", "").Replace("UniRx.Async.", "") + "<" + innerFormat + ">";
|
||||
}
|
||||
|
||||
static bool IgnoreLine(MethodBase methodInfo)
|
||||
@@ -248,11 +220,19 @@ namespace UniRx.Async
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (declareType.StartsWith("UniRx.Async.UniTaskCompletionSourceCore"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (declareType.StartsWith("UniRx.Async.AwaiterActions"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static string SimplifyPath(string path)
|
||||
static string AppendHyperLink(string path, string line)
|
||||
{
|
||||
var fi = new FileInfo(path);
|
||||
if (fi.Directory == null)
|
||||
@@ -261,7 +241,9 @@ namespace UniRx.Async
|
||||
}
|
||||
else
|
||||
{
|
||||
return fi.Directory.Name + "/" + fi.Name;
|
||||
var fname = fi.FullName.Replace(Path.DirectorySeparatorChar, '/').Replace(Application.dataPath, "");
|
||||
var withAssetsPath = "Assets/" + fname;
|
||||
return "<a href=\"" + withAssetsPath + "\" line=\"" + line + "\">" + withAssetsPath + ":" + line + "</a>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,14 @@ using System;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using UniRx.Async.Internal;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace UniRx.Async.Editor
|
||||
{
|
||||
public class UniTaskTrackerViewItem : TreeViewItem
|
||||
{
|
||||
static Regex removeHref = new Regex("<a href.+>(.+)</a>", RegexOptions.Compiled);
|
||||
|
||||
public string TaskType { get; set; }
|
||||
public string Elapsed { get; set; }
|
||||
public string Status { get; set; }
|
||||
@@ -43,7 +46,8 @@ namespace UniRx.Async.Editor
|
||||
}
|
||||
sb.Append(str[i]);
|
||||
}
|
||||
return sb.ToString();
|
||||
|
||||
return removeHref.Replace(sb.ToString(), "$1");
|
||||
}
|
||||
|
||||
public UniTaskTrackerViewItem(int id) : base(id)
|
||||
@@ -128,7 +132,7 @@ namespace UniRx.Async.Editor
|
||||
|
||||
var children = new List<TreeViewItem>();
|
||||
|
||||
TaskTracker.ForEachActiveTask((trackingId, awaiterType, status, created, stackTrace) =>
|
||||
TaskTracker2.ForEachActiveTask((trackingId, awaiterType, status, created, stackTrace) =>
|
||||
{
|
||||
children.Add(new UniTaskTrackerViewItem(trackingId) { TaskType = awaiterType, Status = status.ToString(), Elapsed = (DateTime.UtcNow - created).TotalSeconds.ToString("00.00"), Position = stackTrace });
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ using UniRx.Async.Internal;
|
||||
|
||||
namespace UniRx.Async.Editor
|
||||
{
|
||||
// TODO:Remove
|
||||
public class UniTaskTrackerWindow : EditorWindow
|
||||
{
|
||||
static int interval;
|
||||
@@ -206,6 +207,202 @@ namespace UniRx.Async.Editor
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class UniTaskTrackerWindow2 : EditorWindow
|
||||
{
|
||||
static int interval;
|
||||
|
||||
static UniTaskTrackerWindow2 window;
|
||||
|
||||
// TODO:Remove 2
|
||||
[MenuItem("Window/UniTask Tracker 2")]
|
||||
public static void OpenWindow()
|
||||
{
|
||||
if (window != null)
|
||||
{
|
||||
window.Close();
|
||||
}
|
||||
|
||||
// will called OnEnable(singleton instance will be set).
|
||||
GetWindow<UniTaskTrackerWindow2>("UniTask Tracker").Show();
|
||||
}
|
||||
|
||||
static readonly GUILayoutOption[] EmptyLayoutOption = new GUILayoutOption[0];
|
||||
|
||||
UniTaskTrackerTreeView treeView;
|
||||
object splitterState;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
window = this; // set singleton.
|
||||
splitterState = SplitterGUILayout.CreateSplitterState(new float[] { 75f, 25f }, new int[] { 32, 32 }, null);
|
||||
treeView = new UniTaskTrackerTreeView();
|
||||
TaskTracker2.EditorEnableState.EnableAutoReload = EditorPrefs.GetBool(TaskTracker2.EnableAutoReloadKey, false);
|
||||
TaskTracker2.EditorEnableState.EnableTracking = EditorPrefs.GetBool(TaskTracker2.EnableTrackingKey, false);
|
||||
TaskTracker2.EditorEnableState.EnableStackTrace = EditorPrefs.GetBool(TaskTracker2.EnableStackTraceKey, false);
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
// Head
|
||||
RenderHeadPanel();
|
||||
|
||||
// Splittable
|
||||
SplitterGUILayout.BeginVerticalSplit(this.splitterState, EmptyLayoutOption);
|
||||
{
|
||||
// Column Tabble
|
||||
RenderTable();
|
||||
|
||||
// StackTrace details
|
||||
RenderDetailsPanel();
|
||||
}
|
||||
SplitterGUILayout.EndVerticalSplit();
|
||||
}
|
||||
|
||||
#region HeadPanel
|
||||
|
||||
public static bool EnableAutoReload => TaskTracker2.EditorEnableState.EnableAutoReload;
|
||||
public static bool EnableTracking => TaskTracker2.EditorEnableState.EnableTracking;
|
||||
public static bool EnableStackTrace => TaskTracker2.EditorEnableState.EnableStackTrace;
|
||||
static readonly GUIContent EnableAutoReloadHeadContent = EditorGUIUtility.TrTextContent("Enable AutoReload", "Reload automatically.", (Texture)null);
|
||||
static readonly GUIContent ReloadHeadContent = EditorGUIUtility.TrTextContent("Reload", "Reload View.", (Texture)null);
|
||||
static readonly GUIContent GCHeadContent = EditorGUIUtility.TrTextContent("GC.Collect", "Invoke GC.Collect.", (Texture)null);
|
||||
static readonly GUIContent EnableTrackingHeadContent = EditorGUIUtility.TrTextContent("Enable Tracking", "Start to track async/await UniTask. Performance impact: low", (Texture)null);
|
||||
static readonly GUIContent EnableStackTraceHeadContent = EditorGUIUtility.TrTextContent("Enable StackTrace", "Capture StackTrace when task is started. Performance impact: high", (Texture)null);
|
||||
|
||||
// [Enable Tracking] | [Enable StackTrace]
|
||||
void RenderHeadPanel()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(EmptyLayoutOption);
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar, EmptyLayoutOption);
|
||||
|
||||
if (GUILayout.Toggle(EnableAutoReload, EnableAutoReloadHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption) != EnableAutoReload)
|
||||
{
|
||||
TaskTracker2.EditorEnableState.EnableAutoReload = !EnableAutoReload;
|
||||
}
|
||||
|
||||
if (GUILayout.Toggle(EnableTracking, EnableTrackingHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption) != EnableTracking)
|
||||
{
|
||||
TaskTracker2.EditorEnableState.EnableTracking = !EnableTracking;
|
||||
}
|
||||
|
||||
if (GUILayout.Toggle(EnableStackTrace, EnableStackTraceHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption) != EnableStackTrace)
|
||||
{
|
||||
TaskTracker2.EditorEnableState.EnableStackTrace = !EnableStackTrace;
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button(ReloadHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption))
|
||||
{
|
||||
TaskTracker2.CheckAndResetDirty();
|
||||
treeView.ReloadAndSort();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
if (GUILayout.Button(GCHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption))
|
||||
{
|
||||
GC.Collect(0);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TableColumn
|
||||
|
||||
Vector2 tableScroll;
|
||||
GUIStyle tableListStyle;
|
||||
|
||||
void RenderTable()
|
||||
{
|
||||
if (tableListStyle == null)
|
||||
{
|
||||
tableListStyle = new GUIStyle("CN Box");
|
||||
tableListStyle.margin.top = 0;
|
||||
tableListStyle.padding.left = 3;
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginVertical(tableListStyle, EmptyLayoutOption);
|
||||
|
||||
this.tableScroll = EditorGUILayout.BeginScrollView(this.tableScroll, new GUILayoutOption[]
|
||||
{
|
||||
GUILayout.ExpandWidth(true),
|
||||
GUILayout.MaxWidth(2000f)
|
||||
});
|
||||
var controlRect = EditorGUILayout.GetControlRect(new GUILayoutOption[]
|
||||
{
|
||||
GUILayout.ExpandHeight(true),
|
||||
GUILayout.ExpandWidth(true)
|
||||
});
|
||||
|
||||
|
||||
treeView?.OnGUI(controlRect);
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (EnableAutoReload)
|
||||
{
|
||||
if (interval++ % 120 == 0)
|
||||
{
|
||||
if (TaskTracker2.CheckAndResetDirty())
|
||||
{
|
||||
treeView.ReloadAndSort();
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Details
|
||||
|
||||
static GUIStyle detailsStyle;
|
||||
Vector2 detailsScroll;
|
||||
|
||||
void RenderDetailsPanel()
|
||||
{
|
||||
if (detailsStyle == null)
|
||||
{
|
||||
detailsStyle = new GUIStyle("CN Message");
|
||||
detailsStyle.wordWrap = false;
|
||||
detailsStyle.stretchHeight = true;
|
||||
detailsStyle.margin.right = 15;
|
||||
}
|
||||
|
||||
string message = "";
|
||||
var selected = treeView.state.selectedIDs;
|
||||
if (selected.Count > 0)
|
||||
{
|
||||
var first = selected[0];
|
||||
var item = treeView.CurrentBindingItems.FirstOrDefault(x => x.id == first) as UniTaskTrackerViewItem;
|
||||
if (item != null)
|
||||
{
|
||||
message = item.Position;
|
||||
}
|
||||
}
|
||||
|
||||
detailsScroll = EditorGUILayout.BeginScrollView(this.detailsScroll, EmptyLayoutOption);
|
||||
var vector = detailsStyle.CalcSize(new GUIContent(message));
|
||||
EditorGUILayout.SelectableLabel(message, detailsStyle, new GUILayoutOption[]
|
||||
{
|
||||
GUILayout.ExpandHeight(true),
|
||||
GUILayout.ExpandWidth(true),
|
||||
GUILayout.MinWidth(vector.x),
|
||||
GUILayout.MinHeight(vector.y)
|
||||
});
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,10 +1,13 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace UniRx.Async
|
||||
{
|
||||
// TODO:rename to UniTaskStatus
|
||||
|
||||
public enum AwaiterStatus
|
||||
{
|
||||
/// <summary>The operation has not yet completed.</summary>
|
||||
@@ -17,6 +20,24 @@ namespace UniRx.Async
|
||||
Canceled = 3
|
||||
}
|
||||
|
||||
|
||||
|
||||
// similar as IValueTaskSource
|
||||
public interface IUniTaskSource
|
||||
{
|
||||
AwaiterStatus GetStatus(short token);
|
||||
void OnCompleted(Action<object> continuation, object state, short token);
|
||||
void GetResult(short token);
|
||||
|
||||
AwaiterStatus UnsafeGetStatus(); // only for debug use.
|
||||
}
|
||||
|
||||
public interface IUniTaskSource<out T> : IUniTaskSource
|
||||
{
|
||||
new T GetResult(short token);
|
||||
}
|
||||
|
||||
|
||||
public interface IAwaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
AwaiterStatus Status { get; }
|
||||
|
||||
53
Assets/UniRx.Async/Internal/PromisePool.cs
Normal file
53
Assets/UniRx.Async/Internal/PromisePool.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
internal interface IPromisePoolItem
|
||||
{
|
||||
void Reset();
|
||||
}
|
||||
|
||||
internal class PromisePool<T>
|
||||
where T : class, IPromisePoolItem
|
||||
{
|
||||
int count = 0;
|
||||
readonly ConcurrentQueue<T> queue = new ConcurrentQueue<T>();
|
||||
readonly int maxSize;
|
||||
|
||||
public PromisePool(int maxSize = 256)
|
||||
{
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T TryRent()
|
||||
{
|
||||
if (queue.TryDequeue(out var value))
|
||||
{
|
||||
Interlocked.Decrement(ref count);
|
||||
return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryReturn(T value)
|
||||
{
|
||||
value.Reset(); // reset when return.
|
||||
|
||||
if (count < maxSize)
|
||||
{
|
||||
queue.Enqueue(value);
|
||||
Interlocked.Increment(ref count);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/UniRx.Async/Internal/PromisePool.cs.meta
Normal file
11
Assets/UniRx.Async/Internal/PromisePool.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fcb1f7467a3e2b64c8a016c8aee2f9b4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -10,6 +10,7 @@ namespace UniRx.Async.Internal
|
||||
{
|
||||
// public for add user custom.
|
||||
|
||||
// TODO: Remove
|
||||
public static class TaskTracker
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
@@ -147,6 +148,123 @@ namespace UniRx.Async.Internal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class TaskTracker2
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
|
||||
static int trackingId = 0;
|
||||
|
||||
public const string EnableAutoReloadKey = "UniTaskTrackerWindow_EnableAutoReloadKey";
|
||||
public const string EnableTrackingKey = "UniTaskTrackerWindow_EnableTrackingKey";
|
||||
public const string EnableStackTraceKey = "UniTaskTrackerWindow_EnableStackTraceKey";
|
||||
|
||||
public static class EditorEnableState
|
||||
{
|
||||
static bool enableAutoReload;
|
||||
public static bool EnableAutoReload
|
||||
{
|
||||
get { return enableAutoReload; }
|
||||
set
|
||||
{
|
||||
enableAutoReload = value;
|
||||
UnityEditor.EditorPrefs.SetBool(EnableAutoReloadKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
static bool enableTracking;
|
||||
public static bool EnableTracking
|
||||
{
|
||||
get { return enableTracking; }
|
||||
set
|
||||
{
|
||||
enableTracking = value;
|
||||
UnityEditor.EditorPrefs.SetBool(EnableTrackingKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
static bool enableStackTrace;
|
||||
public static bool EnableStackTrace
|
||||
{
|
||||
get { return enableStackTrace; }
|
||||
set
|
||||
{
|
||||
enableStackTrace = value;
|
||||
UnityEditor.EditorPrefs.SetBool(EnableStackTraceKey, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static List<KeyValuePair<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>> listPool = new List<KeyValuePair<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>>();
|
||||
|
||||
static readonly WeakDictionary<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)> tracking = new WeakDictionary<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>();
|
||||
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
public static void TrackActiveTask(IUniTaskSource task, int skipFrame)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
dirty = true;
|
||||
if (!EditorEnableState.EnableTracking) return;
|
||||
var stackTrace = EditorEnableState.EnableStackTrace ? new StackTrace(skipFrame, true).CleanupAsyncStackTrace() : "";
|
||||
tracking.TryAdd(task, (Interlocked.Increment(ref trackingId), DateTime.UtcNow, stackTrace));
|
||||
#endif
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
public static void RemoveTracking(IUniTaskSource task)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
dirty = true;
|
||||
if (!EditorEnableState.EnableTracking) return;
|
||||
var success = tracking.TryRemove(task);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool dirty;
|
||||
|
||||
public static bool CheckAndResetDirty()
|
||||
{
|
||||
var current = dirty;
|
||||
dirty = false;
|
||||
return current;
|
||||
}
|
||||
|
||||
/// <summary>(trackingId, awaiterType, awaiterStatus, createdTime, stackTrace)</summary>
|
||||
public static void ForEachActiveTask(Action<int, string, AwaiterStatus, DateTime, string> action)
|
||||
{
|
||||
lock (listPool)
|
||||
{
|
||||
var count = tracking.ToList(ref listPool, clear: false);
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
string typeName = null;
|
||||
var keyType = listPool[i].Key.GetType();
|
||||
if (keyType.IsNested)
|
||||
{
|
||||
typeName = keyType.DeclaringType.Name + "." + keyType.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
typeName = keyType.Name;
|
||||
}
|
||||
|
||||
action(listPool[i].Value.trackingId, typeName, listPool[i].Key.UnsafeGetStatus(), listPool[i].Value.addTime, listPool[i].Value.stackTrace);
|
||||
listPool[i] = new KeyValuePair<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>(null, (0, default(DateTime), null)); // clear
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
listPool.Clear();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace UniRx.Async
|
||||
{
|
||||
@@ -86,6 +87,16 @@ namespace UniRx.Async
|
||||
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))
|
||||
|
||||
@@ -3,14 +3,628 @@
|
||||
#pragma warning disable CS0436
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
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.Internal;
|
||||
|
||||
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 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
|
||||
{
|
||||
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)
|
||||
{
|
||||
TaskTracker2.RemoveTracking(this);
|
||||
core.SetCancellation(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentFrameCount == delayFrameCount)
|
||||
{
|
||||
TaskTracker2.RemoveTracking(this);
|
||||
core.SetResult(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
currentFrameCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
core.Reset();
|
||||
currentFrameCount = default;
|
||||
delayFrameCount = default;
|
||||
cancellationToken = default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Lightweight unity specified task-like object.
|
||||
/// </summary>
|
||||
[AsyncMethodBuilder(typeof(AsyncUniTask2MethodBuilder))] // TODO:AsyncUniTask2
|
||||
public partial struct UniTask2
|
||||
{
|
||||
// static readonly UniTask<AsyncUnit> DefaultAsyncUnitTask = new UniTask<AsyncUnit>(AsyncUnit.Default);
|
||||
|
||||
readonly IUniTaskSource awaiter;
|
||||
readonly short token;
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UniTask2(IUniTaskSource awaiter, short token)
|
||||
{
|
||||
this.awaiter = awaiter;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public AwaiterStatus Status
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
return awaiter.GetStatus(token);
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Awaiter GetAwaiter()
|
||||
{
|
||||
return new Awaiter(this);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void GetResult()
|
||||
{
|
||||
awaiter.GetResult(token);
|
||||
}
|
||||
|
||||
// TODO:can be suppress?
|
||||
|
||||
///// <summary>
|
||||
///// returns (bool IsCanceled) instead of throws OperationCanceledException.
|
||||
///// </summary>
|
||||
//public UniTask<bool> SuppressCancellationThrow()
|
||||
//{
|
||||
// var status = Status;
|
||||
// if (status == AwaiterStatus.Succeeded) return CompletedTasks.False;
|
||||
// if (status == AwaiterStatus.Canceled) return CompletedTasks.True;
|
||||
// //return new UniTask<bool>(new IsCanceledAwaiter(awaiter));
|
||||
//}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var status = this.awaiter.UnsafeGetStatus();
|
||||
return (status == AwaiterStatus.Succeeded) ? "()" : "(" + status + ")";
|
||||
}
|
||||
|
||||
//public static implicit operator UniTask<AsyncUnit>(UniTask2 task)
|
||||
//{
|
||||
// // TODO:
|
||||
// throw new NotImplementedException();
|
||||
|
||||
// //if (task.awaiter != null)
|
||||
// //{
|
||||
// // if (task.awaiter.IsCompleted)
|
||||
// // {
|
||||
// // return DefaultAsyncUnitTask;
|
||||
// // }
|
||||
// // else
|
||||
// // {
|
||||
// // // UniTask<T> -> UniTask is free but UniTask -> UniTask<T> requires wrapping cost.
|
||||
// // return new UniTask<AsyncUnit>(new AsyncUnitAwaiter(task.awaiter));
|
||||
// // }
|
||||
// //}
|
||||
// //else
|
||||
// //{
|
||||
// // return DefaultAsyncUnitTask;
|
||||
// //}
|
||||
//}
|
||||
|
||||
//class AsyncUnitAwaiter : IAwaiter<AsyncUnit>
|
||||
//{
|
||||
// readonly IAwaiter2 awaiter;
|
||||
|
||||
// public AsyncUnitAwaiter(IAwaiter2 awaiter)
|
||||
// {
|
||||
// this.awaiter = awaiter;
|
||||
// }
|
||||
|
||||
// public bool IsCompleted => awaiter.IsCompleted;
|
||||
|
||||
// public AwaiterStatus Status => awaiter.Status;
|
||||
|
||||
// public AsyncUnit GetResult()
|
||||
// {
|
||||
// awaiter.GetResult();
|
||||
// return AsyncUnit.Default;
|
||||
// }
|
||||
|
||||
// public void OnCompleted(Action continuation)
|
||||
// {
|
||||
// awaiter.OnCompleted(continuation);
|
||||
// }
|
||||
|
||||
// public void UnsafeOnCompleted(Action continuation)
|
||||
// {
|
||||
// awaiter.UnsafeOnCompleted(continuation);
|
||||
// }
|
||||
|
||||
// void IAwaiter.GetResult()
|
||||
// {
|
||||
// awaiter.GetResult();
|
||||
// }
|
||||
//}
|
||||
|
||||
class IsCanceledAwaiter : IUniTaskSource
|
||||
{
|
||||
readonly IUniTaskSource awaiter;
|
||||
|
||||
public IsCanceledAwaiter(IUniTaskSource awaiter)
|
||||
{
|
||||
this.awaiter = awaiter;
|
||||
}
|
||||
|
||||
//public bool IsCompleted => awaiter.IsCompleted;
|
||||
|
||||
//public AwaiterStatus Status => awaiter.Status;
|
||||
|
||||
//public bool GetResult()
|
||||
//{
|
||||
// if (awaiter.Status == AwaiterStatus.Canceled)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// awaiter.GetResult();
|
||||
// return false;
|
||||
//}
|
||||
|
||||
//public void OnCompleted(Action continuation)
|
||||
//{
|
||||
// awaiter.OnCompleted(continuation);
|
||||
//}
|
||||
|
||||
//public void UnsafeOnCompleted(Action continuation)
|
||||
//{
|
||||
// awaiter.UnsafeOnCompleted(continuation);
|
||||
//}
|
||||
|
||||
//void IAwaiter.GetResult()
|
||||
//{
|
||||
// awaiter.GetResult();
|
||||
//}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
// TODO: bool
|
||||
if (awaiter.GetStatus(token) == AwaiterStatus.Canceled)
|
||||
{
|
||||
//return true;
|
||||
}
|
||||
|
||||
awaiter.GetResult(token);
|
||||
// return false
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public AwaiterStatus GetStatus(short token)
|
||||
{
|
||||
return awaiter.GetStatus(token);
|
||||
}
|
||||
|
||||
public AwaiterStatus UnsafeGetStatus()
|
||||
{
|
||||
return awaiter.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
awaiter.OnCompleted(continuation, state, token);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct Awaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
readonly UniTask2 task;
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Awaiter(in UniTask2 task)
|
||||
{
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
return task.Status.IsCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
public AwaiterStatus Status
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
return task.Status;
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void GetResult()
|
||||
{
|
||||
task.GetResult();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
task.awaiter.OnCompleted(AwaiterActions.InvokeActionDelegate, continuation, task.token);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
task.awaiter.OnCompleted(AwaiterActions.InvokeActionDelegate, continuation, task.token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class AwaiterActions
|
||||
{
|
||||
internal static readonly Action<object> InvokeActionDelegate = InvokeAction;
|
||||
|
||||
static void InvokeAction(object state)
|
||||
{
|
||||
((Action)state).Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Lightweight unity specified task-like object.
|
||||
/// </summary>
|
||||
[AsyncMethodBuilder(typeof(AsyncUniTask2MethodBuilder))] // TODO:AsyncUniTask2~T
|
||||
public struct UniTask2<T>
|
||||
{
|
||||
// static readonly UniTask<AsyncUnit> DefaultAsyncUnitTask = new UniTask<AsyncUnit>(AsyncUnit.Default);
|
||||
|
||||
readonly IUniTaskSource<T> awaiter;
|
||||
readonly short token;
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UniTask2(IUniTaskSource<T> awaiter, short token)
|
||||
{
|
||||
this.awaiter = awaiter;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public AwaiterStatus Status
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
return awaiter.GetStatus(token);
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Awaiter GetAwaiter()
|
||||
{
|
||||
return new Awaiter(this);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
T GetResult()
|
||||
{
|
||||
return awaiter.GetResult(token);
|
||||
}
|
||||
|
||||
// TODO:can be suppress?
|
||||
|
||||
///// <summary>
|
||||
///// returns (bool IsCanceled) instead of throws OperationCanceledException.
|
||||
///// </summary>
|
||||
//public UniTask<bool> SuppressCancellationThrow()
|
||||
//{
|
||||
// var status = Status;
|
||||
// if (status == AwaiterStatus.Succeeded) return CompletedTasks.False;
|
||||
// if (status == AwaiterStatus.Canceled) return CompletedTasks.True;
|
||||
// //return new UniTask<bool>(new IsCanceledAwaiter(awaiter));
|
||||
//}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var status = this.awaiter.UnsafeGetStatus();
|
||||
return (status == AwaiterStatus.Succeeded) ? "()" : "(" + status + ")";
|
||||
}
|
||||
|
||||
//public static implicit operator UniTask<AsyncUnit>(UniTask2 task)
|
||||
//{
|
||||
// // TODO:
|
||||
// throw new NotImplementedException();
|
||||
|
||||
// //if (task.awaiter != null)
|
||||
// //{
|
||||
// // if (task.awaiter.IsCompleted)
|
||||
// // {
|
||||
// // return DefaultAsyncUnitTask;
|
||||
// // }
|
||||
// // else
|
||||
// // {
|
||||
// // // UniTask<T> -> UniTask is free but UniTask -> UniTask<T> requires wrapping cost.
|
||||
// // return new UniTask<AsyncUnit>(new AsyncUnitAwaiter(task.awaiter));
|
||||
// // }
|
||||
// //}
|
||||
// //else
|
||||
// //{
|
||||
// // return DefaultAsyncUnitTask;
|
||||
// //}
|
||||
//}
|
||||
|
||||
//class AsyncUnitAwaiter : IAwaiter<AsyncUnit>
|
||||
//{
|
||||
// readonly IAwaiter2 awaiter;
|
||||
|
||||
// public AsyncUnitAwaiter(IAwaiter2 awaiter)
|
||||
// {
|
||||
// this.awaiter = awaiter;
|
||||
// }
|
||||
|
||||
// public bool IsCompleted => awaiter.IsCompleted;
|
||||
|
||||
// public AwaiterStatus Status => awaiter.Status;
|
||||
|
||||
// public AsyncUnit GetResult()
|
||||
// {
|
||||
// awaiter.GetResult();
|
||||
// return AsyncUnit.Default;
|
||||
// }
|
||||
|
||||
// public void OnCompleted(Action continuation)
|
||||
// {
|
||||
// awaiter.OnCompleted(continuation);
|
||||
// }
|
||||
|
||||
// public void UnsafeOnCompleted(Action continuation)
|
||||
// {
|
||||
// awaiter.UnsafeOnCompleted(continuation);
|
||||
// }
|
||||
|
||||
// void IAwaiter.GetResult()
|
||||
// {
|
||||
// awaiter.GetResult();
|
||||
// }
|
||||
//}
|
||||
|
||||
class IsCanceledAwaiter : IUniTaskSource
|
||||
{
|
||||
readonly IUniTaskSource awaiter;
|
||||
|
||||
public IsCanceledAwaiter(IUniTaskSource awaiter)
|
||||
{
|
||||
this.awaiter = awaiter;
|
||||
}
|
||||
|
||||
//public bool IsCompleted => awaiter.IsCompleted;
|
||||
|
||||
//public AwaiterStatus Status => awaiter.Status;
|
||||
|
||||
//public bool GetResult()
|
||||
//{
|
||||
// if (awaiter.Status == AwaiterStatus.Canceled)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// awaiter.GetResult();
|
||||
// return false;
|
||||
//}
|
||||
|
||||
//public void OnCompleted(Action continuation)
|
||||
//{
|
||||
// awaiter.OnCompleted(continuation);
|
||||
//}
|
||||
|
||||
//public void UnsafeOnCompleted(Action continuation)
|
||||
//{
|
||||
// awaiter.UnsafeOnCompleted(continuation);
|
||||
//}
|
||||
|
||||
//void IAwaiter.GetResult()
|
||||
//{
|
||||
// awaiter.GetResult();
|
||||
//}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
// TODO: bool
|
||||
if (awaiter.GetStatus(token) == AwaiterStatus.Canceled)
|
||||
{
|
||||
//return true;
|
||||
}
|
||||
|
||||
awaiter.GetResult(token);
|
||||
// return false
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public AwaiterStatus GetStatus(short token)
|
||||
{
|
||||
return awaiter.GetStatus(token);
|
||||
}
|
||||
|
||||
public AwaiterStatus UnsafeGetStatus()
|
||||
{
|
||||
return awaiter.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
awaiter.OnCompleted(continuation, state, token);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct Awaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
readonly UniTask2<T> task;
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Awaiter(in UniTask2<T> task)
|
||||
{
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
return task.Status.IsCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
public AwaiterStatus Status
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
return task.Status;
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T GetResult()
|
||||
{
|
||||
return task.GetResult();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
task.awaiter.OnCompleted(AwaiterActions.InvokeActionDelegate, continuation, task.token);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
task.awaiter.OnCompleted(AwaiterActions.InvokeActionDelegate, continuation, task.token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Lightweight unity specified task-like object.
|
||||
/// </summary>
|
||||
|
||||
@@ -6,11 +6,14 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using UniRx.Async.Internal;
|
||||
|
||||
namespace UniRx.Async
|
||||
{
|
||||
// TODO: Remove all?
|
||||
|
||||
internal class ExceptionHolder
|
||||
{
|
||||
ExceptionDispatchInfo exception;
|
||||
@@ -408,6 +411,587 @@ namespace UniRx.Async
|
||||
((ICriticalNotifyCompletion)this).UnsafeOnCompleted(continuation);
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct UniTaskCompletionSourceCore<TResult>
|
||||
{
|
||||
// Struct Size: TResult + (8 + 2 + 1 + 1 + 8 + 8)
|
||||
|
||||
TResult result;
|
||||
object error; // Exception or OperationCanceledException
|
||||
short version;
|
||||
bool completed;
|
||||
bool hasUnhandledError;
|
||||
|
||||
Action<object> continuation;
|
||||
object continuationState;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ReportUnhandledError();
|
||||
|
||||
unchecked
|
||||
{
|
||||
version += 1; // incr version.
|
||||
}
|
||||
completed = false;
|
||||
result = default;
|
||||
error = null;
|
||||
hasUnhandledError = false;
|
||||
continuation = null;
|
||||
continuationState = null;
|
||||
}
|
||||
|
||||
void ReportUnhandledError()
|
||||
{
|
||||
if (hasUnhandledError)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (error is OperationCanceledException oc)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(oc);
|
||||
}
|
||||
else if (error is ExceptionDispatchInfo ei)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ei.SourceException);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Completes with a successful result.</summary>
|
||||
/// <param name="result">The result.</param>
|
||||
public void SetResult(TResult result)
|
||||
{
|
||||
this.result = result;
|
||||
SignalCompletion();
|
||||
}
|
||||
|
||||
/// <summary>Completes with an error.</summary>
|
||||
/// <param name="error">The exception.</param>
|
||||
public void SetException(Exception error)
|
||||
{
|
||||
this.hasUnhandledError = true;
|
||||
this.error = ExceptionDispatchInfo.Capture(error);
|
||||
SignalCompletion();
|
||||
}
|
||||
|
||||
public void SetCancellation(CancellationToken cancellationToken)
|
||||
{
|
||||
this.error = new OperationCanceledException(cancellationToken);
|
||||
SignalCompletion();
|
||||
}
|
||||
|
||||
/// <summary>Gets the operation version.</summary>
|
||||
public short Version => version;
|
||||
|
||||
/// <summary>Gets the status of the operation.</summary>
|
||||
/// <param name="token">Opaque value that was provided to the <see cref="UniTask"/>'s constructor.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public AwaiterStatus GetStatus(short token)
|
||||
{
|
||||
ValidateToken(token);
|
||||
return (continuation == null || !completed) ? AwaiterStatus.Pending
|
||||
: (error == null) ? AwaiterStatus.Succeeded
|
||||
: (error is OperationCanceledException) ? AwaiterStatus.Canceled
|
||||
: AwaiterStatus.Faulted;
|
||||
}
|
||||
|
||||
/// <summary>Gets the status of the operation without token validation.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public AwaiterStatus UnsafeGetStatus()
|
||||
{
|
||||
return (continuation == null || !completed) ? AwaiterStatus.Pending
|
||||
: (error == null) ? AwaiterStatus.Succeeded
|
||||
: (error is OperationCanceledException) ? AwaiterStatus.Canceled
|
||||
: AwaiterStatus.Faulted;
|
||||
}
|
||||
|
||||
/// <summary>Gets the result of the operation.</summary>
|
||||
/// <param name="token">Opaque value that was provided to the <see cref="UniTask"/>'s constructor.</param>
|
||||
// [StackTraceHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TResult GetResult(short token)
|
||||
{
|
||||
ValidateToken(token);
|
||||
if (!completed)
|
||||
{
|
||||
throw new InvalidOperationException("not yet completed.");
|
||||
}
|
||||
|
||||
if (error != null)
|
||||
{
|
||||
hasUnhandledError = false;
|
||||
if (error is OperationCanceledException oce)
|
||||
{
|
||||
throw oce;
|
||||
}
|
||||
else if (error is ExceptionDispatchInfo edi)
|
||||
{
|
||||
edi.Throw();
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Critical: invalid exception type was held.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>Schedules the continuation action for this operation.</summary>
|
||||
/// <param name="continuation">The continuation to invoke when the operation has completed.</param>
|
||||
/// <param name="state">The state object to pass to <paramref name="continuation"/> when it's invoked.</param>
|
||||
/// <param name="token">Opaque value that was provided to the <see cref="UniTask"/>'s constructor.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void OnCompleted(Action<object> continuation, object state, short token /*, ValueTaskSourceOnCompletedFlags flags */)
|
||||
{
|
||||
if (continuation == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(continuation));
|
||||
}
|
||||
ValidateToken(token);
|
||||
|
||||
/* no use ValueTaskSourceOnCOmpletedFlags, always no capture ExecutionContext and SynchronizationContext. */
|
||||
|
||||
object oldContinuation = this.continuation;
|
||||
if (oldContinuation == null)
|
||||
{
|
||||
continuationState = state;
|
||||
oldContinuation = Interlocked.CompareExchange(ref this.continuation, continuation, null);
|
||||
}
|
||||
|
||||
if (oldContinuation != null)
|
||||
{
|
||||
// Operation already completed, so we need to queue the supplied callback.
|
||||
if (!ReferenceEquals(oldContinuation, UniTaskCompletionSourceCoreShared.s_sentinel))
|
||||
{
|
||||
throw new InvalidOperationException("already completed.");
|
||||
}
|
||||
|
||||
continuation(state);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ValidateToken(short token)
|
||||
{
|
||||
if (token != version)
|
||||
{
|
||||
throw new InvalidOperationException("token version is not matched.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Signals that the operation has completed. Invoked after the result or error has been set.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SignalCompletion()
|
||||
{
|
||||
if (completed)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
completed = true;
|
||||
|
||||
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
|
||||
{
|
||||
continuation(continuationState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UniTaskCompletionSource2 : IUniTaskSource
|
||||
{
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
bool handled = false;
|
||||
|
||||
public UniTaskCompletionSource2()
|
||||
{
|
||||
TaskTracker2.TrackActiveTask(this, 2);
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
void MarkHandled()
|
||||
{
|
||||
if (!handled)
|
||||
{
|
||||
handled = true;
|
||||
TaskTracker2.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
|
||||
public UniTask2 Task
|
||||
{
|
||||
get
|
||||
{
|
||||
return new UniTask2(this, core.Version);
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
// Reset, re-active tracker
|
||||
handled = false;
|
||||
TaskTracker2.TrackActiveTask(this, 2);
|
||||
core.Reset();
|
||||
}
|
||||
|
||||
public void SetResult()
|
||||
{
|
||||
core.SetResult(AsyncUnit.Default);
|
||||
}
|
||||
|
||||
public void SetCancellation(CancellationToken cancellationToken)
|
||||
{
|
||||
core.SetCancellation(cancellationToken);
|
||||
}
|
||||
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
core.SetException(exception);
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
MarkHandled();
|
||||
core.GetResult(token);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
~UniTaskCompletionSource2()
|
||||
{
|
||||
// clear error information.
|
||||
core.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public class AutoResetUniTaskCompletionSource : IUniTaskSource, IPromisePoolItem
|
||||
{
|
||||
static readonly PromisePool<AutoResetUniTaskCompletionSource> pool = new PromisePool<AutoResetUniTaskCompletionSource>();
|
||||
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
AutoResetUniTaskCompletionSource()
|
||||
{
|
||||
}
|
||||
|
||||
public static AutoResetUniTaskCompletionSource Create()
|
||||
{
|
||||
// TODO:Add TaskTracker
|
||||
return pool.TryRent() ?? new AutoResetUniTaskCompletionSource();
|
||||
}
|
||||
|
||||
public static AutoResetUniTaskCompletionSource CreateFromCanceled(CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
var source = Create();
|
||||
source.SetCancellation(cancellationToken);
|
||||
token = source.core.Version;
|
||||
return source;
|
||||
}
|
||||
|
||||
public static AutoResetUniTaskCompletionSource CreateFromException(Exception exception, out short token)
|
||||
{
|
||||
var source = Create();
|
||||
source.SetException(exception);
|
||||
token = source.core.Version;
|
||||
return source;
|
||||
}
|
||||
|
||||
public static AutoResetUniTaskCompletionSource CreateCompleted(out short token)
|
||||
{
|
||||
var source = Create();
|
||||
source.SetResult();
|
||||
token = source.core.Version;
|
||||
return source;
|
||||
}
|
||||
|
||||
public UniTask2 Task
|
||||
{
|
||||
get
|
||||
{
|
||||
return new UniTask2(this, core.Version);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetResult()
|
||||
{
|
||||
core.SetResult(AsyncUnit.Default);
|
||||
}
|
||||
|
||||
public void SetCancellation(CancellationToken cancellationToken)
|
||||
{
|
||||
core.SetCancellation(cancellationToken);
|
||||
}
|
||||
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
core.SetException(exception);
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO:Remove TaskTracker
|
||||
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);
|
||||
}
|
||||
|
||||
void IPromisePoolItem.Reset()
|
||||
{
|
||||
core.Reset();
|
||||
}
|
||||
|
||||
~AutoResetUniTaskCompletionSource()
|
||||
{
|
||||
if (pool.TryReturn(this))
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UniTaskCompletionSource2<T> : IUniTaskSource<T>
|
||||
{
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
bool handled = false;
|
||||
|
||||
public UniTaskCompletionSource2()
|
||||
{
|
||||
// TODO: TaskTracker.TrackActiveTask
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
void MarkHandled()
|
||||
{
|
||||
if (!handled)
|
||||
{
|
||||
handled = true;
|
||||
// TODO:
|
||||
// TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
|
||||
public UniTask2<T> Task
|
||||
{
|
||||
get
|
||||
{
|
||||
return new UniTask2<T>(this, core.Version);
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
// TODO:Reset, reactive tracker: TaskTracker.TrackActiveTask
|
||||
handled = false;
|
||||
core.Reset();
|
||||
}
|
||||
|
||||
public void SetResult(T result)
|
||||
{
|
||||
core.SetResult(result);
|
||||
}
|
||||
|
||||
public void SetCancellation(CancellationToken cancellationToken)
|
||||
{
|
||||
core.SetCancellation(cancellationToken);
|
||||
}
|
||||
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
core.SetException(exception);
|
||||
}
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
MarkHandled();
|
||||
return core.GetResult(token);
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
GetResult(token);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
~UniTaskCompletionSource2()
|
||||
{
|
||||
// clear error information.
|
||||
core.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public class AutoResetUniTaskCompletionSource<T> : IUniTaskSource<T>, IPromisePoolItem
|
||||
{
|
||||
static readonly PromisePool<AutoResetUniTaskCompletionSource<T>> pool = new PromisePool<AutoResetUniTaskCompletionSource<T>>();
|
||||
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
|
||||
AutoResetUniTaskCompletionSource()
|
||||
{
|
||||
}
|
||||
|
||||
public static AutoResetUniTaskCompletionSource<T> Create()
|
||||
{
|
||||
// TODO:Add TaskTracker
|
||||
return pool.TryRent() ?? new AutoResetUniTaskCompletionSource<T>();
|
||||
}
|
||||
|
||||
public static AutoResetUniTaskCompletionSource<T> CreateFromCanceled(CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
var source = Create();
|
||||
source.SetCancellation(cancellationToken);
|
||||
token = source.core.Version;
|
||||
return source;
|
||||
}
|
||||
|
||||
public static AutoResetUniTaskCompletionSource<T> CreateFromException(Exception exception, out short token)
|
||||
{
|
||||
var source = Create();
|
||||
source.SetException(exception);
|
||||
token = source.core.Version;
|
||||
return source;
|
||||
}
|
||||
|
||||
public static AutoResetUniTaskCompletionSource<T> CreateFromResult(T result, out short token)
|
||||
{
|
||||
var source = Create();
|
||||
source.SetResult(result);
|
||||
token = source.core.Version;
|
||||
return source;
|
||||
}
|
||||
|
||||
public UniTask2<T> Task
|
||||
{
|
||||
get
|
||||
{
|
||||
return new UniTask2<T>(this, core.Version);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetResult(T result)
|
||||
{
|
||||
core.SetResult(result);
|
||||
}
|
||||
|
||||
public void SetCancellation(CancellationToken cancellationToken)
|
||||
{
|
||||
core.SetCancellation(cancellationToken);
|
||||
}
|
||||
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
core.SetException(exception);
|
||||
}
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO:Remove TaskTracker
|
||||
return core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
}
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
GetResult(token);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void IPromisePoolItem.Reset()
|
||||
{
|
||||
core.Reset();
|
||||
}
|
||||
|
||||
~AutoResetUniTaskCompletionSource()
|
||||
{
|
||||
if (pool.TryReturn(this))
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class UniTaskCompletionSourceCoreShared // separated out of generic to avoid unnecessary duplication
|
||||
{
|
||||
internal static readonly Action<object> s_sentinel = CompletionSentinel;
|
||||
|
||||
private static void CompletionSentinel(object _) // named method to aid debugging
|
||||
{
|
||||
throw new InvalidOperationException("The sentinel delegate should never be invoked.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -19,12 +19,12 @@ namespace UniRx.Async
|
||||
public static bool PropagateOperationCanceledException = false;
|
||||
|
||||
/// <summary>
|
||||
/// Write log type when catch unobserved exception and not registered UnobservedTaskException. Default is Warning.
|
||||
/// Write log type when catch unobserved exception and not registered UnobservedTaskException. Default is Error.
|
||||
/// </summary>
|
||||
public static UnityEngine.LogType UnobservedExceptionWriteLogType = UnityEngine.LogType.Warning;
|
||||
public static UnityEngine.LogType UnobservedExceptionWriteLogType = UnityEngine.LogType.Error;
|
||||
|
||||
/// <summary>
|
||||
/// Dispatch exception event to Unity MainThread.
|
||||
/// Dispatch exception event to Unity MainThread. Default is true.
|
||||
/// </summary>
|
||||
public static bool DispatchUnityMainThread = true;
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace UniRx.Async
|
||||
|
||||
if (UnobservedTaskException != null)
|
||||
{
|
||||
if (Thread.CurrentThread.ManagedThreadId == PlayerLoopHelper.MainThreadId)
|
||||
if (!DispatchUnityMainThread || Thread.CurrentThread.ManagedThreadId == PlayerLoopHelper.MainThreadId)
|
||||
{
|
||||
// allows inlining call.
|
||||
UnobservedTaskException.Invoke(ex);
|
||||
|
||||
@@ -491,6 +491,127 @@ namespace UniRx.Async
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: try to check API.
|
||||
class ResourceRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, IPromisePoolItem
|
||||
{
|
||||
static readonly PromisePool<ResourceRequestConfiguredSource> pool = new PromisePool<ResourceRequestConfiguredSource>();
|
||||
|
||||
ResourceRequest asyncOperation;
|
||||
IProgress<float> progress;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
UniTaskCompletionSourceCore<UnityEngine.Object> core;
|
||||
|
||||
ResourceRequestConfiguredSource()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static IUniTaskSource<UnityEngine.Object> Create(ResourceRequest asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource<UnityEngine.Object>.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
var result = pool.TryRent() ?? new ResourceRequestConfiguredSource();
|
||||
|
||||
result.asyncOperation = asyncOperation;
|
||||
result.progress = progress;
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// TODO:capture???
|
||||
//var capturedStackTraceForDebugging = TaskTracker.CaptureStackTrace(2);
|
||||
// TODO:Add ActiveTask?
|
||||
// TaskTracker.TrackActiveTask(
|
||||
#endif
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
public UnityEngine.Object GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
return core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
}
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
GetResult(token);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// TODO:Remove Tracking
|
||||
// TaskTracker.RemoveTracking();
|
||||
core.SetCancellation(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (progress != null)
|
||||
{
|
||||
progress.Report(asyncOperation.progress);
|
||||
}
|
||||
|
||||
if (asyncOperation.isDone)
|
||||
{
|
||||
// TODO:Remove Tracking
|
||||
// TaskTracker.RemoveTracking();
|
||||
core.SetResult(asyncOperation.asset);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
core.Reset();
|
||||
asyncOperation = default;
|
||||
progress = default;
|
||||
cancellationToken = default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public struct AssetBundleRequestAwaiter : IAwaiter<UnityEngine.Object>
|
||||
{
|
||||
AssetBundleRequest asyncOperation;
|
||||
|
||||
Reference in New Issue
Block a user