From 35a893ad9e2c9bfe5b842d1e9381fc1019ed1397 Mon Sep 17 00:00:00 2001 From: neuecc Date: Mon, 7 Sep 2020 17:56:02 +0900 Subject: [PATCH] Add UniTask.RunOnThreadPool --- .gitignore | 2 + README.md | 20 ++ .../Plugins/UniTask/Runtime/UniTask.Run.cs | 233 ++++++++++++++++++ 3 files changed, 255 insertions(+) diff --git a/.gitignore b/.gitignore index ef4bd12..98662c9 100644 --- a/.gitignore +++ b/.gitignore @@ -253,3 +253,5 @@ src/UniTask/UnityEngine.UI.Player.csproj src/UniTask/DOTween.Modules.Player.csproj src/UniTask/Assembly-CSharp.Player.csproj + +src/UniTask/Unity.EditorCoroutines.Editor.csproj diff --git a/README.md b/README.md index 09ecda9..b10fbd8 100644 --- a/README.md +++ b/README.md @@ -780,6 +780,26 @@ public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () => UniTask itself's unit test is written by Unity Test Runner and [Cysharp/RuntimeUnitTestToolkit](https://github.com/Cysharp/RuntimeUnitTestToolkit) to check on CI and IL2CPP working. +ThreadPool limitation +--- +Most UniTask methods run in a single thread (PlayerLoop), but only `UniTask.Run` and `UniTask.SwitchToThreadPool` run on a thread pool. If you use a thread pool, it won't work with WebGL and so on. + +`UniTask.Run` will be deprecated in the future (marked with an Obsolete) and only `RunOnThreadPool` will be used. Also, if you use `UniTask.Run`, consider whether you can use `UniTask.Create` or `UniTask.Void`. + +IEnumerator.ToUniTask limitation +--- +You can convert coroutine(IEnumerator) to UniTask(or await directly) but has some limitations. + +* `WaitForEndOfFrame`/`WaitForFixedUpdate` is not supported, used `yield return null` instead. +* Consuming loop timing is not same as StartCoroutine, it is used specified PlayerLoopTiming, and default's `PlayerLoopTiming.Update` is run before MonoBehaviour's Update and StartCoroutine's loop. + +For UnityEditor +--- +UniTask can run on Unity Edtitor like Editor Coroutine. However, there are some limitations. + +* Delay, DelayFrame is not work correctly because can not get deltaTime in editor. Return the result of the await immediately; you can use `DelayType.Realtime` to wait for the right time. +* All PlayerLoopTiming run on timing, `EditorApplication.update`. + Compare with Standard Task API --- UniTask has many standard Task-like APIs. This table shows what is the alternative apis. diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Run.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Run.cs index 40cea40..253ac6d 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Run.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Run.cs @@ -7,6 +7,10 @@ namespace Cysharp.Threading.Tasks { public partial struct UniTask { + #region OBSOLETE_RUN + + // Run is a confusing name, use only RunOnThreadPool in the future. + /// Run action on the threadPool and return to main thread if configureAwait = true. public static async UniTask Run(Action action, bool configureAwait = true, CancellationToken cancellationToken = default) { @@ -232,6 +236,235 @@ namespace Cysharp.Threading.Tasks return result; } } + + #endregion + + + /// Run action on the threadPool and return to main thread if configureAwait = true. + public static async UniTask RunOnThreadPool(Action action, bool configureAwait = true, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + await UniTask.SwitchToThreadPool(); + + cancellationToken.ThrowIfCancellationRequested(); + + if (configureAwait) + { + try + { + action(); + } + finally + { + await UniTask.Yield(); + } + } + else + { + action(); + } + + cancellationToken.ThrowIfCancellationRequested(); + } + + /// Run action on the threadPool and return to main thread if configureAwait = true. + public static async UniTask RunOnThreadPool(Action action, object state, bool configureAwait = true, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + await UniTask.SwitchToThreadPool(); + + cancellationToken.ThrowIfCancellationRequested(); + + if (configureAwait) + { + try + { + action(state); + } + finally + { + await UniTask.Yield(); + } + } + else + { + action(state); + } + + cancellationToken.ThrowIfCancellationRequested(); + } + + /// Run action on the threadPool and return to main thread if configureAwait = true. + public static async UniTask RunOnThreadPool(Func action, bool configureAwait = true, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + await UniTask.SwitchToThreadPool(); + + cancellationToken.ThrowIfCancellationRequested(); + + if (configureAwait) + { + try + { + await action(); + } + finally + { + await UniTask.Yield(); + } + } + else + { + await action(); + } + + cancellationToken.ThrowIfCancellationRequested(); + } + + /// Run action on the threadPool and return to main thread if configureAwait = true. + public static async UniTask RunOnThreadPool(Func action, object state, bool configureAwait = true, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + await UniTask.SwitchToThreadPool(); + + cancellationToken.ThrowIfCancellationRequested(); + + if (configureAwait) + { + try + { + await action(state); + } + finally + { + await UniTask.Yield(); + } + } + else + { + await action(state); + } + + cancellationToken.ThrowIfCancellationRequested(); + } + + /// Run action on the threadPool and return to main thread if configureAwait = true. + public static async UniTask RunOnThreadPool(Func func, bool configureAwait = true, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + await UniTask.SwitchToThreadPool(); + + cancellationToken.ThrowIfCancellationRequested(); + + if (configureAwait) + { + try + { + return func(); + } + finally + { + await UniTask.Yield(); + cancellationToken.ThrowIfCancellationRequested(); + } + } + else + { + return func(); + } + } + + /// Run action on the threadPool and return to main thread if configureAwait = true. + public static async UniTask RunOnThreadPool(Func> func, bool configureAwait = true, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + await UniTask.SwitchToThreadPool(); + + cancellationToken.ThrowIfCancellationRequested(); + + if (configureAwait) + { + try + { + return await func(); + } + finally + { + cancellationToken.ThrowIfCancellationRequested(); + await UniTask.Yield(); + cancellationToken.ThrowIfCancellationRequested(); + } + } + else + { + var result = await func(); + cancellationToken.ThrowIfCancellationRequested(); + return result; + } + } + + /// Run action on the threadPool and return to main thread if configureAwait = true. + public static async UniTask RunOnThreadPool(Func func, object state, bool configureAwait = true, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + await UniTask.SwitchToThreadPool(); + + cancellationToken.ThrowIfCancellationRequested(); + + if (configureAwait) + { + try + { + return func(state); + } + finally + { + await UniTask.Yield(); + cancellationToken.ThrowIfCancellationRequested(); + } + } + else + { + return func(state); + } + } + + /// Run action on the threadPool and return to main thread if configureAwait = true. + public static async UniTask RunOnThreadPool(Func> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + await UniTask.SwitchToThreadPool(); + + cancellationToken.ThrowIfCancellationRequested(); + + if (configureAwait) + { + try + { + return await func(state); + } + finally + { + cancellationToken.ThrowIfCancellationRequested(); + await UniTask.Yield(); + cancellationToken.ThrowIfCancellationRequested(); + } + } + else + { + var result = await func(state); + cancellationToken.ThrowIfCancellationRequested(); + return result; + } + } } }