Mirror of Svelto.ECS because we're a fan of it
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.

194 lines
6.3KB

  1. #if later
  2. using System;
  3. using System.Runtime.CompilerServices;
  4. using System.Threading;
  5. using Svelto.Common;
  6. using Svelto.Utilities;
  7. namespace Svelto.ECS.DataStructures
  8. {
  9. /// <summary>
  10. /// Burst friendly Ring Buffer on steroid:
  11. /// it can: Enqueue/Dequeue, it wraps if there is enough space after dequeuing
  12. /// It resizes if there isn't enough space left.
  13. /// It's a "bag", you can queue and dequeue any T. Just be sure that you dequeue what you queue! No check on type
  14. /// is done.
  15. /// You can reserve a position in the queue to update it later.
  16. /// The datastructure is a struct and it's "copyable"
  17. /// I eventually decided to call it NativeBag and not NativeBag because it can also be used as
  18. /// a preallocated memory pool where any kind of T can be stored as long as T is unmanaged
  19. /// </summary>
  20. public struct ThreadSafeNativeBag : IDisposable
  21. {
  22. public uint count
  23. {
  24. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  25. get
  26. {
  27. unsafe
  28. {
  29. #if DEBUG && !PROFILE_SVELTO
  30. if (_queue == null)
  31. throw new Exception("SimpleNativeArray: null-access");
  32. #endif
  33. return _queue->size;
  34. }
  35. }
  36. }
  37. public uint capacity
  38. {
  39. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  40. get
  41. {
  42. unsafe
  43. {
  44. #if DEBUG && !PROFILE_SVELTO
  45. if (_queue == null)
  46. throw new Exception("SimpleNativeArray: null-access");
  47. #endif
  48. return _queue->capacity;
  49. }
  50. }
  51. }
  52. public ThreadSafeNativeBag(Allocator allocator)
  53. {
  54. unsafe
  55. {
  56. var sizeOf = MemoryUtilities.SizeOf<UnsafeBlob>();
  57. var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator);
  58. //clear to nullify the pointers
  59. //MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf);
  60. listData->allocator = allocator;
  61. _queue = listData;
  62. }
  63. _writingGuard = 0;
  64. }
  65. public ThreadSafeNativeBag(Allocator allocator, uint capacity)
  66. {
  67. unsafe
  68. {
  69. var sizeOf = MemoryUtilities.SizeOf<UnsafeBlob>();
  70. var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator);
  71. //clear to nullify the pointers
  72. //MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf);
  73. listData->allocator = allocator;
  74. _queue = listData;
  75. _queue->Realloc(capacity);
  76. }
  77. _writingGuard = 0;
  78. }
  79. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  80. public bool IsEmpty()
  81. {
  82. unsafe
  83. {
  84. if (_queue == null || _queue->ptr == null)
  85. return true;
  86. }
  87. return count == 0;
  88. }
  89. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  90. public unsafe void Dispose()
  91. {
  92. if (_queue != null)
  93. {
  94. _queue->Dispose();
  95. MemoryUtilities.Free((IntPtr) _queue, _queue->allocator);
  96. _queue = null;
  97. }
  98. }
  99. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  100. public void Enqueue<T>(in T item) where T : struct
  101. {
  102. unsafe
  103. {
  104. #if DEBUG && !PROFILE_SVELTO
  105. if (_queue == null)
  106. throw new Exception("SimpleNativeArray: null-access");
  107. #endif
  108. var sizeOf = MemoryUtilities.SizeOf<T>();
  109. var alignedSize = (uint) MemoryUtilities.SizeOfAligned<T>();
  110. Interlocked.MemoryBarrier();
  111. Reset:
  112. var oldCapacity = _queue->capacity;
  113. var spaceleft = oldCapacity - (_queue->_writeIndex - _queue->_readIndex) - sizeOf;
  114. while (spaceleft < 0)
  115. {
  116. //if _writingGuard is not equal to 0, it means that another thread increased the
  117. //value so it's possible the reallocing is already happening OR it means that
  118. //writing are still in progress and we must be sure that are all flushed first
  119. if (Interlocked.CompareExchange(ref _writingGuard, 1, 0) != 0)
  120. {
  121. ThreadUtility.Yield();
  122. goto Reset;
  123. }
  124. var newCapacity = (uint) ((oldCapacity + alignedSize) * 2.0f);
  125. Svelto.Console.Log($"realloc {newCapacity}");
  126. _queue->Realloc(newCapacity);
  127. Volatile.Write(ref _writingGuard, 0);
  128. }
  129. int writeIndex;
  130. //look for the first available slot to write in
  131. writeIndex = _queue->_writeIndex;
  132. if (Interlocked.CompareExchange(ref _queue->_writeIndex, (int) (writeIndex + alignedSize)
  133. , writeIndex) != writeIndex)
  134. {
  135. ThreadUtility.Yield();
  136. goto Reset;
  137. }
  138. Interlocked.Increment(ref _writingGuard);
  139. _queue->Write(item, (uint) writeIndex);
  140. Interlocked.Decrement(ref _writingGuard);
  141. }
  142. }
  143. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  144. public void Clear()
  145. {
  146. unsafe
  147. {
  148. #if DEBUG && !PROFILE_SVELTO
  149. if (_queue == null)
  150. throw new Exception("SimpleNativeArray: null-access");
  151. #endif
  152. _queue->Clear();
  153. }
  154. }
  155. public T Dequeue<T>() where T : struct
  156. {
  157. unsafe
  158. {
  159. return _queue->Read<T>();
  160. }
  161. }
  162. #if UNITY_COLLECTIONS || UNITY_JOBS || UNITY_BURST
  163. [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
  164. #endif
  165. unsafe UnsafeBlob* _queue;
  166. int _writingGuard;
  167. }
  168. }
  169. #endif