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

I implemented my own “job” type to which I can pass managed types. This is my inspiration and here’s my implementation:
Code

    public interface IJobManaged
    {
        void Execute();
    }
   public static class IJobManagedExtensions
    {
     
        public static JobHandle Schedule<T>(this T jobDependentCodeData,
            JobHandle dependsOn = default)
            where T :  struct,IJobManaged
        {
            GCHandle gcHandle = GCHandle.Alloc(jobDependentCodeData);
            var jobHandle = new GCHandleJob()
            {
                handle = gcHandle
            }.Schedule(dependsOn);
         
         
            IJobManagedCoroutineStarter.StartTask(GetGCHandleFreeCoroutine(gcHandle,jobHandle));
         
            return jobHandle;
        }
     
        private static IEnumerator GetGCHandleFreeCoroutine(GCHandle gcHandle, JobHandle jobHandle)
        {
            while (!jobHandle.IsCompleted)
            {
                //Debug.Log("AAAAAA");  if I uncomment this, everything works fine
                yield return null;
            }
         
            Debug.Log("Free");
            gcHandle.Free();
        }

        private struct GCHandleJob : IJob
        {
            public GCHandle handle;
         
            public void Execute()
            {
                IJobManaged task = (IJobManaged) handle.Target;
                task.Execute();
            }
        }
    }

And I have a problem running following code:

    public void ManagedJobTest()
    {
        Debug.Log("Test");
        NativeArray<float> a = new NativeArray<float>(100000,Allocator.Persistent);
        a[0] = 7.557f;

        var jobHandle = new JobManagedTest()
        {
            a = a
        }.Schedule(new JobHandle());
     
        var jobHandle2 = new JobManagedTest()
        {
            a = a
        }.Schedule(jobHandle);
     
        a.Dispose(jobHandle2);
    }

    private struct JobManagedTest : IJobManaged
    {
        public NativeArray<float> a;
        public void Execute()
        {
            Debug.Log(a[0]);
        }
    }

The thing is everything works fine when Debug.Log in GetGCHandleFreeCoroutine is uncommented but if it isn’t there I get:

InvalidOperationException: The Unity.Collections.NativeArray`1[System.Single] 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 <9dd7ba599535486bb48a2633dd80a88b>:0)
Unity.Collections.NativeArray`1[T].CheckElementReadAccess (System.Int32 index) (at <9dd7ba599535486bb48a2633dd80a88b>:0)
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

1 Like

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.

        private struct GCHandleJob : IJob
        {
            public GCHandle handle;
    
            public void Execute()
            {
                IJobManaged task = (IJobManaged) handle.Target;
                task.Execute();
            }
        }

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.

How can I disable all safety checks on that job?

Again, how can I disable all safety checks on that job?

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)]

Is there a way to do it without forcing Burst compilation?

1 Like

3 years late, but have you managed a solution to this?

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)]?

Oh definitely. The error requiring prior knowledge and experience is not ideal.

1 Like