Search Unity

How to fix "uses the [NativeContainer] but has no AtomicSafetyHandle embedded, this is an internal "

Discussion in 'Entity Component System' started by davenirline, Feb 10, 2019.

  1. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    987
    I'm implementing an A* search using a job. I'm using a heap implementation that was shared here (I think it was by Tertle). I get this error when I try to schedule a job that uses such heap.

    InvalidOperationException: AStarSearch`2.openSet uses the [NativeContainer] but has no AtomicSafetyHandle embedded, this is an internal error in the container type.
    Unity.Jobs.LowLevel.Unsafe.JobsUtility.CreateJobReflectionData (System.Type type, Unity.Jobs.LowLevel.Unsafe.JobType jobType, System.Object managedJobFunction0, System.Object managedJobFunction1, System.Object managedJobFunction2) (at C:/buildslave/unity/build/Runtime/Jobs/ScriptBindings/Jobs.bindings.cs:96)
    Unity.Jobs.IJobExtensions+JobStruct`1[T].Initialize () (at C:/buildslave/unity/build/Runtime/Jobs/Managed/IJob.cs:23)
    Unity.Jobs.IJobExtensions.Schedule[T] (T jobData, Unity.Jobs.JobHandle dependsOn) (at C:/buildslave/unity/build/Runtime/Jobs/Managed/IJob.cs:36)
    Game.AStarSearchExecutionSystem.Process (Unity.Entities.ArchetypeChunk& chunk, Game.SimpleReachability reachability, Unity.Jobs.JobHandle sharedDependency) (at Assets/Game/Scripts/Grid2DTest/AStarSearchExecutionSystem.cs:80)
    Game.AStarSearchExecutionSystem.OnUpdate (Unity.Jobs.JobHandle inputDeps) (at Assets/Game/Scripts/Grid2DTest/AStarSearchExecutionSystem.cs:56)
    Unity.Entities.JobComponentSystem.InternalUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.23/Unity.Entities/ComponentSystem.cs:597)
    Unity.Entities.ScriptBehaviourManager.Update () (at Library/PackageCache/com.unity.entities@0.0.12-preview.23/Unity.Entities/ScriptBehaviourManager.cs:83)
    Unity.Entities.ScriptBehaviourUpdateOrder+DummyDelagateWrapper.TriggerUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.23/Unity.Entities/ScriptBehaviourUpdateOrder.cs:706)

    Here's the heap implementation:
    Code (CSharp):
    1. [NativeContainerSupportsDeallocateOnJobCompletion]
    2. [NativeContainerSupportsMinMaxWriteRestriction]
    3. [StructLayout(LayoutKind.Sequential)]
    4. [NativeContainer]
    5. public unsafe struct NativeHeap : IDisposable {
    6.     [NativeDisableUnsafePtrRestriction]
    7.     private void* buffer;
    8.  
    9.     private int capacity;
    10.    
    11. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    12.     public AtomicSafetyHandle safety;
    13.    
    14.     [NativeSetClassTypeToNullOnSchedule]
    15.     public DisposeSentinel disposeSentinel;
    16. #endif
    17.    
    18.     private Allocator allocatorLabel;
    19.  
    20.     private int head;
    21.     private int length;
    22.     private int minIndex;
    23.     private int maxIndex;
    24.  
    25.     public NativeHeap(int capacity, Allocator allocator ) {
    26.         Allocate(capacity, allocator, out this);
    27.     }
    28.  
    29.     private static void Allocate(int capacity, Allocator allocator, out NativeHeap heap) {
    30.         long size = (long) UnsafeUtility.SizeOf<HeapNode>() * capacity;
    31.  
    32.         if (allocator <= Allocator.None) {
    33.             throw new ArgumentException("Allocator must be Temp, TempJob or Persistent", nameof(allocator));
    34.         }
    35.  
    36.         if (capacity < 0) {
    37.             throw new ArgumentOutOfRangeException(nameof(capacity), "Length must be >= 0");
    38.         }
    39.  
    40.         if (size > int.MaxValue) {
    41.             throw new ArgumentOutOfRangeException(nameof(capacity),
    42.                 $"Length * sizeof(T) cannot exceed {(object) int.MaxValue} bytes");
    43.         }
    44.  
    45.         heap.buffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf<HeapNode>(), allocator);
    46.         heap.capacity = capacity;
    47.         heap.allocatorLabel = allocator;
    48.         heap.minIndex = 0;
    49.         heap.maxIndex = capacity - 1;
    50.         heap.head = -1;
    51.         heap.length = 0;
    52.  
    53. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    54.         DisposeSentinel.Create(out heap.safety, out heap.disposeSentinel, 1, allocator);
    55. #endif
    56.     }
    57.  
    58.     public bool HasItems {
    59.         get {
    60. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    61.             AtomicSafetyHandle.CheckReadAndThrow(this.safety);
    62. #endif
    63.             return this.head >= 0;
    64.         }
    65.     }
    66.  
    67.     public void Push(HeapNode node) {
    68. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    69.         if (this.length == this.capacity) {
    70.             //throw new IndexOutOfRangeException($"Capacity Reached");
    71.             Expand();
    72.         }
    73.  
    74.         AtomicSafetyHandle.CheckReadAndThrow(this.safety);
    75. #endif
    76.  
    77.         if (this.head < 0) {
    78.             this.head = this.length;
    79.         } else if (node.cost < this[this.head].cost) {
    80.             node.next = this.head;
    81.             this.head = this.length;
    82.         } else {
    83.             int currentPtr = this.head;
    84.             HeapNode current = this[currentPtr];
    85.  
    86.             while (current.next >= 0 && this[current.next].cost <= node.cost) {
    87.                 currentPtr = current.next;
    88.                 current = this[current.next];
    89.             }
    90.  
    91.             node.next = current.next;
    92.             current.next = this.length;
    93.  
    94.             UnsafeUtility.WriteArrayElement(this.buffer, currentPtr, current);
    95.         }
    96.  
    97.         UnsafeUtility.WriteArrayElement(this.buffer, this.length, node);
    98.         ++this.length;
    99.     }
    100.  
    101.     private void Expand() {
    102.         Allocate(this.capacity * 2, this.allocatorLabel, out NativeHeap newHeap);
    103.        
    104.         // Copy memory to newHeap
    105.         int byteLength = this.length * UnsafeUtility.SizeOf(typeof(HeapNode));
    106.         UnsafeUtility.MemCpy(newHeap.buffer, this.buffer, byteLength);
    107.        
    108.         // Free the memory held by the current buffer
    109.         UnsafeUtility.Free(this.buffer, this.allocatorLabel);
    110.         this.buffer = null;
    111.        
    112.         // Overwrite properties from newHeap
    113.         this.buffer = newHeap.buffer;
    114.         this.capacity = this.capacity * 2;
    115.         this.maxIndex = this.capacity - 1;
    116.     }
    117.  
    118.     public HeapNode Pop() {
    119. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    120.         if (!this.HasItems) {
    121.             throw new InvalidOperationException("Can't execute. Heap has no more elements.");
    122.         }
    123.        
    124.         AtomicSafetyHandle.CheckWriteAndThrow(this.safety);
    125. #endif
    126.         HeapNode result = this.Top;
    127.         this.head = this[this.head].next;
    128.         --this.length;
    129.  
    130.         return result;
    131.     }
    132.  
    133.     public HeapNode Top {
    134.         get {
    135. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    136.             if (!this.HasItems) {
    137.                 throw new InvalidOperationException("Can't execute. Heap has no more elements.");
    138.             }
    139.            
    140.             AtomicSafetyHandle.CheckReadAndThrow(this.safety);
    141. #endif
    142.             return this[this.head];
    143.         }
    144.     }
    145.  
    146.     public int Head {
    147.         get {
    148. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    149.             AtomicSafetyHandle.CheckReadAndThrow(this.safety);
    150. #endif
    151.            
    152.             return this.head;
    153.         }
    154.     }
    155.  
    156.     public int Length {
    157.         get {
    158. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    159.             AtomicSafetyHandle.CheckReadAndThrow(this.safety);
    160. #endif
    161.             return this.length;
    162.         }
    163.     }
    164.  
    165.     public int Capacity {
    166.         get {
    167. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    168.             AtomicSafetyHandle.CheckReadAndThrow(this.safety);
    169. #endif
    170.             return this.capacity;
    171.         }
    172.     }
    173.  
    174.     public HeapNode this[int index] {
    175.         get {
    176. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    177.             if (index < this.minIndex || index > this.maxIndex) {
    178.                 FailOutOfRangeError(index);
    179.             }
    180.  
    181.             AtomicSafetyHandle.CheckReadAndThrow(this.safety);
    182. #endif
    183.  
    184.             return UnsafeUtility.ReadArrayElement<HeapNode>(this.buffer, index);
    185.         }
    186.     }
    187.  
    188.     public void Clear() {
    189.         this.head = -1;
    190.         this.length = 0;
    191.     }
    192.  
    193.     public void Dispose() {
    194.         if (!UnsafeUtility.IsValidAllocator(this.allocatorLabel)) {
    195.             throw new InvalidOperationException(
    196.                 "The NativeArray can not be Disposed because it was not allocated with a valid allocator.");
    197.         }
    198. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    199.         DisposeSentinel.Dispose(ref this.safety, ref this.disposeSentinel);
    200. #endif
    201.         UnsafeUtility.Free(this.buffer, this.allocatorLabel);
    202.         this.buffer = null;
    203.         this.capacity = 0;
    204.     }
    205.  
    206. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    207.     private void FailOutOfRangeError(int index) {
    208.         if (index < this.capacity && (this.minIndex != 0 || this.maxIndex != this.capacity - 1)) {
    209.             throw new IndexOutOfRangeException(
    210.                 $"Index {(object) index} is out of restricted IJobParallelFor range [{(object) this.minIndex}...{(object) this.maxIndex}] in ReadWriteBuffer.\nReadWriteBuffers are restricted to only read & write the element at the job index. You can use double buffering strategies to avoid race conditions due to reading & writing in parallel to the same elements from a job.");
    211.         }
    212.  
    213.         throw new IndexOutOfRangeException(
    214.             $"Index {(object) index} is out of range of '{(object) this.capacity}' Length.");
    215.     }
    216. #endif
    217. }
    Where did I go wrong?
     
  2. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    987
  3. TheDemiurge

    TheDemiurge

    Joined:
    Jul 26, 2010
    Posts:
    42
    Could've updated your reply on what was wrong....
    But in case anyone else stumbles across this and also can't quite figure it out right away, the AtomicSafetyHandle has to be named m_Safety, and the DisposeSentinel has to be named m_DisposeSentinel.