TaskPool.cs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Runtime.CompilerServices;
  5. using System.Runtime.InteropServices;
  6. using System.Threading;
  7. namespace Cysharp.Threading.Tasks
  8. {
  9. // internally used but public, allow to user create custom operator with pooling.
  10. public static class TaskPool
  11. {
  12. internal static int MaxPoolSize;
  13. // avoid to use ConcurrentDictionary for safety of WebGL build.
  14. static Dictionary<Type, Func<int>> sizes = new Dictionary<Type, Func<int>>();
  15. static TaskPool()
  16. {
  17. try
  18. {
  19. var value = Environment.GetEnvironmentVariable("UNITASK_MAX_POOLSIZE");
  20. if (value != null)
  21. {
  22. if (int.TryParse(value, out var size))
  23. {
  24. MaxPoolSize = size;
  25. return;
  26. }
  27. }
  28. }
  29. catch { }
  30. MaxPoolSize = int.MaxValue;
  31. }
  32. public static void SetMaxPoolSize(int maxPoolSize)
  33. {
  34. MaxPoolSize = maxPoolSize;
  35. }
  36. public static IEnumerable<(Type, int)> GetCacheSizeInfo()
  37. {
  38. lock (sizes)
  39. {
  40. foreach (var item in sizes)
  41. {
  42. yield return (item.Key, item.Value());
  43. }
  44. }
  45. }
  46. public static void RegisterSizeGetter(Type type, Func<int> getSize)
  47. {
  48. lock (sizes)
  49. {
  50. sizes[type] = getSize;
  51. }
  52. }
  53. }
  54. public interface ITaskPoolNode<T>
  55. {
  56. ref T NextNode { get; }
  57. }
  58. // mutable struct, don't mark readonly.
  59. [StructLayout(LayoutKind.Auto)]
  60. public struct TaskPool<T>
  61. where T : class, ITaskPoolNode<T>
  62. {
  63. int gate;
  64. int size;
  65. T root;
  66. public int Size => size;
  67. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  68. public bool TryPop(out T result)
  69. {
  70. if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
  71. {
  72. var v = root;
  73. if (!(v is null))
  74. {
  75. ref var nextNode = ref v.NextNode;
  76. root = nextNode;
  77. nextNode = null;
  78. size--;
  79. result = v;
  80. Volatile.Write(ref gate, 0);
  81. return true;
  82. }
  83. Volatile.Write(ref gate, 0);
  84. }
  85. result = default;
  86. return false;
  87. }
  88. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  89. public bool TryPush(T item)
  90. {
  91. if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
  92. {
  93. if (size < TaskPool.MaxPoolSize)
  94. {
  95. item.NextNode = root;
  96. root = item;
  97. size++;
  98. Volatile.Write(ref gate, 0);
  99. return true;
  100. }
  101. else
  102. {
  103. Volatile.Write(ref gate, 0);
  104. }
  105. }
  106. return false;
  107. }
  108. }
  109. }