Global counter inside jobs?

So, I need a global counter to keep track of the number of destroyed entities inside ForEach/ScheduleParallel jobs. I just need to increment it and then check it on LateUpdate(), so I’d say there’s no concurrency issue, as each entity just performs an increment, so parallel writings should theoretically work…
I tried with a NativeArray and then, after the job, updating the global counter (a field of a singleton monob):


var savedAlliesCount = new NativeArray<int>(1, Allocator.Persistent);
foreach stuff...
_entityCommandBuffer.DestroyEntity(entityInQueryIndex, e);
savedAlliesCount[0] += 1;
})
.ScheduleParallel(Dependency);
_commandBufferSystem.AddJobHandleForProducer(Dependency);
Debug.Log(savedAlliesCount[0]);
GameManager.main.SavedAllies += savedAlliesCount[0];

My dumb, but I can’t help figuring out why the last two commands are not executed… I guess it has to do with the ecb. Have you got any hint? Or other solutions for global variable updates?

Thank you in advance :slight_smile:

You can’t increment like that in parallel. It’s a race condition.
Use Interlocked.Increment instead with getting a ref to savedAlliesCount. UnsafeUtility.AsRef will help you there. You just need the pointer from the NativeArray.

Or use Schedule instead.

@Enzi thank you for the answer!
I wouldn’t have considered race condition because it appears to me to be easily worked around using a single shared variable between threads (and I think your solution could be about this…), instead of copying it, performing a local increment for each thread, and then update the original one.
Anyway, the code of your solution I tried to implement:

 [NativeDisableUnsafePtrRestriction]
private int* _savedAlliesPtr;  // class fields

_savedAlliesPtr = (int*)NativeArrayUnsafeUtility.GetUnsafePtr(SavedAllies);
int refArray = UnsafeUtility.AsRef<int>(_savedAlliesPtr); //in OnUpdate

Interlocked.Increment(ref refArray); // inside ScheduleParallel

Yet it seems the value in the NativeArray[0] stays 0… Maybe you could help me spot what’s wrong? Last try, I promise, then I’ll switch to Schedule() :smile:

Try to set refArray inside ScheduledParallel. I’m honestly not sure what the auto-magic snapping does with the variable but I’m pretty sure it will be copied and the actual ref is gone, meaning the job gets the int by value. So, snapping the ptr should be enough. Even easier, also get the ptr inside ScheduleParallel.

Then you are missing the ref which also ends in another copy.
ref int refArray = ref UnsafeUtility.AsRef(_savedAlliesPtr);

The Interlocked.Increment should work. If not you can also try Interlocked.Add(ref refArray, 1);

1 Like

https://docs.unity3d.com/Packages/com.unity.jobs@0.51/manual/custom_job_types.html#custom-nativecontainers

Jobs package actually has a NativeCounter as an example of a custom NativeContainer

2 Likes