Persistent malloc in Burst compiled jobs

Hello.
Why are jobs only allowed to allocate temp memory (as far as UnsafeUtility.Malloc is concerned)?
Is it a hard limitation or something that could be disabled by circumventing default safety features in some way?
Afaik, it’s already possible to call any allocator through native plugins, even in burst compiled jobs.

If you allocate persistant container in a job, how to you get the ‘pointer’ to that container back to the main thread to be able to deallocate it later ?

I see what you mean. Unity’s containers are a managed type in c#'s type system so the following isn’t possible for them:

Job:

    [BurstCompile]
    public unsafe struct MallocTest : IJob
    {
        [NativeDisableUnsafePtrRestriction] public IntPtr* ptr;
        public void Execute()
        {
            ptr[0] = (IntPtr)UnsafeUtility.Malloc(8, 0, Allocator.Persistent);
        }
    }

Usage:

            unsafe
            {
                var arr = (IntPtr*)UnsafeUtility.Malloc(sizeof(IntPtr), UnsafeUtility.AlignOf<IntPtr>(), Allocator.Persistent);
                var j = new MallocTest() { ptr = arr };
                Debug.Log(j.ptr[0].ToInt64());
                j.Schedule().Complete();
                Debug.Log(j.ptr[0].ToInt64());
                UnsafeUtility.Free((void*)j.ptr[0], Allocator.Persistent);
                UnsafeUtility.Free(arr, Allocator.Persistent);
            }

What confused me was that when you attempt to create persistent containers, the error raised claims that jobs can allocate only temp memory. And, if I recall correctly, Unity’s native containers use UnsafeUtility internally. From that, I inferred that there would be additional caveats regarding UnsafeUtility in jobs.

1 Like

I guess the main reason is, that persistent allocations are very very slow as they need full thread synchronization. Temp allocations are separated per thread and need no synchronization.

Unity’s containers (eg UnsafeList) use AllocatorManager to do persistent allocations in jobs. Can’t give you any details, didn’t require it so far.

The persistent safe containers can’t be allocated because they require a managed DisposeSentinel and the safety system.

1 Like

Exactly. This is still a source of confusion in 2021.

To reiterate: You can allocate Persistent memory from jobs with UnsafeUtility.Malloc

using UnityEngine;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using IntPtr = System.IntPtr;

public class MallocTester : MonoBehaviour
{
    unsafe void Start ()
    {
        Debug.Log($"Test start [{System.DateTime.Now.ToShortTimeString()}]" );
        IntPtr* intptr = (IntPtr*) UnsafeUtility.Malloc( UnsafeUtility.SizeOf<IntPtr>() , UnsafeUtility.AlignOf<IntPtr>() , Allocator.Persistent );
        *intptr = new IntPtr(0);// clears allocated memory
        var job = new MallocTest{ Ptr = intptr };
        job.Schedule().Complete();
      
        Debug.Log($"\tFree new job malloc:\t<b>{(long)(*job.Ptr).ToPointer()} (0x{System.Convert.ToString((long)(*job.Ptr).ToPointer(),16).ToUpper()})</b>" );
        UnsafeUtility.Free( (*job.Ptr).ToPointer() , Allocator.Persistent );
        Debug.Log($"\tFree original malloc:\t<b>{(long)intptr} (0x{System.Convert.ToString((long)intptr,16).ToUpper()})</b>" );
        UnsafeUtility.Free( intptr , Allocator.Persistent );
    }

    [Unity.Burst.BurstCompile]
    public unsafe struct MallocTest : IJob
    {
        [NativeDisableUnsafePtrRestriction] public IntPtr* Ptr;
        public void Execute ()
        {
            void* newAlloc = UnsafeUtility.Malloc( UnsafeUtility.SizeOf<float>() * 128 , UnsafeUtility.AlignOf<float>() , Allocator.Persistent );
            *Ptr = (IntPtr) newAlloc;
            Debug.Log($"\tAllocated in-job:\t\t<b>{(long)newAlloc}</b>");
        }
    }
}
1 Like

I am facing this issue in Unity 2022.3.28 version with URP. Does anyone have any idea why its happening or do I need to change the version? I have tried in 2022.3.20 and 2022.3.24.