Search Unity

  1. Unity 2019.1 beta is now available.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. We're looking for insight from anyone who has experience with game testing to help us better Unity. Take our survey here. If chosen to participate you'll be entered into a sweepstake to win an Amazon gift card.
    Dismiss Notice
  4. On February 28th the Feedback website will shut down and be redirected to the Unity forums. See the full post for more information.
    Dismiss Notice
  5. Want to provide direct feedback to the Unity team? Join the Unity Advisory Panel.
    Dismiss Notice
  6. Unity 2018.3 is now released.
    Dismiss Notice
  7. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice

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

Discussion in 'Entity Component System and C# Job system' started by davenirline, Feb 10, 2019.

  1. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    308
    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:
    308