Unity JobSystem 原理初探(3)-Job与回调函数

Unity JobSystem 原理初探(3)-Job与回调函数

Job与Group的生成

我们这里来详细讨论ForwardJobForEachToManaged,可以根据第一篇文章得知

ForwardJobForEachToManaged
ExecuteJobCopyData
ExecuteJob_0
ScriptingInvocation::Invoke
scripting_method_invoke
il2cpp_runtime_invoke//回到gameassembly.dll
gameassembly.il2cpp::vm::Runtime::Invoke
gameassembly.ExecuteJobFunction_Invoke
gameassembly._MyJob_Execute//最后才是执行回调函数

ForwardJobForEachToManaged就是回到函数的起始函数,用来运行最终用户程序.它是如何被JobSystem使用就要看下面:

struct JobGroup *__fastcall JobQueue::CreateForEachJobBatch(
        JobQueue *a1,
        __int64 a2,
        __int64 a3,
        unsigned int a4,
        __int64 a5,
        const void *a6,
        struct JobGroup *a7)
{
  __int64 *v7; // rdi
  __int64 i; // rcx
  __int64 v10; // [rsp+0h] [rbp-68h] BYREF
  struct JobGroup *JobsForEach; // [rsp+30h] [rbp-38h]
  char v12[40]; // [rsp+40h] [rbp-28h] BYREF

  v7 = &v10;
  for ( i = 22i64; i; --i )
  {
    *(_DWORD *)v7 = -858993460;
    v7 = (__int64 *)((char *)v7 + 4);
  }
  qmemcpy(v12, a6, 0x10ui64);
  JobsForEach = (struct JobGroup *)JobQueue::CreateJobsForEach(a1, a2, a3, a4, a5, v12);//a2就是ForwardJobForEachToManaged
  JobQueue::CreateBatchImpl(a1, JobsForEach, a7);
  return JobsForEach;
}

可以很清楚的看到CreateJobsForEach创建了JobInfo *JobJobGroup Group,组包含了job也提供了完成任务后的擦屁股函数ForwardJobForEachCleanup*.这个非常重要希望能够理解.

JobGroup *__fastcall JobQueue::CreateJobsForEach(
        __int64 a1,
        __int64 a2,
        __int64 a3,
        unsigned int a4,
        __int64 a5,
        const void *a6)
{
  ...
  JobGroup *Group; // [rsp+30h] [rbp-98h]
  struct JobInfo *Job; // [rsp+38h] [rbp-90h]
  ...
  qmemcpy(v15, a6, sizeof(v15));
  Group = (JobGroup *)JobQueue::CreateGroup(a1, a4, v15);//建立了组
  Job = JobGroup::FirstJob(Group);
  for ( j = 0; j < a4; ++j )
  {
    *((_QWORD *)Job + 1) = a2;//ForwardJobForEachToManaged
    *((_QWORD *)Job + 2) = a3;
    *((_DWORD *)Job + 6) |= 0x20000000u;
    *((_DWORD *)Job + 6) |= 0x40000000u;
    *((_DWORD *)Job + 6) &= ~0x80000000;
    *((_DWORD *)Job + 6) = j & 0x1FFFFFFF | *((_DWORD *)Job + 6) & 0xE0000000;
    Job = *(struct JobInfo **)Job;
  }
  *((_QWORD *)Group + 4) = a5;//完成所有Job后再执行的函数,ForwardJobForEachCleanup
  *((_QWORD *)Group + 5) = a3;
  if ( a5 )
    ++*((_DWORD *)Group + 4);
  return Group;
}

Job与Group的执行

这里就要从上篇文章,创建线程说起**Thread::Run(v48, (void *(__stdcall )(void ))JobQueue::WorkLoop, v50, 0);

可以看到WorkLoop函数地址放在了Thread+3的位置上.

void __thiscall Thread::Run(Thread *this, void *(__cdecl *a2)(void *), void *a3, SIZE_T dwStackSize, int a5)
{
  *((_DWORD *)this + 2) = a3;//JobQueue::WorkLoop的参数
  *((_BYTE *)this + 17) = 0;
  *((_DWORD *)this + 3) = a2;//JobQueue::WorkLoop函数
  *((_BYTE *)this + 16) = 1;
  PlatformThread::Create(this, this, dwStackSize, a5);
}

可以看到真正的线程回调是Thread::RunThreadWrapper

