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.

281 lines
7.8KB

  1. #if DEBUG && !PROFILE_SVELTO
  2. #define ENABLE_DEBUG_CHECKS
  3. #endif
  4. #if DEBUG && !PROFILE_SVELTO
  5. //#define ENABLE_THREAD_SAFE_CHECKS
  6. #endif
  7. using System;
  8. using System.Diagnostics;
  9. using System.Runtime.CompilerServices;
  10. using System.Threading;
  11. using Svelto.Common;
  12. namespace Svelto.ECS.DataStructures
  13. {
  14. /// <summary>
  15. /// Burst friendly RingBuffer on steroid:
  16. /// it can: Enqueue/Dequeue, it wraps if there is enough space after dequeuing
  17. /// It resizes if there isn't enough space left.
  18. /// It's a "bag", you can queue and dequeue any T. Just be sure that you dequeue what you queue! No check on type
  19. /// is done.
  20. /// You can reserve a position in the queue to update it later.
  21. /// The datastructure is a struct and it's "copyable"
  22. /// I eventually decided to call it NativeBag and not NativeBag because it can also be used as
  23. /// a preallocated memory pool where any kind of T can be stored as long as T is unmanaged
  24. /// </summary>
  25. public struct NativeBag : IDisposable
  26. {
  27. public uint count
  28. {
  29. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  30. get
  31. {
  32. unsafe
  33. {
  34. BasicTests();
  35. #if ENABLE_THREAD_SAFE_CHECKS
  36. try
  37. {
  38. #endif
  39. return _queue->size;
  40. #if ENABLE_THREAD_SAFE_CHECKS
  41. }
  42. finally
  43. {
  44. Volatile.Write(ref _threadSentinel, 0);
  45. }
  46. #endif
  47. }
  48. }
  49. }
  50. public uint capacity
  51. {
  52. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  53. get
  54. {
  55. unsafe
  56. {
  57. BasicTests();
  58. #if ENABLE_THREAD_SAFE_CHECKS
  59. try
  60. {
  61. #endif
  62. return _queue->capacity;
  63. #if ENABLE_THREAD_SAFE_CHECKS
  64. }
  65. finally
  66. {
  67. Volatile.Write(ref _threadSentinel, 0);
  68. }
  69. #endif
  70. }
  71. }
  72. }
  73. public NativeBag(Allocator allocator)
  74. {
  75. unsafe
  76. {
  77. var sizeOf = MemoryUtilities.SizeOf<UnsafeBlob>();
  78. var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator);
  79. //clear to nullify the pointers
  80. //MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf);
  81. listData->allocator = allocator;
  82. _queue = listData;
  83. #if ENABLE_THREAD_SAFE_CHECKS
  84. _threadSentinel = 0;
  85. #endif
  86. }
  87. }
  88. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  89. public bool IsEmpty()
  90. {
  91. unsafe
  92. {
  93. BasicTests();
  94. #if ENABLE_THREAD_SAFE_CHECKS
  95. try
  96. {
  97. #endif
  98. if (_queue == null || _queue->ptr == null)
  99. return true;
  100. #if ENABLE_THREAD_SAFE_CHECKS
  101. }
  102. finally
  103. {
  104. Volatile.Write(ref _threadSentinel, 0);
  105. }
  106. #endif
  107. }
  108. return count == 0;
  109. }
  110. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  111. public unsafe void Dispose()
  112. {
  113. if (_queue != null)
  114. {
  115. #if ENABLE_THREAD_SAFE_CHECKS
  116. //todo: this must be unit tested
  117. if (Interlocked.CompareExchange(ref _threadSentinel, 1, 0) != 0)
  118. throw new Exception("NativeBag is not thread safe, reading and writing operations can happen" +
  119. "on different threads, but not simultaneously");
  120. try
  121. {
  122. #endif
  123. _queue->Dispose();
  124. MemoryUtilities.Free((IntPtr) _queue, _queue->allocator);
  125. _queue = null;
  126. #if ENABLE_THREAD_SAFE_CHECKS
  127. }
  128. finally
  129. {
  130. Volatile.Write(ref _threadSentinel, 0);
  131. }
  132. #endif
  133. }
  134. }
  135. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  136. public ref T ReserveEnqueue<T>(out UnsafeArrayIndex index) where T : struct
  137. {
  138. unsafe
  139. {
  140. BasicTests();
  141. var sizeOf = MemoryUtilities.SizeOf<T>();
  142. if (_queue->space - sizeOf < 0)
  143. _queue->Realloc((uint) ((_queue->capacity + MemoryUtilities.Align4((uint) sizeOf)) * 2.0f));
  144. #if ENABLE_THREAD_SAFE_CHECKS
  145. try
  146. {
  147. #endif
  148. return ref _queue->Reserve<T>(out index);
  149. #if ENABLE_THREAD_SAFE_CHECKS
  150. }
  151. finally
  152. {
  153. Volatile.Write(ref _threadSentinel, 0);
  154. }
  155. #endif
  156. }
  157. }
  158. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  159. public void Enqueue<T>(in T item) where T : struct
  160. {
  161. unsafe
  162. {
  163. BasicTests();
  164. #if ENABLE_THREAD_SAFE_CHECKS
  165. try
  166. {
  167. #endif
  168. var sizeOf = MemoryUtilities.SizeOf<T>();
  169. if (_queue->space - sizeOf < 0)
  170. _queue->Realloc((uint) ((_queue->capacity + MemoryUtilities.Align4((uint) sizeOf)) * 2.0f));
  171. _queue->Write(item);
  172. #if ENABLE_THREAD_SAFE_CHECKS
  173. }
  174. finally
  175. {
  176. Volatile.Write(ref _threadSentinel, 0);
  177. }
  178. #endif
  179. }
  180. }
  181. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  182. public void Clear()
  183. {
  184. unsafe
  185. {
  186. BasicTests();
  187. #if ENABLE_THREAD_SAFE_CHECKS
  188. try
  189. {
  190. #endif
  191. _queue->Clear();
  192. #if ENABLE_THREAD_SAFE_CHECKS
  193. }
  194. finally
  195. {
  196. Volatile.Write(ref _threadSentinel, 0);
  197. }
  198. #endif
  199. }
  200. }
  201. public T Dequeue<T>() where T : struct
  202. {
  203. unsafe
  204. {
  205. BasicTests();
  206. #if ENABLE_THREAD_SAFE_CHECKS
  207. try
  208. {
  209. #endif
  210. return _queue->Read<T>();
  211. #if ENABLE_THREAD_SAFE_CHECKS
  212. }
  213. finally
  214. {
  215. Volatile.Write(ref _threadSentinel, 0);
  216. }
  217. #endif
  218. }
  219. }
  220. internal ref T AccessReserved<T>(UnsafeArrayIndex reserverIndex) where T : struct
  221. {
  222. unsafe
  223. {
  224. BasicTests();
  225. #if ENABLE_THREAD_SAFE_CHECKS
  226. try
  227. {
  228. #endif
  229. return ref _queue->AccessReserved<T>(reserverIndex);
  230. #if ENABLE_THREAD_SAFE_CHECKS
  231. }
  232. finally
  233. {
  234. Volatile.Write(ref _threadSentinel, 0);
  235. }
  236. #endif
  237. }
  238. }
  239. [Conditional("ENABLE_DEBUG_CHECKS")]
  240. unsafe void BasicTests()
  241. {
  242. if (_queue == null)
  243. throw new Exception("SimpleNativeArray: null-access");
  244. #if ENABLE_THREAD_SAFE_CHECKS
  245. todo: this must be unit tested
  246. if (Interlocked.CompareExchange(ref _threadSentinel, 1, 0) != 0)
  247. throw new Exception("NativeBag is not thread safe, reading and writing operations can happen"
  248. + "on different threads, but not simultaneously");
  249. #endif
  250. }
  251. #if ENABLE_THREAD_SAFE_CHECKS
  252. int _threadSentinel;
  253. #endif
  254. #if UNITY_NATIVE
  255. [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
  256. #endif
  257. unsafe UnsafeBlob* _queue;
  258. }
  259. }