Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Bug NativeArray`1[System.Single] has been declared as [WriteOnly] in the job but you are reading

Discussion in 'Entity Component System' started by Zylkowski_a, Aug 9, 2020.

  1. Zylkowski_a

    Zylkowski_a

    Joined:
    Jul 27, 2019
    Posts:
    157
    I implemented my own "job" type to which I can pass managed types. This is my inspiration and here's my implementation:
    Code (CSharp):
    1.     public interface IJobManaged
    2.     {
    3.         void Execute();
    4.     }
    Code (CSharp):
    1.    public static class IJobManagedExtensions
    2.     {
    3.      
    4.         public static JobHandle Schedule<T>(this T jobDependentCodeData,
    5.             JobHandle dependsOn = default)
    6.             where T :  struct,IJobManaged
    7.         {
    8.             GCHandle gcHandle = GCHandle.Alloc(jobDependentCodeData);
    9.             var jobHandle = new GCHandleJob()
    10.             {
    11.                 handle = gcHandle
    12.             }.Schedule(dependsOn);
    13.          
    14.          
    15.             IJobManagedCoroutineStarter.StartTask(GetGCHandleFreeCoroutine(gcHandle,jobHandle));
    16.          
    17.             return jobHandle;
    18.         }
    19.      
    20.         private static IEnumerator GetGCHandleFreeCoroutine(GCHandle gcHandle, JobHandle jobHandle)
    21.         {
    22.             while (!jobHandle.IsCompleted)
    23.             {
    24.                 //Debug.Log("AAAAAA");  if I uncomment this, everything works fine
    25.                 yield return null;
    26.             }
    27.          
    28.             Debug.Log("Free");
    29.             gcHandle.Free();
    30.         }
    31.  
    32.         private struct GCHandleJob : IJob
    33.         {
    34.             public GCHandle handle;
    35.          
    36.             public void Execute()
    37.             {
    38.                 IJobManaged task = (IJobManaged) handle.Target;
    39.                 task.Execute();
    40.             }
    41.         }
    42.     }

    And I have a problem running following code:
    Code (CSharp):
    1.     public void ManagedJobTest()
    2.     {
    3.         Debug.Log("Test");
    4.         NativeArray<float> a = new NativeArray<float>(100000,Allocator.Persistent);
    5.         a[0] = 7.557f;
    6.  
    7.         var jobHandle = new JobManagedTest()
    8.         {
    9.             a = a
    10.         }.Schedule(new JobHandle());
    11.      
    12.         var jobHandle2 = new JobManagedTest()
    13.         {
    14.             a = a
    15.         }.Schedule(jobHandle);
    16.      
    17.         a.Dispose(jobHandle2);
    18.     }
    19.  
    20.     private struct JobManagedTest : IJobManaged
    21.     {
    22.         public NativeArray<float> a;
    23.         public void Execute()
    24.         {
    25.             Debug.Log(a[0]);
    26.         }
    27.     }
    The thing is everything works fine when Debug.Log in GetGCHandleFreeCoroutine is uncommented but if it isn't there I get:
    Code (CSharp):
    1. InvalidOperationException: The Unity.Collections.NativeArray`1[System.Single] has been declared as [WriteOnly] in the job, but you are reading from it.
    2. Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckReadAndThrowNoEarlyOut (Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle handle) (at <9dd7ba599535486bb48a2633dd80a88b>:0)
    3. Unity.Collections.NativeArray`1[T].CheckElementReadAccess (System.Int32 index) (at <9dd7ba599535486bb48a2633dd80a88b>:0)
    4. Unity.Collections.NativeArray`1[T].get_Item (System.Int32 index) (at <9dd7ba599535486bb48a2633dd80a88b>:0)
    Why does it happen and why simple Debug.Log makes it work? It also works when I get rid of a.Dispose() but that's not viable solution because it would be a memory leak
     
    Egad_McDad likes this.
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,626
    I've run into this a couple of times when writing my own jobs.
    What you're doing breaks the safety system.
    This is all the safety system sees when the job starts and injects the appropriate safety handles.

    Code (CSharp):
    1.         private struct GCHandleJob : IJob
    2.         {
    3.             public GCHandle handle;
    4.    
    5.             public void Execute()
    6.             {
    7.                 IJobManaged task = (IJobManaged) handle.Target;
    8.                 task.Execute();
    9.             }
    10.         }
    It doesn't see the NativeArray so it can't inject a safety handle into it.
    If you want to do it this way you pretty much have to turn off the safety system for it.

    -edit-

    Why does uncommenting that debug line make it work? No idea.
     
  3. Zylkowski_a

    Zylkowski_a

    Joined:
    Jul 27, 2019
    Posts:
    157
    How can I disable all safety checks on that job?
     
  4. Zylkowski_a

    Zylkowski_a

    Joined:
    Jul 27, 2019
    Posts:
    157
    Again, how can I disable all safety checks on that job?
     
  5. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,626
    I recommend against doing this as it will probably bite you in the ass at some point in the future when you've forgotten

    [BurstCompile(DisableSafetyChecks = true)]
     
  6. Zylkowski_a

    Zylkowski_a

    Joined:
    Jul 27, 2019
    Posts:
    157
    Is there a way to do it without forcing Burst compilation?
     
    chadfranklin47 likes this.
  7. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    217
    3 years late, but have you managed a solution to this?
     
  8. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    217
    Is it not a bug though that without the safety system being able to see the NativeArray to inject into, reading from a NativeArray throws this error? Without any injection, wouldn't it be best to default the safety handle to a state that doesn't throw this error? It would at least be helpful to get an error that matched what is going on: "Don't read from a NativeArray the safety system isn't aware of".

    The error only appears when passing the job's jobHandle into the Dispose() method as a dependency. Doing JobHandle.Complete() and nativeArray.Dispose() separately, the error does not occur. Why is this?

    In my own code, although tedious, I can avoid the error by circumventing the safety system completely... by using UnsafeList for example. This may not be possible for all of my use cases though. Do you know of any other solutions besides
    [BurstCompile(DisableSafetyChecks = true)]
    ?
     
    Last edited: Sep 21, 2023
  9. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,626
    Oh definitely. The error requiring prior knowledge and experience is not ideal.
     
    chadfranklin47 likes this.