void __thiscall PlatformThread::Create(PlatformThread *this, struct Thread *lpParameter, SIZE_T dwStackSize, int a4)
{
  *(_DWORD *)this = CreateThread(
                      0,
                      dwStackSize,
                      (LPTHREAD_START_ROUTINE)Thread::RunThreadWrapper,
                      (LPVOID)lpParameter,
                      0,
                      (LPDWORD)this + 1);
}

再来看线程启动过程中,执行情况,果然调用了JobQueue::WorkLoop函数.

const unsigned __int8 *__stdcall Thread::RunThreadWrapper(LPVOID lpThreadParameter)
{
  MemoryManager *v1; // ecx
  char *v2; // esi
  const char *v3; // eax
  signed __int32 v4; // eax
  const unsigned __int8 *v5; // edi
  MemoryManager *v6; // ecx

  v1 = MemoryManager::g_MemoryManager;
  if ( !MemoryManager::g_MemoryManager )
  {
    MemoryManager::InitializeMemory();
    v1 = MemoryManager::g_MemoryManager;
  }
  v2 = (char *)lpThreadParameter;
  MemoryManager::ThreadInitialize(v1, *((_DWORD *)lpThreadParameter + 6));
  PlatformThread::Enter((PlatformThread *)v2, (struct Thread *)v2);
  v3 = (const char *)*((_DWORD *)v2 + 7);
  if ( !v3 )
    v3 = v2 + 32;
  ThreadHelper::SetThreadNameInternal((const struct Thread *)v2, v3);
  v4 = (*((int (__cdecl **)(_DWORD))v2 + 3))(*((_DWORD *)v2 + 2));//调用了JobQueue::WorkLoop函数
  v2[16] = 0;
  v5 = (const unsigned __int8 *)v4;
  _InterlockedExchange((volatile signed __int32 *)&lpThreadParameter, v4);
  v6 = MemoryManager::g_MemoryManager;
  if ( !MemoryManager::g_MemoryManager )
  {
    MemoryManager::InitializeMemory();
    v6 = MemoryManager::g_MemoryManager;
  }
  MemoryManager::ThreadCleanup(v6);
  NullGpuProgram::ApplyGpuProgram((NullGpuProgram *)v2, (const struct GpuProgramParameters *)v2, v5);
  return v5;
}

可以看到JobQueue::WorkLoop的主要功能就是调用JobQueue::ProcessJobs,这里并没有循环

void *__cdecl JobQueue::WorkLoop(void *a1)
{
  JobQueue *v1; // esi
  int v3; // [esp+0h] [ebp-8h]
  ThreadJobIndex *savedregs; // [esp+8h] [ebp+0h]

  TlsSetValue(dword_1136C0A8, (LPVOID)1);
  v1 = (JobQueue *)*((_DWORD *)a1 + 14);
  if ( *((_BYTE *)v1 + 181) )
    ThreadJobIndex::AllocActiveJobIndex(0, v3);
  JobQueue::ProcessJobs(v1, (struct JobQueue::ThreadInfo *)a1, 0);
  if ( *((_BYTE *)v1 + 181) )
    ThreadJobIndex::FreeActiveJobIndex(savedregs);
  return 0;
}

JobQueue::ProcessJobs负责了循环的主要功能while ( 1 )和后面执行的JobQueue::Exec.

