diff --git a/Assets/Scenes/SandboxMain.cs b/Assets/Scenes/SandboxMain.cs index 1faf82d..f8c9e07 100644 --- a/Assets/Scenes/SandboxMain.cs +++ b/Assets/Scenes/SandboxMain.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Threading; +using System.Threading.Tasks; using UniRx.Async; using UnityEngine; using UnityEngine.Networking; @@ -12,15 +13,111 @@ public class SandboxMain : MonoBehaviour { public Button okButton; public Button cancelButton; + public Text text; + CancellationTokenSource cts; - async void Start() + UniTaskCompletionSource2 ucs; + + void Start() { - UnityEngine.Debug.Log("DOWNLOAD START:" + Time.frameCount); + // Setup unobserverd tskexception handling + TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; - var req = await UnityWebRequest.Get(Path.Combine(Application.streamingAssetsPath, "test.txt")).SendWebRequest(); + // Optional: disable ExecutionContext if you don't use AsyncLocal. + //if (!ExecutionContext.IsFlowSuppressed()) + //{ + // ExecutionContext.SuppressFlow(); + //} - UnityEngine.Debug.Log("DOWNLOAD RESULT:" + Time.frameCount + ", " + req.downloadHandler.text); + //// Optional: disable SynchronizationContext(to boostup performance) if you completely use UniTask only + //SynchronizationContext.SetSynchronizationContext(null); + + // ----- + + Application.logMessageReceived += Application_logMessageReceived; + + + ucs = new UniTaskCompletionSource2(); + + okButton.onClick.AddListener(UniTask.VoidUnityAction(async () => + { + await OuterAsync(true); + })); + + cancelButton.onClick.AddListener(async () => + { + text.text = ""; + + ucs.SetResult(); + + await ucs.Task; + }); + } + + private void Application_logMessageReceived(string condition, string stackTrace, LogType type) + { + text.text += "\n" + condition; + } + + async UniTask2 OuterAsync(bool b) + { + UnityEngine.Debug.Log("START OUTER"); + + await InnerAsync(b); + await InnerAsync(b); + + UnityEngine.Debug.Log("END OUTER"); + + // throw new InvalidOperationException("NAZO ERROR!?"); // error!? + } + + async UniTask2 InnerAsync(bool b) + { + if (b) + { + UnityEngine.Debug.Log("Start delay:" + Time.frameCount); + await UniTask2.DelayFrame(60); + UnityEngine.Debug.Log("End delay:" + Time.frameCount); + await UniTask2.DelayFrame(60); + UnityEngine.Debug.Log("Onemore end delay:" + Time.frameCount); + } + else + { + //UnityEngine.Debug.Log("Empty END"); + } + } + + + + + + + + + + + + + + + + + + + + + + + + + + + private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) + { + // e.SetObserved(); + // or other custom write code. + UnityEngine.Debug.LogError("Unobserved:" + e.Exception.ToString()); } } diff --git a/Assets/Scenes/SandboxMain.unity b/Assets/Scenes/SandboxMain.unity index 59e7906..8f96fdb 100644 --- a/Assets/Scenes/SandboxMain.unity +++ b/Assets/Scenes/SandboxMain.unity @@ -96,6 +96,7 @@ LightmapSettings: m_PVRFilteringAtrousPositionSigmaAO: 1 m_ExportTrainingData: 0 m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 m_LightingDataAsset: {fileID: 0} m_UseShadowmask: 1 --- !u!196 &4 @@ -168,7 +169,7 @@ MonoBehaviour: m_GameObject: {fileID: 16537670} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: @@ -202,8 +203,6 @@ MonoBehaviour: m_OnClick: m_PersistentCalls: m_Calls: [] - m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0, - Culture=neutral, PublicKeyToken=null --- !u!114 &16537673 MonoBehaviour: m_ObjectHideFlags: 0 @@ -213,7 +212,7 @@ MonoBehaviour: m_GameObject: {fileID: 16537670} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} @@ -222,8 +221,6 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, - Version=1.0.0.0, Culture=neutral, PublicKeyToken=null m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} m_Type: 1 m_PreserveAspect: 0 @@ -233,6 +230,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &16537674 CanvasRenderer: m_ObjectHideFlags: 0 @@ -282,6 +280,7 @@ MonoBehaviour: m_EditorClassIdentifier: okButton: {fileID: 16537672} cancelButton: {fileID: 628393011} + text: {fileID: 2101290655} --- !u!20 &519420031 Camera: m_ObjectHideFlags: 0 @@ -387,7 +386,7 @@ MonoBehaviour: m_GameObject: {fileID: 628393009} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: @@ -421,8 +420,6 @@ MonoBehaviour: m_OnClick: m_PersistentCalls: m_Calls: [] - m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0, - Culture=neutral, PublicKeyToken=null --- !u!114 &628393012 MonoBehaviour: m_ObjectHideFlags: 0 @@ -432,7 +429,7 @@ MonoBehaviour: m_GameObject: {fileID: 628393009} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} @@ -441,8 +438,6 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, - Version=1.0.0.0, Culture=neutral, PublicKeyToken=null m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} m_Type: 1 m_PreserveAspect: 0 @@ -452,6 +447,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &628393013 CanvasRenderer: m_ObjectHideFlags: 0 @@ -506,7 +502,7 @@ MonoBehaviour: m_GameObject: {fileID: 865871444} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} @@ -515,8 +511,6 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, - Version=1.0.0.0, Culture=neutral, PublicKeyToken=null m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} m_FontSize: 14 @@ -566,7 +560,7 @@ MonoBehaviour: m_GameObject: {fileID: 872009839} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1077351063, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} m_Name: m_EditorClassIdentifier: m_HorizontalAxis: Horizontal @@ -585,7 +579,7 @@ MonoBehaviour: m_GameObject: {fileID: 872009839} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -619905303, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} m_Name: m_EditorClassIdentifier: m_FirstSelected: {fileID: 0} @@ -633,7 +627,7 @@ MonoBehaviour: m_GameObject: {fileID: 1556045504} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} m_Name: m_EditorClassIdentifier: m_IgnoreReversedGraphics: 1 @@ -650,7 +644,7 @@ MonoBehaviour: m_GameObject: {fileID: 1556045504} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} m_Name: m_EditorClassIdentifier: m_UiScaleMode: 0 @@ -697,6 +691,7 @@ RectTransform: m_Children: - {fileID: 16537671} - {fileID: 628393010} + - {fileID: 2101290654} m_Father: {fileID: 0} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -751,7 +746,7 @@ MonoBehaviour: m_GameObject: {fileID: 1584557231} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} @@ -760,8 +755,6 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, - Version=1.0.0.0, Culture=neutral, PublicKeyToken=null m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} m_FontSize: 14 @@ -786,3 +779,80 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1584557231} m_CullTransparentMesh: 0 +--- !u!1 &2101290653 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2101290654} + - component: {fileID: 2101290656} + - component: {fileID: 2101290655} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2101290654 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2101290653} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1556045508} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 1.5, y: -61.4} + m_SizeDelta: {x: 588.7, y: 398.7} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2101290655 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2101290653} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: New Text +--- !u!222 &2101290656 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2101290653} + m_CullTransparentMesh: 0 diff --git a/Assets/UniRx.Async/CompilerServices/AsyncUniTaskMethodBuilder.cs b/Assets/UniRx.Async/CompilerServices/AsyncUniTaskMethodBuilder.cs index 586f859..1d15f0b 100644 --- a/Assets/UniRx.Async/CompilerServices/AsyncUniTaskMethodBuilder.cs +++ b/Assets/UniRx.Async/CompilerServices/AsyncUniTaskMethodBuilder.cs @@ -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 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(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + { + if (promise == null) + { + promise = AutoResetUniTaskCompletionSource.Create(); + } + if (runner == null) + { + runner = MoveNextRunner2.Create(ref stateMachine); + } + + awaiter.OnCompleted(runner.CallMoveNext); + } + + // 6. AwaitUnsafeOnCompleted + [DebuggerHidden] + [SecuritySafeCritical] + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + { + if (promise == null) + { + promise = AutoResetUniTaskCompletionSource.Create(); + } + if (runner == null) + { + runner = MoveNextRunner2.Create(ref stateMachine); + } + + awaiter.OnCompleted(runner.CallMoveNext); + } + + // 7. Start + [DebuggerHidden] + public void Start(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 + { + // cache items. + AutoResetUniTaskCompletionSource promise; + IMoveNextRunner runner; + + // 1. Static Create method. + [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static AsyncUniTask2MethodBuilder Create() + { + return default; + } + + // 2. TaskLike Task property. + [DebuggerHidden] + public UniTask2 Task + { + 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(T result) + { + // runner is finished, return first. + if (runner != null) + { + runner.Return(); + runner = null; + } + + if (promise == null) + { + promise = AutoResetUniTaskCompletionSource.Create(); + } + promise.SetResult(result); + } + + // 5. AwaitOnCompleted + [DebuggerHidden] + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + { + if (promise == null) + { + promise = AutoResetUniTaskCompletionSource.Create(); + } + if (runner == null) + { + runner = MoveNextRunner2.Create(ref stateMachine); + } + + awaiter.OnCompleted(runner.CallMoveNext); + } + + // 6. AwaitUnsafeOnCompleted + [DebuggerHidden] + [SecuritySafeCritical] + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + { + if (promise == null) + { + promise = AutoResetUniTaskCompletionSource.Create(); + } + if (runner == null) + { + runner = MoveNextRunner2.Create(ref stateMachine); + } + + awaiter.OnCompleted(runner.CallMoveNext); + } + + // 7. Start + [DebuggerHidden] + public void Start(ref TStateMachine stateMachine) + where TStateMachine : IAsyncStateMachine + { + stateMachine.MoveNext(); + } + + // 8. SetStateMachine + [DebuggerHidden] + public void SetStateMachine(IAsyncStateMachine stateMachine) + { + // don't use boxed stateMachine. + } + } } #endif \ No newline at end of file diff --git a/Assets/UniRx.Async/CompilerServices/AsyncUniTaskVoidMethodBuilder.cs b/Assets/UniRx.Async/CompilerServices/AsyncUniTaskVoidMethodBuilder.cs index 2fcd55f..9fad249 100644 --- a/Assets/UniRx.Async/CompilerServices/AsyncUniTaskVoidMethodBuilder.cs +++ b/Assets/UniRx.Async/CompilerServices/AsyncUniTaskVoidMethodBuilder.cs @@ -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(); - moveNext = runner.Run; - runner.StateMachine = stateMachine; // set after create delegate. + runner = MoveNextRunner2.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(); - moveNext = runner.Run; - runner.StateMachine = stateMachine; // set after create delegate. + runner = MoveNextRunner2.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. } } } diff --git a/Assets/UniRx.Async/CompilerServices/MoveNextRunner.cs b/Assets/UniRx.Async/CompilerServices/MoveNextRunner.cs index ddf7a03..7c7155f 100644 --- a/Assets/UniRx.Async/CompilerServices/MoveNextRunner.cs +++ b/Assets/UniRx.Async/CompilerServices/MoveNextRunner.cs @@ -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 where TStateMachine : IAsyncStateMachine { @@ -18,6 +21,52 @@ namespace UniRx.Async.CompilerServices StateMachine.MoveNext(); } } + + internal interface IMoveNextRunner + { + Action CallMoveNext { get; } + void Return(); + } + + internal class MoveNextRunner2 : IMoveNextRunner, IPromisePoolItem + where TStateMachine : IAsyncStateMachine + { + static PromisePool> pool = new PromisePool>(); + + TStateMachine stateMachine; + internal readonly Action callMoveNext; + + public Action CallMoveNext => callMoveNext; + + MoveNextRunner2() + { + callMoveNext = MoveNext; + } + + public static MoveNextRunner2 Create(ref TStateMachine stateMachine) + { + var result = pool.TryRent() ?? new MoveNextRunner2(); + 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 \ No newline at end of file diff --git a/Assets/UniRx.Async/DiagnosticsExtensions.cs b/Assets/UniRx.Async/DiagnosticsExtensions.cs index 041b0c3..d0dfd33 100644 --- a/Assets/UniRx.Async/DiagnosticsExtensions.cs +++ b/Assets/UniRx.Async/DiagnosticsExtensions.cs @@ -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 "" + withAssetsPath + ":" + line + ""; } } } diff --git a/Assets/UniRx.Async/Editor/UniTaskTrackerTreeView.cs b/Assets/UniRx.Async/Editor/UniTaskTrackerTreeView.cs index 7e03931..7e5e3cd 100644 --- a/Assets/UniRx.Async/Editor/UniTaskTrackerTreeView.cs +++ b/Assets/UniRx.Async/Editor/UniTaskTrackerTreeView.cs @@ -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("(.+)", 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(); - 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 }); }); diff --git a/Assets/UniRx.Async/Editor/UniTaskTrackerWindow.cs b/Assets/UniRx.Async/Editor/UniTaskTrackerWindow.cs index 73bbb18..f35e1f8 100644 --- a/Assets/UniRx.Async/Editor/UniTaskTrackerWindow.cs +++ b/Assets/UniRx.Async/Editor/UniTaskTrackerWindow.cs @@ -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("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 \ No newline at end of file diff --git a/Assets/UniRx.Async/IAwaiter.cs b/Assets/UniRx.Async/IAwaiter.cs index 6450ea5..46708d1 100644 --- a/Assets/UniRx.Async/IAwaiter.cs +++ b/Assets/UniRx.Async/IAwaiter.cs @@ -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 { /// The operation has not yet completed. @@ -17,6 +20,24 @@ namespace UniRx.Async Canceled = 3 } + + + // similar as IValueTaskSource + public interface IUniTaskSource + { + AwaiterStatus GetStatus(short token); + void OnCompleted(Action continuation, object state, short token); + void GetResult(short token); + + AwaiterStatus UnsafeGetStatus(); // only for debug use. + } + + public interface IUniTaskSource : IUniTaskSource + { + new T GetResult(short token); + } + + public interface IAwaiter : ICriticalNotifyCompletion { AwaiterStatus Status { get; } diff --git a/Assets/UniRx.Async/Internal/PromisePool.cs b/Assets/UniRx.Async/Internal/PromisePool.cs new file mode 100644 index 0000000..5667bac --- /dev/null +++ b/Assets/UniRx.Async/Internal/PromisePool.cs @@ -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 + where T : class, IPromisePoolItem + { + int count = 0; + readonly ConcurrentQueue queue = new ConcurrentQueue(); + 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; + } + } + } +} diff --git a/Assets/UniRx.Async/Internal/PromisePool.cs.meta b/Assets/UniRx.Async/Internal/PromisePool.cs.meta new file mode 100644 index 0000000..76cc60b --- /dev/null +++ b/Assets/UniRx.Async/Internal/PromisePool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fcb1f7467a3e2b64c8a016c8aee2f9b4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniRx.Async/Internal/TaskTracker.cs b/Assets/UniRx.Async/Internal/TaskTracker.cs index 9d2e65c..a1d2ae6 100644 --- a/Assets/UniRx.Async/Internal/TaskTracker.cs +++ b/Assets/UniRx.Async/Internal/TaskTracker.cs @@ -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> listPool = new List>(); + + static readonly WeakDictionary tracking = new WeakDictionary(); + + [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; + } + + /// (trackingId, awaiterType, awaiterStatus, createdTime, stackTrace) + public static void ForEachActiveTask(Action 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(null, (0, default(DateTime), null)); // clear + } + } + catch + { + listPool.Clear(); + throw; + } + } + } + } } #endif \ No newline at end of file diff --git a/Assets/UniRx.Async/UniTask.Factory.cs b/Assets/UniRx.Async/UniTask.Factory.cs index 0150450..291ce5c 100644 --- a/Assets/UniRx.Async/UniTask.Factory.cs +++ b/Assets/UniRx.Async/UniTask.Factory.cs @@ -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 asyncAction) + { + return () => Void(asyncAction); + } + + public static UnityAction VoidUnityAction(Func asyncAction) + { + return () => Void(asyncAction); + } + /// /// helper of create add UniTaskVoid to delegate. /// For example: FooEvent += (sender, e) => UniTask.Void(async arg => { /* */ }, (sender, e)) diff --git a/Assets/UniRx.Async/UniTask.cs b/Assets/UniRx.Async/UniTask.cs index 7b80b45..72779bd 100644 --- a/Assets/UniRx.Async/UniTask.cs +++ b/Assets/UniRx.Async/UniTask.cs @@ -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(DelayPromiseCore2.Create(frameCount, timing, cancellationToken, out var token), token); + } + + } + + + public class DelayPromiseCore2 : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem + { + static readonly PromisePool pool = new PromisePool(); + + int delayFrameCount; + CancellationToken cancellationToken; + + int currentFrameCount; + UniTaskCompletionSourceCore 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 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; + } + } + + + + + + + + + /// + /// Lightweight unity specified task-like object. + /// + [AsyncMethodBuilder(typeof(AsyncUniTask2MethodBuilder))] // TODO:AsyncUniTask2 + public partial struct UniTask2 + { + // static readonly UniTask DefaultAsyncUnitTask = new UniTask(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? + + ///// + ///// returns (bool IsCanceled) instead of throws OperationCanceledException. + ///// + //public UniTask SuppressCancellationThrow() + //{ + // var status = Status; + // if (status == AwaiterStatus.Succeeded) return CompletedTasks.False; + // if (status == AwaiterStatus.Canceled) return CompletedTasks.True; + // //return new UniTask(new IsCanceledAwaiter(awaiter)); + //} + + public override string ToString() + { + var status = this.awaiter.UnsafeGetStatus(); + return (status == AwaiterStatus.Succeeded) ? "()" : "(" + status + ")"; + } + + //public static implicit operator UniTask(UniTask2 task) + //{ + // // TODO: + // throw new NotImplementedException(); + + // //if (task.awaiter != null) + // //{ + // // if (task.awaiter.IsCompleted) + // // { + // // return DefaultAsyncUnitTask; + // // } + // // else + // // { + // // // UniTask -> UniTask is free but UniTask -> UniTask requires wrapping cost. + // // return new UniTask(new AsyncUnitAwaiter(task.awaiter)); + // // } + // //} + // //else + // //{ + // // return DefaultAsyncUnitTask; + // //} + //} + + //class AsyncUnitAwaiter : IAwaiter + //{ + // 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 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 InvokeActionDelegate = InvokeAction; + + static void InvokeAction(object state) + { + ((Action)state).Invoke(); + } + } + + + + + /// + /// Lightweight unity specified task-like object. + /// + [AsyncMethodBuilder(typeof(AsyncUniTask2MethodBuilder))] // TODO:AsyncUniTask2~T + public struct UniTask2 + { + // static readonly UniTask DefaultAsyncUnitTask = new UniTask(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)] + T GetResult() + { + return awaiter.GetResult(token); + } + + // TODO:can be suppress? + + ///// + ///// returns (bool IsCanceled) instead of throws OperationCanceledException. + ///// + //public UniTask SuppressCancellationThrow() + //{ + // var status = Status; + // if (status == AwaiterStatus.Succeeded) return CompletedTasks.False; + // if (status == AwaiterStatus.Canceled) return CompletedTasks.True; + // //return new UniTask(new IsCanceledAwaiter(awaiter)); + //} + + public override string ToString() + { + var status = this.awaiter.UnsafeGetStatus(); + return (status == AwaiterStatus.Succeeded) ? "()" : "(" + status + ")"; + } + + //public static implicit operator UniTask(UniTask2 task) + //{ + // // TODO: + // throw new NotImplementedException(); + + // //if (task.awaiter != null) + // //{ + // // if (task.awaiter.IsCompleted) + // // { + // // return DefaultAsyncUnitTask; + // // } + // // else + // // { + // // // UniTask -> UniTask is free but UniTask -> UniTask requires wrapping cost. + // // return new UniTask(new AsyncUnitAwaiter(task.awaiter)); + // // } + // //} + // //else + // //{ + // // return DefaultAsyncUnitTask; + // //} + //} + + //class AsyncUnitAwaiter : IAwaiter + //{ + // 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 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 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); + } + } + } + + /// /// Lightweight unity specified task-like object. /// diff --git a/Assets/UniRx.Async/UniTaskCompletionSource.cs b/Assets/UniRx.Async/UniTaskCompletionSource.cs index c9a148d..9432666 100644 --- a/Assets/UniRx.Async/UniTaskCompletionSource.cs +++ b/Assets/UniRx.Async/UniTaskCompletionSource.cs @@ -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 + { + // Struct Size: TResult + (8 + 2 + 1 + 1 + 8 + 8) + + TResult result; + object error; // Exception or OperationCanceledException + short version; + bool completed; + bool hasUnhandledError; + + Action 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 + { + } + } + } + + /// Completes with a successful result. + /// The result. + public void SetResult(TResult result) + { + this.result = result; + SignalCompletion(); + } + + /// Completes with an error. + /// The exception. + 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(); + } + + /// Gets the operation version. + public short Version => version; + + /// Gets the status of the operation. + /// Opaque value that was provided to the 's constructor. + [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; + } + + /// Gets the status of the operation without token validation. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public AwaiterStatus UnsafeGetStatus() + { + return (continuation == null || !completed) ? AwaiterStatus.Pending + : (error == null) ? AwaiterStatus.Succeeded + : (error is OperationCanceledException) ? AwaiterStatus.Canceled + : AwaiterStatus.Faulted; + } + + /// Gets the result of the operation. + /// Opaque value that was provided to the 's constructor. + // [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; + } + + /// Schedules the continuation action for this operation. + /// The continuation to invoke when the operation has completed. + /// The state object to pass to when it's invoked. + /// Opaque value that was provided to the 's constructor. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void OnCompleted(Action 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."); + } + } + + /// Signals that the operation has completed. Invoked after the result or error has been set. + [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 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 continuation, object state, short token) + { + core.OnCompleted(continuation, state, token); + } + + ~UniTaskCompletionSource2() + { + // clear error information. + core.Reset(); + } + } + + public class AutoResetUniTaskCompletionSource : IUniTaskSource, IPromisePoolItem + { + static readonly PromisePool pool = new PromisePool(); + + UniTaskCompletionSourceCore 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 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 : IUniTaskSource + { + UniTaskCompletionSourceCore core; + bool handled = false; + + public UniTaskCompletionSource2() + { + // TODO: TaskTracker.TrackActiveTask + } + + [Conditional("UNITY_EDITOR")] + void MarkHandled() + { + if (!handled) + { + handled = true; + // TODO: + // TaskTracker.RemoveTracking(this); + } + } + + public UniTask2 Task + { + get + { + return new UniTask2(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 continuation, object state, short token) + { + core.OnCompleted(continuation, state, token); + } + + ~UniTaskCompletionSource2() + { + // clear error information. + core.Reset(); + } + } + + public class AutoResetUniTaskCompletionSource : IUniTaskSource, IPromisePoolItem + { + static readonly PromisePool> pool = new PromisePool>(); + + UniTaskCompletionSourceCore 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 CreateFromResult(T result, out short token) + { + var source = Create(); + source.SetResult(result); + token = source.core.Version; + return source; + } + + public UniTask2 Task + { + get + { + return new UniTask2(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 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 s_sentinel = CompletionSentinel; + + private static void CompletionSentinel(object _) // named method to aid debugging + { + throw new InvalidOperationException("The sentinel delegate should never be invoked."); + } + } } #endif \ No newline at end of file diff --git a/Assets/UniRx.Async/UniTaskScheduler.cs b/Assets/UniRx.Async/UniTaskScheduler.cs index 9270445..e1d8fe1 100644 --- a/Assets/UniRx.Async/UniTaskScheduler.cs +++ b/Assets/UniRx.Async/UniTaskScheduler.cs @@ -19,12 +19,12 @@ namespace UniRx.Async public static bool PropagateOperationCanceledException = false; /// - /// 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. /// - public static UnityEngine.LogType UnobservedExceptionWriteLogType = UnityEngine.LogType.Warning; + public static UnityEngine.LogType UnobservedExceptionWriteLogType = UnityEngine.LogType.Error; /// - /// Dispatch exception event to Unity MainThread. + /// Dispatch exception event to Unity MainThread. Default is true. /// 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); diff --git a/Assets/UniRx.Async/UnityAsyncExtensions.cs b/Assets/UniRx.Async/UnityAsyncExtensions.cs index 66ce02b..f14bbe5 100644 --- a/Assets/UniRx.Async/UnityAsyncExtensions.cs +++ b/Assets/UniRx.Async/UnityAsyncExtensions.cs @@ -491,6 +491,127 @@ namespace UniRx.Async } } + + + // TODO: try to check API. + class ResourceRequestConfiguredSource : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem + { + static readonly PromisePool pool = new PromisePool(); + + ResourceRequest asyncOperation; + IProgress progress; + CancellationToken cancellationToken; + + UniTaskCompletionSourceCore core; + + ResourceRequestConfiguredSource() + { + + } + + public static IUniTaskSource Create(ResourceRequest asyncOperation, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, out short token) + { + if (cancellationToken.IsCancellationRequested) + { + return AutoResetUniTaskCompletionSource.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 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 { AssetBundleRequest asyncOperation; diff --git a/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset index a4a08ca..4d57782 100644 --- a/ProjectSettings/EditorBuildSettings.asset +++ b/ProjectSettings/EditorBuildSettings.asset @@ -8,7 +8,7 @@ EditorBuildSettings: - enabled: 1 path: Assets/Scenes/SandboxMain.unity guid: 2cda990e2423bbf4892e6590ba056729 - - enabled: 1 - path: Assets/Scenes/NextScene.unity - guid: dfced9d8875377c44a1d53620d36f0d5 + - enabled: 0 + path: + guid: 00000000000000000000000000000000 m_configObjects: {} diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index f788aee..a94ae28 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -3,7 +3,7 @@ --- !u!129 &1 PlayerSettings: m_ObjectHideFlags: 0 - serializedVersion: 17 + serializedVersion: 20 productGUID: 904cd7a3163037f42a9204c0e2f2b7bd AndroidProfiler: 0 AndroidFilterTouchesWhenObscured: 0 @@ -52,8 +52,7 @@ PlayerSettings: m_StackTraceTypes: 010000000100000001000000010000000100000001000000 iosShowActivityIndicatorOnLoading: -1 androidShowActivityIndicatorOnLoading: -1 - iosAppInBackgroundBehavior: 0 - displayResolutionDialog: 0 + iosUseCustomAppBackgroundBehavior: 0 iosAllowHTTPDownload: 1 allowedAutorotateToPortrait: 1 allowedAutorotateToPortraitUpsideDown: 1 @@ -80,11 +79,11 @@ PlayerSettings: usePlayerLog: 1 bakeCollisionMeshes: 0 forceSingleInstance: 0 + useFlipModelSwapchain: 1 resizableWindow: 1 useMacAppStoreValidation: 0 macAppStoreCategory: public.app-category.games gpuSkinning: 0 - graphicsJobs: 0 xboxPIXTextureCapture: 0 xboxEnableAvatar: 0 xboxEnableKinect: 0 @@ -92,7 +91,6 @@ PlayerSettings: xboxEnableFitness: 0 visibleInBackground: 1 allowFullscreenSwitch: 1 - graphicsJobMode: 0 fullscreenMode: 3 xboxSpeechDB: 0 xboxEnableHeadOrientation: 0 @@ -105,6 +103,7 @@ PlayerSettings: xboxOneMonoLoggingLevel: 0 xboxOneLoggingLevel: 1 xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 xboxOnePresentImmediateThreshold: 0 switchQueueCommandMemory: 0 switchQueueControlMemory: 16384 @@ -112,6 +111,7 @@ PlayerSettings: switchNVNShaderPoolsGranularity: 33554432 switchNVNDefaultPoolsGranularity: 16777216 switchNVNOtherPoolsGranularity: 16777216 + vulkanNumSwapchainBuffers: 3 vulkanEnableSetSRGBWrite: 0 m_SupportedAspectRatios: 4:3: 1 @@ -150,11 +150,13 @@ PlayerSettings: sharedDepthBuffer: 1 dashSupport: 1 lowOverheadMode: 0 + protectedContext: 0 + v2Signing: 1 enable360StereoCapture: 0 isWsaHolographicRemotingEnabled: 0 - protectGraphicsMemory: 0 enableFrameTimingStats: 0 useHDRDisplay: 0 + D3DHDRBitDepth: 0 m_ColorGamuts: 00000000 targetPixelDensity: 30 resolutionScalingMode: 0 @@ -164,7 +166,7 @@ PlayerSettings: Standalone: com.Company.ProductName buildNumber: {} AndroidBundleVersionCode: 1 - AndroidMinSdkVersion: 16 + AndroidMinSdkVersion: 19 AndroidTargetSdkVersion: 0 AndroidPreferredInstallLocation: 1 aotOptions: @@ -179,10 +181,10 @@ PlayerSettings: StripUnusedMeshComponents: 1 VertexChannelCompressionMask: 4054 iPhoneSdkVersion: 988 - iOSTargetOSVersionString: 9.0 + iOSTargetOSVersionString: 10.0 tvOSSdkVersion: 0 tvOSRequireExtendedGameController: 0 - tvOSTargetOSVersionString: 9.0 + tvOSTargetOSVersionString: 10.0 uIPrerenderedIcon: 0 uIRequiresPersistentWiFi: 0 uIRequiresFullScreen: 1 @@ -272,16 +274,46 @@ PlayerSettings: androidGamepadSupportLevel: 0 AndroidValidateAppBundleSize: 1 AndroidAppBundleSizeToValidate: 100 - resolutionDialogBanner: {fileID: 0} m_BuildTargetIcons: [] m_BuildTargetPlatformIcons: [] m_BuildTargetBatching: [] + m_BuildTargetGraphicsJobs: + - m_BuildTarget: MacStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: Switch + m_GraphicsJobs: 0 + - m_BuildTarget: MetroSupport + m_GraphicsJobs: 0 + - m_BuildTarget: AppleTVSupport + m_GraphicsJobs: 0 + - m_BuildTarget: BJMSupport + m_GraphicsJobs: 0 + - m_BuildTarget: LinuxStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: PS4Player + m_GraphicsJobs: 0 + - m_BuildTarget: iOSSupport + m_GraphicsJobs: 0 + - m_BuildTarget: WindowsStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: XboxOnePlayer + m_GraphicsJobs: 0 + - m_BuildTarget: LuminSupport + m_GraphicsJobs: 0 + - m_BuildTarget: AndroidPlayer + m_GraphicsJobs: 0 + - m_BuildTarget: WebGLSupport + m_GraphicsJobs: 0 + m_BuildTargetGraphicsJobMode: + - m_BuildTarget: PS4Player + m_GraphicsJobMode: 0 + - m_BuildTarget: XboxOnePlayer + m_GraphicsJobMode: 0 m_BuildTargetGraphicsAPIs: - m_BuildTarget: AndroidPlayer m_APIs: 150000000b000000 m_Automatic: 0 m_BuildTargetVRSettings: [] - m_BuildTargetEnableVuforiaSettings: [] openGLRequireES31: 0 openGLRequireES31AEP: 0 openGLRequireES32: 0 @@ -400,6 +432,7 @@ PlayerSettings: switchRatingsInt_9: 0 switchRatingsInt_10: 0 switchRatingsInt_11: 0 + switchRatingsInt_12: 0 switchLocalCommunicationIds_0: switchLocalCommunicationIds_1: switchLocalCommunicationIds_2: @@ -481,6 +514,7 @@ PlayerSettings: ps4UseResolutionFallback: 0 ps4ReprojectionSupport: 0 ps4UseAudio3dBackend: 0 + ps4UseLowGarlicFragmentationMode: 1 ps4SocialScreenEnabled: 0 ps4ScriptOptimizationLevel: 0 ps4Audio3dVirtualSpeakerCount: 14 @@ -499,6 +533,7 @@ PlayerSettings: ps4contentSearchFeaturesUsed: 0 ps4attribEyeToEyeDistanceSettingVR: 0 ps4IncludedModules: [] + ps4attribVROutputEnabled: 0 monoEnv: splashScreenBackgroundSourceLandscape: {fileID: 0} splashScreenBackgroundSourcePortrait: {fileID: 0} @@ -602,13 +637,6 @@ PlayerSettings: luminVersion: m_VersionCode: 1 m_VersionName: - facebookSdkVersion: 7.9.4 - facebookAppId: - facebookCookies: 1 - facebookLogging: 1 - facebookStatus: 1 - facebookXfbml: 0 - facebookFrictionlessRequests: 1 apiCompatibilityLevel: 6 cloudProjectId: framebufferDepthMemorylessMode: 0 diff --git a/ProjectSettings/UnityConnectSettings.asset b/ProjectSettings/UnityConnectSettings.asset index c3ae9a0..fa0b146 100644 --- a/ProjectSettings/UnityConnectSettings.asset +++ b/ProjectSettings/UnityConnectSettings.asset @@ -4,7 +4,7 @@ UnityConnectSettings: m_ObjectHideFlags: 0 serializedVersion: 1 - m_Enabled: 1 + m_Enabled: 0 m_TestMode: 0 m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events m_EventUrl: https://cdp.cloud.unity3d.com/v1/events