Can't use NativeBitArray.AsNativeArray: Assertion failed on expression: '!IsSecondaryVersion(handle)'

I’ve been writing an editor tool in the form of a MonoBehavior with [ExecuteInEditMode]. That tool uses native Unity Collections such NativeBitArray.
One function of my script needs to save that bit array to a file, so I use AsByteArray to get its bytes and dump that.

            var presenceBytes = grid.presence.AsNativeArray<byte>();
            binaryWriter.Write((uint)presenceBytes.Length);
            for (int i = 0; i < presenceBytes.Length; ++i)
            {
                binaryWriter.Write(presenceBytes[i]);
            }

It has worked in the past but today it keeps printing this error:

Assertion failed on expression: '!IsSecondaryVersion(handle)'
UnityEngine.StackTraceUtility:ExtractStackTrace ()
Unity.Collections.NativeBitArray:AsNativeArray<byte> () (at ./Library/PackageCache/com.unity.collections@2.1.0-pre.18/Unity.Collections/NativeBitArray.cs:179)

What’s weird is that this specifically happens on the line where I do AsByteArray. If I do anything else such as iterating and reading all bits of the array, it passes through them just fine and prints expected results:

            int setCount = 0;
            int unsetCount = 0;
            for (int i = 0; i < grid.presence.Length; ++i)
            {
                if (grid.presence.IsSet(i))
                {
                    ++setCount;
                }
                else
                {
                    ++unsetCount;
                }
            }
            Debug.Log($"Set: {setCount}, Unset: {unsetCount}");

And all other features of my script that access that bit array work fine. It’s just AsByteArray failing with that weird error.
I checked the number of bits in that array, and it has 49,152, which is divisible by 8, and even 64, so there shouldn’t even be a problem with interpreting it as bytes…

NativeBitArray is in Unity.Collections, so you should be able to look at the source code and see what the assert is. The path is in the error message!

I already had a look… but I don’t understand what it means, or why it happens here.
Besides, this is not visible source code:

            AtomicSafetyHandle.UseSecondaryVersion(ref m_Safety);

Which ends up being:

    [ThreadSafe]
    [MethodImpl(MethodImplOptions.InternalCall)]
    public static extern void UseSecondaryVersion(ref AtomicSafetyHandle handle);

I can only assume it’s related to checks Unity does for jobs, but I don’t even use jobs here.

NativeBitArray.AsNativeArray is bugged. It requires this change:

--- a/NativeBitArray.cs
+++ b/NativeBitArray.cs
@@ -176,8 +176,9 @@ namespace Unity.Collections
 
             var array = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T>(m_BitArray->Ptr, length, Allocator.None);
 #if ENABLE_UNITY_COLLECTIONS_CHECKS
-            AtomicSafetyHandle.UseSecondaryVersion(ref m_Safety);
-            NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref array, m_Safety);
+            var safety = m_Safety;
+            AtomicSafetyHandle.UseSecondaryVersion(ref safety);
+            NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref array, safety);
 #endif
             return array;
         }

The existing code switches the NBA’s own held safety handle to said handle’s secondary handle when it should be performing that switch on a separate copy of the handle à la NativeList<T>.AsArray.

Are you disposing of the NativeArray at any point? Maybe it’s an issue with multiple NativeArray versions created without disposing, partly to do with running in Edit Mode.

Disposal of the NativeArray is a non-factor. NativeBitArray.AsNativeArray acts like NativeList.AsArray in that they both alias the existing memory, using the secondary version on the safety handle for invalidation checking and using Allocator.None for the allocator label. Neither should ever be disposed on their own.

1 Like