void __thiscall JobQueue::ProcessJobs(JobQueue *this, struct JobQueue::ThreadInfo *a2, void *a3)
{
  JobQueue *v3; // edi
  int v4; // esi
  char *v5; // eax
  int v6; // ebx
  volatile signed __int32 *v7; // esi
  signed __int32 i; // edx
  signed __int32 v9; // eax
  struct AtomicNode *v10; // eax
  int v11; // eax
  double v12; // [esp+4h] [ebp-18h]
  struct JobInfo *v13; // [esp+14h] [ebp-8h]
  int v14; // [esp+18h] [ebp-4h]

  v3 = this;
  if ( BootConfig::Data::HasKey(dword_113E0DC4, dword_113E0DC8) )
  {
    v4 = dword_113E0DCC;
    v5 = (char *)BootConfig::Data::GetValue(dword_113E0DC4, dword_113E0DC8, 0);
    v14 = BootConfig::ParameterParser<int>::Parse(v5, v4);
  }
  else
  {
    v14 = dword_113E0DCC;
  }
  v6 = 0;
  v7 = (volatile signed __int32 *)((char *)v3 + 164);
  _InterlockedExchangeAdd((volatile signed __int32 *)v3 + 41, 1u);
  if ( *((_DWORD *)v3 + 42) != 1 )
  {
    while ( 1 )
    {
      for ( i = *v7; i >> 16 < (unsigned __int16)i; i = v9 )
      {
        v9 = _InterlockedCompareExchange(v7, ((unsigned __int16)i - 1) | (i >> 16 << 16), i);
        if ( i == v9 )
        {
          if ( _InterlockedExchangeAdd((volatile signed __int32 *)v3 + 24, 0xFFFFFFFF) <= 0 )
            Baselib_SystemSemaphore_Acquire(*((HANDLE *)v3 + 25));
          _InterlockedExchangeAdd(v7, 1u);
          break;
        }
      }
      v10 = AtomicStack::Pop(*(AtomicStack **)v3);
      v13 = v10;
      if ( v10 )
        break;
      if ( JobQueue::ExecuteJobFromQueue(v3, 0) )
        goto LABEL_22;
      if ( *((_DWORD *)v3 + 42) == 2 )
        goto LABEL_24;
      if ( *((_DWORD *)v3 + 40) < (unsigned int)(unsigned __int16)*v7 || v6 == v14 )
      {
        _InterlockedExchangeAdd(v7, 0xFFFFFFFF);
        if ( _InterlockedExchangeAdd((volatile signed __int32 *)v3 + 8, 0xFFFFFFFF) <= 0 )
          Baselib_SystemSemaphore_Acquire(*((HANDLE *)v3 + 9));
        _InterlockedExchangeAdd(v7, 1u);
        goto LABEL_22;
      }
      CurrentThread::SleepForSeconds(0, v12);
      ++v6;
LABEL_23:
      if ( *((_DWORD *)v3 + 42) == 1 )
        goto LABEL_24;
    }
    v11 = AtomicList::Tag(*((AtomicList **)v10 + 4));
    JobQueue::Exec(v3, v13, v11 + 1, 1, 0);
LABEL_22:
    v6 = 0;
    goto LABEL_23;
  }
LABEL_24:
  _InterlockedExchangeAdd(v7, 0xFFFFFFFF);c
}

这里看到了熟悉的JobInfo,就要执行上面填装时的回调函数了

JobQueue::CreateJobsForEach   
	*((_QWORD *)Job + 1) = a2;//ForwardJobForEachToManaged
    *((_QWORD *)Job + 2) = a3;
int __thiscall JobQueue::Exec(JobQueue *this, struct JobInfo *a2, int a3, int a4, bool a5)
{
  int v5; // esi
  int v6; // edx
  int v7; // edi
  void (__cdecl *v8)(int, int); // eax
  signed __int32 v9; // edi
  int v10; // ebx
  struct AtomicNode *v11; // eax
  int v12; // ecx
  struct AtomicNode *v13; // edi
  JobQueue *v15; // [esp+8h] [ebp-Ch]
  void (__cdecl *v16)(_DWORD); // [esp+Ch] [ebp-8h]
  signed int v17; // [esp+10h] [ebp-4h]

  v15 = this;
  _InterlockedDecrement((volatile signed __int32 *)this + 40);
  v17 = 0;
  v5 = *((_DWORD *)a2 + 4);
  v6 = *((_DWORD *)a2 + 3);
  v7 = *((_DWORD *)a2 + 2);
  v8 = (void (__cdecl *)(int, int))*((_DWORD *)a2 + 1);
  v16 = *(void (__cdecl **)(_DWORD))(v5 + 16);
  if ( v6 & 0x20000000 )
    v8(v7, v6 & 0x1FFFFFFF);
  else
    ((void (__cdecl *)(int))v8)(v7);//执行ForwardJobForEachToManaged
  v9 = _InterlockedExchangeAdd((volatile signed __int32 *)(v5 + 8), -a4) - a4;
  v10 = v9 & 0x7FFFFFFF;
  if ( v16 && v10 == 1 )
  {
    v16(*(_DWORD *)(v5 + 20));
    v9 = _InterlockedDecrement((volatile signed __int32 *)(v5 + 8));
    v10 = 0;
  }

结束

至此,整个JobSystem的回调流程就完成了,其中细节不胜完整,遇到问题可以再深入研究.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值