Search Unity

NativeQueue.Concurrent dependency question

Discussion in 'Entity Component System' started by snacktime, May 14, 2019.

  1. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Trying to figure out if the safety system is just misreporting something or what is going on exactly.

    I have 3 jobs job1 job2 and job3. The first two are IJobForeach and being passed the concurrent version of the queue. The last one is an IJob using the regular version. These are all scheduled sequentially in the same dependency chain.

    It complains about job1 needs to be completed before job1 can be scheduled.

    Actually job2 was the last job I added to the code and that's when it started complaining. Remove job2 and no complaints.

    It doesn't seem like there should be a safety issue as these jobs are all chained. Plus job1 and job2 both write to a couple of the same components, so the system is going to ensure those don't run in parallel anyways. So for now I just set NativeDisableContainerSafetyRestriction on the queue in job1 to shut up the warnings.
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Code?

    I don't think that should be throwing warnings.

    -edit-

    actually i can replicate the issue.

    I suspect you're doing this which throws dependency errors.

    Code (CSharp):
    1.         handle = new Job1
    2.         {
    3.             Queue = this.queue.ToConcurrent(),
    4.         }.Schedule(4096, 4, handle);
    5.  
    6.         handle = new Job2
    7.         {
    8.             Queue = this.queue.ToConcurrent(),
    9.         }.Schedule(2048, 8, handle);
    10.  
    11.         handle = new Job3
    12.         {
    13.             Queue = this.queue,
    14.         }.Schedule(handle);
    15.  
    16.         return handle;
    Do this instead which does not throw errors.

    Code (CSharp):
    1.         var concurrent = this.queue.ToConcurrent();
    2.  
    3.         handle = new Job1
    4.         {
    5.             Queue = concurrent,
    6.         }.Schedule(4096, 4, handle);
    7.  
    8.         handle = new Job2
    9.         {
    10.             Queue = concurrent,
    11.         }.Schedule(2048, 8, handle);
    12.  
    13.         handle = new Job3
    14.         {
    15.             Queue = this.queue,
    16.         }.Schedule(handle);
    17.  
    18.         return handle;
    The reason this happens is ToConcurrent does a write safety check

    Code (CSharp):
    1. public Concurrent ToConcurrent()
    2.         {
    3.             NativeQueue<T>.Concurrent concurrent;
    4. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    5.             AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
    6.             concurrent.m_Safety = m_Safety;
    7.             AtomicSafetyHandle.UseSecondaryVersion(ref concurrent.m_Safety);
    8. #endif
    So if you call it after starting a job where it's being used it's going to give dependency errors.

    Example, at first glance you might think this is the same as the top example that throws errors but it's not and will work fine.

    Code (CSharp):
    1.         var concurrent1 = this.queue.ToConcurrent();
    2.         var concurrent2 = this.queue.ToConcurrent();
    3.  
    4.         handle = new Job1
    5.         {
    6.             Queue = concurrent1,
    7.         }.Schedule(4096, 4, handle);
    8.  
    9.         handle = new Job2
    10.         {
    11.             Queue = concurrent2,
    12.         }.Schedule(2048, 8, handle);
    13.  
    14.         handle = new Job3
    15.         {
    16.             Queue = this.queue,
    17.         }.Schedule(handle);
    18.  
    19.         return handle;
    Again, this will work fine as well

    Code (CSharp):
    1.         var job1 = new Job1
    2.         {
    3.             Queue = this.queue.ToConcurrent(),
    4.         };
    5.  
    6.         var job2 = new Job2
    7.         {
    8.             Queue = this.queue.ToConcurrent(),
    9.         };
    10.  
    11.         handle = job1.Schedule(4096, 4, handle);
    12.         handle = job2.Schedule(2048, 8, handle);
    13.  
    14.         handle = new Job3
    15.         {
    16.             Queue = this.queue,
    17.         }.Schedule(handle);
    18.  
    19.         return handle;
    Disabling the safety system is a silly way to solve a problem even if it's safe to do so. You should try to understand why you have the problem.
     
    Last edited: May 14, 2019
    Singtaa and Shinyclef like this.
  3. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Ok ya I thought of that and then for some reason didn't try it. The second approach works for me also.
     
  4. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Posted a bit more why it happens (and a few more working examples)
     
    Deleted User and Shinyclef like this.