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.

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