diff --git a/src/UniTask.NetCoreSandbox/Program.cs b/src/UniTask.NetCoreSandbox/Program.cs index f7eab42..2c6da20 100644 --- a/src/UniTask.NetCoreSandbox/Program.cs +++ b/src/UniTask.NetCoreSandbox/Program.cs @@ -234,7 +234,7 @@ namespace NetCoreSandbox await c; - foreach (var item in Cysharp.Threading.Tasks.Internal.StackNodeMonitor.GetCacheSizeInfo()) + foreach (var item in Cysharp.Threading.Tasks.Internal.TaskPoolMonitor.GetCacheSizeInfo()) { Console.WriteLine(item); } diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/CompilerServices/MoveNextRunner.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/CompilerServices/MoveNextRunner.cs index cb614f9..64ae8e1 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/CompilerServices/MoveNextRunner.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/CompilerServices/MoveNextRunner.cs @@ -29,9 +29,11 @@ namespace Cysharp.Threading.Tasks.CompilerServices void SetException(Exception exception); } - internal sealed class MoveNextRunner : IMoveNextRunner, IStackNode> + internal sealed class MoveNextRunner : IMoveNextRunner, ITaskPoolNode> where TStateMachine : IAsyncStateMachine { + static TaskPool> pool; + TStateMachine stateMachine; public Action MoveNext { get; } @@ -43,7 +45,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices public static void SetStateMachine(ref AsyncUniTaskVoidMethodBuilder builder, ref TStateMachine stateMachine) { - if (!StackNodeHelper.TryPop(ref cacheLock, ref size, ref nodeRoot, out var result)) + if (!pool.TryPop(out var result)) { result = new MoveNextRunner(); } @@ -52,13 +54,9 @@ namespace Cysharp.Threading.Tasks.CompilerServices result.stateMachine = stateMachine; // copy struct StateMachine(in release build). } - static int cacheLock; - static int size; - static MoveNextRunner nodeRoot; - static MoveNextRunner() { - StackNodeMonitor.RegisterSizeGettter(typeof(MoveNextRunner), () => size); + TaskPoolMonitor.RegisterSizeGettter(typeof(MoveNextRunner), () => pool.Size); } public MoveNextRunner NextNode { get; set; } @@ -66,7 +64,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices public void Return() { stateMachine = default; - StackNodeHelper.TryPush(ref cacheLock, ref size, ref nodeRoot, this); + pool.TryPush(this); } [DebuggerHidden] @@ -77,9 +75,11 @@ namespace Cysharp.Threading.Tasks.CompilerServices } } - internal class MoveNextRunnerPromise : IMoveNextRunnerPromise, IUniTaskSource, IStackNode> + internal class MoveNextRunnerPromise : IMoveNextRunnerPromise, IUniTaskSource, ITaskPoolNode> where TStateMachine : IAsyncStateMachine { + static TaskPool> pool; + TStateMachine stateMachine; public Action MoveNext { get; } @@ -93,7 +93,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices public static void SetStateMachine(ref AsyncUniTaskMethodBuilder builder, ref TStateMachine stateMachine) { - if (!StackNodeHelper.TryPop(ref cacheLock, ref size, ref nodeRoot, out var result)) + if (!pool.TryPop(out var result)) { result = new MoveNextRunnerPromise(); } @@ -103,15 +103,11 @@ namespace Cysharp.Threading.Tasks.CompilerServices result.stateMachine = stateMachine; // copy struct StateMachine(in release build). } - static int cacheLock; - static int size; - static MoveNextRunnerPromise nodeRoot; - public MoveNextRunnerPromise NextNode { get; set; } static MoveNextRunnerPromise() { - StackNodeMonitor.RegisterSizeGettter(typeof(MoveNextRunnerPromise), () => size); + TaskPoolMonitor.RegisterSizeGettter(typeof(MoveNextRunnerPromise), () => pool.Size); } bool TryReturn() @@ -119,7 +115,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices TaskTracker.RemoveTracking(this); core.Reset(); stateMachine = default; - return StackNodeHelper.TryPush(ref cacheLock, ref size, ref nodeRoot, this); + return pool.TryPush(this); } [DebuggerHidden] @@ -190,9 +186,11 @@ namespace Cysharp.Threading.Tasks.CompilerServices } } - internal class MoveNextRunnerPromise : IMoveNextRunnerPromise, IUniTaskSource, IStackNode> + internal class MoveNextRunnerPromise : IMoveNextRunnerPromise, IUniTaskSource, ITaskPoolNode> where TStateMachine : IAsyncStateMachine { + static TaskPool> pool; + TStateMachine stateMachine; public Action MoveNext { get; } @@ -206,7 +204,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices public static void SetStateMachine(ref AsyncUniTaskMethodBuilder builder, ref TStateMachine stateMachine) { - if (!StackNodeHelper.TryPop(ref cacheLock, ref size, ref nodeRoot, out var result)) + if (!pool.TryPop(out var result)) { result = new MoveNextRunnerPromise(); } @@ -216,15 +214,11 @@ namespace Cysharp.Threading.Tasks.CompilerServices result.stateMachine = stateMachine; // copy struct StateMachine(in release build). } - static int cacheLock; - static int size; - static MoveNextRunnerPromise nodeRoot; - public MoveNextRunnerPromise NextNode { get; set; } static MoveNextRunnerPromise() { - StackNodeMonitor.RegisterSizeGettter(typeof(MoveNextRunnerPromise), () => size); + TaskPoolMonitor.RegisterSizeGettter(typeof(MoveNextRunnerPromise), () => pool.Size); } bool TryReturn() @@ -232,7 +226,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices TaskTracker.RemoveTracking(this); core.Reset(); stateMachine = default; - return StackNodeHelper.TryPush(ref cacheLock, ref size, ref nodeRoot, this); + return pool.TryPush(this); } [DebuggerHidden] diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/EnumeratorAsyncExtensions.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/EnumeratorAsyncExtensions.cs index b5cf765..ac13142 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/EnumeratorAsyncExtensions.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/EnumeratorAsyncExtensions.cs @@ -30,9 +30,34 @@ namespace Cysharp.Threading.Tasks return new UniTask(EnumeratorPromise.Create(enumerator, timing, cancellationToken, out var token), token); } - class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem + class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { - static readonly PromisePool pool = new PromisePool(); + static TaskPool pool; + public EnumeratorPromise NextNode { get; set; } + + static EnumeratorPromise() + { + TaskPoolMonitor.RegisterSizeGettter(typeof(EnumeratorPromise), () => pool.Size); + } + + static EnumeratorPromise Create() + { + if (!pool.TryPop(out var result)) + { + result = new EnumeratorPromise(); + } + TaskTracker.TrackActiveTask(result, 4); + return result; + } + + bool TryReturn() + { + TaskTracker.RemoveTracking(this); + core.Reset(); + innerEnumerator = default; + cancellationToken = default; + return pool.TryPush(this); + } IEnumerator innerEnumerator; CancellationToken cancellationToken; @@ -50,13 +75,11 @@ namespace Cysharp.Threading.Tasks return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } - var result = pool.TryRent() ?? new EnumeratorPromise(); + var result = Create(); result.innerEnumerator = ConsumeEnumerator(innerEnumerator); result.cancellationToken = cancellationToken; - TaskTracker.TrackActiveTask(result, 3); - PlayerLoopHelper.AddAction(timing, result); token = result.core.Version; @@ -67,12 +90,11 @@ namespace Cysharp.Threading.Tasks { try { - TaskTracker.RemoveTracking(this); core.GetResult(token); } finally { - pool.TryReturn(this); + TryReturn(); } } @@ -125,7 +147,7 @@ namespace Cysharp.Threading.Tasks ~EnumeratorPromise() { - if (pool.TryReturn(this)) + if (TryReturn()) { GC.ReRegisterForFinalize(this); } diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/PooledDelegate.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/PooledDelegate.cs index a8e69ea..7b12afc 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/PooledDelegate.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/PooledDelegate.cs @@ -1,18 +1,22 @@ using System; -using System.Collections.Concurrent; using System.Runtime.CompilerServices; namespace Cysharp.Threading.Tasks.Internal { - internal sealed class PooledDelegate + internal sealed class PooledDelegate : ITaskPoolNode> { - static readonly ConcurrentQueue> pool = new ConcurrentQueue>(); + static TaskPool> pool; + + public PooledDelegate NextNode { get; set; } + + static PooledDelegate() + { + TaskPoolMonitor.RegisterSizeGettter(typeof(PooledDelegate), () => pool.Size); + } readonly Action runDelegate; - Action continuation; - PooledDelegate() { runDelegate = Run; @@ -21,7 +25,7 @@ namespace Cysharp.Threading.Tasks.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Action Create(Action continuation) { - if (!pool.TryDequeue(out var item)) + if (!pool.TryPop(out var item)) { item = new PooledDelegate(); } @@ -37,9 +41,9 @@ namespace Cysharp.Threading.Tasks.Internal continuation = null; if (call != null) { - pool.Enqueue(this); + pool.TryPush(this); call.Invoke(); } } } -} +} \ No newline at end of file diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/StackNode.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/TaskPool.cs similarity index 69% rename from src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/StackNode.cs rename to src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/TaskPool.cs index dd6d9e8..2076bf0 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/StackNode.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/TaskPool.cs @@ -8,16 +8,11 @@ namespace Cysharp.Threading.Tasks.Internal { // internaly used but public, allow to user create custom operator with pooling. - public interface IStackNode + public static class TaskPool { - T NextNode { get; set; } - } + internal static int MaxPoolSize; - public static class StackNodeHelper - { - static int MaxPoolSize; - - static StackNodeHelper() + static TaskPool() { try { @@ -40,15 +35,28 @@ namespace Cysharp.Threading.Tasks.Internal { MaxPoolSize = maxPoolSize; } + } - // Strictness as a Stack is not required. - // If there is a conflict, it will go through as is. + + public interface ITaskPoolNode + { + T NextNode { get; set; } + } + + // mutable struct, don't mark readonly. + public struct TaskPool + where T : class, ITaskPoolNode + { + int gate; + int size; + T root; + + public int Size => size; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryPop(ref int stackLock, ref int size, ref T root, out T result) - where T : class, IStackNode + public bool TryPop(out T result) { - if (Interlocked.CompareExchange(ref stackLock, 1, 0) == 0) + if (Interlocked.CompareExchange(ref gate, 1, 0) == 0) { var v = root; if (!(v is null)) @@ -57,40 +65,39 @@ namespace Cysharp.Threading.Tasks.Internal v.NextNode = null; size--; result = v; - Volatile.Write(ref stackLock, 0); + Volatile.Write(ref gate, 0); return true; } - Volatile.Write(ref stackLock, 0); + Volatile.Write(ref gate, 0); } result = default; return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryPush(ref int stackLock, ref int size, ref T root, T item) - where T : class, IStackNode + public bool TryPush(T item) { - if (Interlocked.CompareExchange(ref stackLock, 1, 0) == 0) + if (Interlocked.CompareExchange(ref gate, 1, 0) == 0) { - if (size < MaxPoolSize) + if (size < TaskPool.MaxPoolSize) { item.NextNode = root; root = item; size++; - Volatile.Write(ref stackLock, 0); + Volatile.Write(ref gate, 0); return true; } else { - Volatile.Write(ref stackLock, 0); + Volatile.Write(ref gate, 0); } } return false; } } - public static class StackNodeMonitor + public static class TaskPoolMonitor { static ConcurrentDictionary> sizes = new ConcurrentDictionary>(); diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/StackNode.cs.meta b/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/TaskPool.cs.meta similarity index 100% rename from src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/StackNode.cs.meta rename to src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/TaskPool.cs.meta