There is a path of execution where FMassCommandBuffer::OwnerThreadId can no longer match the correct thread id when running the task for a FMassEntityQuery::ParallelForEachEntityChunk. This in turn leads to the 2nd check in the COMMAND_PUSHING_CHECK macro to fail incorrectly, since OwnerThreadId doesn’t match FPlatformTLS::GetCurrentThreadId().
checkf(OwnerThreadId == FPlatformTLS::GetCurrentThreadId(), TEXT(“Commands can be pushed only in the same thread where the command buffer was created.”))
- The task runs as normal for the given worker id for a first batch of jobs, ParallelForImpl::ParallelForInternal::FParallelForExecutor::operator() -> ParallelForImpl::CallBody -> FMassEntityQuery::ParallelForEachEntityChunk::lamba2 runs, calls the lazy init FTaskContext::GetCommandBuffer, which allocates the command buffer and creates it with the current thread id, for the FTaskContext associated with that worker id.
- The first batch for that task completes, and in FParallelExecutor::operator(), we’re a background priority task, and fall through to rescheduling the task. We launch a new task with the same worker id.
- The new (rescheduled) task is picked up by a different worker thread, which starts working another batch of jobs.
- We once again hit the lazy init FTaskContext::GetCommandBuffer, which uses the same FTaskContext as before (since it has the same worker index). We see that the command buffer already exists, so we don’t create a new one.
- We’re now in a state where we call the body (the lambda for our FMassEntityQuery::ParallelForEachEntityChunk call) with FMassCommandBuffer::OwnerThreadId not matching our current thread id (it matches the thread id from the task that ran the 1st batch). And commands pushed to the command buffer will fail the check. This results in the check failing, but we’re not actually performing unsafe concurrency, since the task should still be exclusively accessing the command buffer in this situation.