설명 없음
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AllocatorManager.cs 78KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764
  1. #pragma warning disable 0649
  2. using System;
  3. using System.Diagnostics;
  4. using System.Runtime.InteropServices;
  5. using AOT;
  6. using Unity.Burst;
  7. using Unity.Collections.LowLevel.Unsafe;
  8. using Unity.Mathematics;
  9. using UnityEngine.Assertions;
  10. using Unity.Jobs.LowLevel.Unsafe;
  11. using System.Runtime.CompilerServices;
  12. using System.Threading;
  13. namespace Unity.Collections
  14. {
  15. [GenerateTestsForBurstCompatibility]
  16. struct Spinner
  17. {
  18. int m_Lock;
  19. /// <summary>
  20. /// Continually spin until the lock can be acquired.
  21. /// </summary>
  22. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  23. internal void Acquire()
  24. {
  25. for (; ; )
  26. {
  27. // Optimistically assume the lock is free on the first try.
  28. if (Interlocked.CompareExchange(ref m_Lock, 1, 0) == 0)
  29. {
  30. return;
  31. }
  32. // Wait for lock to be released without generate cache misses.
  33. while (Volatile.Read(ref m_Lock) == 1)
  34. {
  35. continue;
  36. }
  37. // Future improvement: the 'continue` instruction above could be swapped for a 'pause' intrinsic
  38. // instruction when the CPU supports it, to further reduce contention by reducing load-store unit
  39. // utilization. However, this would need to be optional because if you don't use hyper-threading
  40. // and you don't care about power efficiency, using the 'pause' instruction will slow down lock
  41. // acquisition in the contended scenario.
  42. }
  43. }
  44. /// <summary>
  45. /// Try to acquire the lock and immediately return without spinning.
  46. /// </summary>
  47. /// <returns><see langword="true"/> if the lock was acquired, <see langword="false"/> otherwise.</returns>
  48. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  49. internal bool TryAcquire()
  50. {
  51. // First do a memory load (read) to check if lock is free in order to prevent uncessary cache missed.
  52. return Volatile.Read(ref m_Lock) == 0 &&
  53. Interlocked.CompareExchange(ref m_Lock, 1, 0) == 0;
  54. }
  55. /// <summary>
  56. /// Try to acquire the lock, and spin only if <paramref name="spin"/> is <see langword="true"/>.
  57. /// </summary>
  58. /// <param name="spin">Set to true to spin the lock.</param>
  59. /// <returns><see langword="true"/> if the lock was acquired, <see langword="false" otherwise./></returns>
  60. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  61. internal bool TryAcquire(bool spin)
  62. {
  63. if (spin)
  64. {
  65. Acquire();
  66. return true;
  67. }
  68. return TryAcquire();
  69. }
  70. /// <summary>
  71. /// Release the lock
  72. /// </summary>
  73. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  74. internal void Release()
  75. {
  76. Volatile.Write(ref m_Lock, 0);
  77. }
  78. }
  79. /// <summary>
  80. /// Manages custom memory allocators.
  81. /// </summary>
  82. public static class AllocatorManager
  83. {
  84. internal static Block AllocateBlock<T>(ref this T t, int sizeOf, int alignOf, int items) where T : unmanaged, IAllocator
  85. {
  86. CheckValid(t.Handle);
  87. Block block = default;
  88. block.Range.Pointer = IntPtr.Zero;
  89. block.Range.Items = items;
  90. block.Range.Allocator = t.Handle;
  91. block.BytesPerItem = sizeOf;
  92. // Make the alignment multiple of cacheline size
  93. block.Alignment = math.max(JobsUtility.CacheLineSize, alignOf);
  94. var error = t.Try(ref block);
  95. CheckFailedToAllocate(error);
  96. return block;
  97. }
  98. internal static Block AllocateBlock<T,U>(ref this T t, U u, int items) where T : unmanaged, IAllocator where U : unmanaged
  99. {
  100. return AllocateBlock(ref t, UnsafeUtility.SizeOf<U>(), UnsafeUtility.AlignOf<U>(), items);
  101. }
  102. /// <summary>
  103. /// Allocates memory directly from an allocator.
  104. /// </summary>
  105. /// <typeparam name="T">The type of allocator.</typeparam>
  106. /// /// <param name="t">The allocator of type T used to allocator memory.</param>
  107. /// <param name="sizeOf">The number of bytes to allocate to item.</param>
  108. /// <param name="alignOf">The alignment in bytes.</param>
  109. /// <param name="items">The number of items. Defaults to 1.</param>
  110. /// <returns>A pointer to the allocated memory.</returns>
  111. public static unsafe void* Allocate<T>(ref this T t, int sizeOf, int alignOf, int items = 1) where T : unmanaged, IAllocator
  112. {
  113. return (void*)AllocateBlock(ref t, sizeOf, alignOf, items).Range.Pointer;
  114. }
  115. internal static unsafe U* Allocate<T,U>(ref this T t, U u, int items) where T : unmanaged, IAllocator where U : unmanaged
  116. {
  117. return (U*)Allocate(ref t, UnsafeUtility.SizeOf<U>(), UnsafeUtility.AlignOf<U>(), items);
  118. }
  119. internal static unsafe void* AllocateStruct<T, U>(ref this T t, U u, int items) where T : unmanaged, IAllocator where U : unmanaged
  120. {
  121. return (void*)Allocate(ref t, UnsafeUtility.SizeOf<U>(), UnsafeUtility.AlignOf<U>(), items);
  122. }
  123. internal static unsafe void FreeBlock<T>(ref this T t, ref Block block) where T : unmanaged, IAllocator
  124. {
  125. CheckValid(t.Handle);
  126. block.Range.Items = 0;
  127. var error = t.Try(ref block);
  128. CheckFailedToFree(error);
  129. }
  130. internal static unsafe void Free<T>(ref this T t, void* pointer, int sizeOf, int alignOf, int items) where T : unmanaged, IAllocator
  131. {
  132. if (pointer == null)
  133. return;
  134. Block block = default;
  135. block.AllocatedItems = items;
  136. block.Range.Pointer = (IntPtr)pointer;
  137. block.BytesPerItem = sizeOf;
  138. block.Alignment = alignOf;
  139. t.FreeBlock(ref block);
  140. }
  141. internal static unsafe void Free<T,U>(ref this T t, U* pointer, int items) where T : unmanaged, IAllocator where U : unmanaged
  142. {
  143. Free(ref t, pointer, UnsafeUtility.SizeOf<U>(), UnsafeUtility.AlignOf<U>(), items);
  144. }
  145. /// <summary>
  146. /// Allocates memory from an allocator.
  147. /// </summary>
  148. /// <param name="handle">A handle to the allocator.</param>
  149. /// <param name="itemSizeInBytes">The number of bytes to allocate.</param>
  150. /// <param name="alignmentInBytes">The alignment in bytes (must be a power of two).</param>
  151. /// <param name="items">The number of values to allocate space for. Defaults to 1.</param>
  152. /// <returns>A pointer to the allocated memory.</returns>
  153. public unsafe static void* Allocate(AllocatorHandle handle, int itemSizeInBytes, int alignmentInBytes, int items = 1)
  154. {
  155. return handle.Allocate(itemSizeInBytes, alignmentInBytes, items);
  156. }
  157. /// <summary>
  158. /// Allocates enough memory for an unmanaged value of a given type.
  159. /// </summary>
  160. /// <typeparam name="T">The type of value to allocate for.</typeparam>
  161. /// <param name="handle">A handle to the allocator.</param>
  162. /// <param name="items">The number of values to allocate for space for. Defaults to 1.</param>
  163. /// <returns>A pointer to the allocated memory.</returns>
  164. public unsafe static T* Allocate<T>(AllocatorHandle handle, int items = 1) where T : unmanaged
  165. {
  166. return handle.Allocate(default(T), items);
  167. }
  168. /// <summary>
  169. /// Frees an allocation.
  170. /// </summary>
  171. /// <remarks>For some allocators, the size of the allocation must be known to properly deallocate.
  172. /// Other allocators only need the pointer when deallocating and so will ignore `itemSizeInBytes`, `alignmentInBytes` and `items`.</remarks>
  173. /// <param name="handle">A handle to the allocator.</param>
  174. /// <param name="pointer">A pointer to the allocated memory.</param>
  175. /// <param name="itemSizeInBytes">The size in bytes of the allocation.</param>
  176. /// <param name="alignmentInBytes">The alignment in bytes (must be a power of two).</param>
  177. /// <param name="items">The number of values that the memory was allocated for.</param>
  178. public unsafe static void Free(AllocatorHandle handle, void* pointer, int itemSizeInBytes, int alignmentInBytes, int items = 1)
  179. {
  180. handle.Free(pointer, itemSizeInBytes, alignmentInBytes, items);
  181. }
  182. /// <summary>
  183. /// Frees an allocation.
  184. /// </summary>
  185. /// <param name="handle">A handle to the allocator.</param>
  186. /// <param name="pointer">A pointer to the allocated memory.</param>
  187. public unsafe static void Free(AllocatorHandle handle, void* pointer)
  188. {
  189. handle.Free((byte*)pointer, 1);
  190. }
  191. /// <summary>
  192. /// Frees an allocation.
  193. /// </summary>
  194. /// <remarks>For some allocators, the size of the allocation must be known to properly deallocate.
  195. /// Other allocators only need the pointer when deallocating and so will ignore `T` and `items`.</remarks>
  196. /// <typeparam name="T">The type of value that the memory was allocated for.</typeparam>
  197. /// <param name="handle">A handle to the allocator.</param>
  198. /// <param name="pointer">A pointer to the allocated memory.</param>
  199. /// <param name="items">The number of values that the memory was allocated for.</param>
  200. public unsafe static void Free<T>(AllocatorHandle handle, T* pointer, int items = 1) where T : unmanaged
  201. {
  202. handle.Free(pointer, items);
  203. }
  204. /// <summary>
  205. /// Corresponds to Allocator.Invalid.
  206. /// </summary>
  207. /// <value>Corresponds to Allocator.Invalid.</value>
  208. public static readonly AllocatorHandle Invalid = new AllocatorHandle { Index = 0 };
  209. /// <summary>
  210. /// Corresponds to Allocator.None.
  211. /// </summary>
  212. /// <value>Corresponds to Allocator.None.</value>
  213. public static readonly AllocatorHandle None = new AllocatorHandle { Index = 1 };
  214. /// <summary>
  215. /// Corresponds to Allocator.Temp.
  216. /// </summary>
  217. /// <value>Corresponds to Allocator.Temp.</value>
  218. public static readonly AllocatorHandle Temp = new AllocatorHandle { Index = 2 };
  219. /// <summary>
  220. /// Corresponds to Allocator.TempJob.
  221. /// </summary>
  222. /// <value>Corresponds to Allocator.TempJob.</value>
  223. public static readonly AllocatorHandle TempJob = new AllocatorHandle { Index = 3 };
  224. /// <summary>
  225. /// Corresponds to Allocator.Persistent.
  226. /// </summary>
  227. /// <value>Corresponds to Allocator.Persistent.</value>
  228. public static readonly AllocatorHandle Persistent = new AllocatorHandle { Index = 4 };
  229. /// <summary>
  230. /// Corresponds to Allocator.AudioKernel.
  231. /// </summary>
  232. /// <value>Corresponds to Allocator.AudioKernel.</value>
  233. /// <remarks>Do not use. Reserved for internal use.</remarks>
  234. public static readonly AllocatorHandle AudioKernel = new AllocatorHandle { Index = 5 };
  235. /// <summary>
  236. /// Used for calling an allocator function.
  237. /// </summary>
  238. public delegate int TryFunction(IntPtr allocatorState, ref Block block);
  239. /// <summary>
  240. /// Convert an Allocator to an AllocatorHandle, keeping the Version.
  241. /// </summary>
  242. /// <param name="a">The Allocator to convert.</param>
  243. /// <returns>The AllocatorHandle of an allocator.</returns>
  244. public static AllocatorHandle ConvertToAllocatorHandle(Allocator a)
  245. {
  246. ushort index = (ushort)((uint)a & 0xFFFF);
  247. ushort version = (ushort)((uint)a >> 16);
  248. return new AllocatorHandle { Index = index, Version = version };
  249. }
  250. /// <summary>
  251. /// Represents the allocator function used within an allocator.
  252. /// </summary>
  253. [StructLayout(LayoutKind.Sequential)]
  254. public struct AllocatorHandle : IAllocator, IEquatable<AllocatorHandle>, IComparable<AllocatorHandle>
  255. {
  256. internal ref TableEntry TableEntry => ref SharedStatics.TableEntry.Ref.Data.ElementAt(Index);
  257. internal bool IsInstalled => ((SharedStatics.IsInstalled.Ref.Data.ElementAt(Index>>6) >> (Index&63)) & 1) != 0;
  258. internal void IncrementVersion()
  259. {
  260. #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG
  261. if (IsInstalled && IsCurrent)
  262. {
  263. // When allocator version is larger than 0x7FFF, allocator.ToAllocator
  264. // returns a negative value which causes problem when comparing to Allocator.None.
  265. // So only lower 15 bits of version is valid.
  266. Version = OfficialVersion = (ushort)(++OfficialVersion & 0x7FFF);
  267. }
  268. #endif
  269. }
  270. internal void Rewind()
  271. {
  272. #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG
  273. InvalidateDependents();
  274. IncrementVersion();
  275. #endif
  276. }
  277. internal void Install(TableEntry tableEntry)
  278. {
  279. #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG
  280. // if this allocator has never been visited before, then the unsafelists for its child allocators
  281. // and child safety handles are uninitialized, which means their allocator is Allocator.Invalid.
  282. // rectify that here.
  283. if (ChildAllocators.Allocator.Value != (int)Allocator.Persistent)
  284. {
  285. ChildAllocators = new UnsafeList<AllocatorHandle>(0, Allocator.Persistent);
  286. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  287. ChildSafetyHandles = new UnsafeList<AtomicSafetyHandle>(0, Allocator.Persistent);
  288. #endif
  289. }
  290. #endif
  291. Rewind();
  292. TableEntry = tableEntry;
  293. }
  294. #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG
  295. internal ref ushort OfficialVersion => ref SharedStatics.Version.Ref.Data.ElementAt(Index);
  296. internal ref UnsafeList<AllocatorHandle> ChildAllocators => ref SharedStatics.ChildAllocators.Ref.Data.ElementAt(Index);
  297. internal ref AllocatorHandle Parent => ref SharedStatics.Parent.Ref.Data.ElementAt(Index);
  298. internal ref int IndexInParent => ref SharedStatics.IndexInParent.Ref.Data.ElementAt(Index);
  299. internal bool IsCurrent => (Version == 0) || (Version == OfficialVersion);
  300. internal bool IsValid => (Index < FirstUserIndex) || (IsInstalled && IsCurrent);
  301. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  302. internal ref Spinner ChildSpinLock => ref SharedStatics.ChildSpinLock.Ref.Data.ElementAt(Index);
  303. internal ref UnsafeList<AtomicSafetyHandle> ChildSafetyHandles => ref SharedStatics.ChildSafetyHandles.Ref.Data.ElementAt(Index);
  304. /// <summary>
  305. /// <para>Determines if the handle is still valid, because we intend to release it if it is.</para>
  306. /// </summary>
  307. /// <param name="handle">Safety handle.</param>
  308. internal static unsafe bool CheckExists(AtomicSafetyHandle handle)
  309. {
  310. bool res = false;
  311. int* versionNode = (int*) (void*) handle.versionNode;
  312. res = (handle.version == (*versionNode & AtomicSafetyHandle.ReadWriteDisposeCheck));
  313. return res;
  314. }
  315. internal static unsafe bool AreTheSame(AtomicSafetyHandle a, AtomicSafetyHandle b)
  316. {
  317. if(a.version != b.version)
  318. return false;
  319. if(a.versionNode != b.versionNode)
  320. return false;
  321. return true;
  322. }
  323. /// <summary>
  324. /// For internal use only.
  325. /// </summary>
  326. /// <value>For internal use only.</value>
  327. public const int InvalidChildSafetyHandleIndex = -1;
  328. internal int AddSafetyHandle(AtomicSafetyHandle handle)
  329. {
  330. if(!NeedsUseAfterFreeTracking())
  331. return InvalidChildSafetyHandleIndex;
  332. ChildSpinLock.Acquire();
  333. var result = ChildSafetyHandles.Length;
  334. ChildSafetyHandles.Add(handle);
  335. ChildSpinLock.Release();
  336. return result;
  337. }
  338. internal bool TryRemoveSafetyHandle(AtomicSafetyHandle handle, int safetyHandleIndex)
  339. {
  340. if(!NeedsUseAfterFreeTracking())
  341. return false;
  342. if(safetyHandleIndex == InvalidChildSafetyHandleIndex)
  343. return false;
  344. ChildSpinLock.Acquire();
  345. safetyHandleIndex = math.min(safetyHandleIndex, ChildSafetyHandles.Length - 1);
  346. while(safetyHandleIndex >= 0)
  347. {
  348. unsafe
  349. {
  350. var safetyHandle = ChildSafetyHandles.Ptr + safetyHandleIndex;
  351. if(AreTheSame(*safetyHandle, handle))
  352. {
  353. ChildSafetyHandles.RemoveAtSwapBack(safetyHandleIndex);
  354. ChildSpinLock.Release();
  355. return true;
  356. }
  357. }
  358. --safetyHandleIndex;
  359. }
  360. ChildSpinLock.Release();
  361. return false;
  362. }
  363. #endif
  364. internal bool NeedsUseAfterFreeTracking()
  365. {
  366. if (IsValid == false)
  367. return false;
  368. if (ChildAllocators.Allocator.Value != (int)Allocator.Persistent)
  369. return false;
  370. return true;
  371. }
  372. internal static bool AreTheSame(AllocatorHandle a, AllocatorHandle b)
  373. {
  374. if (a.Index != b.Index)
  375. return false;
  376. if (a.Version != b.Version)
  377. return false;
  378. return true;
  379. }
  380. /// <summary>
  381. /// For internal use only.
  382. /// </summary>
  383. /// <value>For internal use only.</value>
  384. public const int InvalidChildAllocatorIndex = -1;
  385. internal int AddChildAllocator(AllocatorHandle handle)
  386. {
  387. if(!NeedsUseAfterFreeTracking())
  388. return InvalidChildAllocatorIndex;
  389. var result = ChildAllocators.Length;
  390. ChildAllocators.Add(handle);
  391. handle.Parent = this;
  392. handle.IndexInParent = result;
  393. return result;
  394. }
  395. internal bool TryRemoveChildAllocator(AllocatorHandle handle, int childAllocatorIndex)
  396. {
  397. if(!NeedsUseAfterFreeTracking())
  398. return false;
  399. if(childAllocatorIndex == InvalidChildAllocatorIndex)
  400. return false;
  401. childAllocatorIndex = math.min(childAllocatorIndex, ChildAllocators.Length - 1);
  402. while(childAllocatorIndex >= 0)
  403. {
  404. unsafe
  405. {
  406. var allocatorHandle = ChildAllocators.Ptr + childAllocatorIndex;
  407. if(AreTheSame(*allocatorHandle, handle))
  408. {
  409. ChildAllocators.RemoveAtSwapBack(childAllocatorIndex);
  410. return true;
  411. }
  412. }
  413. --childAllocatorIndex;
  414. }
  415. return false;
  416. }
  417. // when you rewind an allocator, it invalidates and unregisters all of its child allocators - allocators that use as
  418. // backing memory, memory that was allocated from this (parent) allocator. the rewind operation was itself unmanaged,
  419. // until we added a managed global table of delegates alongside the unmanaged global table of function pointers. once
  420. // this table was added, the "unregister" extension function became managed, because it manipulates a managed array of
  421. // delegates.
  422. // a workaround (UnmanagedUnregister) was found that makes it possible for rewind to become unmanaged again: only in
  423. // the case that we rewind an allocator and invalidate all of its child allocators, we then unregister the child
  424. // allocators without touching the managed array of delegates as well.
  425. // this can "leak" delegates - it's possible for this to cause us to hold onto a GC reference to a delegate until
  426. // the end of the program, long after the delegate is no longer needed. but, there are only 65,536 such slots to
  427. // burn, and delegates are small data structures, and the leak ends when a delegate slot is reused, and most importantly,
  428. // when we've rewound an allocator while child allocators remain registered, we are likely before long to encounter
  429. // a use-before-free crash or a safety handle violation, both of which are likely to terminate the session before
  430. // anything can leak.
  431. internal void InvalidateDependents()
  432. {
  433. if (!NeedsUseAfterFreeTracking())
  434. return;
  435. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  436. ChildSpinLock.Acquire();
  437. for (var i = 0; i < ChildSafetyHandles.Length; ++i)
  438. {
  439. unsafe
  440. {
  441. AtomicSafetyHandle* handle = ChildSafetyHandles.Ptr + i;
  442. if(CheckExists(*handle))
  443. AtomicSafetyHandle.Release(*handle);
  444. }
  445. }
  446. ChildSafetyHandles.Clear();
  447. ChildSafetyHandles.TrimExcess();
  448. ChildSpinLock.Release();
  449. #endif
  450. if (Parent.IsValid)
  451. Parent.TryRemoveChildAllocator(this, IndexInParent);
  452. Parent = default;
  453. IndexInParent = InvalidChildAllocatorIndex;
  454. for (var i = 0; i < ChildAllocators.Length; ++i)
  455. {
  456. unsafe
  457. {
  458. AllocatorHandle* handle = (AllocatorHandle*)ChildAllocators.Ptr + i;
  459. if (handle->IsValid)
  460. handle->UnmanagedUnregister(); // see above comment
  461. }
  462. }
  463. ChildAllocators.Clear();
  464. ChildAllocators.TrimExcess();
  465. }
  466. #endif
  467. /// <summary>
  468. /// Implicitly convert an Allocator to an AllocatorHandle with its Version being reset to 0.
  469. /// </summary>
  470. /// <param name="a">The Allocator to convert.</param>
  471. /// <returns>The AllocatorHandle of an allocator.</returns>
  472. public static implicit operator AllocatorHandle(Allocator a) => new AllocatorHandle
  473. {
  474. Index = (ushort)((uint)a & 0xFFFF),
  475. Version = 0
  476. };
  477. /// <summary>
  478. /// This allocator's index into the global table of allocator functions.
  479. /// </summary>
  480. /// <value>This allocator's index into the global table of allocator functions.</value>
  481. public ushort Index;
  482. /// <summary>
  483. /// This allocator's version number.
  484. /// </summary>
  485. /// <remarks>An allocator function is uniquely identified by its *combination* of <see cref="Index"/> and <see cref="Version"/> together: each
  486. /// index has a version number that starts at 0; the version number is incremented each time the allocator is invalidated. Only the
  487. /// lower 15 bits of Version is in use because when allocator version is larger than 0x7FFF, allocator.ToAllocator returns a negative value
  488. /// which causes problem when comparing to Allocator.None.
  489. /// </remarks>
  490. /// <value>This allocator's version number.</value>
  491. public ushort Version;
  492. /// <summary>
  493. /// The <see cref="Index"/> cast to int.
  494. /// </summary>
  495. /// <value>The <see cref="Index"/> cast to int.</value>
  496. public int Value => Index;
  497. /// <summary>
  498. /// Allocates a block from this allocator.
  499. /// </summary>
  500. /// <typeparam name="T">The type of value to allocate for.</typeparam>
  501. /// <param name="block">Outputs the allocated block.</param>
  502. /// <param name="items">The number of values to allocate for.</param>
  503. /// <returns>0 if successful. Otherwise, returns the error code from the allocator function.</returns>
  504. public int TryAllocateBlock<T>(out Block block, int items) where T : unmanaged
  505. {
  506. block = new Block
  507. {
  508. Range = new Range { Items = items, Allocator = this },
  509. BytesPerItem = UnsafeUtility.SizeOf<T>(),
  510. Alignment = 1 << math.min(3, math.tzcnt(UnsafeUtility.SizeOf<T>()))
  511. };
  512. var returnCode = Try(ref block);
  513. return returnCode;
  514. }
  515. /// <summary>
  516. /// Allocates a block with this allocator function.
  517. /// </summary>
  518. /// <typeparam name="T">The type of value to allocate for.</typeparam>
  519. /// <param name="items">The number of values to allocate for.</param>
  520. /// <returns>The allocated block.</returns>
  521. /// <exception cref="ArgumentException">Thrown if the allocator is not valid or if the allocation failed.</exception>
  522. public Block AllocateBlock<T>(int items) where T : unmanaged
  523. {
  524. CheckValid(this);
  525. var error = TryAllocateBlock<T>(out Block block, items);
  526. CheckAllocatedSuccessfully(error);
  527. return block;
  528. }
  529. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  530. static void CheckAllocatedSuccessfully(int error)
  531. {
  532. if (error != 0)
  533. throw new ArgumentException($"Error {error}: Failed to Allocate");
  534. }
  535. /// <summary>
  536. /// For internal use only.
  537. /// </summary>
  538. /// <value>For internal use only.</value>
  539. public TryFunction Function => default;
  540. /// <summary>
  541. /// Tries to allocate the block with this allocator.
  542. /// </summary>
  543. /// <param name="block">The block to allocate.</param>
  544. /// <returns>0 if successful. Otherwise, returns an error code.</returns>
  545. public int Try(ref Block block)
  546. {
  547. block.Range.Allocator = this;
  548. var error = AllocatorManager.Try(ref block);
  549. return error;
  550. }
  551. /// <summary>
  552. /// This handle.
  553. /// </summary>
  554. /// <value>This handle.</value>
  555. public AllocatorHandle Handle { get { return this; } set { this = value; } }
  556. /// <summary>
  557. /// Retrieve the Allocator associated with this allocator handle.
  558. /// </summary>
  559. /// <value>The Allocator retrieved.</value>
  560. public Allocator ToAllocator
  561. {
  562. get
  563. {
  564. uint lo = Index;
  565. uint hi = Version;
  566. uint value = (hi << 16) | lo;
  567. return (Allocator)value;
  568. }
  569. }
  570. /// <summary>
  571. /// Check whether this allocator is a custom allocator.
  572. /// </summary>
  573. /// <remarks>The AllocatorHandle is a custom allocator if its Index is larger or equal to `FirstUserIndex`.</remarks>
  574. /// <value>True if this AllocatorHandle is a custom allocator.</value>
  575. public bool IsCustomAllocator { get { return this.Index >= FirstUserIndex; } }
  576. /// <summary>
  577. /// Check whether this allocator will automatically dispose allocations.
  578. /// </summary>
  579. /// <value>True if allocations made by this AllocatorHandle are not automatically disposed.</value>
  580. public bool IsAutoDispose { get { return ((SharedStatics.IsAutoDispose.Ref.Data.ElementAt(Index >> 6) >> (Index & 63)) & 1) != 0; } }
  581. /// <summary>
  582. /// Dispose the allocator.
  583. /// </summary>
  584. public void Dispose()
  585. {
  586. Rewind();
  587. #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG
  588. ChildAllocators.Dispose();
  589. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  590. ChildSafetyHandles.Dispose();
  591. #endif
  592. #endif
  593. TableEntry = default;
  594. }
  595. /// <summary>
  596. /// <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> instances are equal if they refer to the same instance at the same version.
  597. /// </summary>
  598. /// <param name="obj">Object containing an <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/>.</param>
  599. /// <returns>Returns true if both handles are for the same allocator instance at the same version, otherwise false.</returns>
  600. public override bool Equals(object obj)
  601. {
  602. if (obj is AllocatorHandle)
  603. return Value == ((AllocatorHandle) obj).Value;
  604. if (obj is Allocator)
  605. return ToAllocator == ((Allocator)obj);
  606. return false;
  607. }
  608. /// <summary>
  609. /// <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> instances are equal if they refer to the same instance at the same version.
  610. /// </summary>
  611. /// <param name="other"><seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> to compare against.</param>
  612. /// <returns>Returns true if both handles are for the same allocator instance at the same version, otherwise false.</returns>
  613. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  614. public bool Equals(AllocatorHandle other)
  615. {
  616. return Value == other.Value;
  617. }
  618. /// <summary>
  619. /// <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> instances are equal if they refer to the same instance at the same version.
  620. /// </summary>
  621. /// <param name="other"><seealso cref="Unity.Collections.Allocator"/> to compare against.</param>
  622. /// <returns>Returns true if both handles are for the same allocator instance at the same version, otherwise false.</returns>
  623. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  624. public bool Equals(Allocator other)
  625. {
  626. return ToAllocator == other;
  627. }
  628. /// <summary>
  629. /// A hash used for comparisons.
  630. /// </summary>
  631. /// <returns>A unique hash code.</returns>
  632. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  633. public override int GetHashCode()
  634. {
  635. return Value;
  636. }
  637. /// <summary>
  638. /// Evaluates if one <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> is equal to the other.
  639. /// </summary>
  640. /// <param name="lhs">The left-hand side</param>
  641. /// <param name="rhs">The right-hand side</param>
  642. /// <returns>True if the left-hand side's <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> is equal to the right-hand side's.</returns>
  643. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  644. public static bool operator ==(AllocatorHandle lhs, AllocatorHandle rhs)
  645. {
  646. return lhs.Value == rhs.Value;
  647. }
  648. /// <summary>
  649. /// Evaluates if one <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> is not equal to the other.
  650. /// </summary>
  651. /// <param name="lhs">The left-hand side</param>
  652. /// <param name="rhs">The right-hand side</param>
  653. /// <returns>True if the left-hand side's <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> is not equal to the right-hand side's.</returns>
  654. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  655. public static bool operator !=(AllocatorHandle lhs, AllocatorHandle rhs)
  656. {
  657. return lhs.Value != rhs.Value;
  658. }
  659. /// <summary>
  660. /// Evaluates if one <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> is less than the other.
  661. /// </summary>
  662. /// <param name="lhs">The left-hand side</param>
  663. /// <param name="rhs">The right-hand side</param>
  664. /// <returns>True if the left-hand side's <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> is less than the right-hand side's.</returns>
  665. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  666. public static bool operator <(AllocatorHandle lhs, AllocatorHandle rhs)
  667. {
  668. return lhs.Value < rhs.Value;
  669. }
  670. /// <summary>
  671. /// Evaluates if one <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> is greater than the other.
  672. /// </summary>
  673. /// <param name="lhs">The left-hand side</param>
  674. /// <param name="rhs">The right-hand side</param>
  675. /// <returns>True if the left-hand side's <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> is greater than the right-hand side's.</returns>
  676. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  677. public static bool operator >(AllocatorHandle lhs, AllocatorHandle rhs)
  678. {
  679. return lhs.Value > rhs.Value;
  680. }
  681. /// <summary>
  682. /// Evaluates if one <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> is less than or equal to the other.
  683. /// </summary>
  684. /// <param name="lhs">The left-hand side</param>
  685. /// <param name="rhs">The right-hand side</param>
  686. /// <returns>True if the left-hand side's <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> is less than or equal to the right-hand side's.</returns>
  687. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  688. public static bool operator <=(AllocatorHandle lhs, AllocatorHandle rhs)
  689. {
  690. return lhs.Value <= rhs.Value;
  691. }
  692. /// <summary>
  693. /// Evaluates if one <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> is greater than or equal to the other.
  694. /// </summary>
  695. /// <param name="lhs">The left-hand side</param>
  696. /// <param name="rhs">The right-hand side</param>
  697. /// <returns>True if the left-hand side's <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> is greater than or equal to the right-hand side's.</returns>
  698. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  699. public static bool operator >=(AllocatorHandle lhs, AllocatorHandle rhs)
  700. {
  701. return lhs.Value >= rhs.Value;
  702. }
  703. /// <summary>
  704. /// Compare this <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> against a given one
  705. /// </summary>
  706. /// <param name="other">The other <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> to compare to</param>
  707. /// <returns>Difference between <seealso cref="Unity.Collections.AllocatorManager.AllocatorHandle"/> values</returns>
  708. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  709. public int CompareTo(AllocatorHandle other)
  710. {
  711. return Value - other.Value;
  712. }
  713. }
  714. /// <summary>
  715. /// For internal use only.
  716. /// </summary>
  717. [StructLayout(LayoutKind.Sequential)]
  718. public struct BlockHandle
  719. {
  720. /// <summary>
  721. /// Represents the handle.
  722. /// </summary>
  723. /// <value>Represents the handle.</value>
  724. public ushort Value;
  725. }
  726. /// <summary>
  727. /// A range of allocated memory.
  728. /// </summary>
  729. /// <remarks>The name is perhaps misleading: only in combination with a <see cref="Block"/> does
  730. /// a `Range` have sufficient information to represent the number of bytes in an allocation. The reason `Range` is its own type that's separate from `Block`
  731. /// stems from some efficiency concerns in the implementation details. In most cases, a `Range` is only used in conjunction with an associated `Block`.
  732. /// </remarks>
  733. [StructLayout(LayoutKind.Sequential)]
  734. public struct Range : IDisposable
  735. {
  736. /// <summary>
  737. /// Pointer to the start of this range.
  738. /// </summary>
  739. /// <value>Pointer to the start of this range.</value>
  740. public IntPtr Pointer; // 0
  741. /// <summary>
  742. /// Number of items allocated in this range.
  743. /// </summary>
  744. /// <remarks>The actual allocation may be larger. See <see cref="Block.AllocatedItems"/>.</remarks>
  745. /// <value>Number of items allocated in this range. </value>
  746. public int Items; // 8
  747. /// <summary>
  748. /// The allocator function used for this range.
  749. /// </summary>
  750. /// <value>The allocator function used for this range.</value>
  751. public AllocatorHandle Allocator; // 12
  752. /// <summary>
  753. /// Deallocates the memory represented by this range.
  754. /// </summary>
  755. /// <remarks>
  756. /// Same as disposing the <see cref="Block"/> which contains this range.
  757. ///
  758. /// Cannot be used with allocators which need the allocation size to deallocate.
  759. /// </remarks>
  760. public void Dispose()
  761. {
  762. Block block = new Block { Range = this };
  763. block.Dispose();
  764. this = block.Range;
  765. }
  766. }
  767. /// <summary>
  768. /// Represents an individual allocation within an allocator.
  769. /// </summary>
  770. /// <remarks>A block consists of a <see cref="Range"/> plus metadata about the type of elements for which the block was allocated.</remarks>
  771. [StructLayout(LayoutKind.Sequential)]
  772. public struct Block : IDisposable
  773. {
  774. /// <summary>
  775. /// The range of memory encompassed by this block.
  776. /// </summary>
  777. /// <value>The range of memory encompassed by this block.</value>
  778. public Range Range;
  779. /// <summary>
  780. /// Number of bytes per item.
  781. /// </summary>
  782. /// <value>Number of bytes per item.</value>
  783. public int BytesPerItem;
  784. /// <summary>
  785. /// Number of items allocated for.
  786. /// </summary>
  787. /// <value>Number of items allocated for.</value>
  788. public int AllocatedItems;
  789. /// <summary>
  790. /// Log2 of the byte alignment.
  791. /// </summary>
  792. /// <remarks>The alignment must always be power of 2. Storing the alignment as its log2 helps enforces this.</remarks>
  793. /// <value>Log2 of the byte alignment.</value>
  794. public byte Log2Alignment;
  795. /// <summary>
  796. /// This field only exists to pad the `Block` struct. Ignore it.
  797. /// </summary>
  798. /// <value>This field only exists to pad the `Block` struct. Ignore it.</value>
  799. public byte Padding0;
  800. /// <summary>
  801. /// This field only exists to pad the `Block` struct. Ignore it.
  802. /// </summary>
  803. /// <value>This field only exists to pad the `Block` struct. Ignore it.</value>
  804. public ushort Padding1;
  805. /// <summary>
  806. /// This field only exists to pad the `Block` struct. Ignore it.
  807. /// </summary>
  808. /// <value>This field only exists to pad the `Block` struct. Ignore it.</value>
  809. public uint Padding2;
  810. /// <summary>
  811. /// Number of bytes requested for this block.
  812. /// </summary>
  813. /// <remarks>The actual allocation size may be larger due to alignment.</remarks>
  814. /// <value>Number of bytes requested for this block.</value>
  815. public long Bytes => (long) BytesPerItem * Range.Items;
  816. /// <summary>
  817. /// Number of bytes allocated for this block.
  818. /// </summary>
  819. /// <remarks>The requested allocation size may be smaller. Any excess is due to alignment</remarks>
  820. /// <value>Number of bytes allocated for this block.</value>
  821. public long AllocatedBytes => (long) BytesPerItem * AllocatedItems;
  822. /// <summary>
  823. /// The alignment.
  824. /// </summary>
  825. /// <remarks>Must be power of 2 that's greater than or equal to 0.
  826. ///
  827. /// Set alignment *before* the allocation is made. Setting it after has no effect on the allocation.</remarks>
  828. /// <param name="value">A new alignment. If not a power of 2, it will be rounded up to the next largest power of 2.</param>
  829. /// <value>The alignment.</value>
  830. public int Alignment
  831. {
  832. get => 1 << Log2Alignment;
  833. set => Log2Alignment = (byte)(32 - math.lzcnt(math.max(1, value) - 1));
  834. }
  835. /// <summary>
  836. /// Deallocates this block.
  837. /// </summary>
  838. /// <remarks>Same as <see cref="TryAllocate"/>.</remarks>
  839. public void Dispose()
  840. {
  841. TryFree();
  842. }
  843. /// <summary>
  844. /// Attempts to allocate this block.
  845. /// </summary>
  846. /// <returns>0 if successful. Otherwise, returns the error code from the allocator function.</returns>
  847. public int TryAllocate()
  848. {
  849. Range.Pointer = IntPtr.Zero;
  850. return Try(ref this);
  851. }
  852. /// <summary>
  853. /// Attempts to free this block.
  854. /// </summary>
  855. /// <returns>0 if successful. Otherwise, returns the error code from the allocator function.</returns>
  856. public int TryFree()
  857. {
  858. Range.Items = 0;
  859. return Try(ref this);
  860. }
  861. /// <summary>
  862. /// Allocates this block.
  863. /// </summary>
  864. /// <exception cref="ArgumentException">Thrown if safety checks are enabled and the allocation fails.</exception>
  865. public void Allocate()
  866. {
  867. var error = TryAllocate();
  868. CheckFailedToAllocate(error);
  869. }
  870. /// <summary>
  871. /// Frees the block.
  872. /// </summary>
  873. /// <exception cref="ArgumentException">Thrown if safety checks are enabled and the deallocation fails.</exception>
  874. public void Free()
  875. {
  876. var error = TryFree();
  877. CheckFailedToFree(error);
  878. }
  879. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  880. void CheckFailedToAllocate(int error)
  881. {
  882. if (error != 0)
  883. throw new ArgumentException($"Error {error}: Failed to Allocate {this}");
  884. }
  885. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  886. void CheckFailedToFree(int error)
  887. {
  888. if (error != 0)
  889. throw new ArgumentException($"Error {error}: Failed to Free {this}");
  890. }
  891. }
  892. /// <summary>
  893. /// An allocator function pointer.
  894. /// </summary>
  895. public interface IAllocator : IDisposable
  896. {
  897. /// <summary>
  898. /// The allocator function. It can allocate, deallocate, or reallocate.
  899. /// </summary>
  900. TryFunction Function { get; }
  901. /// <summary>
  902. /// Invoke the allocator function.
  903. /// </summary>
  904. /// <param name="block">The block to allocate, deallocate, or reallocate. See <see cref="AllocatorManager.Try"/></param>
  905. /// <returns>0 if successful. Otherwise, returns the error code from the allocator function.</returns>
  906. int Try(ref Block block);
  907. /// <summary>
  908. /// This allocator.
  909. /// </summary>
  910. /// <value>This allocator.</value>
  911. AllocatorHandle Handle { get; set; }
  912. /// <summary>
  913. /// Cast the Allocator index into Allocator
  914. /// </summary>
  915. Allocator ToAllocator { get; }
  916. /// <summary>
  917. /// Check whether an allocator is a custom allocator
  918. /// </summary>
  919. bool IsCustomAllocator { get; }
  920. /// <summary>
  921. /// Check whether an allocator will automatically dispose allocations.
  922. /// </summary>
  923. /// <remarks>Allocations made by allocator are not automatically disposed by default.</remarks>
  924. bool IsAutoDispose { get { return false; } }
  925. }
  926. /// <summary>
  927. /// Memory allocation Success status
  928. /// </summary>
  929. public const int kErrorNone = 0;
  930. /// <summary>
  931. /// Memory allocation Buffer Overflow status
  932. /// </summary>
  933. public const int kErrorBufferOverflow = -1;
  934. [BurstDiscard]
  935. private static void CheckDelegate(ref bool useDelegate)
  936. {
  937. //@TODO: This should use BurstCompiler.IsEnabled once that is available as an efficient API.
  938. useDelegate = true;
  939. }
  940. private static bool UseDelegate()
  941. {
  942. bool result = false;
  943. CheckDelegate(ref result);
  944. return result;
  945. }
  946. private static int allocate_block(ref Block block)
  947. {
  948. TableEntry tableEntry = default;
  949. tableEntry = block.Range.Allocator.TableEntry;
  950. var function = new FunctionPointer<TryFunction>(tableEntry.function);
  951. // this is a path for bursted caller, for non-Burst C#, it generates garbage each time we call Invoke
  952. return function.Invoke(tableEntry.state, ref block);
  953. }
  954. [BurstDiscard]
  955. private static void forward_mono_allocate_block(ref Block block, ref int error)
  956. {
  957. TableEntry tableEntry = default;
  958. tableEntry = block.Range.Allocator.TableEntry;
  959. var index = block.Range.Allocator.Handle.Index;
  960. if (index >= MaxNumCustomAllocators)
  961. {
  962. throw new ArgumentException("Allocator index into TryFunction delegate table exceeds maximum.");
  963. }
  964. ref TryFunction function = ref Managed.TryFunctionDelegates[block.Range.Allocator.Handle.Index];
  965. error = function(tableEntry.state, ref block);
  966. }
  967. internal static Allocator LegacyOf(AllocatorHandle handle)
  968. {
  969. if (handle.Value >= FirstUserIndex)
  970. return Allocator.Persistent;
  971. return (Allocator) handle.Value;
  972. }
  973. static unsafe int TryLegacy(ref Block block)
  974. {
  975. if (block.Range.Pointer == IntPtr.Zero) // Allocate
  976. {
  977. block.Range.Pointer = (IntPtr)Memory.Unmanaged.Allocate(block.Bytes, block.Alignment, LegacyOf(block.Range.Allocator));
  978. block.AllocatedItems = block.Range.Items;
  979. return (block.Range.Pointer == IntPtr.Zero) ? -1 : 0;
  980. }
  981. if (block.Bytes == 0) // Free
  982. {
  983. if (LegacyOf(block.Range.Allocator) != Allocator.None)
  984. {
  985. Memory.Unmanaged.Free((void*)block.Range.Pointer, LegacyOf(block.Range.Allocator));
  986. }
  987. block.Range.Pointer = IntPtr.Zero;
  988. block.AllocatedItems = 0;
  989. return 0;
  990. }
  991. // Reallocate (keep existing pointer and change size if possible. otherwise, allocate new thing and copy)
  992. return -1;
  993. }
  994. /// <summary>
  995. /// Invokes the allocator function of a block.
  996. /// </summary>
  997. /// <remarks>The allocator function is looked up from a global table.
  998. ///
  999. /// - If the block range's Pointer is null, it will allocate.
  1000. /// - If the block range's Pointer is not null, it will reallocate.
  1001. /// - If the block range's Items is 0, it will deallocate.
  1002. /// </remarks>
  1003. /// <param name="block">The block to allocate, deallocate, or reallocate.</param>
  1004. /// <returns>0 if successful. Otherwise, returns the error code from the block's allocator function.</returns>
  1005. public static unsafe int Try(ref Block block)
  1006. {
  1007. if (block.Range.Allocator.Value < FirstUserIndex)
  1008. return TryLegacy(ref block);
  1009. TableEntry tableEntry = default;
  1010. tableEntry = block.Range.Allocator.TableEntry;
  1011. var function = new FunctionPointer<TryFunction>(tableEntry.function);
  1012. #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG
  1013. // if the allocator being passed in has a version of 0, that means "whatever the current version is."
  1014. // so we patch it here, with whatever the current version is...
  1015. if (block.Range.Allocator.Version == 0)
  1016. block.Range.Allocator.Version = block.Range.Allocator.OfficialVersion;
  1017. #endif
  1018. if (UseDelegate())
  1019. {
  1020. int error = kErrorNone;
  1021. forward_mono_allocate_block(ref block, ref error);
  1022. return error;
  1023. }
  1024. return allocate_block(ref block);
  1025. }
  1026. /// <summary>
  1027. /// A stack allocator with no storage of its own. Uses the storage of its parent.
  1028. /// </summary>
  1029. [BurstCompile]
  1030. internal struct StackAllocator : IAllocator, IDisposable
  1031. {
  1032. public AllocatorHandle Handle { get { return m_handle; } set { m_handle = value; } }
  1033. public Allocator ToAllocator { get { return m_handle.ToAllocator; } }
  1034. public bool IsCustomAllocator { get { return m_handle.IsCustomAllocator; } }
  1035. internal AllocatorHandle m_handle;
  1036. internal Block m_storage;
  1037. internal long m_top;
  1038. public void Initialize(Block storage)
  1039. {
  1040. m_storage = storage;
  1041. m_top = 0;
  1042. #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG
  1043. m_storage.Range.Allocator.AddChildAllocator(Handle);
  1044. #endif
  1045. }
  1046. public unsafe int Try(ref Block block)
  1047. {
  1048. if (block.Range.Pointer == IntPtr.Zero) // Allocate
  1049. {
  1050. if (m_top + block.Bytes > m_storage.Bytes)
  1051. {
  1052. return -1;
  1053. }
  1054. block.Range.Pointer = (IntPtr)((byte*)m_storage.Range.Pointer + m_top);
  1055. block.AllocatedItems = block.Range.Items;
  1056. m_top += block.Bytes;
  1057. return 0;
  1058. }
  1059. if (block.Bytes == 0) // Free
  1060. {
  1061. if ((byte*)block.Range.Pointer - (byte*)m_storage.Range.Pointer == (long)(m_top - block.AllocatedBytes))
  1062. {
  1063. m_top -= block.AllocatedBytes;
  1064. var blockSizeInBytes = block.AllocatedItems * block.BytesPerItem;
  1065. block.Range.Pointer = IntPtr.Zero;
  1066. block.AllocatedItems = 0;
  1067. return 0;
  1068. }
  1069. return -1;
  1070. }
  1071. // Reallocate (keep existing pointer and change size if possible. otherwise, allocate new thing and copy)
  1072. return -1;
  1073. }
  1074. [BurstCompile]
  1075. [MonoPInvokeCallback(typeof(TryFunction))]
  1076. public static unsafe int Try(IntPtr allocatorState, ref Block block)
  1077. {
  1078. return ((StackAllocator*)allocatorState)->Try(ref block);
  1079. }
  1080. public TryFunction Function => Try;
  1081. public void Dispose()
  1082. {
  1083. m_handle.Rewind();
  1084. }
  1085. }
  1086. /// <summary>
  1087. /// Slab allocator with no backing storage.
  1088. /// </summary>
  1089. [BurstCompile]
  1090. internal struct SlabAllocator : IAllocator, IDisposable
  1091. {
  1092. public AllocatorHandle Handle { get { return m_handle; } set { m_handle = value; } }
  1093. public Allocator ToAllocator { get { return m_handle.ToAllocator; } }
  1094. public bool IsCustomAllocator { get { return m_handle.IsCustomAllocator; } }
  1095. internal AllocatorHandle m_handle;
  1096. internal Block Storage;
  1097. internal int Log2SlabSizeInBytes;
  1098. internal FixedList4096Bytes<int> Occupied;
  1099. internal long budgetInBytes;
  1100. internal long allocatedBytes;
  1101. public long BudgetInBytes => budgetInBytes;
  1102. public long AllocatedBytes => allocatedBytes;
  1103. internal int SlabSizeInBytes
  1104. {
  1105. get => 1 << Log2SlabSizeInBytes;
  1106. set => Log2SlabSizeInBytes = (byte)(32 - math.lzcnt(math.max(1, value) - 1));
  1107. }
  1108. internal int Slabs => (int)(Storage.Bytes >> Log2SlabSizeInBytes);
  1109. internal void Initialize(Block storage, int slabSizeInBytes, long budget)
  1110. {
  1111. #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG
  1112. storage.Range.Allocator.AddChildAllocator(Handle);
  1113. #endif
  1114. Assert.IsTrue((slabSizeInBytes & (slabSizeInBytes - 1)) == 0);
  1115. Storage = storage;
  1116. Log2SlabSizeInBytes = 0;
  1117. Occupied = default;
  1118. budgetInBytes = budget;
  1119. allocatedBytes = 0;
  1120. SlabSizeInBytes = slabSizeInBytes;
  1121. Occupied.Length = (Slabs + 31) / 32;
  1122. }
  1123. public int Try(ref Block block)
  1124. {
  1125. if (block.Range.Pointer == IntPtr.Zero) // Allocate
  1126. {
  1127. if (block.Bytes + allocatedBytes > budgetInBytes)
  1128. return -2; //over allocator budget
  1129. if (block.Bytes > SlabSizeInBytes)
  1130. return -1;
  1131. for (var wordIndex = 0; wordIndex < Occupied.Length; ++wordIndex)
  1132. {
  1133. var word = Occupied[wordIndex];
  1134. if (word == -1)
  1135. continue;
  1136. for (var bitIndex = 0; bitIndex < 32; ++bitIndex)
  1137. if ((word & (1 << bitIndex)) == 0)
  1138. {
  1139. Occupied[wordIndex] |= 1 << bitIndex;
  1140. block.Range.Pointer = Storage.Range.Pointer +
  1141. (int)(SlabSizeInBytes * (wordIndex * 32U + bitIndex));
  1142. block.AllocatedItems = SlabSizeInBytes / block.BytesPerItem;
  1143. allocatedBytes += block.Bytes;
  1144. return 0;
  1145. }
  1146. }
  1147. return -1;
  1148. }
  1149. if (block.Bytes == 0) // Free
  1150. {
  1151. var slabIndex = ((ulong)block.Range.Pointer - (ulong)Storage.Range.Pointer) >>
  1152. Log2SlabSizeInBytes;
  1153. int wordIndex = (int)(slabIndex >> 5);
  1154. int bitIndex = (int)(slabIndex & 31);
  1155. Occupied[wordIndex] &= ~(1 << bitIndex);
  1156. block.Range.Pointer = IntPtr.Zero;
  1157. var blockSizeInBytes = block.AllocatedItems * block.BytesPerItem;
  1158. allocatedBytes -= blockSizeInBytes;
  1159. block.AllocatedItems = 0;
  1160. return 0;
  1161. }
  1162. // Reallocate (keep existing pointer and change size if possible. otherwise, allocate new thing and copy)
  1163. return -1;
  1164. }
  1165. [BurstCompile]
  1166. [MonoPInvokeCallback(typeof(TryFunction))]
  1167. public static unsafe int Try(IntPtr allocatorState, ref Block block)
  1168. {
  1169. return ((SlabAllocator*)allocatorState)->Try(ref block);
  1170. }
  1171. public TryFunction Function => Try;
  1172. public void Dispose()
  1173. {
  1174. m_handle.Rewind();
  1175. }
  1176. }
  1177. internal struct TableEntry
  1178. {
  1179. internal IntPtr function;
  1180. internal IntPtr state;
  1181. }
  1182. internal struct Array16<T> where T : unmanaged
  1183. {
  1184. internal T f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15;
  1185. }
  1186. internal struct Array256<T> where T : unmanaged
  1187. {
  1188. internal Array16<T> f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15;
  1189. }
  1190. internal struct Array4096<T> where T : unmanaged
  1191. {
  1192. internal Array256<T> f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15;
  1193. }
  1194. internal struct Array32768<T> : IIndexable<T> where T : unmanaged
  1195. {
  1196. internal Array4096<T> f0, f1, f2, f3, f4, f5, f6, f7;
  1197. public int Length { get { return 32768; } set {} }
  1198. public ref T ElementAt(int index)
  1199. {
  1200. unsafe { fixed(Array4096<T>* p = &f0) { return ref UnsafeUtility.AsRef<T>((T*)p + index); } }
  1201. }
  1202. }
  1203. /// <summary>
  1204. /// Contains arrays of the allocator function pointers.
  1205. /// </summary>
  1206. internal sealed class SharedStatics
  1207. {
  1208. internal sealed class IsInstalled { internal static readonly SharedStatic<Long1024> Ref = SharedStatic<Long1024>.GetOrCreate<IsInstalled>(); }
  1209. internal sealed class TableEntry { internal static readonly SharedStatic<Array32768<AllocatorManager.TableEntry>> Ref = SharedStatic<Array32768<AllocatorManager.TableEntry>>.GetOrCreate<TableEntry>(); }
  1210. internal sealed class IsAutoDispose { internal static readonly SharedStatic<Long1024> Ref = SharedStatic<Long1024>.GetOrCreate<IsAutoDispose>(); }
  1211. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  1212. internal sealed class ChildSpinLock { internal static readonly SharedStatic<Array32768<Spinner>> Ref = SharedStatic<Array32768<Spinner>>.GetOrCreate<ChildSpinLock>(); }
  1213. internal sealed class ChildSafetyHandles { internal static readonly SharedStatic<Array32768<UnsafeList<AtomicSafetyHandle>>> Ref = SharedStatic<Array32768<UnsafeList<AtomicSafetyHandle>>>.GetOrCreate<ChildSafetyHandles>(); }
  1214. #endif
  1215. #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG
  1216. internal sealed class Version { internal static readonly SharedStatic<Array32768<ushort>> Ref = SharedStatic<Array32768<ushort>>.GetOrCreate<Version>(); }
  1217. internal sealed class ChildAllocators { internal static readonly SharedStatic<Array32768<UnsafeList<AllocatorHandle>>> Ref = SharedStatic<Array32768<UnsafeList<AllocatorHandle>>>.GetOrCreate<ChildAllocators>(); }
  1218. internal sealed class Parent { internal static readonly SharedStatic<Array32768<AllocatorHandle>> Ref = SharedStatic<Array32768<AllocatorHandle>>.GetOrCreate<Parent>(); }
  1219. internal sealed class IndexInParent { internal static readonly SharedStatic<Array32768<int>> Ref = SharedStatic<Array32768<int>>.GetOrCreate<IndexInParent>(); }
  1220. #endif
  1221. }
  1222. internal static class Managed
  1223. {
  1224. /// <summary>
  1225. /// Global delegate table to hold TryFunction delegates for managed callers
  1226. /// </summary>
  1227. internal static TryFunction[] TryFunctionDelegates = new TryFunction[MaxNumCustomAllocators];
  1228. /// <summary>
  1229. /// Register TryFunction delegates for managed caller to avoid garbage collections
  1230. /// </summary>
  1231. /// <param name="index">Index into the TryFunction delegates table.</param>
  1232. /// <param name="function">TryFunction delegate to be registered.</param>
  1233. [ExcludeFromBurstCompatTesting("Uses managed delegate")]
  1234. public static void RegisterDelegate(int index, TryFunction function)
  1235. {
  1236. if(index >= MaxNumCustomAllocators)
  1237. {
  1238. throw new ArgumentException("index to be registered in TryFunction delegate table exceeds maximum.");
  1239. }
  1240. // Register TryFunction delegates for managed caller to avoid garbage collections
  1241. Managed.TryFunctionDelegates[index] = function;
  1242. }
  1243. /// <summary>
  1244. /// Unregister TryFunction delegate
  1245. /// </summary>
  1246. /// <param name="int">Index into the TryFunction delegates table.</param>
  1247. [ExcludeFromBurstCompatTesting("Uses managed delegate")]
  1248. public static void UnregisterDelegate(int index)
  1249. {
  1250. if (index >= MaxNumCustomAllocators)
  1251. {
  1252. throw new ArgumentException("index to be unregistered in TryFunction delegate table exceeds maximum.");
  1253. }
  1254. Managed.TryFunctionDelegates[index] = default;
  1255. }
  1256. }
  1257. /// <summary>
  1258. /// For internal use only.
  1259. /// </summary>
  1260. public static void Initialize()
  1261. {
  1262. }
  1263. /// <summary>
  1264. /// Saves an allocator's function pointers at a particular index in the global function table.
  1265. /// </summary>
  1266. /// <param name="handle">The global function table index at which to install the allocator function.</param>
  1267. /// <param name="allocatorState">IntPtr to allocator's custom state.</param>
  1268. /// <param name="functionPointer">The allocator function to install in the global function table.</param>
  1269. /// <param name="function">The allocator function to install in the global function table.</param>
  1270. /// <param name="IsAutoDispose">Flag indicating if the allocator will automatically dispose allocations.</param>
  1271. internal static void Install(AllocatorHandle handle,
  1272. IntPtr allocatorState,
  1273. FunctionPointer<TryFunction> functionPointer,
  1274. TryFunction function,
  1275. bool IsAutoDispose = false)
  1276. {
  1277. if(functionPointer.Value == IntPtr.Zero)
  1278. handle.Unregister();
  1279. else
  1280. {
  1281. int error = ConcurrentMask.TryAllocate(ref SharedStatics.IsInstalled.Ref.Data, handle.Value, 1);
  1282. if (ConcurrentMask.Succeeded(error))
  1283. {
  1284. handle.Install(new TableEntry { state = allocatorState, function = functionPointer.Value });
  1285. Managed.RegisterDelegate(handle.Index, function);
  1286. // If the allocator will automatically dispose allocations.
  1287. if (IsAutoDispose)
  1288. {
  1289. ConcurrentMask.TryAllocate(ref SharedStatics.IsAutoDispose.Ref.Data, handle.Value, 1);
  1290. }
  1291. }
  1292. }
  1293. }
  1294. /// <summary>
  1295. /// Saves an allocator's function pointers at a particular index in the global function table.
  1296. /// </summary>
  1297. /// <param name="handle">The global function table index at which to install the allocator function.</param>
  1298. /// <param name="allocatorState">IntPtr to allocator's custom state.</param>
  1299. /// <param name="function">The allocator function to install in the global function table.</param>
  1300. internal static void Install(AllocatorHandle handle, IntPtr allocatorState, TryFunction function)
  1301. {
  1302. var functionPointer = (function == null)
  1303. ? new FunctionPointer<TryFunction>(IntPtr.Zero)
  1304. : BurstCompiler.CompileFunctionPointer(function);
  1305. Install(handle, allocatorState, functionPointer, function);
  1306. }
  1307. /// <summary>
  1308. /// Saves an allocator's function pointers in a free slot of the global function table. Thread safe.
  1309. /// </summary>
  1310. /// <param name="allocatorState">IntPtr to allocator's custom state.</param>
  1311. /// <param name="functionPointer">Function pointer to create or save in the function table.</param>
  1312. /// <param name="IsAutoDispose">Flag indicating if the allocator will automatically dispose allocations.</param>
  1313. /// <param name="isGlobal">Flag indicating if the allocator is a global allocator.</param>
  1314. /// <param name="globalIndex">Index into the global function table of the allocator to be created.</param>
  1315. /// <returns>Returns a handle to the newly registered allocator function.</returns>
  1316. internal static AllocatorHandle Register(IntPtr allocatorState,
  1317. FunctionPointer<TryFunction> functionPointer,
  1318. bool IsAutoDispose = false,
  1319. bool isGlobal = false,
  1320. int globalIndex = 0)
  1321. {
  1322. int error;
  1323. int offset;
  1324. if (isGlobal)
  1325. {
  1326. if (globalIndex < GlobalAllocatorBaseIndex)
  1327. {
  1328. throw new ArgumentException($"Error: {globalIndex} is less than GlobalAllocatorBaseIndex");
  1329. }
  1330. error = ConcurrentMask.TryAllocate(ref SharedStatics.IsInstalled.Ref.Data, globalIndex, 1);
  1331. offset = globalIndex;
  1332. }
  1333. else
  1334. {
  1335. error = ConcurrentMask.TryAllocate(ref SharedStatics.IsInstalled.Ref.Data, out offset, (FirstUserIndex + 63) >> 6, (int)(GlobalAllocatorBaseIndex - 1), 1);
  1336. }
  1337. var tableEntry = new TableEntry { state = allocatorState, function = functionPointer.Value };
  1338. AllocatorHandle handle = default;
  1339. if(ConcurrentMask.Succeeded(error))
  1340. {
  1341. handle.Index = (ushort)offset;
  1342. handle.Install(tableEntry);
  1343. // If the allocator will automatically dispose allocations.
  1344. if (IsAutoDispose)
  1345. {
  1346. ConcurrentMask.TryAllocate(ref SharedStatics.IsAutoDispose.Ref.Data, offset, 1);
  1347. }
  1348. #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG
  1349. handle.Version = handle.OfficialVersion;
  1350. #endif
  1351. }
  1352. return handle;
  1353. }
  1354. static class AllocatorCache<T> where T : unmanaged, IAllocator
  1355. {
  1356. public static FunctionPointer<TryFunction> TryFunction;
  1357. public static TryFunction CachedFunction;
  1358. }
  1359. /// <summary>
  1360. /// Saves an allocator's function pointers in a free slot of the global function table. Thread safe.
  1361. /// </summary>
  1362. /// <typeparam name="T">The type of allocator to register.</typeparam>
  1363. /// <param name="t">Reference to the allocator.</param>
  1364. /// <param name="IsAutoDispose">Flag indicating if the allocator will automatically dispose allocations.</param>
  1365. /// <param name="isGlobal">Flag indicating if the allocator is a global allocator.</param>
  1366. /// <param name="globalIndex">Index into the global function table of the allocator to be created.</param>
  1367. [ExcludeFromBurstCompatTesting("Uses managed delegate")]
  1368. public static unsafe void Register<T>(ref this T t, bool IsAutoDispose = false, bool isGlobal = false, int globalIndex = 0) where T : unmanaged, IAllocator
  1369. {
  1370. FunctionPointer<TryFunction> functionPointer;
  1371. var func = t.Function;
  1372. if (func == null)
  1373. functionPointer = new FunctionPointer<TryFunction>(IntPtr.Zero);
  1374. else
  1375. {
  1376. if (func != AllocatorCache<T>.CachedFunction)
  1377. {
  1378. AllocatorCache<T>.TryFunction = BurstCompiler.CompileFunctionPointer(func);
  1379. AllocatorCache<T>.CachedFunction = func;
  1380. }
  1381. functionPointer = AllocatorCache<T>.TryFunction;
  1382. }
  1383. t.Handle = Register((IntPtr)UnsafeUtility.AddressOf(ref t), functionPointer, IsAutoDispose, isGlobal, globalIndex);
  1384. Managed.RegisterDelegate(t.Handle.Index, t.Function);
  1385. #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG
  1386. if (!t.Handle.IsValid)
  1387. throw new InvalidOperationException("Allocator registration succeeded, but failed to produce valid handle.");
  1388. #endif
  1389. }
  1390. /// <summary>
  1391. /// Removes an allocator's function pointers from the global function table, without managed code
  1392. /// </summary>
  1393. /// <typeparam name="T">The type of allocator to unregister.</typeparam>
  1394. /// <param name="t">Reference to the allocator.</param>
  1395. public static void UnmanagedUnregister<T>(ref this T t) where T : unmanaged, IAllocator
  1396. {
  1397. if(t.Handle.IsInstalled)
  1398. {
  1399. t.Handle.Install(default);
  1400. ConcurrentMask.TryFree(ref SharedStatics.IsInstalled.Ref.Data, t.Handle.Value, 1);
  1401. ConcurrentMask.TryFree(ref SharedStatics.IsAutoDispose.Ref.Data, t.Handle.Value, 1);
  1402. }
  1403. }
  1404. /// <summary>
  1405. /// Removes an allocator's function pointers from the global function table.
  1406. /// </summary>
  1407. /// <typeparam name="T">The type of allocator to unregister.</typeparam>
  1408. /// <param name="t">Reference to the allocator.</param>
  1409. [ExcludeFromBurstCompatTesting("Uses managed delegate")]
  1410. public static void Unregister<T>(ref this T t) where T : unmanaged, IAllocator
  1411. {
  1412. if(t.Handle.IsInstalled)
  1413. {
  1414. t.Handle.Dispose();
  1415. ConcurrentMask.TryFree(ref SharedStatics.IsInstalled.Ref.Data, t.Handle.Value, 1);
  1416. ConcurrentMask.TryFree(ref SharedStatics.IsAutoDispose.Ref.Data, t.Handle.Value, 1);
  1417. Managed.UnregisterDelegate(t.Handle.Index);
  1418. }
  1419. }
  1420. /// <summary>
  1421. /// Create a custom allocator by allocating a backing storage to store the allocator and then register it
  1422. /// </summary>
  1423. /// <typeparam name="T">The type of allocator to create.</typeparam>
  1424. /// <param name="backingAllocator">Allocator used to allocate backing storage.</param>
  1425. /// <param name="isGlobal">Flag indicating if the allocator is a global allocator.</param>
  1426. /// <param name="globalIndex">Index into the global function table of the allocator to be created.</param>
  1427. /// <returns>Returns reference to the newly created allocator.</returns>
  1428. [ExcludeFromBurstCompatTesting("Register uses managed delegate")]
  1429. internal static ref T CreateAllocator<T>(AllocatorHandle backingAllocator, bool isGlobal = false, int globalIndex = 0)
  1430. where T : unmanaged, IAllocator
  1431. {
  1432. unsafe
  1433. {
  1434. var allocatorPtr = (T*)Memory.Unmanaged.Allocate(UnsafeUtility.SizeOf<T>(), 16, backingAllocator);
  1435. *allocatorPtr = default;
  1436. ref T allocator = ref UnsafeUtility.AsRef<T>(allocatorPtr);
  1437. Register(ref allocator, allocatorPtr->IsAutoDispose, isGlobal, globalIndex);
  1438. return ref allocator;
  1439. }
  1440. }
  1441. /// <summary>
  1442. /// Destroy a custom allocator by unregistering the allocator and freeing its backing storage
  1443. /// </summary>
  1444. /// <typeparam name="T">The type of allocator to destroy.</typeparam>
  1445. /// <param name="t">Reference to the allocator.</param>
  1446. /// <param name="backingAllocator">Allocator used in allocating the backing storage.</param>
  1447. [ExcludeFromBurstCompatTesting("Registration uses managed delegates")]
  1448. internal static void DestroyAllocator<T>(ref this T t, AllocatorHandle backingAllocator)
  1449. where T : unmanaged, IAllocator
  1450. {
  1451. Unregister(ref t);
  1452. unsafe
  1453. {
  1454. var allocatorPtr = UnsafeUtility.AddressOf<T>(ref t);
  1455. Memory.Unmanaged.Free(allocatorPtr, backingAllocator);
  1456. }
  1457. }
  1458. /// <summary>
  1459. /// For internal use only.
  1460. /// </summary>
  1461. public static void Shutdown()
  1462. {
  1463. }
  1464. /// <summary>
  1465. /// Index in the global function table of the first user-defined allocator.
  1466. /// </summary>
  1467. /// <remarks>The indexes from 0 up to `FirstUserIndex` are reserved and so should not be used for your own allocators.</remarks>
  1468. /// <value>Index in the global function table of the first user-defined allocator.</value>
  1469. public const ushort FirstUserIndex = 64;
  1470. /// <summary>
  1471. /// Maximum number of user-defined allocators.
  1472. /// </summary>
  1473. public const ushort MaxNumCustomAllocators = 32768;
  1474. /// <summary>
  1475. /// Number of global scratchpad allocators reserved in the global function table.
  1476. /// </summary>
  1477. /// <remarks>Number of global scratchpad allocators reserved in the global function table. Make sure it is larger than or equals to the max number of jobs that can run at the same time.</remarks>
  1478. #if UNITY_2022_2_14F1_OR_NEWER
  1479. internal static readonly ushort NumGlobalScratchAllocators = (ushort) (JobsUtility.ThreadIndexCount);
  1480. #else
  1481. internal const ushort NumGlobalScratchAllocators = JobsUtility.MaxJobThreadCount + 1;
  1482. #endif
  1483. /// <summary>
  1484. /// Max number of global allocators reserved in the global function table.
  1485. /// </summary>
  1486. /// <remarks>Max number of global allocators reserved in the global function table. Make sure it is larger than or equals to NumGlobalScratchAllocators.</remarks>
  1487. #if UNITY_2022_2_14F1_OR_NEWER
  1488. internal static readonly ushort MaxNumGlobalAllocators = (ushort)(JobsUtility.ThreadIndexCount);
  1489. #else
  1490. internal const ushort MaxNumGlobalAllocators = JobsUtility.MaxJobThreadCount + 1;
  1491. #endif
  1492. /// <summary>
  1493. /// Base index in the global function table for global allocators.
  1494. /// </summary>
  1495. /// <remarks>The indexes from `GlobalAllocatorBaseIndex` up to `MaxNumCustomAllocators` are reserved which
  1496. /// should not be used for your own allocators.</remarks>
  1497. /// <value>Base index in the global function table for global allocators.</value>
  1498. internal static readonly uint GlobalAllocatorBaseIndex = (uint)(MaxNumCustomAllocators - MaxNumGlobalAllocators);
  1499. /// <summary>
  1500. /// Index in the global function table of the first global scratchpad allocator.
  1501. /// </summary>
  1502. /// <remarks>The indexes from `GlobalAllocatorBaseIndex` up to `NumGlobalScratchAllocators` are reserved for global scratchpad allocators.</remarks>
  1503. /// <value>Index in the global function table of the first global scratchpad allocator.</value>
  1504. internal static readonly uint FirstGlobalScratchpadAllocatorIndex = GlobalAllocatorBaseIndex;
  1505. internal static bool IsCustomAllocator(AllocatorHandle allocator)
  1506. {
  1507. return allocator.Index >= FirstUserIndex;
  1508. }
  1509. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  1510. internal static void CheckFailedToAllocate(int error)
  1511. {
  1512. if (error != 0)
  1513. throw new ArgumentException("failed to allocate");
  1514. }
  1515. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  1516. internal static void CheckFailedToFree(int error)
  1517. {
  1518. if (error != 0)
  1519. throw new ArgumentException("failed to free");
  1520. }
  1521. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  1522. internal static void CheckValid(AllocatorHandle handle)
  1523. {
  1524. #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG
  1525. if (handle.IsValid == false)
  1526. throw new ArgumentException("allocator handle is not valid.");
  1527. #endif
  1528. }
  1529. }
  1530. /// <summary>
  1531. /// Provides a wrapper for custom allocator.
  1532. /// </summary>
  1533. /// <typeparam name="T">The type of the allocator.</typeparam>
  1534. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(AllocatorManager.AllocatorHandle) })]
  1535. public unsafe struct AllocatorHelper<T> : IDisposable
  1536. where T : unmanaged, AllocatorManager.IAllocator
  1537. {
  1538. /// <summary>
  1539. /// Pointer to a custom allocator.
  1540. /// </summary>
  1541. readonly T* m_allocator;
  1542. /// <summary>
  1543. /// Allocator used to allocate backing storage of T.
  1544. /// </summary>
  1545. AllocatorManager.AllocatorHandle m_backingAllocator;
  1546. /// <summary>
  1547. /// Get the custom allocator.
  1548. /// </summary>
  1549. public ref T Allocator => ref UnsafeUtility.AsRef<T>(m_allocator);
  1550. /// <summary>
  1551. /// Allocate the custom allocator from backingAllocator and register it.
  1552. /// </summary>
  1553. /// <param name="backingAllocator">Allocator used to allocate backing storage.</param>
  1554. /// <param name="isGlobal">Flag indicating if the allocator is a global allocator.</param>
  1555. /// <param name="globalIndex">Index into the global function table of the allocator to be created.</param>
  1556. [ExcludeFromBurstCompatTesting("CreateAllocator is unburstable")]
  1557. public AllocatorHelper(AllocatorManager.AllocatorHandle backingAllocator, bool isGlobal = false, int globalIndex = 0)
  1558. {
  1559. ref var allocator = ref AllocatorManager.CreateAllocator<T>(backingAllocator, isGlobal, globalIndex);
  1560. m_allocator = (T*)UnsafeUtility.AddressOf<T>(ref allocator);
  1561. m_backingAllocator = backingAllocator;
  1562. }
  1563. /// <summary>
  1564. /// Dispose the custom allocator backing memory and unregister it.
  1565. /// </summary>
  1566. [ExcludeFromBurstCompatTesting("DestroyAllocator is unburstable")]
  1567. public void Dispose()
  1568. {
  1569. ref var allocator = ref UnsafeUtility.AsRef<T>(m_allocator);
  1570. AllocatorManager.DestroyAllocator(ref allocator, m_backingAllocator);
  1571. }
  1572. }
  1573. }
  1574. #pragma warning restore 0649