Search Unity

Question I have a trouble with job system and NativeContainer.

Discussion in 'C# Job System' started by Selshas, Mar 16, 2022.

  1. Selshas

    Selshas

    Joined:
    May 14, 2021
    Posts:
    7
    I'm recently trying to implement C# Job System to replace some part of my old multithreaded code base.
    I'm new to this concept so I have some unexpected errors. here is entire testing code.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Collections;
    5. using Unity.Jobs;
    6.  
    7. public class JobTester : MonoBehaviour
    8. {
    9.     struct Job_Test : IJobParallelFor
    10.     {
    11.         [NativeDisableParallelForRestriction]
    12.         public NativeArray<byte> result;
    13.  
    14.         public void Execute(int index)
    15.         {
    16.             result[index] = (byte)index;
    17.         }
    18.     }
    19.     struct Job_Test2 : IJobParallelFor
    20.     {
    21.         [NativeDisableParallelForRestriction]
    22.         public NativeArray<byte> input;
    23.  
    24.         public void Execute(int index)
    25.         {
    26.             NativeArray<byte> input2 = JobTester.instance.native;
    27.             // Debug.Log(input[index]); // 1)
    28.             Debug.Log(input2[index]); // 2)
    29.         }
    30.     }
    31.  
    32.     static JobTester instance = null;
    33.     NativeArray<byte> native;
    34.  
    35.     // Start is called before the first frame update
    36.     void Start()
    37.     {
    38.         instance = this;
    39.  
    40.         byte[] managed = new byte[4];
    41.         native = new NativeArray<byte>(managed, Allocator.Persistent);
    42.  
    43.         Job_Test testJob = new Job_Test();
    44.         testJob.result = native;
    45.         JobHandle handler = testJob.Schedule(4, 1);
    46.         handler.Complete();
    47.  
    48.         Job_Test2 testJob2 = new Job_Test2();
    49.         testJob2.input = native;
    50.         JobHandle handler2 = testJob2.Schedule(4, 1);
    51.         handler2.Complete();
    52.  
    53.         native.Dispose();
    54.     }
    55. }
    56.  

    So.. what I'm trying to do is that. multiple jobs accessing same container has their own responsibility to partial update of the container(the range is not overlapped).

    Because the actual implementations are capsulated in their class, I needed to set a container which stores the results is static or at least it should have to allow global access. So I used Singleton as you can see above, class JobTester is singleton and has static member variable 'instance'.(of course it is extremely simplified as it is just test code)

    Where I have trouble is line 28 which is marked with 2). somehow
    Debug.Log(input2[index]);
    is causing an error at runtime.


    InvalidOperationException: The Unity.Collections.NativeArray`1[System.Byte] has been declared as [WriteOnly] in the job, but you are reading from it.
    Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckReadAndThrowNoEarlyOut (Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle handle) (at <3be1a7ff939c43f181c0a10b5a0189ac>:0)
    Unity.Collections.NativeArray`1[T].CheckElementReadAccess (System.Int32 index) (at <3be1a7ff939c43f181c0a10b5a0189ac>:0)
    Unity.Collections.NativeArray`1[T].get_Item (System.Int32 index) (at <3be1a7ff939c43f181c0a10b5a0189ac>:0)
    JobTester+Job_Test2.Execute (System.Int32 index) (at Assets/JobTester.cs:28)
    Unity.Jobs.IJobParallelForExtensions+ParallelForJobStruct`1[T].Execute (T& jobData, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at <3be1a7ff939c43f181c0a10b5a0189ac>:0)


    I've thought the container 'native' is still in write mode even the job is completed. So if I turn this to read mode by reading some data from main thread before Job2 is run (like Debug.Log(native[0]);), the error is gone. to prove this, I've sent this container through input field of Job2 and read in same way. it is line 27 marked by 1) and commented.

    in conclusion, 1) is worked well. I have no problem to read and write to the container. but 2) is causing error above. If my hypothesis was right, both 1) 2) should have to cause same error but it didn't. I don't know why. I don't know what is different. as I know, these two method should be pointing same address of the resource.

    why, accessing same resource but through static instance is causing error unlike ordinary approach?
     
    Last edited: Mar 16, 2022
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    The DOTS forum is here, do you want me to move your post?
     
  3. Selshas

    Selshas

    Joined:
    May 14, 2021
    Posts:
    7
    Technically, I'm not using DOTS but job system. that's why I wrote this here but if it could help to solve my problem, please. It would be pleasure.

    Can It be copied if it is possible?
     
  4. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    The job system is part of DOTS. DOTS is not ECS or something specific.
     
  5. Selshas

    Selshas

    Joined:
    May 14, 2021
    Posts:
    7
    of course. yes.
     
  6. elliotc-unity

    elliotc-unity

    Unity Technologies

    Joined:
    Nov 5, 2015
    Posts:
    230
    The error message is buggy, but it's correct that this is not allowed. The problematic line is actually this:

    NativeArray<byte> input2 = JobTester.instance.native;


    It is not safe for an arbitrary job to access a static variable, because some other job or the main thread might be accessing the same variable at the same time, and there's no way to know if for example the array might get reallocated and you use the old freed version and then crash the editor.

    If you want to use that other array, you should put it on your job struct and access it from there. Then the job safety system can prove (or disprove) the safety of your access properly.
     
    Last edited: Mar 16, 2022
  7. Selshas

    Selshas

    Joined:
    May 14, 2021
    Posts:
    7
    The reason why I used static variable is that, It use to be implemented for chunk based operation. so each job has own responsibility to specific chunk and they would select which container they will use according to the data of the chunk. it is simultaneous operation using IJobParallel interface, so it couldn't be a member of declaration of the job because the containers are not linear and all jobs can have different set of containers they use. this is initially what I thought.

    I should try to make those containers packed in single flatten linear memory and use offsets to access so that any job can search them by their index.

    anyway, thanks to reply.
     
    elliotc-unity likes this.