| using System; | 
| using System.Collections.Concurrent; | 
| using System.Collections.Generic; | 
| using System.Runtime.CompilerServices; | 
| using System.Runtime.InteropServices; | 
| using System.Threading; | 
|   | 
| namespace Cysharp.Threading.Tasks | 
| { | 
|     // internally used but public, allow to user create custom operator with pooling. | 
|   | 
|     public static class TaskPool | 
|     { | 
|         internal static int MaxPoolSize; | 
|   | 
|         // avoid to use ConcurrentDictionary for safety of WebGL build. | 
|         static Dictionary<Type, Func<int>> sizes = new Dictionary<Type, Func<int>>(); | 
|   | 
|         static TaskPool() | 
|         { | 
|             try | 
|             { | 
|                 var value = Environment.GetEnvironmentVariable("UNITASK_MAX_POOLSIZE"); | 
|                 if (value != null) | 
|                 { | 
|                     if (int.TryParse(value, out var size)) | 
|                     { | 
|                         MaxPoolSize = size; | 
|                         return; | 
|                     } | 
|                 } | 
|             } | 
|             catch { } | 
|   | 
|             MaxPoolSize = int.MaxValue; | 
|         } | 
|   | 
|         public static void SetMaxPoolSize(int maxPoolSize) | 
|         { | 
|             MaxPoolSize = maxPoolSize; | 
|         } | 
|   | 
|         public static IEnumerable<(Type, int)> GetCacheSizeInfo() | 
|         { | 
|             lock (sizes) | 
|             { | 
|                 foreach (var item in sizes) | 
|                 { | 
|                     yield return (item.Key, item.Value()); | 
|                 } | 
|             } | 
|         } | 
|   | 
|         public static void RegisterSizeGetter(Type type, Func<int> getSize) | 
|         { | 
|             lock (sizes) | 
|             { | 
|                 sizes[type] = getSize; | 
|             } | 
|         } | 
|     } | 
|   | 
|     public interface ITaskPoolNode<T> | 
|     { | 
|         ref T NextNode { get; } | 
|     } | 
|   | 
|     // mutable struct, don't mark readonly. | 
|     [StructLayout(LayoutKind.Auto)] | 
|     public struct TaskPool<T> | 
|         where T : class, ITaskPoolNode<T> | 
|     { | 
|         int gate; | 
|         int size; | 
|         T root; | 
|   | 
|         public int Size => size; | 
|   | 
|         [MethodImpl(MethodImplOptions.AggressiveInlining)] | 
|         public bool TryPop(out T result) | 
|         { | 
|             if (Interlocked.CompareExchange(ref gate, 1, 0) == 0) | 
|             { | 
|                 var v = root; | 
|                 if (!(v is null)) | 
|                 { | 
|                     ref var nextNode = ref v.NextNode; | 
|                     root = nextNode; | 
|                     nextNode = null; | 
|                     size--; | 
|                     result = v; | 
|                     Volatile.Write(ref gate, 0); | 
|                     return true; | 
|                 } | 
|   | 
|                 Volatile.Write(ref gate, 0); | 
|             } | 
|             result = default; | 
|             return false; | 
|         } | 
|   | 
|         [MethodImpl(MethodImplOptions.AggressiveInlining)] | 
|         public bool TryPush(T item) | 
|         { | 
|             if (Interlocked.CompareExchange(ref gate, 1, 0) == 0) | 
|             { | 
|                 if (size < TaskPool.MaxPoolSize) | 
|                 { | 
|                     item.NextNode = root; | 
|                     root = item; | 
|                     size++; | 
|                     Volatile.Write(ref gate, 0); | 
|                     return true; | 
|                 } | 
|                 else | 
|                 { | 
|                     Volatile.Write(ref gate, 0); | 
|                 } | 
|             } | 
|             return false; | 
|         } | 
|     } | 
| } |