From 6351d4c5a47a9c721cbf0683ab4ad852b7caa8f2 Mon Sep 17 00:00:00 2001 From: neuecc Date: Thu, 25 Feb 2021 11:12:15 +0900 Subject: [PATCH 01/11] test1 --- .../UniTask/Runtime/PlayerLoopHelper.cs | 114 +++++++++++++----- 1 file changed, 81 insertions(+), 33 deletions(-) diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs index aea1109..1e2512c 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs @@ -98,6 +98,68 @@ namespace Cysharp.Threading.Tasks #endif } + [Flags] + public enum InjectPlayerLoopTimings + { + // Preset + All = + Initialization | LastInitialization | + EarlyUpdate | LastEarlyUpdate | + FixedUpdate | LastFixedUpdate | + PreUpdate | LastPreUpdate | + Update | LastUpdate | + PreLateUpdate | LastPreLateUpdate | + PostLateUpdate | LastPostLateUpdate | +#if UNITY_2020_2_OR_NEWER + TimeUpdate | LastTimeUpdate, +#endif + + Standard = + Initialization | + EarlyUpdate | + FixedUpdate | + PreUpdate | + Update | + PreLateUpdate | + PostLateUpdate | LastPostLateUpdate | +#if UNITY_2020_2_OR_NEWER + TimeUpdate, +#endif + + Minimum = + Update | FixedUpdate | LastPostLateUpdate, + + // PlayerLoopTiming + + Initialization = 1, + LastInitialization = 2, + + EarlyUpdate = 4, + LastEarlyUpdate = 8, + + FixedUpdate = 16, + LastFixedUpdate = 32, + + PreUpdate = 64, + LastPreUpdate = 128, + + Update = 256, + LastUpdate = 512, + + PreLateUpdate = 1024, + LastPreLateUpdate = 2048, + + PostLateUpdate = 4096, + LastPostLateUpdate = 8192, + +#if UNITY_2020_2_OR_NEWER + // Unity 2020.2 added TimeUpdate https://docs.unity3d.com/2020.2/Documentation/ScriptReference/PlayerLoop.TimeUpdate.html + TimeUpdate = 16384, + LastTimeUpdate = 32768, +#endif + + } + public interface IPlayerLoopItem { bool MoveNext(); @@ -118,8 +180,9 @@ namespace Cysharp.Threading.Tasks static PlayerLoopRunner[] runners; internal static bool IsEditorApplicationQuitting { get; private set; } static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem, - Type loopRunnerYieldType, ContinuationQueue cq, Type lastLoopRunnerYieldType, ContinuationQueue lastCq, - Type loopRunnerType, PlayerLoopRunner runner, Type lastLoopRunnerType, PlayerLoopRunner lastRunner) + bool injectOnFirst, + Type loopRunnerYieldType, ContinuationQueue cq, + Type loopRunnerType, PlayerLoopRunner runner) { #if UNITY_EDITOR @@ -134,22 +197,11 @@ namespace Cysharp.Threading.Tasks runner.Run(); runner.Clear(); } - if (lastRunner != null) - { - lastRunner.Run(); - lastRunner.Clear(); - } - if (cq != null) { cq.Run(); cq.Clear(); } - if (lastCq != null) - { - lastCq.Run(); - lastCq.Clear(); - } IsEditorApplicationQuitting = false; } }; @@ -161,36 +213,30 @@ namespace Cysharp.Threading.Tasks updateDelegate = cq.Run }; - var lastYieldLoop = new PlayerLoopSystem - { - type = lastLoopRunnerYieldType, - updateDelegate = lastCq.Run - }; - var runnerLoop = new PlayerLoopSystem { type = loopRunnerType, updateDelegate = runner.Run }; - var lastRunnerLoop = new PlayerLoopSystem - { - type = lastLoopRunnerType, - updateDelegate = lastRunner.Run - }; - // Remove items from previous initializations. var source = loopSystem.subSystemList - .Where(ls => ls.type != loopRunnerYieldType && ls.type != loopRunnerType && ls.type != lastLoopRunnerYieldType && ls.type != lastLoopRunnerType) + .Where(ls => ls.type != loopRunnerYieldType && ls.type != loopRunnerType) .ToArray(); - var dest = new PlayerLoopSystem[source.Length + 4]; + var dest = new PlayerLoopSystem[source.Length + 2]; - Array.Copy(source, 0, dest, 2, source.Length); - dest[0] = yieldLoop; - dest[1] = runnerLoop; - dest[dest.Length - 2] = lastYieldLoop; - dest[dest.Length - 1] = lastRunnerLoop; + Array.Copy(source, 0, dest, injectOnFirst ? 2 : 0, source.Length); + if (injectOnFirst) + { + dest[0] = yieldLoop; + dest[1] = runnerLoop; + } + else + { + dest[dest.Length - 2] = yieldLoop; + dest[dest.Length - 1] = runnerLoop; + } return dest; } @@ -311,7 +357,7 @@ namespace Cysharp.Threading.Tasks throw new Exception("Target PlayerLoopSystem does not found. Type:" + systemType.FullName); } - public static void Initialize(ref PlayerLoopSystem playerLoop) + public static void Initialize(ref PlayerLoopSystem playerLoop, InjectPlayerLoopTimings injectTimings = InjectPlayerLoopTimings.All) { #if UNITY_2020_2_OR_NEWER yielders = new ContinuationQueue[16]; @@ -324,6 +370,8 @@ namespace Cysharp.Threading.Tasks var copyList = playerLoop.subSystemList.ToArray(); var i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Initialization)); + if (injectTimings | InjectPlayerLoopTimings. + copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), yielders[0] = new ContinuationQueue(PlayerLoopTiming.Initialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldInitialization), yielders[1] = new ContinuationQueue(PlayerLoopTiming.LastInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), runners[0] = new PlayerLoopRunner(PlayerLoopTiming.Initialization), From cae512e4dedc58d5dad7a1c66957f0768c7dafc5 Mon Sep 17 00:00:00 2001 From: neuecc Date: Thu, 25 Feb 2021 19:25:48 +0900 Subject: [PATCH 02/11] Delay automatically fallback to Realtime when run on EditMode #234 --- .../Assets/Plugins/UniTask/Runtime/UniTask.Delay.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Delay.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Delay.cs index fd13bd2..3105d9f 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Delay.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Delay.cs @@ -106,6 +106,14 @@ namespace Cysharp.Threading.Tasks throw new ArgumentOutOfRangeException("Delay does not allow minus delayTimeSpan. delayTimeSpan:" + delayTimeSpan); } +#if UNITY_EDITOR + // force use Realtime. + if (!UnityEditor.EditorApplication.isPlaying) + { + delayType = DelayType.Realtime; + } +#endif + switch (delayType) { case DelayType.UnscaledDeltaTime: From 4710268e0a5b8fb79244b0ff7e161ab54767632d Mon Sep 17 00:00:00 2001 From: neuecc Date: Thu, 25 Feb 2021 19:26:37 +0900 Subject: [PATCH 03/11] Add PlayerLoopHelper.Initialize(InbjectPlayerLoopTimings) --- .../UniTask/Runtime/PlayerLoopHelper.cs | 166 ++++++++++++------ src/UniTask/Assets/Scenes/EditorTest1.cs | 32 ++-- src/UniTask/Assets/Scenes/SandboxMain.cs | 36 +++- .../Assets/TempAsm/FooMonoBehaviour.cs | 16 +- .../ProjectSettings/ProjectVersion.txt | 4 +- 5 files changed, 177 insertions(+), 77 deletions(-) diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs index 1e2512c..063908e 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs @@ -101,7 +101,9 @@ namespace Cysharp.Threading.Tasks [Flags] public enum InjectPlayerLoopTimings { - // Preset + /// + /// Preset: All loops(default). + /// All = Initialization | LastInitialization | EarlyUpdate | LastEarlyUpdate | @@ -114,6 +116,9 @@ namespace Cysharp.Threading.Tasks TimeUpdate | LastTimeUpdate, #endif + /// + /// Preset: All without last except LastPostLateUpdate. + /// Standard = Initialization | EarlyUpdate | @@ -126,6 +131,9 @@ namespace Cysharp.Threading.Tasks TimeUpdate, #endif + /// + /// Preset: Minimum pattern, Update | FixedUpdate | LastPostLateUpdate + /// Minimum = Update | FixedUpdate | LastPostLateUpdate, @@ -167,6 +175,9 @@ namespace Cysharp.Threading.Tasks public static class PlayerLoopHelper { + static readonly ContinuationQueue ThrowMarkerContinuationQueue = new ContinuationQueue(PlayerLoopTiming.Initialization); + static readonly PlayerLoopRunner ThrowMarkerPlayerLoopRunner = new PlayerLoopRunner(PlayerLoopTiming.Initialization); + public static SynchronizationContext UnitySynchronizationContext => unitySynchronizationContetext; public static int MainThreadId => mainThreadId; internal static string ApplicationDataPath => applicationDataPath; @@ -220,10 +231,7 @@ namespace Cysharp.Threading.Tasks }; // Remove items from previous initializations. - var source = loopSystem.subSystemList - .Where(ls => ls.type != loopRunnerYieldType && ls.type != loopRunnerType) - .ToArray(); - + var source = RemoveRunner(loopSystem, loopRunnerYieldType, loopRunnerType); var dest = new PlayerLoopSystem[source.Length + 2]; Array.Copy(source, 0, dest, injectOnFirst ? 2 : 0, source.Length); @@ -241,6 +249,13 @@ namespace Cysharp.Threading.Tasks return dest; } + static PlayerLoopSystem[] RemoveRunner(PlayerLoopSystem loopSystem, Type loopRunnerYieldType, Type loopRunnerType) + { + return loopSystem.subSystemList + .Where(ls => ls.type != loopRunnerYieldType && ls.type != loopRunnerType) + .ToArray(); + } + static PlayerLoopSystem[] InsertUniTaskSynchronizationContext(PlayerLoopSystem loopSystem) { var loop = new PlayerLoopSystem @@ -357,6 +372,22 @@ namespace Cysharp.Threading.Tasks throw new Exception("Target PlayerLoopSystem does not found. Type:" + systemType.FullName); } + static void InsertLoop(PlayerLoopSystem[] copyList, InjectPlayerLoopTimings injectTimings, Type loopType, InjectPlayerLoopTimings targetTimings, + int index, bool injectOnFirst, Type loopRunnerYieldType, Type loopRunnerType, PlayerLoopTiming playerLoopTiming) + { + var i = FindLoopSystemIndex(copyList, loopType); + if ((injectTimings & targetTimings) == targetTimings) + { + copyList[i].subSystemList = InsertRunner(copyList[i], injectOnFirst, + loopRunnerYieldType, yielders[index] = new ContinuationQueue(playerLoopTiming), + loopRunnerType, runners[index] = new PlayerLoopRunner(playerLoopTiming)); + } + else + { + copyList[i].subSystemList = RemoveRunner(copyList[i], loopRunnerYieldType, loopRunnerType); + } + } + public static void Initialize(ref PlayerLoopSystem playerLoop, InjectPlayerLoopTimings injectTimings = InjectPlayerLoopTimings.All) { #if UNITY_2020_2_OR_NEWER @@ -369,60 +400,82 @@ namespace Cysharp.Threading.Tasks var copyList = playerLoop.subSystemList.ToArray(); - var i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Initialization)); - if (injectTimings | InjectPlayerLoopTimings. + // Initialization + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Initialization), + InjectPlayerLoopTimings.Initialization, 0, true, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), PlayerLoopTiming.Initialization); + + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Initialization), + InjectPlayerLoopTimings.LastInitialization, 1, false, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastInitialization), PlayerLoopTiming.LastInitialization); - copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), yielders[0] = new ContinuationQueue(PlayerLoopTiming.Initialization), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldInitialization), yielders[1] = new ContinuationQueue(PlayerLoopTiming.LastInitialization), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), runners[0] = new PlayerLoopRunner(PlayerLoopTiming.Initialization), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastInitialization), runners[1] = new PlayerLoopRunner(PlayerLoopTiming.LastInitialization)); // EarlyUpdate - i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.EarlyUpdate)); - copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), yielders[2] = new ContinuationQueue(PlayerLoopTiming.EarlyUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldEarlyUpdate), yielders[3] = new ContinuationQueue(PlayerLoopTiming.LastEarlyUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerEarlyUpdate), runners[2] = new PlayerLoopRunner(PlayerLoopTiming.EarlyUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastEarlyUpdate), runners[3] = new PlayerLoopRunner(PlayerLoopTiming.LastEarlyUpdate)); + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.EarlyUpdate), + InjectPlayerLoopTimings.EarlyUpdate, 2, true, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerEarlyUpdate), PlayerLoopTiming.EarlyUpdate); + + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.EarlyUpdate), + InjectPlayerLoopTimings.LastEarlyUpdate, 3, false, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldEarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastEarlyUpdate), PlayerLoopTiming.LastEarlyUpdate); + // FixedUpdate - i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.FixedUpdate)); - copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), yielders[4] = new ContinuationQueue(PlayerLoopTiming.FixedUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldFixedUpdate), yielders[5] = new ContinuationQueue(PlayerLoopTiming.LastFixedUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerFixedUpdate), runners[4] = new PlayerLoopRunner(PlayerLoopTiming.FixedUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastFixedUpdate), runners[5] = new PlayerLoopRunner(PlayerLoopTiming.LastFixedUpdate)); + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.FixedUpdate), + InjectPlayerLoopTimings.FixedUpdate, 4, true, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerFixedUpdate), PlayerLoopTiming.FixedUpdate); + + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.FixedUpdate), + InjectPlayerLoopTimings.LastFixedUpdate, 5, false, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldFixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastFixedUpdate), PlayerLoopTiming.LastFixedUpdate); + // PreUpdate - i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.PreUpdate)); - copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), yielders[6] = new ContinuationQueue(PlayerLoopTiming.PreUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreUpdate), yielders[7] = new ContinuationQueue(PlayerLoopTiming.LastPreUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreUpdate), runners[6] = new PlayerLoopRunner(PlayerLoopTiming.PreUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreUpdate), runners[7] = new PlayerLoopRunner(PlayerLoopTiming.LastPreUpdate)); + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreUpdate), + InjectPlayerLoopTimings.PreUpdate, 6, true, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreUpdate), PlayerLoopTiming.PreUpdate); + + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreUpdate), + InjectPlayerLoopTimings.LastPreUpdate, 7, false, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreUpdate), PlayerLoopTiming.LastPreUpdate); + // Update - i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Update)); - copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), yielders[8] = new ContinuationQueue(PlayerLoopTiming.Update), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldUpdate), yielders[9] = new ContinuationQueue(PlayerLoopTiming.LastUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerUpdate), runners[8] = new PlayerLoopRunner(PlayerLoopTiming.Update), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastUpdate), runners[9] = new PlayerLoopRunner(PlayerLoopTiming.LastUpdate)); + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Update), + InjectPlayerLoopTimings.Update, 8, true, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerUpdate), PlayerLoopTiming.Update); + + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Update), + InjectPlayerLoopTimings.LastUpdate, 9, false, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastUpdate), PlayerLoopTiming.LastUpdate); + // PreLateUpdate - i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.PreLateUpdate)); - copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), yielders[10] = new ContinuationQueue(PlayerLoopTiming.PreLateUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreLateUpdate), yielders[11] = new ContinuationQueue(PlayerLoopTiming.LastPreLateUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreLateUpdate), runners[10] = new PlayerLoopRunner(PlayerLoopTiming.PreLateUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreLateUpdate), runners[11] = new PlayerLoopRunner(PlayerLoopTiming.LastPreLateUpdate)); + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreLateUpdate), + InjectPlayerLoopTimings.PreLateUpdate, 10, true, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreLateUpdate), PlayerLoopTiming.PreLateUpdate); + + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreLateUpdate), + InjectPlayerLoopTimings.LastPreLateUpdate, 11, false, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreLateUpdate), PlayerLoopTiming.LastPreLateUpdate); + // PostLateUpdate - i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.PostLateUpdate)); - copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), yielders[12] = new ContinuationQueue(PlayerLoopTiming.PostLateUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPostLateUpdate), yielders[13] = new ContinuationQueue(PlayerLoopTiming.LastPostLateUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), runners[12] = new PlayerLoopRunner(PlayerLoopTiming.PostLateUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPostLateUpdate), runners[13] = new PlayerLoopRunner(PlayerLoopTiming.LastPostLateUpdate)); + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PostLateUpdate), + InjectPlayerLoopTimings.PostLateUpdate, 12, true, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), PlayerLoopTiming.PostLateUpdate); + + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PostLateUpdate), + InjectPlayerLoopTimings.LastPostLateUpdate, 13, false, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPostLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPostLateUpdate), PlayerLoopTiming.LastPostLateUpdate); + #if UNITY_2020_2_OR_NEWER // TimeUpdate - i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.TimeUpdate)); - copyList[i].subSystemList = InsertRunner(copyList[i], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldTimeUpdate), yielders[14] = new ContinuationQueue(PlayerLoopTiming.TimeUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldTimeUpdate), yielders[15] = new ContinuationQueue(PlayerLoopTiming.LastTimeUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerTimeUpdate), runners[14] = new PlayerLoopRunner(PlayerLoopTiming.TimeUpdate), - typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastTimeUpdate), runners[15] = new PlayerLoopRunner(PlayerLoopTiming.LastTimeUpdate)); + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.TimeUpdate), + InjectPlayerLoopTimings.TimeUpdate, 14, true, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldTimeUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerTimeUpdate), PlayerLoopTiming.TimeUpdate); + + InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.TimeUpdate), + InjectPlayerLoopTimings.LastTimeUpdate, 15, false, + typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldTimeUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastTimeUpdate), PlayerLoopTiming.LastTimeUpdate); #endif // Insert UniTaskSynchronizationContext to Update loop - i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Update)); + var i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Update)); copyList[i].subSystemList = InsertUniTaskSynchronizationContext(copyList[i]); playerLoop.subSystemList = copyList; @@ -431,12 +484,27 @@ namespace Cysharp.Threading.Tasks public static void AddAction(PlayerLoopTiming timing, IPlayerLoopItem action) { - runners[(int)timing].AddAction(action); + var runner = runners[(int)timing]; + if (runner == null) + { + ThrowInvalidLoopTiming(timing); + } + runner.AddAction(action); + } + + static void ThrowInvalidLoopTiming(PlayerLoopTiming playerLoopTiming) + { + throw new InvalidOperationException("Target playerLoopTiming is not injected. Please check PlayerLoopHelper.Initialize. PlayerLoopTiming:" + playerLoopTiming); } public static void AddContinuation(PlayerLoopTiming timing, Action continuation) { - yielders[(int)timing].Enqueue(continuation); + var q = yielders[(int)timing]; + if (q == null) + { + ThrowInvalidLoopTiming(timing); + } + q.Enqueue(continuation); } // Diagnostics helper diff --git a/src/UniTask/Assets/Scenes/EditorTest1.cs b/src/UniTask/Assets/Scenes/EditorTest1.cs index 95a810b..4872b8f 100644 --- a/src/UniTask/Assets/Scenes/EditorTest1.cs +++ b/src/UniTask/Assets/Scenes/EditorTest1.cs @@ -11,29 +11,29 @@ public class Test1 public static async UniTaskVoid TestFunc() { await DoSomeThing(); - string[] scenes = new string[] - { - "Assets/Scenes/SandboxMain.unity", - }; + //string[] scenes = new string[] + //{ + // "Assets/Scenes/SandboxMain.unity", + //}; - try - { - Debug.Log("Build Begin"); - BuildPipeline.BuildPlayer(scenes, Application.dataPath + "../target", BuildTarget.StandaloneWindows, BuildOptions.CompressWithLz4); - Debug.Log("Build After"); - } - catch (Exception e) - { - Debug.LogError(e.Message); - } + //try + //{ + // Debug.Log("Build Begin"); + // BuildPipeline.BuildPlayer(scenes, Application.dataPath + "../target", BuildTarget.StandaloneWindows, BuildOptions.CompressWithLz4); + // Debug.Log("Build After"); + //} + //catch (Exception e) + //{ + // Debug.LogError(e.Message); + //} } public static async UniTask DoSomeThing() { Debug.Log("Dosomething"); - await UniTask.Delay(1500, DelayType.Realtime); + await UniTask.Delay(1500, DelayType.DeltaTime); Debug.Log("Dosomething 2"); - await UniTask.Delay(1000, DelayType.Realtime); + await UniTask.Delay(1000, DelayType.DeltaTime); Debug.Log("Dosomething 3"); } } diff --git a/src/UniTask/Assets/Scenes/SandboxMain.cs b/src/UniTask/Assets/Scenes/SandboxMain.cs index 516f7f9..2ef7faa 100644 --- a/src/UniTask/Assets/Scenes/SandboxMain.cs +++ b/src/UniTask/Assets/Scenes/SandboxMain.cs @@ -549,19 +549,51 @@ public class SandboxMain : MonoBehaviour async UniTaskVoid Start() { + var defaultLoop = PlayerLoop.GetDefaultPlayerLoop(); + PlayerLoopHelper.Initialize(ref defaultLoop, InjectPlayerLoopTimings.All); + var cts = new CancellationTokenSource(); - TestAsync(cts.Token).Forget(); + // TestAsync(cts.Token).Forget(); okButton.onClick.AddListener(UniTask.UnityAction(async () => { - cts.Cancel(); + PlayerLoopHelper.DumpCurrentPlayerLoop(); await UniTask.Yield(); })); + cancelButton.onClick.AddListener(UniTask.UnityAction(async () => + { + await UniTask.Yield(PlayerLoopTiming.Initialization); + + RunCheck(PlayerLoopTiming.Initialization).Forget(); + RunCheck(PlayerLoopTiming.LastInitialization).Forget(); + RunCheck(PlayerLoopTiming.EarlyUpdate).Forget(); + RunCheck(PlayerLoopTiming.LastEarlyUpdate).Forget(); + RunCheck(PlayerLoopTiming.FixedUpdate).Forget(); + RunCheck(PlayerLoopTiming.LastFixedUpdate).Forget(); + RunCheck(PlayerLoopTiming.PreUpdate).Forget(); + RunCheck(PlayerLoopTiming.LastPreUpdate).Forget(); + RunCheck(PlayerLoopTiming.Update).Forget(); + RunCheck(PlayerLoopTiming.LastUpdate).Forget(); + RunCheck(PlayerLoopTiming.PreLateUpdate).Forget(); + RunCheck(PlayerLoopTiming.LastPreLateUpdate).Forget(); + RunCheck(PlayerLoopTiming.PostLateUpdate).Forget(); + RunCheck(PlayerLoopTiming.LastPostLateUpdate).Forget(); + + })); + await UniTask.Yield(); } + async UniTaskVoid RunCheck(PlayerLoopTiming timing) + { + //await UniTask.Yield(timing); + //UnityEngine.Debug.Log("Yield:" + timing); + await UniTask.DelayFrame(1, timing); + UnityEngine.Debug.Log("Delay:" + timing); + } + private void Application_logMessageReceived2(string condition, string stackTrace, LogType type) { throw new NotImplementedException(); diff --git a/src/UniTask/Assets/TempAsm/FooMonoBehaviour.cs b/src/UniTask/Assets/TempAsm/FooMonoBehaviour.cs index 6c62ccb..3173c0c 100644 --- a/src/UniTask/Assets/TempAsm/FooMonoBehaviour.cs +++ b/src/UniTask/Assets/TempAsm/FooMonoBehaviour.cs @@ -12,16 +12,16 @@ public class FooMonoBehaviour : MonoBehaviour } - private async UniTask Download(UnityWebRequest req, string filePath) - { - _ = req.SendWebRequest(); + //private async UniTask Download(UnityWebRequest req, string filePath) + //{ + // _ = req.SendWebRequest(); - // var aaa = await foo; - // Debug.Log(aaa); - await UniTask.Yield(); - //File.WriteAllText(filePath, req.downloadHandler.text ?? string.Empty); - } + // // var aaa = await foo; + // // Debug.Log(aaa); + // await UniTask.Yield(); + // //File.WriteAllText(filePath, req.downloadHandler.text ?? string.Empty); + //} } diff --git a/src/UniTask/ProjectSettings/ProjectVersion.txt b/src/UniTask/ProjectSettings/ProjectVersion.txt index 0560e11..a328ffd 100644 --- a/src/UniTask/ProjectSettings/ProjectVersion.txt +++ b/src/UniTask/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2020.2.0f1 -m_EditorVersionWithRevision: 2020.2.0f1 (3721df5a8b28) +m_EditorVersion: 2020.2.1f1 +m_EditorVersionWithRevision: 2020.2.1f1 (270dd8c3da1c) From dfd0fe9fe435478c6225f6162f1781bae813d768 Mon Sep 17 00:00:00 2001 From: neuecc Date: Thu, 25 Feb 2021 20:30:44 +0900 Subject: [PATCH 04/11] Add TimeoutController --- src/UniTask.NetCore/UniTask.NetCore.csproj | 1 + .../CancellationTokenSourceExtensions.cs | 1 + .../UniTask/Runtime/TimeoutController.cs | 258 ++++++++++++++++++ .../UniTask/Runtime/TimeoutController.cs.meta | 11 + src/UniTask/Assets/Scenes/SandboxMain.cs | 50 ++-- 5 files changed, 305 insertions(+), 16 deletions(-) create mode 100644 src/UniTask/Assets/Plugins/UniTask/Runtime/TimeoutController.cs create mode 100644 src/UniTask/Assets/Plugins/UniTask/Runtime/TimeoutController.cs.meta diff --git a/src/UniTask.NetCore/UniTask.NetCore.csproj b/src/UniTask.NetCore/UniTask.NetCore.csproj index 1bb7900..80dd978 100644 --- a/src/UniTask.NetCore/UniTask.NetCore.csproj +++ b/src/UniTask.NetCore/UniTask.NetCore.csproj @@ -41,6 +41,7 @@ ..\UniTask\Assets\Plugins\UniTask\Runtime\UniTaskSynchronizationContext.cs; ..\UniTask\Assets\Plugins\UniTask\Runtime\CancellationTokenSourceExtensions.cs; ..\UniTask\Assets\Plugins\UniTask\Runtime\EnumeratorAsyncExtensions.cs; +..\UniTask\Assets\Plugins\UniTask\Runtime\TimeoutController.cs; ..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopHelper.cs; ..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Delay.cs; ..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs; diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/CancellationTokenSourceExtensions.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/CancellationTokenSourceExtensions.cs index 5913908..87dcbe7 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/CancellationTokenSourceExtensions.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/CancellationTokenSourceExtensions.cs @@ -7,6 +7,7 @@ using System; namespace Cysharp.Threading.Tasks { + public static class CancellationTokenSourceExtensions { public static void CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update) diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/TimeoutController.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/TimeoutController.cs new file mode 100644 index 0000000..6dc5517 --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/TimeoutController.cs @@ -0,0 +1,258 @@ +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + +using System.Threading; +using System; +using Cysharp.Threading.Tasks.Internal; + +namespace Cysharp.Threading.Tasks +{ + // CancellationTokenSource itself can not reuse but CancelAfter(Timeout.InfiniteTimeSpan) allows reuse if did not reach timeout. + // Similar discussion: + // https://github.com/dotnet/runtime/issues/4694 + // https://github.com/dotnet/runtime/issues/48492 + // This TimeoutController emulate similar implementation, using CancelAfterSlim; to achieve zero allocation timeout. + + public sealed class TimeoutController : IDisposable + { + CancellationTokenSource timeoutSource; + CancellationTokenSource linkedSource; + StoppableDelayRealtimePromise timeoutDelay; + + readonly CancellationTokenSource originalLinkCancellationTokenSource; + + public TimeoutController() + { + this.timeoutSource = new CancellationTokenSource(); + this.originalLinkCancellationTokenSource = null; + this.linkedSource = null; + this.timeoutDelay = null; + } + + public TimeoutController(CancellationTokenSource linkCancellationTokenSource) + { + this.timeoutSource = new CancellationTokenSource(); + this.originalLinkCancellationTokenSource = linkCancellationTokenSource; + this.linkedSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutSource.Token, linkCancellationTokenSource.Token); + this.timeoutDelay = null; + } + + public CancellationToken Timeout(TimeSpan timeout) + { + if (originalLinkCancellationTokenSource != null && originalLinkCancellationTokenSource.IsCancellationRequested) + { + return originalLinkCancellationTokenSource.Token; + } + + if (timeoutSource.IsCancellationRequested) + { + timeoutSource.Dispose(); + timeoutSource = new CancellationTokenSource(); + if (linkedSource != null) + { + this.linkedSource.Cancel(); + this.linkedSource.Dispose(); + this.linkedSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutSource.Token, originalLinkCancellationTokenSource.Token); + } + } + + if (timeoutDelay == null) + { + RunDelayAsync(timeout).Forget(); // timeoutDelay = ... in RunDelayAsync(immediately, before await) + } + else + { + timeoutDelay.RestartStopwatch(); // already running RunDelayAsync + } + + return (linkedSource != null) ? linkedSource.Token : timeoutSource.Token; + } + + public bool IsTimeout() + { + return timeoutSource.IsCancellationRequested; + } + + public void Reset() + { + if (timeoutDelay != null) + { + timeoutDelay.Stop(); // stop delay, will finish RunDelayAsync + timeoutDelay = null; + } + } + + async UniTaskVoid RunDelayAsync(TimeSpan timeout) + { + timeoutDelay = StoppableDelayRealtimePromise.Create(timeout, PlayerLoopTiming.Update, (linkedSource == null) ? CancellationToken.None : linkedSource.Token, out var version); + try + { + var reason = await new UniTask(timeoutDelay, version); + if (reason == DelayResult.DelayCompleted) + { + // UnityEngine.Debug.Log("DEBUG:Timeout Complete, try to call timeoutSource.Cancel"); + timeoutSource.Cancel(); + } + else if (reason == DelayResult.LinkedTokenCanceled) + { + // UnityEngine.Debug.Log("DEBUG:LinkedSource IsCancellationRequested"); + } + else if (reason == DelayResult.ExternalStopped) + { + // Reset(Promise.Stop) called, do nothing. + // UnityEngine.Debug.Log("DEBUG:Reset called"); + } + } + finally + { + timeoutDelay = null; + } + } + + public void Dispose() + { + if (timeoutDelay != null) + { + timeoutDelay.Stop(); + } + timeoutSource.Dispose(); + if (linkedSource != null) + { + linkedSource.Dispose(); + } + } + + enum DelayResult + { + LinkedTokenCanceled, + ExternalStopped, + DelayCompleted, // as Timeout. + } + + // Stop + SuppressCancellationThrow. + sealed class StoppableDelayRealtimePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode + { + static OperationCanceledException ExterenalStopException = new OperationCanceledException(); + + static TaskPool pool; + StoppableDelayRealtimePromise nextNode; + public ref StoppableDelayRealtimePromise NextNode => ref nextNode; + + static StoppableDelayRealtimePromise() + { + TaskPool.RegisterSizeGetter(typeof(StoppableDelayRealtimePromise), () => pool.Size); + } + + long delayTimeSpanTicks; + ValueStopwatch stopwatch; + CancellationToken cancellationToken; + bool externalStop; + + UniTaskCompletionSourceCore core; + + StoppableDelayRealtimePromise() + { + } + + public static StoppableDelayRealtimePromise Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) + { + if (!pool.TryPop(out var result)) + { + result = new StoppableDelayRealtimePromise(); + } + + result.stopwatch = ValueStopwatch.StartNew(); + result.delayTimeSpanTicks = delayTimeSpan.Ticks; + result.cancellationToken = cancellationToken; + result.externalStop = false; + + TaskTracker.TrackActiveTask(result, 3); + + PlayerLoopHelper.AddAction(timing, result); + + token = result.core.Version; + return result; + } + + public void Stop() + { + externalStop = true; + } + + public void RestartStopwatch() + { + stopwatch = ValueStopwatch.StartNew(); + } + + public DelayResult GetResult(short token) + { + try + { + return core.GetResult(token); + } + finally + { + TryReturn(); + } + } + + void IUniTaskSource.GetResult(short token) + { + GetResult(token); + } + + public UniTaskStatus GetStatus(short token) + { + return core.GetStatus(token); + } + + public UniTaskStatus UnsafeGetStatus() + { + return core.UnsafeGetStatus(); + } + + public void OnCompleted(Action continuation, object state, short token) + { + core.OnCompleted(continuation, state, token); + } + + public bool MoveNext() + { + if (cancellationToken.IsCancellationRequested) + { + core.TrySetResult(DelayResult.LinkedTokenCanceled); + return false; + } + + if (externalStop) + { + core.TrySetResult(DelayResult.ExternalStopped); + return false; + } + + if (stopwatch.IsInvalid) + { + core.TrySetResult(DelayResult.DelayCompleted); + return false; + } + + if (stopwatch.ElapsedTicks >= delayTimeSpanTicks) + { + core.TrySetResult(DelayResult.DelayCompleted); + return false; + } + + return true; + } + + bool TryReturn() + { + TaskTracker.RemoveTracking(this); + core.Reset(); + stopwatch = default; + cancellationToken = default; + externalStop = false; + return pool.TryPush(this); + } + } + } +} \ No newline at end of file diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/TimeoutController.cs.meta b/src/UniTask/Assets/Plugins/UniTask/Runtime/TimeoutController.cs.meta new file mode 100644 index 0000000..4f3d16d --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/TimeoutController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6347ab34d2db6d744a654e8d62d96b96 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UniTask/Assets/Scenes/SandboxMain.cs b/src/UniTask/Assets/Scenes/SandboxMain.cs index 2ef7faa..cd489a9 100644 --- a/src/UniTask/Assets/Scenes/SandboxMain.cs +++ b/src/UniTask/Assets/Scenes/SandboxMain.cs @@ -547,8 +547,13 @@ public class SandboxMain : MonoBehaviour Debug.Log("TestAsync Finished."); } + CancellationTokenSource clickCancelSource = new CancellationTokenSource(); + TimeoutController timeoutController; + async UniTaskVoid Start() { + timeoutController = new TimeoutController(clickCancelSource); + var defaultLoop = PlayerLoop.GetDefaultPlayerLoop(); PlayerLoopHelper.Initialize(ref defaultLoop, InjectPlayerLoopTimings.All); @@ -558,29 +563,42 @@ public class SandboxMain : MonoBehaviour okButton.onClick.AddListener(UniTask.UnityAction(async () => { - PlayerLoopHelper.DumpCurrentPlayerLoop(); + // try timeout + try + { + await UniTask.Delay(TimeSpan.FromSeconds(2), cancellationToken: timeoutController.Timeout(TimeSpan.FromSeconds(3))); + UnityEngine.Debug.Log("Delay Complete, Reset(and reuse)."); + timeoutController.Reset(); + } + catch (OperationCanceledException ex) + { + UnityEngine.Debug.Log("Timeout! FromTimeout?:" + timeoutController.IsTimeout()); + _ = ex; + } + await UniTask.Yield(); })); cancelButton.onClick.AddListener(UniTask.UnityAction(async () => { - await UniTask.Yield(PlayerLoopTiming.Initialization); + clickCancelSource.Cancel(); - RunCheck(PlayerLoopTiming.Initialization).Forget(); - RunCheck(PlayerLoopTiming.LastInitialization).Forget(); - RunCheck(PlayerLoopTiming.EarlyUpdate).Forget(); - RunCheck(PlayerLoopTiming.LastEarlyUpdate).Forget(); - RunCheck(PlayerLoopTiming.FixedUpdate).Forget(); - RunCheck(PlayerLoopTiming.LastFixedUpdate).Forget(); - RunCheck(PlayerLoopTiming.PreUpdate).Forget(); - RunCheck(PlayerLoopTiming.LastPreUpdate).Forget(); - RunCheck(PlayerLoopTiming.Update).Forget(); - RunCheck(PlayerLoopTiming.LastUpdate).Forget(); - RunCheck(PlayerLoopTiming.PreLateUpdate).Forget(); - RunCheck(PlayerLoopTiming.LastPreLateUpdate).Forget(); - RunCheck(PlayerLoopTiming.PostLateUpdate).Forget(); - RunCheck(PlayerLoopTiming.LastPostLateUpdate).Forget(); + //RunCheck(PlayerLoopTiming.Initialization).Forget(); + //RunCheck(PlayerLoopTiming.LastInitialization).Forget(); + //RunCheck(PlayerLoopTiming.EarlyUpdate).Forget(); + //RunCheck(PlayerLoopTiming.LastEarlyUpdate).Forget(); + //RunCheck(PlayerLoopTiming.FixedUpdate).Forget(); + //RunCheck(PlayerLoopTiming.LastFixedUpdate).Forget(); + //RunCheck(PlayerLoopTiming.PreUpdate).Forget(); + //RunCheck(PlayerLoopTiming.LastPreUpdate).Forget(); + //RunCheck(PlayerLoopTiming.Update).Forget(); + //RunCheck(PlayerLoopTiming.LastUpdate).Forget(); + //RunCheck(PlayerLoopTiming.PreLateUpdate).Forget(); + //RunCheck(PlayerLoopTiming.LastPreLateUpdate).Forget(); + //RunCheck(PlayerLoopTiming.PostLateUpdate).Forget(); + //RunCheck(PlayerLoopTiming.LastPostLateUpdate).Forget(); + await UniTask.Yield(); })); await UniTask.Yield(); From 0ec45b9da6ee20141e4179a52add4c69d7c40e11 Mon Sep 17 00:00:00 2001 From: neuecc Date: Thu, 25 Feb 2021 21:56:34 +0900 Subject: [PATCH 05/11] (Breaking Changed)UniTask.WithCancellation -> IgnoreWhenCanceled --- .../UniTask/Runtime/UniTaskExtensions.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTaskExtensions.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTaskExtensions.cs index aed4a17..c26125a 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTaskExtensions.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTaskExtensions.cs @@ -192,7 +192,7 @@ namespace Cysharp.Threading.Tasks /// /// Ignore task result when cancel raised first. /// - public static UniTask WithCancellation(this UniTask task, CancellationToken cancellationToken) + public static UniTask IgnoreWhenCanceled(this UniTask task, CancellationToken cancellationToken) { if (!cancellationToken.CanBeCanceled) { @@ -209,13 +209,13 @@ namespace Cysharp.Threading.Tasks return task; } - return new UniTask(new WithCancellationSource(task, cancellationToken), 0); + return new UniTask(new IgnoreWhenCanceledSource(task, cancellationToken), 0); } /// /// Ignore task result when cancel raised first. /// - public static UniTask WithCancellation(this UniTask task, CancellationToken cancellationToken) + public static UniTask IgnoreWhenCanceled(this UniTask task, CancellationToken cancellationToken) { if (!cancellationToken.CanBeCanceled) { @@ -232,10 +232,10 @@ namespace Cysharp.Threading.Tasks return task; } - return new UniTask(new WithCancellationSource(task, cancellationToken), 0); + return new UniTask(new IgnoreWhenCanceledSource(task, cancellationToken), 0); } - sealed class WithCancellationSource : IUniTaskSource + sealed class IgnoreWhenCanceledSource : IUniTaskSource { static readonly Action cancellationCallbackDelegate = CancellationCallback; @@ -243,7 +243,7 @@ namespace Cysharp.Threading.Tasks CancellationTokenRegistration tokenRegistration; UniTaskCompletionSourceCore core; - public WithCancellationSource(UniTask task, CancellationToken cancellationToken) + public IgnoreWhenCanceledSource(UniTask task, CancellationToken cancellationToken) { this.cancellationToken = cancellationToken; this.tokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallbackDelegate, this); @@ -269,7 +269,7 @@ namespace Cysharp.Threading.Tasks static void CancellationCallback(object state) { - var self = (WithCancellationSource)state; + var self = (IgnoreWhenCanceledSource)state; self.core.TrySetCanceled(self.cancellationToken); } @@ -294,7 +294,7 @@ namespace Cysharp.Threading.Tasks } } - sealed class WithCancellationSource : IUniTaskSource + sealed class IgnoreWhenCanceledSource : IUniTaskSource { static readonly Action cancellationCallbackDelegate = CancellationCallback; @@ -302,7 +302,7 @@ namespace Cysharp.Threading.Tasks CancellationTokenRegistration tokenRegistration; UniTaskCompletionSourceCore core; - public WithCancellationSource(UniTask task, CancellationToken cancellationToken) + public IgnoreWhenCanceledSource(UniTask task, CancellationToken cancellationToken) { this.cancellationToken = cancellationToken; this.tokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallbackDelegate, this); @@ -327,7 +327,7 @@ namespace Cysharp.Threading.Tasks static void CancellationCallback(object state) { - var self = (WithCancellationSource)state; + var self = (IgnoreWhenCanceledSource)state; self.core.TrySetCanceled(self.cancellationToken); } From 7ac9853cf640b9049865f55e7b61270ddd3fcb02 Mon Sep 17 00:00:00 2001 From: neuecc Date: Thu, 25 Feb 2021 22:11:18 +0900 Subject: [PATCH 06/11] (BreakingChange)AsyncOperation.WithCancellation same ToUniTask(ctoken) --- README.md | 135 +++- ...cExtensions.AssetBundleRequestAllAssets.cs | 128 +--- .../UniTask/Runtime/UnityAsyncExtensions.cs | 652 +----------------- .../UniTask/Runtime/UnityAsyncExtensions.tt | 166 +---- src/UniTask/Assets/Scenes/EditorTest1.cs | 3 + src/UniTask/Assets/Scenes/SandboxMain.cs | 60 +- 6 files changed, 192 insertions(+), 952 deletions(-) diff --git a/README.md b/README.md index bafb25f..2123fc9 100644 --- a/README.md +++ b/README.md @@ -127,9 +127,6 @@ async UniTask DemoAsync() // shorthand of WhenAll, tuple can await directly var (google2, bing2, yahoo2) = await (task1, task2, task3); - - // You can handle timeouts easily - await GetTextAsync(UnityWebRequest.Get("http://unity.com")).Timeout(TimeSpan.FromMilliseconds(300)); // return async-value.(or you can use `UniTask`(no result), `UniTaskVoid`(fire and forget)). return (asset as TextAsset)?.text ?? throw new InvalidOperationException("Asset not found"); @@ -154,7 +151,7 @@ UniTask provides three pattern of extension methods. `WithCancellation` is a simple version of `ToUniTask`, both return `UniTask`. For details of cancellation, see: [Cancellation and Exception handling](#cancellation-and-exception-handling) section. -> Note: WithCancellation is returned from native timing of PlayerLoop but ToUniTask is returned from specified PlayerLoopTiming. For details of timing, see: [PlayerLoop](#playerloop) section. +> Note: await directly is returned from native timing of PlayerLoop but WithCancellation and ToUniTask are returned from specified PlayerLoopTiming. For details of timing, see: [PlayerLoop](#playerloop) section. > Note: AssetBundleRequest has `asset` and `allAssets`, default await returns `asset`. If you want to get `allAssets`, you can use `AwaitForAllAssets()` method. @@ -286,6 +283,100 @@ if (isCanceled) Note: Only suppress throws if you call directly into the most source method. Otherwise, the return value will be converted, but the entire pipeline will not suppress throws. +Timeout handling +--- +Timeout is a variation of cancellation. You can set timeout by `CancellationTokenSouce.CancelAfterSlim(TimeSpan)` and pass CancellationToken to async methods. + +```csharp +var cts = new CancellationTokenSource(); +cts.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 5sec timeout. + +try +{ + await UnityWebRequest.Get("http://foo").SendWebRequest().WithCancellation(cts.Token); +} +catch (OperationCanceledException ex) +{ + if (ex.CancellationToken == cts.Token) + { + UnityEngine.Debug.Log("Timeout"); + } +} +``` + +> `CancellationTokenSouce.CancelAfter` is a standard api. However in Unity you should not use it because it depends threading timer. `CancelAfterSlim` is UniTask's extension methods, it uses PlayerLoop instead. + +If you want to use timeout with other source of cancellation, use `CancellationTokenSource.CreateLinkedTokenSource`. + +```csharp +var cancelToken = new CancellationTokenSource(); +cancelButton.onClick.AddListener(()=> +{ + cancelToken.Cancel(); // cancel from button click. +}); + +var timeoutToken = new CancellationTokenSource(); +timeoutToken.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 5sec timeout. + +try +{ + // combine token + var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancelToken.Token, timeoutToken.Token); + + await UnityWebRequest.Get("http://foo").SendWebRequest().WithCancellation(linkedTokenSource.Token); +} +catch (OperationCanceledException ex) +{ + if (timeoutToken.IsCancellationRequested) + { + UnityEngine.Debug.Log("Timeout."); + } + else if (cancelToken.IsCancellationRequested) + { + UnityEngine.Debug.Log("Cancel clicked."); + } +} +``` + +Optimize for reduce allocation of CancellationTokenSource for timeout per call async method, you can use UniTask's `TimeoutController`. + +```csharp +TimeoutController timeoutController = new TimeoutController(); // setup to field for reuse. + +async UniTask FooAsync() +{ + try + { + // you can pass timeoutController.Timeout(TimeSpan) to cancellationToken. + await UnityWebRequest.Get("http://foo").SendWebRequest() + .WithCancellation(timeoutController.Timeout(TimeSpan.FromSeconds(5))); + timeoutController.Reset(); // call Reset(Stop timeout timer and ready for reuse) when succeed. + } + catch (OperationCanceledException ex) + { + if (timeoutController.IsTimeout()) + { + UnityEngine.Debug.Log("timeout"); + } + } +} +``` + +If you want to use timeout with other source of cancellation, use `new TimeoutController(CancellationToken)`. + +```csharp +TimeoutController timeoutController; +CancellationTokenSource clickCancelSource; + +void Start() +{ + this.clickCancelSource = new CancellationTokenSource(); + this.timeoutController = new TimeoutController(clickCancelSource); +} +``` + +Note: UniTask has `.Timeout`, `.TimeoutWithoutException` methods however, if possible, do not use these, please pass `CancellationToken`. Because `.Timeout` work from external of task, can not stop timeoutted task. `.Timeout` means ignore result when timeout. If you pass a `CancellationToken` to the method, it will act from inside of the task, so it is possible to stop a running task. + Progress --- Some async operations for unity have `ToUniTask(IProgress progress = null, ...)` extension methods. @@ -366,7 +457,7 @@ It indicates when to run, you can check [PlayerLoopList.md](https://gist.github. `AsyncOperation` is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after being called, the loaded scene's `Start` is called from `EarlyUpdate.ScriptRunDelayedStartupFrame`. Also `await UnityWebRequest` is returned from `EarlyUpdate.ExecuteMainThreadJobs`. -In UniTask, await directly and `WithCancellation` use native timing, `ToUniTask` uses specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, it causes a different order of Start and continuation after await. So it is recommended not to use `LoadSceneAsync.ToUniTask`. +In UniTask, await directly uses native timing, `WithCancellation` and `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, it causes a different order of Start and continuation after await. So it is recommended not to use `LoadSceneAsync.ToUniTask`. In the stacktrace, you can check where it is running in playerloop. @@ -408,6 +499,37 @@ void Start() } ``` +You can optimize loop cost slightly by remove unuse PlayerLoopTiming injection. You can call `PlayerLoopHelper.Initialize(InjectPlayerLoopTimings)` on initialize. + +```csharp +var loop = PlayerLoop.GetCurrentPlayerLoop(); +PlayerLoopHelper.Initialize(ref loop, InjectPlayerLoopTimings.Minimum); // minimum is Update | FixedUpdate | LastPostLateUpdate +``` + +`InjectPlayerLoopTimings` has three preset, `All` and `Standard`(All without last except LastPostLateUpdate), `Minimum`(`Update | FixedUpdate | LastPostLateUpdate`). Default is All and you can combine custom inject timings like `InjectPlayerLoopTimings.Update | InjectPlayerLoopTimings.FixedUpdate | InjectPlayerLoopTimings.PreLateUpdate`. + +You can make error to use uninjected `PlayerLoopTiming` by [Microsoft.CodeAnalysis.BannedApiAnalyzers](https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md). For example, you can setup `BannedSymbols.txt` like this for `InjectPlayerLoopTimings.Minimum`. + +```txt +F:Cysharp.Threading.Tasks.PlayerLoopTiming.Initialization; Isn't injected this PlayerLoop in this project. +F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastInitialization; Isn't injected this PlayerLoop in this project. +F:Cysharp.Threading.Tasks.PlayerLoopTiming.EarlyUpdate; Isn't injected this PlayerLoop in this project. +F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastEarlyUpdate; Isn't injected this PlayerLoop in this project.d +F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastFixedUpdate; Isn't injected this PlayerLoop in this project. +F:Cysharp.Threading.Tasks.PlayerLoopTiming.PreUpdate; Isn't injected this PlayerLoop in this project. +F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastPreUpdate; Isn't injected this PlayerLoop in this project. +F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastUpdate; Isn't injected this PlayerLoop in this project. +F:Cysharp.Threading.Tasks.PlayerLoopTiming.PreLateUpdate; Isn't injected this PlayerLoop in this project. +F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastPreLateUpdate; Isn't injected this PlayerLoop in this project. +F:Cysharp.Threading.Tasks.PlayerLoopTiming.PostLateUpdate; Isn't injected this PlayerLoop in this project. +F:Cysharp.Threading.Tasks.PlayerLoopTiming.TimeUpdate; Isn't injected this PlayerLoop in this project. +F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastTimeUpdate; Isn't injected this PlayerLoop in this project. +``` + +You can configure `RS0030` severity to error. + +![image](https://user-images.githubusercontent.com/46207/109150837-bb933880-77ac-11eb-85ba-4fd15819dbd0.png) + async void vs async UniTaskVoid --- `async void` is a standard C# task system so it does not run on UniTask systems. It is better not to use it. `async UniTaskVoid` is a lightweight version of `async UniTask` because it does not have awaitable completion and reports errors immediately to `UniTaskScheduler.UnobservedTaskException`. If you don't require awaiting (fire and forget), using `UniTaskVoid` is better. Unfortunately to dismiss warning, you're required to call `Forget()`. @@ -812,8 +934,9 @@ For UnityEditor --- UniTask can run on Unity Editor like an Editor Coroutine. However, there are some limitations. -* Delay, DelayFrame do not work correctly because they can not get deltaTime in editor. Return the result of the await immediately; you can use `DelayType.Realtime` to wait for the right time. +* UniTask.Delay's DelayType.DeltaTime, UnscaledDeltaTime do not work correctly because they can not get deltaTime in editor. Therefore run on EditMode, automatically change DelayType to `DelayType.Realtime` that wait for the right time. * All PlayerLoopTiming run on the timing `EditorApplication.update`. +* `-batchmode` with `-quit` does not work because does not run `EditorApplication.update`(quit on single frame) so should not use `-quit` and quit manually with `Environment.Exit(0)`. Compare with Standard Task API --- diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AssetBundleRequestAllAssets.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AssetBundleRequestAllAssets.cs index 9ff4ffa..90889f3 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AssetBundleRequestAllAssets.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AssetBundleRequestAllAssets.cs @@ -21,10 +21,7 @@ namespace Cysharp.Threading.Tasks public static UniTask AwaitForAllAssets(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken) { - Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); - if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); - if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.allAssets); - return new UniTask(AssetBundleRequestAllAssetsWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token); + return AwaitForAllAssets(asyncOperation, cancellationToken: cancellationToken); } public static UniTask AwaitForAllAssets(this AssetBundleRequest asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) @@ -84,129 +81,6 @@ namespace Cysharp.Threading.Tasks } } - sealed class AssetBundleRequestAllAssetsWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode - { - static TaskPool pool; - AssetBundleRequestAllAssetsWithCancellationSource nextNode; - public ref AssetBundleRequestAllAssetsWithCancellationSource NextNode => ref nextNode; - - static AssetBundleRequestAllAssetsWithCancellationSource() - { - TaskPool.RegisterSizeGetter(typeof(AssetBundleRequestAllAssetsWithCancellationSource), () => pool.Size); - } - - readonly Action continuationAction; - AssetBundleRequest asyncOperation; - CancellationToken cancellationToken; - bool completed; - - UniTaskCompletionSourceCore core; - - AssetBundleRequestAllAssetsWithCancellationSource() - { - continuationAction = Continuation; - } - - public static IUniTaskSource Create(AssetBundleRequest asyncOperation, CancellationToken cancellationToken, out short token) - { - if (cancellationToken.IsCancellationRequested) - { - return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); - } - - if (!pool.TryPop(out var result)) - { - result = new AssetBundleRequestAllAssetsWithCancellationSource(); - } - - result.asyncOperation = asyncOperation; - result.cancellationToken = cancellationToken; - result.completed = false; - - TaskTracker.TrackActiveTask(result, 3); - - PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result); - - asyncOperation.completed += result.continuationAction; - - token = result.core.Version; - return result; - } - - void Continuation(AsyncOperation _) - { - asyncOperation.completed -= continuationAction; - - if (completed) - { - TryReturn(); - } - else - { - completed = true; - if (cancellationToken.IsCancellationRequested) - { - core.TrySetCanceled(cancellationToken); - return; - } - - core.TrySetResult(asyncOperation.allAssets); - } - } - - public UnityEngine.Object[] GetResult(short token) - { - return core.GetResult(token); - } - - void IUniTaskSource.GetResult(short token) - { - GetResult(token); - } - - public UniTaskStatus GetStatus(short token) - { - return core.GetStatus(token); - } - - public UniTaskStatus UnsafeGetStatus() - { - return core.UnsafeGetStatus(); - } - - public void OnCompleted(Action continuation, object state, short token) - { - core.OnCompleted(continuation, state, token); - } - - public bool MoveNext() - { - if (completed) - { - TryReturn(); - return false; - } - - if (cancellationToken.IsCancellationRequested) - { - completed = true; - core.TrySetCanceled(cancellationToken); - return false; - } - - return true; - } - - bool TryReturn() - { - TaskTracker.RemoveTracking(this); - core.Reset(); - asyncOperation = default; - cancellationToken = default; - return pool.TryPush(this); - } - } - sealed class AssetBundleRequestAllAssetsConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { static TaskPool pool; diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.cs index ebb44d0..8f8e3a1 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.cs @@ -23,10 +23,7 @@ namespace Cysharp.Threading.Tasks public static UniTask WithCancellation(this AsyncOperation asyncOperation, CancellationToken cancellationToken) { - Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); - if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); - if (asyncOperation.isDone) return UniTask.CompletedTask; - return new UniTask(AsyncOperationWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token); + return ToUniTask(asyncOperation, cancellationToken: cancellationToken); } public static UniTask ToUniTask(this AsyncOperation asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) @@ -77,125 +74,6 @@ namespace Cysharp.Threading.Tasks } } - sealed class AsyncOperationWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode - { - static TaskPool pool; - AsyncOperationWithCancellationSource nextNode; - public ref AsyncOperationWithCancellationSource NextNode => ref nextNode; - - static AsyncOperationWithCancellationSource() - { - TaskPool.RegisterSizeGetter(typeof(AsyncOperationWithCancellationSource), () => pool.Size); - } - - readonly Action continuationAction; - AsyncOperation asyncOperation; - CancellationToken cancellationToken; - bool completed; - - UniTaskCompletionSourceCore core; - - AsyncOperationWithCancellationSource() - { - continuationAction = Continuation; - } - - public static IUniTaskSource Create(AsyncOperation asyncOperation, CancellationToken cancellationToken, out short token) - { - if (cancellationToken.IsCancellationRequested) - { - return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); - } - - if (!pool.TryPop(out var result)) - { - result = new AsyncOperationWithCancellationSource(); - } - - result.asyncOperation = asyncOperation; - result.cancellationToken = cancellationToken; - result.completed = false; - - TaskTracker.TrackActiveTask(result, 3); - - PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result); - - asyncOperation.completed += result.continuationAction; - - token = result.core.Version; - return result; - } - - void Continuation(AsyncOperation _) - { - asyncOperation.completed -= continuationAction; - - if (completed) - { - TryReturn(); - } - else - { - completed = true; - if (cancellationToken.IsCancellationRequested) - { - core.TrySetCanceled(cancellationToken); - return; - } - - core.TrySetResult(AsyncUnit.Default); - } - } - - public void GetResult(short token) - { - core.GetResult(token); - } - - - public UniTaskStatus GetStatus(short token) - { - return core.GetStatus(token); - } - - public UniTaskStatus UnsafeGetStatus() - { - return core.UnsafeGetStatus(); - } - - public void OnCompleted(Action continuation, object state, short token) - { - core.OnCompleted(continuation, state, token); - } - - public bool MoveNext() - { - if (completed) - { - TryReturn(); - return false; - } - - if (cancellationToken.IsCancellationRequested) - { - completed = true; - core.TrySetCanceled(cancellationToken); - return false; - } - - return true; - } - - bool TryReturn() - { - TaskTracker.RemoveTracking(this); - core.Reset(); - asyncOperation = default; - cancellationToken = default; - return pool.TryPush(this); - } - } - sealed class AsyncOperationConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { static TaskPool pool; @@ -315,10 +193,7 @@ namespace Cysharp.Threading.Tasks public static UniTask WithCancellation(this ResourceRequest asyncOperation, CancellationToken cancellationToken) { - Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); - if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); - if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset); - return new UniTask(ResourceRequestWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token); + return ToUniTask(asyncOperation, cancellationToken: cancellationToken); } public static UniTask ToUniTask(this ResourceRequest asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) @@ -373,129 +248,6 @@ namespace Cysharp.Threading.Tasks } } - sealed class ResourceRequestWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode - { - static TaskPool pool; - ResourceRequestWithCancellationSource nextNode; - public ref ResourceRequestWithCancellationSource NextNode => ref nextNode; - - static ResourceRequestWithCancellationSource() - { - TaskPool.RegisterSizeGetter(typeof(ResourceRequestWithCancellationSource), () => pool.Size); - } - - readonly Action continuationAction; - ResourceRequest asyncOperation; - CancellationToken cancellationToken; - bool completed; - - UniTaskCompletionSourceCore core; - - ResourceRequestWithCancellationSource() - { - continuationAction = Continuation; - } - - public static IUniTaskSource Create(ResourceRequest asyncOperation, CancellationToken cancellationToken, out short token) - { - if (cancellationToken.IsCancellationRequested) - { - return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); - } - - if (!pool.TryPop(out var result)) - { - result = new ResourceRequestWithCancellationSource(); - } - - result.asyncOperation = asyncOperation; - result.cancellationToken = cancellationToken; - result.completed = false; - - TaskTracker.TrackActiveTask(result, 3); - - PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result); - - asyncOperation.completed += result.continuationAction; - - token = result.core.Version; - return result; - } - - void Continuation(AsyncOperation _) - { - asyncOperation.completed -= continuationAction; - - if (completed) - { - TryReturn(); - } - else - { - completed = true; - if (cancellationToken.IsCancellationRequested) - { - core.TrySetCanceled(cancellationToken); - return; - } - - core.TrySetResult(asyncOperation.asset); - } - } - - public UnityEngine.Object GetResult(short token) - { - return core.GetResult(token); - } - - void IUniTaskSource.GetResult(short token) - { - GetResult(token); - } - - public UniTaskStatus GetStatus(short token) - { - return core.GetStatus(token); - } - - public UniTaskStatus UnsafeGetStatus() - { - return core.UnsafeGetStatus(); - } - - public void OnCompleted(Action continuation, object state, short token) - { - core.OnCompleted(continuation, state, token); - } - - public bool MoveNext() - { - if (completed) - { - TryReturn(); - return false; - } - - if (cancellationToken.IsCancellationRequested) - { - completed = true; - core.TrySetCanceled(cancellationToken); - return false; - } - - return true; - } - - bool TryReturn() - { - TaskTracker.RemoveTracking(this); - core.Reset(); - asyncOperation = default; - cancellationToken = default; - return pool.TryPush(this); - } - } - sealed class ResourceRequestConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { static TaskPool pool; @@ -620,10 +372,7 @@ namespace Cysharp.Threading.Tasks public static UniTask WithCancellation(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken) { - Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); - if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); - if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset); - return new UniTask(AssetBundleRequestWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token); + return ToUniTask(asyncOperation, cancellationToken: cancellationToken); } public static UniTask ToUniTask(this AssetBundleRequest asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) @@ -678,129 +427,6 @@ namespace Cysharp.Threading.Tasks } } - sealed class AssetBundleRequestWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode - { - static TaskPool pool; - AssetBundleRequestWithCancellationSource nextNode; - public ref AssetBundleRequestWithCancellationSource NextNode => ref nextNode; - - static AssetBundleRequestWithCancellationSource() - { - TaskPool.RegisterSizeGetter(typeof(AssetBundleRequestWithCancellationSource), () => pool.Size); - } - - readonly Action continuationAction; - AssetBundleRequest asyncOperation; - CancellationToken cancellationToken; - bool completed; - - UniTaskCompletionSourceCore core; - - AssetBundleRequestWithCancellationSource() - { - continuationAction = Continuation; - } - - public static IUniTaskSource Create(AssetBundleRequest asyncOperation, CancellationToken cancellationToken, out short token) - { - if (cancellationToken.IsCancellationRequested) - { - return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); - } - - if (!pool.TryPop(out var result)) - { - result = new AssetBundleRequestWithCancellationSource(); - } - - result.asyncOperation = asyncOperation; - result.cancellationToken = cancellationToken; - result.completed = false; - - TaskTracker.TrackActiveTask(result, 3); - - PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result); - - asyncOperation.completed += result.continuationAction; - - token = result.core.Version; - return result; - } - - void Continuation(AsyncOperation _) - { - asyncOperation.completed -= continuationAction; - - if (completed) - { - TryReturn(); - } - else - { - completed = true; - if (cancellationToken.IsCancellationRequested) - { - core.TrySetCanceled(cancellationToken); - return; - } - - core.TrySetResult(asyncOperation.asset); - } - } - - public UnityEngine.Object GetResult(short token) - { - return core.GetResult(token); - } - - void IUniTaskSource.GetResult(short token) - { - GetResult(token); - } - - public UniTaskStatus GetStatus(short token) - { - return core.GetStatus(token); - } - - public UniTaskStatus UnsafeGetStatus() - { - return core.UnsafeGetStatus(); - } - - public void OnCompleted(Action continuation, object state, short token) - { - core.OnCompleted(continuation, state, token); - } - - public bool MoveNext() - { - if (completed) - { - TryReturn(); - return false; - } - - if (cancellationToken.IsCancellationRequested) - { - completed = true; - core.TrySetCanceled(cancellationToken); - return false; - } - - return true; - } - - bool TryReturn() - { - TaskTracker.RemoveTracking(this); - core.Reset(); - asyncOperation = default; - cancellationToken = default; - return pool.TryPush(this); - } - } - sealed class AssetBundleRequestConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { static TaskPool pool; @@ -926,10 +552,7 @@ namespace Cysharp.Threading.Tasks public static UniTask WithCancellation(this AssetBundleCreateRequest asyncOperation, CancellationToken cancellationToken) { - Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); - if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); - if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.assetBundle); - return new UniTask(AssetBundleCreateRequestWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token); + return ToUniTask(asyncOperation, cancellationToken: cancellationToken); } public static UniTask ToUniTask(this AssetBundleCreateRequest asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) @@ -984,129 +607,6 @@ namespace Cysharp.Threading.Tasks } } - sealed class AssetBundleCreateRequestWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode - { - static TaskPool pool; - AssetBundleCreateRequestWithCancellationSource nextNode; - public ref AssetBundleCreateRequestWithCancellationSource NextNode => ref nextNode; - - static AssetBundleCreateRequestWithCancellationSource() - { - TaskPool.RegisterSizeGetter(typeof(AssetBundleCreateRequestWithCancellationSource), () => pool.Size); - } - - readonly Action continuationAction; - AssetBundleCreateRequest asyncOperation; - CancellationToken cancellationToken; - bool completed; - - UniTaskCompletionSourceCore core; - - AssetBundleCreateRequestWithCancellationSource() - { - continuationAction = Continuation; - } - - public static IUniTaskSource Create(AssetBundleCreateRequest asyncOperation, CancellationToken cancellationToken, out short token) - { - if (cancellationToken.IsCancellationRequested) - { - return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); - } - - if (!pool.TryPop(out var result)) - { - result = new AssetBundleCreateRequestWithCancellationSource(); - } - - result.asyncOperation = asyncOperation; - result.cancellationToken = cancellationToken; - result.completed = false; - - TaskTracker.TrackActiveTask(result, 3); - - PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result); - - asyncOperation.completed += result.continuationAction; - - token = result.core.Version; - return result; - } - - void Continuation(AsyncOperation _) - { - asyncOperation.completed -= continuationAction; - - if (completed) - { - TryReturn(); - } - else - { - completed = true; - if (cancellationToken.IsCancellationRequested) - { - core.TrySetCanceled(cancellationToken); - return; - } - - core.TrySetResult(asyncOperation.assetBundle); - } - } - - public AssetBundle GetResult(short token) - { - return core.GetResult(token); - } - - void IUniTaskSource.GetResult(short token) - { - GetResult(token); - } - - public UniTaskStatus GetStatus(short token) - { - return core.GetStatus(token); - } - - public UniTaskStatus UnsafeGetStatus() - { - return core.UnsafeGetStatus(); - } - - public void OnCompleted(Action continuation, object state, short token) - { - core.OnCompleted(continuation, state, token); - } - - public bool MoveNext() - { - if (completed) - { - TryReturn(); - return false; - } - - if (cancellationToken.IsCancellationRequested) - { - completed = true; - core.TrySetCanceled(cancellationToken); - return false; - } - - return true; - } - - bool TryReturn() - { - TaskTracker.RemoveTracking(this); - core.Reset(); - asyncOperation = default; - cancellationToken = default; - return pool.TryPush(this); - } - } - sealed class AssetBundleCreateRequestConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { static TaskPool pool; @@ -1232,17 +732,7 @@ namespace Cysharp.Threading.Tasks public static UniTask WithCancellation(this UnityWebRequestAsyncOperation asyncOperation, CancellationToken cancellationToken) { - Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); - if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); - if (asyncOperation.isDone) - { - if (asyncOperation.webRequest.IsError()) - { - return UniTask.FromException(new UnityWebRequestException(asyncOperation.webRequest)); - } - return UniTask.FromResult(asyncOperation.webRequest); - } - return new UniTask(UnityWebRequestAsyncOperationWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token); + return ToUniTask(asyncOperation, cancellationToken: cancellationToken); } public static UniTask ToUniTask(this UnityWebRequestAsyncOperation asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) @@ -1312,138 +802,6 @@ namespace Cysharp.Threading.Tasks } } - sealed class UnityWebRequestAsyncOperationWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode - { - static TaskPool pool; - UnityWebRequestAsyncOperationWithCancellationSource nextNode; - public ref UnityWebRequestAsyncOperationWithCancellationSource NextNode => ref nextNode; - - static UnityWebRequestAsyncOperationWithCancellationSource() - { - TaskPool.RegisterSizeGetter(typeof(UnityWebRequestAsyncOperationWithCancellationSource), () => pool.Size); - } - - readonly Action continuationAction; - UnityWebRequestAsyncOperation asyncOperation; - CancellationToken cancellationToken; - bool completed; - - UniTaskCompletionSourceCore core; - - UnityWebRequestAsyncOperationWithCancellationSource() - { - continuationAction = Continuation; - } - - public static IUniTaskSource Create(UnityWebRequestAsyncOperation asyncOperation, CancellationToken cancellationToken, out short token) - { - if (cancellationToken.IsCancellationRequested) - { - return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); - } - - if (!pool.TryPop(out var result)) - { - result = new UnityWebRequestAsyncOperationWithCancellationSource(); - } - - result.asyncOperation = asyncOperation; - result.cancellationToken = cancellationToken; - result.completed = false; - - TaskTracker.TrackActiveTask(result, 3); - - PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result); - - asyncOperation.completed += result.continuationAction; - - token = result.core.Version; - return result; - } - - void Continuation(AsyncOperation _) - { - asyncOperation.completed -= continuationAction; - - if (completed) - { - TryReturn(); - } - else - { - completed = true; - if (cancellationToken.IsCancellationRequested) - { - core.TrySetCanceled(cancellationToken); - return; - } - - var result = asyncOperation.webRequest; - if (result.IsError()) - { - core.TrySetException(new UnityWebRequestException(result)); - } - else - { - core.TrySetResult(result); - } - } - } - - public UnityWebRequest GetResult(short token) - { - return core.GetResult(token); - } - - void IUniTaskSource.GetResult(short token) - { - GetResult(token); - } - - public UniTaskStatus GetStatus(short token) - { - return core.GetStatus(token); - } - - public UniTaskStatus UnsafeGetStatus() - { - return core.UnsafeGetStatus(); - } - - public void OnCompleted(Action continuation, object state, short token) - { - core.OnCompleted(continuation, state, token); - } - - public bool MoveNext() - { - if (completed) - { - TryReturn(); - return false; - } - - if (cancellationToken.IsCancellationRequested) - { - completed = true; - asyncOperation.webRequest.Abort(); - core.TrySetCanceled(cancellationToken); - return false; - } - - return true; - } - - bool TryReturn() - { - TaskTracker.RemoveTracking(this); - core.Reset(); - asyncOperation = default; - cancellationToken = default; - return pool.TryPush(this); - } - } - sealed class UnityWebRequestAsyncOperationConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { static TaskPool pool; diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.tt b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.tt index 9ebc356..65dac9e 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.tt +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.tt @@ -11,7 +11,7 @@ ("ResourceRequest", "UnityEngine.Object", "asset"), ("AssetBundleRequest", "UnityEngine.Object", "asset"), // allAssets? ("AssetBundleCreateRequest", "AssetBundle", "assetBundle"), - ("UnityWebRequestAsyncOperation", "UnityWebRequest", "webRequest") // -> #if ENABLE_UNITYWEBREQUEST + ("UnityWebRequestAsyncOperation", "UnityWebRequest", "webRequest") // -> #if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT) }; Func ToUniTaskReturnType = x => (x == "void") ? "UniTask" : $"UniTask<{x}>"; @@ -27,7 +27,7 @@ using System.Runtime.CompilerServices; using System.Threading; using UnityEngine; using Cysharp.Threading.Tasks.Internal; -#if ENABLE_UNITYWEBREQUEST +#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT) using UnityEngine.Networking; #endif @@ -37,7 +37,7 @@ namespace Cysharp.Threading.Tasks { <# foreach(var t in types) { #> <# if(IsUnityWebRequest(t)) { #> -#if ENABLE_UNITYWEBREQUEST +#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT) <# } else if(IsAssetBundleModule(t)) { #> #if UNITASK_ASSETBUNDLE_SUPPORT <# } #> @@ -51,21 +51,7 @@ namespace Cysharp.Threading.Tasks public static <#= ToUniTaskReturnType(t.returnType) #> WithCancellation(this <#= t.typeName #> asyncOperation, CancellationToken cancellationToken) { - Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); - if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<#= IsVoid(t) ? "" : "<" + t.returnType + ">" #>(cancellationToken); -<# if(IsUnityWebRequest(t)) { #> - if (asyncOperation.isDone) - { - if (asyncOperation.webRequest.IsError()) - { - return UniTask.FromException(new UnityWebRequestException(asyncOperation.webRequest)); - } - return UniTask.FromResult(asyncOperation.webRequest); - } -<# } else { #> - if (asyncOperation.isDone) return <#= IsVoid(t) ? "UniTask.CompletedTask" : $"UniTask.FromResult(asyncOperation.{t.returnField})" #>; -<# } #> - return new <#= ToUniTaskReturnType(t.returnType) #>(<#= t.typeName #>WithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token); + return ToUniTask(asyncOperation, cancellationToken: cancellationToken); } public static <#= ToUniTaskReturnType(t.returnType) #> ToUniTask(this <#= t.typeName #> asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) @@ -151,150 +137,6 @@ namespace Cysharp.Threading.Tasks } } - sealed class <#= t.typeName #>WithCancellationSource : <#= ToIUniTaskSourceReturnType(t.returnType) #>, IPlayerLoopItem, ITaskPoolNode<<#= t.typeName #>WithCancellationSource> - { - static TaskPool<<#= t.typeName #>WithCancellationSource> pool; - <#= t.typeName #>WithCancellationSource nextNode; - public ref <#= t.typeName #>WithCancellationSource NextNode => ref nextNode; - - static <#= t.typeName #>WithCancellationSource() - { - TaskPool.RegisterSizeGetter(typeof(<#= t.typeName #>WithCancellationSource), () => pool.Size); - } - - readonly Action continuationAction; - <#= t.typeName #> asyncOperation; - CancellationToken cancellationToken; - bool completed; - - UniTaskCompletionSourceCore<<#= IsVoid(t) ? "AsyncUnit" : t.returnType #>> core; - - <#= t.typeName #>WithCancellationSource() - { - continuationAction = Continuation; - } - - public static <#= ToIUniTaskSourceReturnType(t.returnType) #> Create(<#= t.typeName #> asyncOperation, CancellationToken cancellationToken, out short token) - { - if (cancellationToken.IsCancellationRequested) - { - return AutoResetUniTaskCompletionSource<#= IsVoid(t) ? "" : $"<{t.returnType}>" #>.CreateFromCanceled(cancellationToken, out token); - } - - if (!pool.TryPop(out var result)) - { - result = new <#= t.typeName #>WithCancellationSource(); - } - - result.asyncOperation = asyncOperation; - result.cancellationToken = cancellationToken; - result.completed = false; - - TaskTracker.TrackActiveTask(result, 3); - - PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result); - - asyncOperation.completed += result.continuationAction; - - token = result.core.Version; - return result; - } - - void Continuation(AsyncOperation _) - { - asyncOperation.completed -= continuationAction; - - if (completed) - { - TryReturn(); - } - else - { - completed = true; - if (cancellationToken.IsCancellationRequested) - { - core.TrySetCanceled(cancellationToken); - return; - } - -<# if(IsUnityWebRequest(t)) { #> - var result = asyncOperation.webRequest; - if (result.IsError()) - { - core.TrySetException(new UnityWebRequestException(result)); - } - else - { - core.TrySetResult(result); - } -<# } else { #> - core.TrySetResult(<#= IsVoid(t) ? "AsyncUnit.Default" : $"asyncOperation.{t.returnField}" #>); -<# } #> - } - } - - public <#= t.returnType #> GetResult(short token) - { -<# if (!IsVoid(t)) { #> - return core.GetResult(token); -<# } else { #> - core.GetResult(token); -<# } #> - } - -<# if (!IsVoid(t)) { #> - void IUniTaskSource.GetResult(short token) - { - GetResult(token); - } -<# } #> - - public UniTaskStatus GetStatus(short token) - { - return core.GetStatus(token); - } - - public UniTaskStatus UnsafeGetStatus() - { - return core.UnsafeGetStatus(); - } - - public void OnCompleted(Action continuation, object state, short token) - { - core.OnCompleted(continuation, state, token); - } - - public bool MoveNext() - { - if (completed) - { - TryReturn(); - return false; - } - - if (cancellationToken.IsCancellationRequested) - { - completed = true; -<# if(IsUnityWebRequest(t)) { #> - asyncOperation.webRequest.Abort(); -<# } #> - core.TrySetCanceled(cancellationToken); - return false; - } - - return true; - } - - bool TryReturn() - { - TaskTracker.RemoveTracking(this); - core.Reset(); - asyncOperation = default; - cancellationToken = default; - return pool.TryPush(this); - } - } - sealed class <#= t.typeName #>ConfiguredSource : <#= ToIUniTaskSourceReturnType(t.returnType) #>, IPlayerLoopItem, ITaskPoolNode<<#= t.typeName #>ConfiguredSource> { static TaskPool<<#= t.typeName #>ConfiguredSource> pool; diff --git a/src/UniTask/Assets/Scenes/EditorTest1.cs b/src/UniTask/Assets/Scenes/EditorTest1.cs index 4872b8f..bae759d 100644 --- a/src/UniTask/Assets/Scenes/EditorTest1.cs +++ b/src/UniTask/Assets/Scenes/EditorTest1.cs @@ -35,6 +35,9 @@ public class Test1 Debug.Log("Dosomething 2"); await UniTask.Delay(1000, DelayType.DeltaTime); Debug.Log("Dosomething 3"); + Debug.Log("and Quit."); + + Environment.Exit(0); } } diff --git a/src/UniTask/Assets/Scenes/SandboxMain.cs b/src/UniTask/Assets/Scenes/SandboxMain.cs index cd489a9..8babe00 100644 --- a/src/UniTask/Assets/Scenes/SandboxMain.cs +++ b/src/UniTask/Assets/Scenes/SandboxMain.cs @@ -542,22 +542,62 @@ public class SandboxMain : MonoBehaviour { Debug.LogError(e); return; + + } } Debug.Log("TestAsync Finished."); } - CancellationTokenSource clickCancelSource = new CancellationTokenSource(); - TimeoutController timeoutController; + + async UniTaskVoid Start() { - timeoutController = new TimeoutController(clickCancelSource); - var defaultLoop = PlayerLoop.GetDefaultPlayerLoop(); - PlayerLoopHelper.Initialize(ref defaultLoop, InjectPlayerLoopTimings.All); + // UniTask.Delay(TimeSpan.FromSeconds(1)).TimeoutWithoutException - var cts = new CancellationTokenSource(); + + var currentLoop = PlayerLoop.GetDefaultPlayerLoop(); + PlayerLoopHelper.Initialize(ref currentLoop, InjectPlayerLoopTimings.Minimum); // minimum is Update | FixedUpdate | LastPostLateUpdate + + + + + + var cancelToken = new CancellationTokenSource(); + cancelButton.onClick.AddListener(()=> + { + cancelToken.Cancel(); // cancel from button click. + }); + + var timeoutToken = new CancellationTokenSource(); + timeoutToken.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 5sec timeout. + + try + { + // combine token + var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancelToken.Token, timeoutToken.Token); + + await UnityWebRequest.Get("http://foo").SendWebRequest().WithCancellation(linkedTokenSource.Token); + } + catch (OperationCanceledException ex) + { + if (timeoutToken.IsCancellationRequested) + { + UnityEngine.Debug.Log("Timeout."); + } + else if (cancelToken.IsCancellationRequested) + { + UnityEngine.Debug.Log("Cancel clicked."); + } + _ = ex; + } + + + + + // TestAsync(cts.Token).Forget(); @@ -566,13 +606,13 @@ public class SandboxMain : MonoBehaviour // try timeout try { - await UniTask.Delay(TimeSpan.FromSeconds(2), cancellationToken: timeoutController.Timeout(TimeSpan.FromSeconds(3))); + //await UniTask.Delay(TimeSpan.FromSeconds(2), cancellationToken: timeoutController.Timeout(TimeSpan.FromSeconds(3))); UnityEngine.Debug.Log("Delay Complete, Reset(and reuse)."); - timeoutController.Reset(); + //timeoutController.Reset(); } catch (OperationCanceledException ex) { - UnityEngine.Debug.Log("Timeout! FromTimeout?:" + timeoutController.IsTimeout()); + //UnityEngine.Debug.Log("Timeout! FromTimeout?:" + timeoutController.IsTimeout()); _ = ex; } @@ -581,7 +621,7 @@ public class SandboxMain : MonoBehaviour cancelButton.onClick.AddListener(UniTask.UnityAction(async () => { - clickCancelSource.Cancel(); + //clickCancelSource.Cancel(); //RunCheck(PlayerLoopTiming.Initialization).Forget(); //RunCheck(PlayerLoopTiming.LastInitialization).Forget(); From 841b6e85ae06493e8781dda4f05e7ed47b846216 Mon Sep 17 00:00:00 2001 From: neuecc Date: Thu, 25 Feb 2021 22:13:14 +0900 Subject: [PATCH 07/11] fix --- src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Delay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Delay.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Delay.cs index 3105d9f..add6e70 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Delay.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Delay.cs @@ -108,7 +108,7 @@ namespace Cysharp.Threading.Tasks #if UNITY_EDITOR // force use Realtime. - if (!UnityEditor.EditorApplication.isPlaying) + if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying) { delayType = DelayType.Realtime; } From 186114996ce4e3bfff49df651a119d290862aced Mon Sep 17 00:00:00 2001 From: neuecc Date: Thu, 25 Feb 2021 13:13:40 +0000 Subject: [PATCH 08/11] docs: update TOC --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2123fc9..faa9437 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ For advanced tips, see blog post: [Extends UnityWebRequest via async decorator p - [Getting started](#getting-started) - [Basics of UniTask and AsyncOperation](#basics-of-unitask-and-asyncoperation) - [Cancellation and Exception handling](#cancellation-and-exception-handling) +- [Timeout handling](#timeout-handling) - [Progress](#progress) - [PlayerLoop](#playerloop) - [async void vs async UniTaskVoid](#async-void-vs-async-unitaskvoid) From 5f96e646d4577d603ce29bd91dd12c915b9bc07b Mon Sep 17 00:00:00 2001 From: neuecc Date: Fri, 26 Feb 2021 10:27:22 +0900 Subject: [PATCH 09/11] netcore --- src/UniTask.NetCoreTests/WithCancellationTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/UniTask.NetCoreTests/WithCancellationTest.cs b/src/UniTask.NetCoreTests/WithCancellationTest.cs index 90236d2..3aeb42a 100644 --- a/src/UniTask.NetCoreTests/WithCancellationTest.cs +++ b/src/UniTask.NetCoreTests/WithCancellationTest.cs @@ -16,7 +16,7 @@ namespace NetCoreTests { CancellationTokenSource cts = new CancellationTokenSource(); - var v = await UniTask.Run(() => 10).WithCancellation(cts.Token); + var v = await UniTask.Run(() => 10).IgnoreWhenCanceled(cts.Token); v.Should().Be(10); } @@ -30,7 +30,7 @@ namespace NetCoreTests { await Task.Delay(TimeSpan.FromSeconds(1)); return 10; - }).WithCancellation(cts.Token); + }).IgnoreWhenCanceled(cts.Token); cts.Cancel(); From 316f3bd963b4908038fec1f6d32dd6d8ceed0252 Mon Sep 17 00:00:00 2001 From: neuecc Date: Fri, 26 Feb 2021 11:00:47 +0900 Subject: [PATCH 10/11] cm --- .../Plugins/UniTask/Runtime/PlayerLoopHelper.cs | 11 +++++++---- .../Runtime/UnityAsyncExtensions.AsyncGPUReadback.cs | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs index 063908e..1f954bf 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs @@ -114,6 +114,8 @@ namespace Cysharp.Threading.Tasks PostLateUpdate | LastPostLateUpdate | #if UNITY_2020_2_OR_NEWER TimeUpdate | LastTimeUpdate, +#else + , #endif /// @@ -128,8 +130,9 @@ namespace Cysharp.Threading.Tasks PreLateUpdate | PostLateUpdate | LastPostLateUpdate | #if UNITY_2020_2_OR_NEWER - TimeUpdate, + TimeUpdate #endif + , /// /// Preset: Minimum pattern, Update | FixedUpdate | LastPostLateUpdate @@ -158,14 +161,14 @@ namespace Cysharp.Threading.Tasks LastPreLateUpdate = 2048, PostLateUpdate = 4096, - LastPostLateUpdate = 8192, + LastPostLateUpdate = 8192 #if UNITY_2020_2_OR_NEWER + , // Unity 2020.2 added TimeUpdate https://docs.unity3d.com/2020.2/Documentation/ScriptReference/PlayerLoop.TimeUpdate.html TimeUpdate = 16384, - LastTimeUpdate = 32768, + LastTimeUpdate = 32768 #endif - } public interface IPlayerLoopItem diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AsyncGPUReadback.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AsyncGPUReadback.cs index 2be6f1f..5805dbb 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AsyncGPUReadback.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AsyncGPUReadback.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member using System; using System.Threading; From 42047070dd4ad6297e189af4c1076d16c6d51ef8 Mon Sep 17 00:00:00 2001 From: neuecc Date: Fri, 26 Feb 2021 11:42:30 +0900 Subject: [PATCH 11/11] more ci --- .../Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs index 1f954bf..a194c36 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopHelper.cs @@ -111,9 +111,9 @@ namespace Cysharp.Threading.Tasks PreUpdate | LastPreUpdate | Update | LastUpdate | PreLateUpdate | LastPreLateUpdate | - PostLateUpdate | LastPostLateUpdate | + PostLateUpdate | LastPostLateUpdate #if UNITY_2020_2_OR_NEWER - TimeUpdate | LastTimeUpdate, + | TimeUpdate | LastTimeUpdate, #else , #endif @@ -128,9 +128,9 @@ namespace Cysharp.Threading.Tasks PreUpdate | Update | PreLateUpdate | - PostLateUpdate | LastPostLateUpdate | + PostLateUpdate | LastPostLateUpdate #if UNITY_2020_2_OR_NEWER - TimeUpdate + | TimeUpdate #endif ,