From 10fb8060facdb019e985f025f341e76c720c4905 Mon Sep 17 00:00:00 2001 From: neuecc Date: Wed, 27 May 2020 07:37:16 +0900 Subject: [PATCH] use PooledDelegate to avoid convert Action to Action allocation --- src/UniTask.NetCore/NetCore/UniTask.Yield.cs | 2 +- .../External/AddressableAsyncExtensions.cs | 4 +- .../External/DoTweenAsyncExtensions.cs | 50 +++++++++++++++++-- .../Runtime/Internal/PooledDelegate.cs | 45 +++++++++++++++++ .../Runtime/Internal/PooledDelegate.cs.meta | 11 ++++ .../Plugins/UniTask/Runtime/UniTaskVoid.cs | 30 ----------- .../UniTask/Runtime/UnityAsyncExtensions.cs | 10 ++-- src/UniTask/Assets/Scenes/SandboxMain.cs | 31 ++++++++---- 8 files changed, 130 insertions(+), 53 deletions(-) create mode 100644 src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/PooledDelegate.cs create mode 100644 src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/PooledDelegate.cs.meta diff --git a/src/UniTask.NetCore/NetCore/UniTask.Yield.cs b/src/UniTask.NetCore/NetCore/UniTask.Yield.cs index 511f706..5a147a6 100644 --- a/src/UniTask.NetCore/NetCore/UniTask.Yield.cs +++ b/src/UniTask.NetCore/NetCore/UniTask.Yield.cs @@ -59,7 +59,7 @@ namespace Cysharp.Threading.Tasks #if NETCOREAPP3_1 - public sealed class ThreadPoolWorkItem : IThreadPoolWorkItem + sealed class ThreadPoolWorkItem : IThreadPoolWorkItem { static readonly ConcurrentQueue pool = new ConcurrentQueue(); diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/External/AddressableAsyncExtensions.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/AddressableAsyncExtensions.cs index 893f4ab..97aabeb 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/External/AddressableAsyncExtensions.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/AddressableAsyncExtensions.cs @@ -72,7 +72,7 @@ namespace Cysharp.Threading.Tasks public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); - continuationAction = continuation.AsFuncOfT(); // allocate delegate. + continuationAction = PooledDelegate.Create(continuation); handle.Completed += continuationAction; } } @@ -249,7 +249,7 @@ namespace Cysharp.Threading.Tasks public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); - continuationAction = continuation.AsFuncOfT(); // allocate delegate. + continuationAction = PooledDelegate.Create(continuation); handle.CompletedTypeless += continuationAction; } } diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs index 523873c..aecdf1d 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs @@ -5,6 +5,7 @@ using Cysharp.Threading.Tasks.Internal; using DG.Tweening; using System; +using System.Collections.Concurrent; using System.Runtime.CompilerServices; using System.Threading; @@ -77,9 +78,8 @@ namespace Cysharp.Threading.Tasks public void UnsafeOnCompleted(System.Action continuation) { - // convert Action -> TweenCallback allocation. // onKill is called after OnCompleted, both Complete(false/true) and Kill(false/true). - tween.onKill = new TweenCallback(continuation); + tween.onKill = PooledTweenCallback.Create(continuation); } } @@ -89,6 +89,8 @@ namespace Cysharp.Threading.Tasks static readonly Action CancellationCallbackDelegate = CancellationCallback; static readonly TweenCallback EmptyTweenCallback = () => { }; + readonly TweenCallback onKillDelegate; + Tween tween; TweenCancelBehaviour cancelBehaviour; CancellationToken cancellationToken; @@ -99,7 +101,7 @@ namespace Cysharp.Threading.Tasks TweenConfiguredSource() { - + onKillDelegate = OnKill; } public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, out short token) @@ -130,8 +132,7 @@ namespace Cysharp.Threading.Tasks cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(CancellationCallbackDelegate, this); } - // allocate delegate. - tween.OnKill(new TweenCallback(OnKill)); + tween.OnKill(onKillDelegate); } void OnKill() @@ -234,6 +235,45 @@ namespace Cysharp.Threading.Tasks } } } + + sealed class PooledTweenCallback + { + static readonly ConcurrentQueue pool = new ConcurrentQueue(); + + readonly TweenCallback runDelegate; + + Action continuation; + + + PooledTweenCallback() + { + runDelegate = Run; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TweenCallback Create(Action continuation) + { + if (!pool.TryDequeue(out var item)) + { + item = new PooledTweenCallback(); + } + + item.continuation = continuation; + return item.runDelegate; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Run() + { + var call = continuation; + continuation = null; + if (call != null) + { + pool.Enqueue(this); + call.Invoke(); + } + } + } } #endif diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/PooledDelegate.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/PooledDelegate.cs new file mode 100644 index 0000000..16f112b --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/PooledDelegate.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Concurrent; +using System.Runtime.CompilerServices; + +namespace Cysharp.Threading.Tasks.Internal +{ + internal class PooledDelegate + { + static readonly ConcurrentQueue> pool = new ConcurrentQueue>(); + + readonly Action runDelegate; + + Action continuation; + + + PooledDelegate() + { + runDelegate = Run; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Action Create(Action continuation) + { + if (!pool.TryDequeue(out var item)) + { + item = new PooledDelegate(); + } + + item.continuation = continuation; + return item.runDelegate; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Run(T _) + { + var call = continuation; + continuation = null; + if (call != null) + { + pool.Enqueue(this); + call.Invoke(); + } + } + } +} diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/PooledDelegate.cs.meta b/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/PooledDelegate.cs.meta new file mode 100644 index 0000000..7f92aff --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/PooledDelegate.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8932579438742fa40b010edd412dbfba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTaskVoid.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTaskVoid.cs index 10711a5..c7e9ed9 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTaskVoid.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTaskVoid.cs @@ -14,36 +14,6 @@ namespace Cysharp.Threading.Tasks public void Forget() { } - - // [DebuggerHidden] - // public Awaiter GetAwaiter() - // { - // return new Awaiter(); - // } - - // public struct Awaiter : ICriticalNotifyCompletion - // { - // [DebuggerHidden] - // public bool IsCompleted => true; - - // [DebuggerHidden] - // public void GetResult() - // { - //#if UNITY_2018_3_OR_NEWER - // UnityEngine.Debug.LogWarning("UniTaskVoid can't await, always fire-and-forget. use Forget instead of await."); - //#endif - // } - - // [DebuggerHidden] - // public void OnCompleted(Action continuation) - // { - // } - - // [DebuggerHidden] - // public void UnsafeOnCompleted(Action continuation) - // { - // } - // } } } diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.cs index 3374c22..db1d7a2 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.cs @@ -70,7 +70,7 @@ namespace Cysharp.Threading.Tasks public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); - continuationAction = continuation.AsFuncOfT(); // allocate delegate. + continuationAction = PooledDelegate.Create(continuation); asyncOperation.completed += continuationAction; } } @@ -242,7 +242,7 @@ namespace Cysharp.Threading.Tasks public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); - continuationAction = continuation.AsFuncOfT(); // allocate delegate. + continuationAction = PooledDelegate.Create(continuation); asyncOperation.completed += continuationAction; } } @@ -419,7 +419,7 @@ namespace Cysharp.Threading.Tasks public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); - continuationAction = continuation.AsFuncOfT(); // allocate delegate. + continuationAction = PooledDelegate.Create(continuation); asyncOperation.completed += continuationAction; } } @@ -596,7 +596,7 @@ namespace Cysharp.Threading.Tasks public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); - continuationAction = continuation.AsFuncOfT(); // allocate delegate. + continuationAction = PooledDelegate.Create(continuation); asyncOperation.completed += continuationAction; } } @@ -774,7 +774,7 @@ namespace Cysharp.Threading.Tasks public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); - continuationAction = continuation.AsFuncOfT(); // allocate delegate. + continuationAction = PooledDelegate.Create(continuation); asyncOperation.completed += continuationAction; } } diff --git a/src/UniTask/Assets/Scenes/SandboxMain.cs b/src/UniTask/Assets/Scenes/SandboxMain.cs index 72625fe..39541b1 100644 --- a/src/UniTask/Assets/Scenes/SandboxMain.cs +++ b/src/UniTask/Assets/Scenes/SandboxMain.cs @@ -13,7 +13,6 @@ using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; - // using DG.Tweening; public struct MyJob : IJob @@ -259,11 +258,15 @@ public class SandboxMain : MonoBehaviour { try { - var cts = new CancellationTokenSource(); - var r = UniAsync("https://bing.com/", cts.Token); - cts.Cancel(); - await r; - Debug.Log("UNIASYNC"); + //var cts = new CancellationTokenSource(); + //var r = UniAsync("https://bing.com/", cts.Token); + //cts.Cancel(); + //await r; + _ = await UnityWebRequest.Get("https://bing.com/").SendWebRequest(); + Debug.Log("UNIASYNC1 "); + + _ = await UnityWebRequest.Get("https://bing.com/").SendWebRequest(); + Debug.Log("UNIASYNC2"); } catch { @@ -291,7 +294,7 @@ public class SandboxMain : MonoBehaviour } - void Start() + async UniTaskVoid Start() { //UniTaskAsyncEnumerable.EveryValueChanged(mcc, x => x.MyProperty) // .Do(_ => { }, () => Debug.Log("COMPLETED")) @@ -301,15 +304,23 @@ public class SandboxMain : MonoBehaviour // }) // .Forget(); - _ = Test1(); + //_ = Test1(); Test2().Forget(); - StartCoroutine(Test3("https://bing.com/")); + //StartCoroutine(Test3("https://bing.com/")); // DG.Tweening.Core.TweenerCore - //okButton.GetComponent().DOMoveX(10.2f, 30); + //Debug.Log("GO MOVEX"); + //await okButton.GetComponent().DOMoveX(-10.2f, 3).WithCancellation(CancellationToken.None); + //Debug.Log("END MOVEX"); + //Debug.Log("AGAIN MOVE"); + //await okButton.GetComponent().DOMoveY(10.2f, 3).WithCancellation(CancellationToken.None); + //Debug.Log("AGAIN END MOVE"); + + + await UniTask.Yield(); // DOTween.To( var cts = new CancellationTokenSource();