Sin descripción
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 59KB

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