Search Unity

  1. Unity 2019.1 is now released.
    Dismiss Notice

JobHandle.CombineDependencies(NativeArray<T>) causes crash

Discussion in 'Data Oriented Technology Stack' started by Joouur, Jan 8, 2019.

  1. Joouur

    Joouur

    Joined:
    Feb 18, 2016
    Posts:
    10
    So, I am fairly new to ECS, and decided to do a project that relies on a lot on the previous dependencies, and other dependencies that could run parallel from each other since they do not rely on previous data.

    The structure that I made for this systems is not complex, is first run the reset jobs, then the computation jobs, also I have a static class called manager.

    Code (CSharp):
    1. //Disable CS0414 warning, Group is being used.
    2. #pragma warning disable 414
    3.  
    4. public class FirstJobSystem : JobComponentSystem
    5. {
    6.   /// <summary>
    7.   /// Component group to get data
    8.   /// </summary>
    9.   private ComponentGroup _group;
    10.   /// <summary>
    11.   /// Queue of Native arrays to dispose before next job starts.
    12.   /// </summary>
    13.   private Queue<NativeArray<JobHandle>> DependenciesToDispose;
    14.  
    15.   /// <summary>
    16.   /// Called when EntityManager is created
    17.   /// </summary>
    18.   protected override void OnCreateManager()
    19.   {
    20.     _group = GetComponentGroup(
    21.       typeof(Info)
    22.     );
    23.     //Initialize the queue
    24.     DependenciesToDispose = new Queue<NativeArray<JobHandle>>();
    25.   }
    26.   /// <summary>
    27.   /// Called when EntityManager is destroyed
    28.   /// </summary>
    29.   protected override void OnDestroyManager()
    30.   {
    31.     //Dispose the native arrays
    32.     foreach (var DTP in DependenciesToDispose)
    33.     {
    34.       DTP.Dispose();
    35.     }
    36.   }
    37.   /// <summary>
    38.   /// Run on update to set the origin and direction per group
    39.   /// </summary>
    40.   /// <param name="inputDeps">Input handles</param>
    41.   /// <returns>Dependecies of jobs</returns>
    42.   protected override JobHandle OnUpdate(JobHandle inputDeps)
    43.   {
    44.     DisposeNativeContainers(1); //Dispose one set of dependencies.
    45.  
    46.     //Get groups ID on int array
    47.     int[] groupsID = manager.GroupsToReset;
    48.     //Initialize one native Array of dependencies
    49.     NativeArray<JobHandle> dependencies = new NativeArray<JobHandle>(
    50.       groupsID.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory
    51.     );
    52.     //Iterate through group array
    53.     for (int l = 0; l < groupsID.Length; ++l)
    54.     {
    55.       if (groupsID[l] == -1) continue; //Go next if not ready
    56.       int length = manager.GroupSet[groupsID[l]].Length;
    57.       //Set jobs and schedule by dependency
    58.      
    59.       //Set the dependency
    60.       dependencies[l] = lastJob.Schedule(
    61.         length,
    62.         length / manager.ResetJobDivider,
    63.         previousDependency
    64.       );
    65.       //Group doesn't need to be reset anymore
    66.       manager.GroupsToReset[l] = -1;
    67.     }
    68.     //Compine dependencies
    69.     inputDeps = JobHandle.CombineDependencies(dependencies);
    70.     DependenciesToDispose.Enqueue(dependencies); //Queue them for dispose
    71.                                                  //Set the reset handler
    72.     manager.ResetHandle = inputDeps;
    73.  
    74.     return inputDeps;
    75.   }
    76.   //Native container dipose function
    77.   private void DisposeNativeContainers(int AmountToDequeue)
    78.   {
    79.     for (int i = 0; i < AmountToDequeue; ++i)
    80.     {
    81.       if (DependenciesToDispose.Count > 0)
    82.       { DependenciesToDispose.Dequeue().Dispose(); }
    83.     }
    84.   }
    85. }
    86.  
    And the second system runs like this
    Code (CSharp):
    1.  
    2. public sealed class SecondJobSystem : JobComponentSystem
    3. {
    4.   /// <summary>
    5.   /// Group that contains all the entities
    6.   /// </summary>
    7.   private ComponentGroup _group;
    8.  
    9.   /// <summary>
    10.   /// When Entity Manager is created
    11.   /// </summary>
    12.   protected override void OnCreateManager()
    13.   {
    14.     // Get the group for entities
    15.     _group = GetComponentGroup(typeof(Data));
    16.   }
    17.   /// <summary>
    18.   /// On destroy the Entity Manager
    19.   ///   --Dispose all the queues
    20.   /// </summary>
    21.   protected override void OnDestroyManager()
    22.   {
    23.     foreach (var DTP in DependenciesToDispose)
    24.     {
    25.       DTP.Dispose();
    26.     }
    27.   }
    28.   /// <summary>
    29.   /// Update function to handle the dependencies and computations
    30.   /// </summary>
    31.   /// <param name="handle">Handle for dependencies</param>
    32.   /// <returns>Dependencies</returns>
    33.   protected override JobHandle OnUpdate(JobHandle handle)
    34.   {//Get iterator to get data
    35.     EntityArray iterator = _group.GetEntityArray();
    36.     if (iterator.Length == 0) return handle; //return if nothing
    37.                                              //Create dependencies native array
    38.     NativeArray<JobHandle> dependencies = new NativeArray<JobHandle>(
    39.       iterator.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory
    40.     );
    41.     //Dispose previous array jobs
    42.     DisposePreviousArrays(iterator.Length);
    43.     for (int i = 0, c = iterator.Length; i < c; ++i)
    44.     {
    45.       //IJobProcessComponentData<data>
    46.       ThirdJob tracersJob = new ThirdJob()
    47.       {
    48.         //Info
    49.       };
    50.       //Schedule the job
    51.       JobHandle thirdDependency = tracersJob.Schedule(this, manager.ResetHandle);
    52.       const int length = 10;
    53.       //Schedule the intersection check
    54.       JobHandle incidents = new Incident()
    55.       {
    56.         //Info
    57.       };
    58.       dependencies[i] = incidents.Schedule(
    59.         length,
    60.         length / manager.IncidentDivider,
    61.         thirdDependency
    62.       );
    63.     }
    64.     //Combine the dependencies.
    65.     handle = JobHandle.CombineDependencies(dependencies);
    66.     //Queue them
    67.     DependenciesToDispose.Enqueue(dependencies);
    68.     return handle;
    69.   }
    70.   /// <summary>
    71.   /// Disponsing function
    72.   /// </summary>
    73.   /// <param name="AmountToDequeue"></param>
    74.   private void DisposePreviousArrays()
    75.   {
    76.     if (DependenciesToDispose.Count > 0)
    77.       DependenciesToDispose.Dequeue().Dispose();
    78.   }
    79. }
    80.  

    After a lot of tinkering and thinking (and a lot of crashes), I found that the stack trace from the Editor.log (it took me a while to think for checking it) is from the Job system, specifically from the the CombineDependencies.

    0x00000001408AC092 (Unity) JobQueue::ScheduleDependencies
    0x00000001408ACA46 (Unity) JobQueue::ScheduleJobMultipleDependencies
    0x00000001408A3F53 (Unity) ScheduleMultiDependencyJob
    0x00000001408AE1B3 (Unity) CombineDependenciesInternalPtr
    0x000000014191D16A (Unity) JobHandle_CUSTOM_CombineDependenciesInternalPtr_Injected
    0x00000000478819D0 (Mono JIT Code) (wrapper managed-to-native) Unity.Jobs.JobHandle:CombineDependenciesInternalPtr_Injected (void*,int,Unity.Jobs.JobHandle&)
    0x0000000047881863 (Mono JIT Code) Unity.Jobs.JobHandle:CombineDependenciesInternalPtr (void*,int)
    0x0000000047886EEB (Mono JIT Code) [C:\buildslave\unity\build\Runtime\Jobs\ScriptBindings\JobHandle.bindings.cs:83] Unity.Jobs.JobHandle:CombineDependencies (Unity.Collections.NativeArray`1<Unity.Jobs.JobHandle>)

    Does anyone has any thoughts on how to solve or if I should think on a better way of tackling the jobs?
     
  2. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    1,267
    You allocate “dependencies” array with NativeArrayOptions.UninitializedMemory and next you not fill all indexes (because you “continue” on 55 row) of this array, this indexes undefined and throw error, allocate array without NativeArrayOptions.UninitializedMemory if you not guarantee than all items will be filled.
     
  3. Joouur

    Joouur

    Joined:
    Feb 18, 2016
    Posts:
    10
    Thanks, I actually figure it out late at night, the way I solved this issue was adding an integer that allows me know how many dependencies I have.

    Code (CSharp):
    1. int active = -1
    2. for (int l = 0; l < groupsID.Length; ++l)
    3. {
    4.         if (groupsID[l] == -1) continue; //Go next if not ready
    5.         active = l;
    6. }
    7. if (active > 1)
    8. {
    9.         //Compine dependencies
    10.         inputDeps = JobHandle.CombineDependencies(dependencies);
    11. }
    12. else if (active == 0)
    13. {
    14.         inputDeps = dependencies[0];
    15. }
    16. DependenciesToDispose.Enqueue(dependencies); //Queue them for dispose
    is a little hacky, but I would figure out a way to distribute them a little better.


    Also a little bit off the topic, I am not that advanced but do you think it would be better to do NativeArrayOptions.ClearMemory, than UninitializedMemory?
     
  4. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    1,267
    UninitializedMemory faster but you must guarantee than items will be filled before they will used.
     
  5. Joouur

    Joouur

    Joined:
    Feb 18, 2016
    Posts:
    10