mirror of https://github.com/zeldaret/botw.git
ksys: Add Task utilities
This commit is contained in:
parent
c5ef0a1fd3
commit
8b7369dffb
|
@ -76,6 +76,16 @@ add_executable(uking
|
|||
|
||||
src/KingSystem/Utils/Thread/Event.cpp
|
||||
src/KingSystem/Utils/Thread/Event.h
|
||||
src/KingSystem/Utils/Thread/Task.cpp
|
||||
src/KingSystem/Utils/Thread/Task.h
|
||||
src/KingSystem/Utils/Thread/TaskQueueBase.cpp
|
||||
src/KingSystem/Utils/Thread/TaskQueueBase.h
|
||||
src/KingSystem/Utils/Thread/TaskQueue.cpp
|
||||
src/KingSystem/Utils/Thread/TaskQueue.h
|
||||
src/KingSystem/Utils/Thread/TaskQueueLock.cpp
|
||||
src/KingSystem/Utils/Thread/TaskQueueLock.h
|
||||
src/KingSystem/Utils/Thread/TaskThread.cpp
|
||||
src/KingSystem/Utils/Thread/TaskThread.h
|
||||
src/KingSystem/Utils/Byaml.cpp
|
||||
src/KingSystem/Utils/Byaml.h
|
||||
src/KingSystem/Utils/ByamlLocal.cpp
|
||||
|
|
|
@ -86769,44 +86769,45 @@
|
|||
0x00000071010c0c5c,Event::setSignal,52,_ZN4ksys4util5Event9setSignalEv
|
||||
0x00000071010c0c90,Event::resetSignal,64,_ZN4ksys4util5Event11resetSignalEv
|
||||
0x00000071010c0ce0,sub_71010C0CE0,32,
|
||||
0x00000071010c0d00,ThreadD0Base::ctor,96,
|
||||
0x00000071010c0d60,sub_71010C0D60,136,
|
||||
0x00000071010c0de8,sub_71010C0DE8,524,
|
||||
0x00000071010c0ff4,sub_71010C0FF4,144,
|
||||
0x00000071010c1084,ThreadD0::init,308,
|
||||
0x00000071010c11b8,ThreadD0::b,156,
|
||||
0x00000071010c1254,ThreadD0::somethingOnDtor,168,
|
||||
0x00000071010c12fc,sub_71010C12FC,8,
|
||||
0x00000071010c1304,ThreadD0::x,228,
|
||||
0x00000071010c13e8,ThreadD0::x_0,188,
|
||||
0x00000071010c1574,ThreadD0::c,212,
|
||||
0x00000071010c1648,ThreadD0::b_0,796,
|
||||
0x00000071010c1964,ThreadD0::stop,192,
|
||||
0x00000071010c1a24,sub_71010C1A24,416,
|
||||
0x00000071010c1bc4,sub_71010C1BC4,52,
|
||||
0x00000071010c1bf8,ThreadD0::a_2,464,
|
||||
0x00000071010c1dc8,ThreadD0::threadPeekAtMessageQueueStuffConditional,48,
|
||||
0x00000071010c1df8,ThreadD0::u,16,
|
||||
0x00000071010c1e08,ThreadD0::a_0,184,
|
||||
0x00000071010c1ec0,ThreadD0::x_4,52,
|
||||
0x00000071010c1ef4,ThreadD0::x_2,72,
|
||||
0x00000071010c1f3c,ThreadD0::x_1,16,
|
||||
0x00000071010c1f4c,ThreadD0::x_3,32,
|
||||
0x00000071010c1f6c,ThreadD0::threadPeekAtMessageQueueStuff,384,
|
||||
0x00000071010c20ec,ThreadD0::a_1,412,
|
||||
0x00000071010c2288,sub_71010C2288,364,
|
||||
0x00000071010c23f4,nullsub_4511,4,
|
||||
0x00000071010c23f8,nullsub_4512,4,
|
||||
0x00000071010c23fc,sub_71010C23FC,860,
|
||||
0x00000071010c2758,sub_71010C2758,112,
|
||||
0x00000071010c27c8,sub_71010C27C8,92,
|
||||
0x00000071010c0d00,ThreadD0Base::ctor,96,_ZN4ksys4util13TaskQueueBaseC1EPN4sead4HeapE
|
||||
0x00000071010c0d60,sub_71010C0D60,136,_ZN4ksys4util13TaskQueueBaseD1Ev
|
||||
0x00000071010c0de8,sub_71010C0DE8,524,_ZN4ksys4util13TaskQueueBase5clearEv
|
||||
0x00000071010c0ff4,sub_71010C0FF4,144,_ZN4ksys4util13TaskQueueBaseD0Ev
|
||||
0x00000071010c1084,ThreadD0::init,308,_ZN4ksys4util13TaskQueueBase4initERKNS1_7InitArgE
|
||||
0x00000071010c11b8,ThreadD0::b,156,_ZN4ksys4util13TaskQueueBase9addThreadEPNS0_10TaskThreadE
|
||||
0x00000071010c1254,ThreadD0::somethingOnDtor,168,_ZN4ksys4util13TaskQueueBase12removeThreadEPNS0_10TaskThreadE
|
||||
0x00000071010c12fc,sub_71010C12FC,8,_ZNK4ksys4util13TaskQueueBase17getNumActiveTasksEv
|
||||
0x00000071010c1304,ThreadD0::x,228,_ZNK4ksys4util13TaskQueueBase16countTasksInLaneEt
|
||||
0x00000071010c13e8,ThreadD0::x_0,188,_ZNK4ksys4util13TaskQueueBase16areNoThreadsBusyEv
|
||||
0x00000071010c14a4,_ZN4ksys4util13TaskQueueBase3x_aEv,0xd0,_ZN4ksys4util13TaskQueueBase19waitForQueueToEmptyEv
|
||||
0x00000071010c1574,ThreadD0::c,212,_ZN4ksys4util13TaskQueueBase18waitForLaneToEmptyEh
|
||||
0x00000071010c1648,ThreadD0::b_0,796,_ZN4ksys4util13TaskQueueBase11cancelTasksEh
|
||||
0x00000071010c1964,ThreadD0::stop,192,_ZNK4ksys4util13TaskQueueBase16isProcessingTaskEh
|
||||
0x00000071010c1a24,sub_71010C1A24,416,_ZN4ksys4util13TaskQueueBase25signalEmptyEventsIfNeededEv
|
||||
0x00000071010c1bc4,sub_71010C1BC4,52,_ZN4ksys4util13TaskQueueBase10blockTasksEh
|
||||
0x00000071010c1bf8,ThreadD0::a_2,464,_ZN4ksys4util13TaskQueueBase26blockTasksAndReloadThreadsEh!
|
||||
0x00000071010c1dc8,ThreadD0::threadPeekAtMessageQueueStuffConditional,48,_ZN4ksys4util13TaskQueueBase12unblockTasksEh
|
||||
0x00000071010c1df8,ThreadD0::u,16,_ZN4ksys4util13TaskQueueBase4lockEPNS0_13TaskQueueLockE
|
||||
0x00000071010c1e08,ThreadD0::a_0,184,_ZNK4ksys4util13TaskQueueBase16getCurrentThreadEv
|
||||
0x00000071010c1ec0,ThreadD0::x_4,52,_ZN4ksys4util13TaskQueueBase16activeTasksBeginEPNS0_13TaskQueueLockE
|
||||
0x00000071010c1ef4,ThreadD0::x_2,72,_ZN4ksys4util13TaskQueueBase22activeTasksRobustBeginEPNS0_13TaskQueueLockE
|
||||
0x00000071010c1f3c,ThreadD0::x_1,16,_ZNK4ksys4util13TaskQueueBase14activeTasksEndEv
|
||||
0x00000071010c1f4c,ThreadD0::x_3,32,_ZNK4ksys4util13TaskQueueBase20activeTasksRobustEndEv
|
||||
0x00000071010c1f6c,ThreadD0::threadPeekAtMessageQueueStuff,384,_ZN4ksys4util13TaskQueueBase24notifyThreadsForNewTasksEv?
|
||||
0x00000071010c20ec,ThreadD0::a_1,412,_ZN4ksys4util13TaskQueueBase4pushERKNS1_7PushArgE?
|
||||
0x00000071010c2288,sub_71010C2288,364,_ZN4ksys4util13TaskQueueBase10removeTaskEPNS0_4TaskEb
|
||||
0x00000071010c23f4,nullsub_4511,4,_ZNK4ksys4util13TaskQueueBase4lockEv
|
||||
0x00000071010c23f8,nullsub_4512,4,_ZNK4ksys4util13TaskQueueBase6unlockEv
|
||||
0x00000071010c23fc,sub_71010C23FC,860,_ZN4ksys4util13TaskQueueBase9fetchTaskEPPNS0_4TaskE?
|
||||
0x00000071010c2758,sub_71010C2758,112,_ZNK4ksys4util13TaskQueueBase27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE
|
||||
0x00000071010c27c8,sub_71010C27C8,92,_ZNK4ksys4util13TaskQueueBase18getRuntimeTypeInfoEv
|
||||
0x00000071010c2824,sub_71010C2824,100,
|
||||
0x00000071010c2888,ctor_1,20,
|
||||
0x00000071010c289c,dtor_1,68,
|
||||
0x00000071010c28e0,sub_71010C28E0,68,
|
||||
0x00000071010c2924,sub_71010C2924,32,
|
||||
0x00000071010c2944,sub_71010C2944,112,
|
||||
0x00000071010c29b4,sub_71010C29B4,92,
|
||||
0x00000071010c2888,ctor_1,20,_ZN4ksys4util13TaskQueueLockC1Ev
|
||||
0x00000071010c289c,dtor_1,68,_ZN4ksys4util13TaskQueueLockD1Ev
|
||||
0x00000071010c28e0,sub_71010C28E0,68,_ZN4ksys4util13TaskQueueLockD0Ev
|
||||
0x00000071010c2924,sub_71010C2924,32,_ZN4ksys4util13TaskQueueLock4lockEPNS0_13TaskQueueBaseE
|
||||
0x00000071010c2944,sub_71010C2944,112,_ZNK4ksys4util13TaskQueueLock27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE
|
||||
0x00000071010c29b4,sub_71010C29B4,92,_ZNK4ksys4util13TaskQueueLock18getRuntimeTypeInfoEv
|
||||
0x00000071010c2a10,sub_71010C2A10,8,
|
||||
0x00000071010c2a18,nullsub_4513,4,
|
||||
0x00000071010c2a1c,sub_71010C2A1C,24,
|
||||
|
@ -90459,34 +90460,35 @@
|
|||
0x00000071011f67b0,sub_71011F67B0,8,
|
||||
0x00000071011f67b8,sub_71011F67B8,204,
|
||||
0x00000071011f6884,sub_71011F6884,92,
|
||||
0x00000071011f68e0,Thread::ctor,108,
|
||||
0x00000071011f694c,Thread::dtor,120,
|
||||
0x00000071011f69c4,Thread::dtorDelete,128,
|
||||
0x00000071011f6a44,Thread::init,224,
|
||||
0x00000071011f6b24,sub_71011F6B24,8,
|
||||
0x00000071011f6b2c,Thread::z,8,
|
||||
0x00000071011f6b34,Thread::x,8,
|
||||
0x00000071011f6b44,sub_71011F6B44,8,
|
||||
0x00000071011f6b4c,Thread::peekMessage,64,
|
||||
0x00000071011f6b8c,Thread::y,12,
|
||||
0x00000071011f6b98,Thread::peekIs1,36,
|
||||
0x00000071011f6bbc,Thread::waitResetSignalAndJam2,128,
|
||||
0x00000071011f6c3c,Thread::waitResetSignalAndJam2AndEvent,156,
|
||||
0x00000071011f6cd8,Thread::waitResetSignalAndJam3,128,
|
||||
0x00000071011f6d58,Thread::jamMessageQueueAndWait,156,
|
||||
0x00000071011f6df4,sub_71011F6DF4,16,
|
||||
0x00000071011f6e04,sub_71011F6E04,12,
|
||||
0x00000071011f6e10,Thread::calc_,624,
|
||||
0x00000071011f7080,nullsub_4674,4,
|
||||
0x00000071011f7084,Thread::peekIs2,36,
|
||||
0x00000071011f70a8,Thread::peekIs3,36,
|
||||
0x00000071011f70cc,Thread::peekIsShutdownMessage,48,
|
||||
0x00000071011f70fc,sub_71011F70FC,76,
|
||||
0x00000071011f7148,Thread::rtti1,112,
|
||||
0x00000071011f71b8,Thread::rtti2,92,
|
||||
0x00000071011f7214,sub_71011F7214,112,
|
||||
0x00000071011f7284,sub_71011F7284,92,
|
||||
0x00000071011f72e0,j__ZdlPv_1226,4,
|
||||
0x00000071011f68e0,Thread::ctor,108,_ZN4ksys4util10TaskThreadC1ERKN4sead14SafeStringBaseIcEEPNS2_4HeapEiNS2_12MessageQueue9BlockTypeElii
|
||||
0x00000071011f694c,Thread::dtor,120,_ZN4ksys4util10TaskThreadD1Ev
|
||||
0x00000071011f69c4,Thread::dtorDelete,128,_ZN4ksys4util10TaskThreadD0Ev
|
||||
0x00000071011f6a44,Thread::init,224,_ZN4ksys4util10TaskThread4initERKNS1_7InitArgE
|
||||
0x00000071011f6b24,sub_71011F6B24,8,_ZNK4ksys4util10TaskThread17getNumActiveTasksEv
|
||||
0x00000071011f6b2c,Thread::z,8,_ZN4ksys4util10TaskThread19waitForQueueToEmptyEv
|
||||
0x00000071011f6b34,Thread::x,8,_ZN4ksys4util10TaskThread11cancelTasksEh
|
||||
0x00000071011f6b44,sub_71011F6B44,8,_ZN4ksys4util10TaskThread4lockEPNS0_13TaskQueueLockE
|
||||
0x00000071011f6b3c,_ZN4ksys4util10TaskThread10clearQueueEv,8,_ZN4ksys4util10TaskThread10clearQueueEv
|
||||
0x00000071011f6b4c,Thread::peekMessage,64,_ZNK4ksys4util10TaskThread33isActiveAndReceivedQueueUpdateMsgEv
|
||||
0x00000071011f6b8c,Thread::y,12,_ZNK4ksys4util10TaskThread8isPausedEv
|
||||
0x00000071011f6b98,Thread::peekIs1,36,_ZNK4ksys4util10TaskThread23receivedQueueUpdatedMsgEv
|
||||
0x00000071011f6bbc,Thread::waitResetSignalAndJam2,128,_ZN4ksys4util10TaskThread5pauseEv
|
||||
0x00000071011f6c3c,Thread::waitResetSignalAndJam2AndEvent,156,_ZN4ksys4util10TaskThread18pauseAndWaitForAckEv
|
||||
0x00000071011f6cd8,Thread::waitResetSignalAndJam3,128,_ZN4ksys4util10TaskThread6resumeEv
|
||||
0x00000071011f6d58,Thread::jamMessageQueueAndWait,156,_ZN4ksys4util10TaskThread19resumeAndWaitForAckEv
|
||||
0x00000071011f6df4,sub_71011F6DF4,16,_ZNK4ksys4util10TaskThread20isBusyProcessingTaskEv
|
||||
0x00000071011f6e04,sub_71011F6E04,12,_ZNK4ksys4util10TaskThread16isLookingForTaskEv
|
||||
0x00000071011f6e10,Thread::calc_,624,_ZN4ksys4util10TaskThread5calc_El?
|
||||
0x00000071011f7080,nullsub_4674,4,_ZN4ksys4util17TaskPostRunResultD2Ev
|
||||
0x00000071011f7084,Thread::peekIs2,36,_ZNK4ksys4util10TaskThread16receivedPauseMsgEv
|
||||
0x00000071011f70a8,Thread::peekIs3,36,_ZNK4ksys4util10TaskThread17receivedResumeMsgEv
|
||||
0x00000071011f70cc,Thread::peekIsShutdownMessage,48,_ZNK4ksys4util10TaskThread15receivedQuitMsgEv
|
||||
0x00000071011f70fc,sub_71011F70FC,76,_ZN4ksys4util10TaskThread17cancelCurrentTaskEv
|
||||
0x00000071011f7148,Thread::rtti1,112,_ZNK4ksys4util10TaskThread27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE
|
||||
0x00000071011f71b8,Thread::rtti2,92,_ZNK4ksys4util10TaskThread18getRuntimeTypeInfoEv
|
||||
0x00000071011f7214,sub_71011F7214,112,_ZNK4ksys4util17TaskPostRunResult27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE
|
||||
0x00000071011f7284,sub_71011F7284,92,_ZNK4ksys4util17TaskPostRunResult18getRuntimeTypeInfoEv
|
||||
0x00000071011f72e0,j__ZdlPv_1226,4,_ZN4ksys4util17TaskPostRunResultD0Ev
|
||||
0x00000071011f72e4,sub_71011F72E4,44,
|
||||
0x00000071011f7310,EventPlusStringDerived::ctor,60,
|
||||
0x00000071011f734c,sub_71011F734C,72,
|
||||
|
@ -90516,57 +90518,57 @@
|
|||
0x00000071011f78cc,sub_71011F78CC,204,
|
||||
0x00000071011f7998,sub_71011F7998,92,
|
||||
0x00000071011f79f4,sub_71011F79F4,140,
|
||||
0x00000071011f7a80,SetMainInvokerClass::ctor,20,
|
||||
0x00000071011f7a94,SetMainInvokerClass::dtor,4,
|
||||
0x00000071011f7a98,j__ZdlPv_1227,4,
|
||||
0x00000071011f7a9c,sub_71011F7A9C,8,
|
||||
0x00000071011f7aa4,EventPlusString::ctor,120,
|
||||
0x00000071011f7b1c,EventPlusString::ctor2,120,
|
||||
0x00000071011f7b94,EventPlusString::dtor,132,
|
||||
0x00000071011f7c18,EventPlusStringDerived::calledFromDtor,112,
|
||||
0x00000071011f7c88,EventPlusString::dtorDelete,140,
|
||||
0x00000071011f7d14,EventPlusString::setMainInvoker,36,
|
||||
0x00000071011f7d38,EventPlusString::submitRequest,544,
|
||||
0x00000071011f7f58,EventPlusString::u,124,
|
||||
0x00000071011f8034,EventPlusString::v,360,
|
||||
0x00000071011f819c,EventPlusString::p,28,
|
||||
0x00000071011f81b8,sub_71011F81B8,28,
|
||||
0x00000071011f81d4,EventPlusString::wait,28,
|
||||
0x00000071011f81f0,EventPlusString::waitTimed,12,
|
||||
0x00000071011f81fc,sub_71011F81FC,8,
|
||||
0x00000071011f8204,EventPlusString::w,40,
|
||||
0x00000071011f822c,EventPlusString::o,40,
|
||||
0x00000071011f8254,sub_71011F8254,12,
|
||||
0x00000071011f8260,sub_71011F8260,8,
|
||||
0x00000071011f8268,sub_71011F8268,48,
|
||||
0x00000071011f8298,sub_71011F8298,12,
|
||||
0x00000071011f82a4,sub_71011F82A4,100,
|
||||
0x00000071011f8308,sub_71011F8308,100,
|
||||
0x00000071011f836c,EventPlusString::return1,8,
|
||||
0x00000071011f8374,EventPlusString::null1,4,
|
||||
0x00000071011f8378,EventPlusString::b,60,
|
||||
0x00000071011f83b4,EventPlusString::null2,4,
|
||||
0x00000071011f83b8,EventPlusString::null2_0,4,
|
||||
0x00000071011f83bc,EventPlusString::null2_1,4,
|
||||
0x00000071011f83c0,EventPlusString::null2_2,4,
|
||||
0x00000071011f83c4,EventPlusString::null2_3,4,
|
||||
0x00000071011f83c8,sub_71011F83C8,8,
|
||||
0x00000071011f83d0,sub_71011F83D0,52,
|
||||
0x00000071011f8404,sub_71011F8404,192,
|
||||
0x00000071011f84c4,nullsub_4686,4,
|
||||
0x00000071011f84c8,nullsub_4687,4,
|
||||
0x00000071011f84cc,sub_71011F84CC,12,
|
||||
0x00000071011f84d8,sub_71011F84D8,8,
|
||||
0x00000071011f84e0,sub_71011F84E0,112,
|
||||
0x00000071011f8550,sub_71011F8550,92,
|
||||
0x00000071011f85ac,EventPlusString::rtti1,112,
|
||||
0x00000071011f861c,EventPlusString::rtti2,92,
|
||||
0x00000071011f8678,sub_71011F8678,112,
|
||||
0x00000071011f86e8,sub_71011F86E8,92,
|
||||
0x00000071011f8744,j__ZdlPv_1228,4,
|
||||
0x00000071011f8748,sub_71011F8748,112,
|
||||
0x00000071011f87b8,sub_71011F87B8,92,
|
||||
0x00000071011f8814,j__ZdlPv_1229,4,
|
||||
0x00000071011f7a80,SetMainInvokerClass::ctor,20,_ZN4ksys4util18TaskDelegateSetterC1Ev
|
||||
0x00000071011f7a94,SetMainInvokerClass::dtor,4,_ZN4ksys4util18TaskDelegateSetterD1Ev
|
||||
0x00000071011f7a98,j__ZdlPv_1227,4,_ZN4ksys4util18TaskDelegateSetterD0Ev
|
||||
0x00000071011f7a9c,sub_71011F7A9C,8,_ZN4ksys4util18TaskDelegateSetter11setDelegateEPN4sead13AnyDelegate1RIPvbEE
|
||||
0x00000071011f7aa4,EventPlusString::ctor,120,_ZN4ksys4util4TaskC1EPN4sead4HeapE
|
||||
0x00000071011f7b1c,EventPlusString::ctor2,120,_ZN4ksys4util4TaskC1EPN4sead4HeapENS2_9IDisposer14HeapNullOptionE
|
||||
0x00000071011f7b94,EventPlusString::dtor,132,_ZN4ksys4util4TaskD1Ev?
|
||||
0x00000071011f7c18,EventPlusStringDerived::calledFromDtor,112,_ZN4ksys4util4Task9finalize_Ev?
|
||||
0x00000071011f7c88,EventPlusString::dtorDelete,140,_ZN4ksys4util4TaskD0Ev?
|
||||
0x00000071011f7d14,EventPlusString::setMainInvoker,36,_ZN4ksys4util4Task11setDelegateERKNS0_18TaskDelegateSetterE
|
||||
0x00000071011f7d38,EventPlusString::submitRequest,544,_ZN4ksys4util4Task13submitRequestERNS0_11TaskRequestE?
|
||||
0x00000071011f7f58,EventPlusString::u,124,_ZNK4ksys4util4Task16canSubmitRequestEv
|
||||
0x00000071011f8034,EventPlusString::v,360,_ZN4ksys4util4Task30processOnCurrentThreadDirectlyEPNS0_10TaskThreadE
|
||||
0x00000071011f819c,EventPlusString::p,28,_ZN4ksys4util4Task15removeFromQueueEv
|
||||
0x00000071011f81b8,sub_71011F81B8,28,_ZN4ksys4util4Task16removeFromQueue2Ev
|
||||
0x00000071011f81d4,EventPlusString::wait,28,_ZN4ksys4util4Task4waitEv
|
||||
0x00000071011f81f0,EventPlusString::waitTimed,12,_ZN4ksys4util4Task4waitERKN4sead8TickSpanE
|
||||
0x00000071011f81fc,sub_71011F81FC,8,_ZNK4ksys4util4Task9getLaneIdEv
|
||||
0x00000071011f8204,EventPlusString::w,40,_ZNK4ksys4util4Task9isSuccessEv
|
||||
0x00000071011f822c,EventPlusString::o,40,_ZNK4ksys4util4Task10isInactiveEv
|
||||
0x00000071011f8254,sub_71011F8254,12,_ZN4ksys4util4Task15setStatusPushedEv
|
||||
0x00000071011f8260,sub_71011F8260,8,_ZN4ksys4util4Task9setThreadEPNS0_10TaskThreadE
|
||||
0x00000071011f8268,sub_71011F8268,48,_ZN4ksys4util4Task3runEv
|
||||
0x00000071011f8298,sub_71011F8298,12,_ZN4ksys4util4Task13onRunFinishedEv
|
||||
0x00000071011f82a4,sub_71011F82A4,100,_ZN4ksys4util4Task21invokePostRunCallbackEPNS0_17TaskPostRunResultE
|
||||
0x00000071011f8308,sub_71011F8308,100,_ZN4ksys4util4Task6finishEv
|
||||
0x00000071011f836c,EventPlusString::return1,8,_ZN4ksys4util4Task14onSetDelegate_ERKNS0_18TaskDelegateSetterE
|
||||
0x00000071011f8374,EventPlusString::null1,4,_ZN4ksys4util4Task8prepare_EPNS0_11TaskRequestE
|
||||
0x00000071011f8378,EventPlusString::b,60,_ZN4ksys4util4Task4run_Ev
|
||||
0x00000071011f83b4,EventPlusString::null2,4,_ZN4ksys4util4Task14onRunFinished_Ev
|
||||
0x00000071011f83b8,EventPlusString::null2_0,4,_ZN4ksys4util4Task9onFinish_Ev
|
||||
0x00000071011f83bc,EventPlusString::null2_1,4,_ZN4ksys4util4Task13onPostFinish_Ev
|
||||
0x00000071011f83c0,EventPlusString::null2_2,4,_ZN4ksys4util4Task10preRemove_Ev
|
||||
0x00000071011f83c4,EventPlusString::null2_3,4,_ZN4ksys4util4Task11postRemove_Ev
|
||||
0x00000071011f83c8,sub_71011F83C8,8,_ZNK4ksys4util4Task11getUserDataEv
|
||||
0x00000071011f83d0,sub_71011F83D0,52,_ZN4ksys4util4Task6cancelEv
|
||||
0x00000071011f8404,sub_71011F8404,192,_ZN4ksys4util4Task8onRemoveEv
|
||||
0x00000071011f84c4,nullsub_4686,4,_ZN4ksys4util25TaskRemoveCallbackContextD2Ev
|
||||
0x00000071011f84c8,nullsub_4687,4,_ZN4ksys4util18TaskPostRunContextD2Ev
|
||||
0x00000071011f84cc,sub_71011F84CC,12,_ZN4ksys4util4Task16setStatusFetchedEv
|
||||
0x00000071011f84d8,sub_71011F84D8,8,_ZN4ksys4util4Task9setLaneIdEh
|
||||
0x00000071011f84e0,sub_71011F84E0,112,_ZNK4ksys4util18TaskDelegateSetter27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE
|
||||
0x00000071011f8550,sub_71011F8550,92,_ZNK4ksys4util18TaskDelegateSetter18getRuntimeTypeInfoEv
|
||||
0x00000071011f85ac,EventPlusString::rtti1,112,_ZNK4ksys4util4Task27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE
|
||||
0x00000071011f861c,EventPlusString::rtti2,92,_ZNK4ksys4util4Task18getRuntimeTypeInfoEv
|
||||
0x00000071011f8678,sub_71011F8678,112,_ZNK4ksys4util25TaskRemoveCallbackContext27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE
|
||||
0x00000071011f86e8,sub_71011F86E8,92,_ZNK4ksys4util25TaskRemoveCallbackContext18getRuntimeTypeInfoEv
|
||||
0x00000071011f8744,j__ZdlPv_1228,4,_ZN4ksys4util25TaskRemoveCallbackContextD0Ev
|
||||
0x00000071011f8748,sub_71011F8748,112,_ZNK4ksys4util18TaskPostRunContext27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE
|
||||
0x00000071011f87b8,sub_71011F87B8,92,_ZNK4ksys4util18TaskPostRunContext18getRuntimeTypeInfoEv
|
||||
0x00000071011f8814,j__ZdlPv_1229,4,_ZN4ksys4util18TaskPostRunContextD0Ev
|
||||
0x00000071011f8818,WorkerThread::ctor,172,
|
||||
0x00000071011f88c4,WorkerThread::dtor,80,
|
||||
0x00000071011f8914,WorkerThread::deleteElements,196,
|
||||
|
@ -94426,14 +94428,14 @@
|
|||
0x00000071012fd5f0,nullsub_4849,4,
|
||||
0x00000071012fd5f4,j__ZdlPv_1323,4,
|
||||
0x00000071012fd5f8,sub_71012FD5F8,532,
|
||||
0x00000071012fd80c,ThreadD0::ctor,60,
|
||||
0x00000071012fd848,sub_71012FD848,52,
|
||||
0x00000071012fd87c,sub_71012FD87C,60,
|
||||
0x00000071012fd8b8,sub_71012FD8B8,8,
|
||||
0x00000071012fd8c0,_ZN4sead15DrawLockContext6unlockEv,8,
|
||||
0x00000071012fd8c8,sub_71012FD8C8,204,
|
||||
0x00000071012fd994,sub_71012FD994,92,
|
||||
0x00000071012fd9f0,sub_71012FD9F0,140,
|
||||
0x00000071012fd80c,ThreadD0::ctor,60,_ZN4ksys4util9TaskQueueC1EPN4sead4HeapE
|
||||
0x00000071012fd848,sub_71012FD848,52,_ZN4ksys4util9TaskQueueD2Ev
|
||||
0x00000071012fd87c,sub_71012FD87C,60,_ZN4ksys4util9TaskQueueD0Ev
|
||||
0x00000071012fd8b8,sub_71012FD8B8,8,_ZNK4ksys4util9TaskQueue4lockEv
|
||||
0x00000071012fd8c0,_ZNK4ksys4util9TaskQueue6unlockEv,8,_ZNK4ksys4util9TaskQueue6unlockEv
|
||||
0x00000071012fd8c8,sub_71012FD8C8,204,_ZNK4ksys4util9TaskQueue27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE
|
||||
0x00000071012fd994,sub_71012FD994,92,_ZNK4ksys4util9TaskQueue18getRuntimeTypeInfoEv
|
||||
0x00000071012fd9f0,sub_71012FD9F0,140,_ZNK4sead15RuntimeTypeInfo6DeriveIN4ksys4util13TaskQueueBaseEE9isDerivedEPKNS0_9InterfaceE
|
||||
0x00000071012fda7c,sub_71012FDA7C,488,
|
||||
0x00000071012fdc64,sub_71012FDC64,528,
|
||||
0x00000071012fde74,sub_71012FDE74,72,
|
||||
|
|
Can't render this file because it is too large.
|
2
lib/sead
2
lib/sead
|
@ -1 +1 @@
|
|||
Subproject commit 9e500212341ace1ecd1d970581d4e37b858bea57
|
||||
Subproject commit 26b88aa2275b7c39d8b0898f0c77567b37ad6c11
|
|
@ -0,0 +1,277 @@
|
|||
#include "KingSystem/Utils/Thread/Task.h"
|
||||
#include <thread/seadThread.h>
|
||||
#include "KingSystem/Utils/Thread/TaskQueueBase.h"
|
||||
#include "KingSystem/Utils/Thread/TaskQueueLock.h"
|
||||
#include "KingSystem/Utils/Thread/TaskThread.h"
|
||||
|
||||
namespace ksys::util {
|
||||
|
||||
TaskDelegateSetter::TaskDelegateSetter() = default;
|
||||
|
||||
TaskDelegateSetter::~TaskDelegateSetter() = default;
|
||||
|
||||
void TaskDelegateSetter::setDelegate(TaskDelegate* delegate) {
|
||||
mDelegate = delegate;
|
||||
}
|
||||
|
||||
Task::Task(sead::Heap* heap) : mEvent(heap, true) {
|
||||
mEvent.setSignal();
|
||||
}
|
||||
|
||||
Task::Task(sead::Heap* heap, sead::IDisposer::HeapNullOption heap_null_option)
|
||||
: mEvent(heap, heap_null_option, true) {
|
||||
mEvent.setSignal();
|
||||
}
|
||||
|
||||
Task::~Task() {
|
||||
finalize_();
|
||||
}
|
||||
|
||||
void Task::deleteDelegate_() {
|
||||
if (mDelegate && mFlags.isOn(Flag::DeleteDelegate) && mFlags.isOff(Flag::DoNotDeleteDelegate)) {
|
||||
delete mDelegate;
|
||||
mDelegate = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// NON_MATCHING: mDelegate2 = nullptr store
|
||||
void Task::finalize_() {
|
||||
if (mStatus == Status::Finalized)
|
||||
return;
|
||||
|
||||
removeFromQueue();
|
||||
deleteDelegate_();
|
||||
mUserData = nullptr;
|
||||
mQueue = nullptr;
|
||||
mPostRunCallback = nullptr;
|
||||
mRemoveCallback = nullptr;
|
||||
mStatus = Status::Finalized;
|
||||
}
|
||||
|
||||
bool Task::setDelegate(const TaskDelegateSetter& setter) {
|
||||
mDelegate = setter.getDelegate();
|
||||
mFlags.reset(Flag::DeleteDelegate);
|
||||
mFlags.reset(Flag::DoNotDeleteDelegate);
|
||||
mFlags.set(Flag::DoNotDeleteDelegate);
|
||||
return onSetDelegate_(setter);
|
||||
}
|
||||
|
||||
// NON_MATCHING: branching
|
||||
bool Task::submitRequest(TaskRequest& request) {
|
||||
// Processing this request is impossible if there is no thread *and* no queue!
|
||||
if (request.mThread == nullptr && request.mQueue == nullptr)
|
||||
return false;
|
||||
|
||||
if (!canSubmitRequest())
|
||||
return false;
|
||||
|
||||
if (request.mSynchronous || request.mHasHandle)
|
||||
mFlags.set(Flag::NeedsToSignalEvent);
|
||||
else
|
||||
mFlags.reset(Flag::NeedsToSignalEvent);
|
||||
|
||||
mFlags.change(Flag::SynchronousRequest, request.mSynchronous);
|
||||
|
||||
if (mListNode.isLinked())
|
||||
return false;
|
||||
|
||||
if (mFlags.isOn(Flag::NeedsToSignalEvent))
|
||||
mEvent.resetSignal();
|
||||
|
||||
mQueue = request.mQueue;
|
||||
if (!mQueue) {
|
||||
mQueue = request.mThread->getTaskQueue();
|
||||
request.mQueue = mQueue;
|
||||
}
|
||||
mUserData = request.mUserData;
|
||||
if (auto* delegate = request.mDelegate) {
|
||||
deleteDelegate_();
|
||||
mFlags.set(Flag::DoNotDeleteDelegate);
|
||||
mDelegate = delegate;
|
||||
}
|
||||
mRemoveCallback = request.mRemoveCallback;
|
||||
mPostRunCallback = request.mPostRunCallback;
|
||||
mName = request.mName;
|
||||
|
||||
prepare_(&request);
|
||||
|
||||
if (request.mSynchronous) {
|
||||
auto* thread = mQueue->getCurrentThread();
|
||||
if (thread) {
|
||||
processOnCurrentThreadDirectly(thread);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
TaskQueueBase::PushArg arg;
|
||||
arg.lane_id = request.mLaneId;
|
||||
arg.task = this;
|
||||
const bool push_ok = mQueue->push(arg);
|
||||
bool b;
|
||||
if (push_ok) {
|
||||
if (request.mSynchronous)
|
||||
mEvent.wait();
|
||||
b = true;
|
||||
} else {
|
||||
b = false;
|
||||
}
|
||||
return push_ok || b;
|
||||
}
|
||||
|
||||
bool Task::canSubmitRequest() const {
|
||||
const bool run_finished_on_current_thread =
|
||||
mThread && mStatus == Status::RunFinished &&
|
||||
mThread == sead::ThreadMgr::instance()->getCurrentThread();
|
||||
const bool cond2 = isInactive();
|
||||
return run_finished_on_current_thread || cond2;
|
||||
}
|
||||
|
||||
void Task::processOnCurrentThreadDirectly(TaskThread* thread) {
|
||||
{
|
||||
TaskQueueLock lock{thread};
|
||||
mThread = thread;
|
||||
mStatus = Status::Pushed;
|
||||
}
|
||||
|
||||
run();
|
||||
|
||||
{
|
||||
TaskQueueLock lock{thread};
|
||||
onRunFinished();
|
||||
}
|
||||
|
||||
TaskPostRunResult result;
|
||||
invokePostRunCallback(&result);
|
||||
|
||||
{
|
||||
TaskQueueLock lock{thread};
|
||||
if (!result.getResult())
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
void Task::removeFromQueue() {
|
||||
if (mQueue)
|
||||
mQueue->removeTask(this, true);
|
||||
}
|
||||
|
||||
void Task::removeFromQueue2() {
|
||||
// TODO: how does this differ from removeFromQueue?
|
||||
removeFromQueue();
|
||||
}
|
||||
|
||||
void Task::run_() {
|
||||
if (mDelegate)
|
||||
mDelegateResult = (*mDelegate)(mUserData);
|
||||
}
|
||||
|
||||
bool Task::wait() {
|
||||
mEvent.wait();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Task::wait(const sead::TickSpan& span) {
|
||||
return mEvent.wait(span);
|
||||
}
|
||||
|
||||
u8 Task::getLaneId() const {
|
||||
return mLaneId;
|
||||
}
|
||||
|
||||
bool Task::isSuccess() const {
|
||||
return (mStatus == Status::PreFinishCallback || mStatus == Status::RunFinished ||
|
||||
mStatus == Status::PostFinishCallback) &&
|
||||
mDelegateResult;
|
||||
}
|
||||
|
||||
bool Task::isInactive() const {
|
||||
return mStatus == Status::Uninitialized || mStatus == Status::RemovedFromQueue ||
|
||||
mStatus == Status::PostFinishCallback;
|
||||
}
|
||||
|
||||
void Task::setStatusPushed() {
|
||||
mStatus = Status::Pushed;
|
||||
}
|
||||
|
||||
void Task::setThread(TaskThread* thread) {
|
||||
mThread = thread;
|
||||
}
|
||||
|
||||
void Task::run() {
|
||||
run_();
|
||||
mStatus = Status::RunFinished;
|
||||
}
|
||||
|
||||
void Task::onRunFinished() {
|
||||
onRunFinished_();
|
||||
}
|
||||
|
||||
void Task::invokePostRunCallback(TaskPostRunResult* result) {
|
||||
mRemoveCallback = nullptr;
|
||||
TaskPostRunContext context;
|
||||
context.mCancelled = mFlags.isOn(Flag::Cancelled);
|
||||
context.mTask = this;
|
||||
context.mUserData = mUserData;
|
||||
if (auto* delegate = mPostRunCallback) {
|
||||
mPostRunCallback = nullptr;
|
||||
delegate->invoke(result, context);
|
||||
}
|
||||
}
|
||||
|
||||
void Task::finish() {
|
||||
mStatus = Status::PreFinishCallback;
|
||||
onFinish_();
|
||||
mStatus = Status::PostFinishCallback;
|
||||
|
||||
mThread = nullptr;
|
||||
signalEvent();
|
||||
|
||||
onPostFinish_();
|
||||
}
|
||||
|
||||
void* Task::getUserData() const {
|
||||
return mUserData;
|
||||
}
|
||||
|
||||
void Task::cancel() {
|
||||
mEvent.resetSignal();
|
||||
mFlags.set(Flag::Cancelled);
|
||||
mFlags.set(Flag::NeedsToSignalEvent);
|
||||
}
|
||||
|
||||
void Task::onRemove() {
|
||||
invokeRemoveCallback_();
|
||||
|
||||
TaskQueueLock lock;
|
||||
mQueue->lock(&lock);
|
||||
|
||||
preRemove_();
|
||||
|
||||
mStatus = Status::RemovedFromQueue;
|
||||
mThread = nullptr;
|
||||
signalEvent();
|
||||
|
||||
postRemove_();
|
||||
}
|
||||
|
||||
void Task::invokeRemoveCallback_() {
|
||||
mPostRunCallback = nullptr;
|
||||
TaskRemoveCallbackContext context;
|
||||
context.mTask = this;
|
||||
context.mUserData = mUserData;
|
||||
|
||||
if (auto* delegate = mRemoveCallback) {
|
||||
mRemoveCallback = nullptr;
|
||||
delegate->invoke(context);
|
||||
}
|
||||
}
|
||||
|
||||
void Task::setStatusFetched() {
|
||||
mStatus = Status::Fetched;
|
||||
}
|
||||
|
||||
void Task::setLaneId(u8 id) {
|
||||
mLaneId = id;
|
||||
}
|
||||
|
||||
} // namespace ksys::util
|
|
@ -0,0 +1,193 @@
|
|||
#pragma once
|
||||
|
||||
#include <basis/seadTypes.h>
|
||||
#include <container/seadListImpl.h>
|
||||
#include <prim/seadDelegate.h>
|
||||
#include <prim/seadRuntimeTypeInfo.h>
|
||||
#include <prim/seadSafeString.h>
|
||||
#include <prim/seadTypedBitFlag.h>
|
||||
#include "KingSystem/Utils/Thread/Event.h"
|
||||
#include "KingSystem/Utils/Types.h"
|
||||
|
||||
namespace ksys::util {
|
||||
|
||||
class Task;
|
||||
class TaskQueueBase;
|
||||
class TaskRequest;
|
||||
class TaskThread;
|
||||
|
||||
class TaskPostRunResult {
|
||||
SEAD_RTTI_BASE(TaskPostRunResult)
|
||||
public:
|
||||
virtual ~TaskPostRunResult() = default;
|
||||
|
||||
bool getResult() const { return mResult; }
|
||||
void setResult(bool result) { mResult = result; }
|
||||
|
||||
private:
|
||||
bool mResult = false;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(TaskPostRunResult, 0x10);
|
||||
|
||||
class TaskPostRunContext {
|
||||
SEAD_RTTI_BASE(TaskPostRunContext)
|
||||
public:
|
||||
virtual ~TaskPostRunContext() = default;
|
||||
|
||||
bool mCancelled;
|
||||
Task* mTask;
|
||||
void* mUserData;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(TaskPostRunContext, 0x20);
|
||||
|
||||
class TaskRemoveCallbackContext {
|
||||
SEAD_RTTI_BASE(TaskRemoveCallbackContext)
|
||||
public:
|
||||
virtual ~TaskRemoveCallbackContext() = default;
|
||||
|
||||
Task* mTask;
|
||||
void* mUserData;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(TaskRemoveCallbackContext, 0x18);
|
||||
|
||||
using TaskDelegate = sead::AnyDelegate1R<void*, bool>;
|
||||
using TaskPostRunCallback = sead::IDelegate2<TaskPostRunResult*, const TaskPostRunContext&>;
|
||||
using TaskRemoveCallback = sead::IDelegate1<const TaskRemoveCallbackContext&>;
|
||||
|
||||
class TaskDelegateSetter {
|
||||
SEAD_RTTI_BASE(TaskDelegateSetter)
|
||||
public:
|
||||
TaskDelegateSetter();
|
||||
explicit TaskDelegateSetter(TaskDelegate* delegate) : TaskDelegateSetter() {
|
||||
setDelegate(delegate);
|
||||
}
|
||||
virtual ~TaskDelegateSetter();
|
||||
TaskDelegate* getDelegate() const { return mDelegate; }
|
||||
void setDelegate(TaskDelegate* delegate);
|
||||
|
||||
private:
|
||||
TaskDelegate* mDelegate = nullptr;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(TaskDelegateSetter, 0x10);
|
||||
|
||||
class TaskRequest {
|
||||
SEAD_RTTI_BASE(TaskRequest)
|
||||
public:
|
||||
virtual ~TaskRequest() = default;
|
||||
|
||||
bool mHasHandle;
|
||||
/// If true, request submissions will block until the request is processed.
|
||||
bool mSynchronous;
|
||||
u8 mLaneId;
|
||||
TaskThread* mThread;
|
||||
TaskQueueBase* mQueue;
|
||||
TaskDelegate* mDelegate;
|
||||
void* mUserData;
|
||||
TaskRemoveCallback* mRemoveCallback;
|
||||
TaskPostRunCallback* mPostRunCallback;
|
||||
sead::SafeString mName;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(TaskRequest, 0x50);
|
||||
|
||||
class Task {
|
||||
SEAD_RTTI_BASE(Task)
|
||||
public:
|
||||
enum class Status {
|
||||
Uninitialized = 0,
|
||||
RemovedFromQueue = 1,
|
||||
Pushed = 2,
|
||||
Fetched = 3,
|
||||
PreFinishCallback = 4,
|
||||
RunFinished = 5,
|
||||
PostFinishCallback = 6,
|
||||
Finalized = 7,
|
||||
};
|
||||
|
||||
explicit Task(sead::Heap* heap);
|
||||
Task(sead::Heap* heap, sead::IDisposer::HeapNullOption heap_null_option);
|
||||
virtual ~Task();
|
||||
|
||||
bool setDelegate(const TaskDelegateSetter& setter);
|
||||
bool submitRequest(TaskRequest& request);
|
||||
bool canSubmitRequest() const;
|
||||
void processOnCurrentThreadDirectly(TaskThread* thread);
|
||||
|
||||
void removeFromQueue();
|
||||
void removeFromQueue2();
|
||||
|
||||
void cancel();
|
||||
bool wait();
|
||||
bool wait(const sead::TickSpan& span);
|
||||
|
||||
Status getStatus() const { return mStatus; }
|
||||
bool isSuccess() const;
|
||||
bool isInactive() const;
|
||||
|
||||
u8 getLaneId() const;
|
||||
void* getUserData() const;
|
||||
TaskQueueBase* getQueue() const { return mQueue; }
|
||||
|
||||
protected:
|
||||
friend class TaskQueueBase;
|
||||
friend class TaskThread;
|
||||
|
||||
enum class Flag {
|
||||
DeleteDelegate = 0x1,
|
||||
DoNotDeleteDelegate = 0x2,
|
||||
NeedsToSignalEvent = 0x4,
|
||||
SynchronousRequest = 0x8,
|
||||
Cancelled = 0x10,
|
||||
};
|
||||
|
||||
static size_t getListNodeOffset() { return offsetof(Task, mListNode); }
|
||||
|
||||
virtual bool onSetDelegate_(const TaskDelegateSetter&) { return true; }
|
||||
virtual void prepare_(TaskRequest* request);
|
||||
virtual void run_();
|
||||
virtual void onRunFinished_() {}
|
||||
virtual void onFinish_() {}
|
||||
virtual void onPostFinish_() {}
|
||||
virtual void preRemove_() {}
|
||||
virtual void postRemove_() {}
|
||||
|
||||
void setLaneId(u8 id);
|
||||
void setThread(TaskThread* thread);
|
||||
void setStatusPushed();
|
||||
void setStatusFetched();
|
||||
void run();
|
||||
void onRunFinished();
|
||||
void invokePostRunCallback(TaskPostRunResult* result);
|
||||
void finish();
|
||||
void onRemove();
|
||||
|
||||
void finalize_();
|
||||
void deleteDelegate_();
|
||||
void invokeRemoveCallback_();
|
||||
|
||||
void signalEvent() {
|
||||
if (!mFlags.isOn(Flag::NeedsToSignalEvent))
|
||||
return;
|
||||
|
||||
mFlags.reset(Flag::Cancelled);
|
||||
mEvent.setSignal();
|
||||
}
|
||||
|
||||
u8 mLaneId = 0;
|
||||
sead::TypedBitFlag<Flag, u8> mFlags = Flag::DoNotDeleteDelegate;
|
||||
bool mDelegateResult = false;
|
||||
TaskDelegate* mDelegate = nullptr;
|
||||
void* mUserData = nullptr;
|
||||
TaskQueueBase* mQueue = nullptr;
|
||||
TaskThread* mThread = nullptr;
|
||||
TaskPostRunCallback* mPostRunCallback = nullptr;
|
||||
TaskRemoveCallback* mRemoveCallback = nullptr;
|
||||
Status mStatus = Status::Uninitialized;
|
||||
sead::ListNode mListNode;
|
||||
Event mEvent;
|
||||
sead::SafeString mName;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(Task, 0xa8);
|
||||
|
||||
inline void Task::prepare_(TaskRequest*) {}
|
||||
|
||||
} // namespace ksys::util
|
|
@ -0,0 +1,7 @@
|
|||
#include "KingSystem/Utils/Thread/TaskQueue.h"
|
||||
|
||||
namespace ksys::util {
|
||||
|
||||
TaskQueue::TaskQueue(sead::Heap* heap) : TaskQueueBase(heap), mCS(heap) {}
|
||||
|
||||
} // namespace ksys::util
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <thread/seadCriticalSection.h>
|
||||
#include "KingSystem/Utils/Thread/TaskQueueBase.h"
|
||||
|
||||
namespace ksys::util {
|
||||
|
||||
class TaskQueue : public TaskQueueBase {
|
||||
SEAD_RTTI_OVERRIDE(TaskQueue, TaskQueueBase)
|
||||
public:
|
||||
explicit TaskQueue(sead::Heap* heap);
|
||||
|
||||
private:
|
||||
void lock() const override { mCS.lock(); }
|
||||
void unlock() const override { mCS.unlock(); }
|
||||
|
||||
mutable sead::CriticalSection mCS;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(TaskQueue, 0xd0);
|
||||
|
||||
} // namespace ksys::util
|
|
@ -0,0 +1,491 @@
|
|||
#include "KingSystem/Utils/Thread/TaskQueueBase.h"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <thread/seadThread.h>
|
||||
#include "KingSystem/Utils/Debug.h"
|
||||
#include "KingSystem/Utils/Thread/Task.h"
|
||||
#include "KingSystem/Utils/Thread/TaskQueueLock.h"
|
||||
#include "KingSystem/Utils/Thread/TaskThread.h"
|
||||
|
||||
namespace ksys::util {
|
||||
|
||||
static const auto cSleepSpan = sead::TickSpan::fromMicroSeconds(10);
|
||||
|
||||
TaskQueueBase::TaskQueueBase(sead::Heap* heap) : mQueueEmptyEvent(heap) {
|
||||
mActiveTasks.initOffset(Task::getListNodeOffset());
|
||||
}
|
||||
|
||||
TaskQueueBase::~TaskQueueBase() {
|
||||
clear();
|
||||
|
||||
for (auto& lane : mLanes) {
|
||||
delete lane.lane_empty_event;
|
||||
lane.lane_empty_event = nullptr;
|
||||
}
|
||||
|
||||
mLanes.freeBuffer();
|
||||
mThreads.freeBuffer();
|
||||
}
|
||||
|
||||
void TaskQueueBase::clear() {
|
||||
lock();
|
||||
|
||||
// Clear all tasks.
|
||||
for (auto it = mActiveTasks.robustBegin(), end = mActiveTasks.robustEnd(); it != end; ++it) {
|
||||
mActiveTasks.erase(std::addressof(*it));
|
||||
it->onRemove();
|
||||
}
|
||||
mActiveTasks.clear();
|
||||
|
||||
for (auto& lane : mLanes)
|
||||
lane.head_task = nullptr;
|
||||
|
||||
const bool is_any_thread_busy = isAnyThreadBusy();
|
||||
|
||||
signalEmptyEventsIfNeeded();
|
||||
{
|
||||
ConditionalScopedLock lock{this};
|
||||
ScopedLock lock1{this};
|
||||
for (auto& thread : mThreads) {
|
||||
thread.cancelCurrentTask();
|
||||
}
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
if (is_any_thread_busy)
|
||||
mQueueEmptyEvent.wait();
|
||||
}
|
||||
|
||||
// NON_MATCHING: swapped operands for a csel. The arg.set_flag1 check looks suspicious.
|
||||
bool TaskQueueBase::init(const InitArg& arg) {
|
||||
if (arg.max_num_threads == 0)
|
||||
return false;
|
||||
|
||||
if (!mThreads.tryAllocBuffer(arg.max_num_threads, arg.heap))
|
||||
return false;
|
||||
|
||||
if (!arg.enable_locks)
|
||||
mFlags.reset(Flag::Lock);
|
||||
else
|
||||
mFlags.set(Flag::Lock);
|
||||
|
||||
if (arg.num_lanes <= 0 || arg.num_lanes > 0x100)
|
||||
return false;
|
||||
|
||||
mLanes.allocBufferAssert(arg.num_lanes, arg.heap);
|
||||
for (auto& lane : mLanes) {
|
||||
lane.lane_empty_event = new (arg.heap) Event(arg.heap, true);
|
||||
lane.lane_empty_event->setSignal();
|
||||
}
|
||||
|
||||
mQueueEmptyEvent.initialize(true);
|
||||
mQueueEmptyEvent.setSignal();
|
||||
mTaskSelectionDelegate = arg.task_selection_delegate;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TaskQueueBase::addThread(TaskThread* thread) {
|
||||
if (mFlags.isOn(Flag::PreventThreadPoolChanges))
|
||||
return false;
|
||||
|
||||
lockIfNeeded();
|
||||
|
||||
if (mThreads.isFull()) {
|
||||
unlockIfNeeded();
|
||||
return false;
|
||||
}
|
||||
|
||||
mThreads.pushBack(thread);
|
||||
unlockIfNeeded();
|
||||
return true;
|
||||
}
|
||||
|
||||
void TaskQueueBase::removeThread(TaskThread* thread) {
|
||||
if (mFlags.isOn(Flag::PreventThreadPoolChanges))
|
||||
return;
|
||||
|
||||
ConditionalScopedLock lock{this};
|
||||
mThreads.erase(mThreads.search(thread));
|
||||
}
|
||||
|
||||
s32 TaskQueueBase::getNumActiveTasks() const {
|
||||
return mActiveTasks.size();
|
||||
}
|
||||
|
||||
s32 TaskQueueBase::countTasksInLane(u16 id) const {
|
||||
lock();
|
||||
|
||||
if (!mLanes[id].head_task) {
|
||||
unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 count = 0;
|
||||
for (auto i = mActiveTasks.begin(mLanes[id].head_task), end = mActiveTasks.end(); i != end;
|
||||
++i) {
|
||||
if (i->getLaneId() != id)
|
||||
break;
|
||||
++count;
|
||||
}
|
||||
unlock();
|
||||
return count;
|
||||
}
|
||||
|
||||
bool TaskQueueBase::areNoThreadsBusy() const {
|
||||
ScopedLock lock{this};
|
||||
|
||||
if (!mActiveTasks.isEmpty())
|
||||
return false;
|
||||
return !isAnyThreadBusy();
|
||||
}
|
||||
|
||||
bool TaskQueueBase::isAnyThreadBusy() const {
|
||||
ConditionalScopedLock lock{this};
|
||||
return std::any_of(mThreads.begin(), mThreads.end(),
|
||||
[](const TaskThread& thread) { return thread.isBusyProcessingTask(); });
|
||||
}
|
||||
|
||||
bool TaskQueueBase::areAllThreadsPaused() const {
|
||||
ConditionalScopedLock lock{this};
|
||||
return std::all_of(mThreads.begin(), mThreads.end(),
|
||||
[](const TaskThread& thread) { return thread.isPaused(); });
|
||||
}
|
||||
|
||||
void TaskQueueBase::waitForQueueToEmpty() {
|
||||
if (areAllThreadsPaused())
|
||||
return;
|
||||
|
||||
for (const auto& lane : mLanes) {
|
||||
if (lane.blocked && lane.head_task)
|
||||
return;
|
||||
}
|
||||
|
||||
mQueueEmptyEvent.wait();
|
||||
}
|
||||
|
||||
void TaskQueueBase::waitForLaneToEmpty(u8 id) {
|
||||
if (areAllThreadsPaused())
|
||||
return;
|
||||
|
||||
const auto& lane = mLanes[id];
|
||||
if (!lane.blocked || !lane.head_task)
|
||||
lane.lane_empty_event->wait();
|
||||
}
|
||||
|
||||
void TaskQueueBase::cancelTasks(u8 id) {
|
||||
lock();
|
||||
|
||||
if (mLanes[id].head_task) {
|
||||
for (auto it = mActiveTasks.robustBegin(mLanes[id].head_task),
|
||||
end = mActiveTasks.robustEnd();
|
||||
it != end; ++it) {
|
||||
if (it->getLaneId() != id)
|
||||
break;
|
||||
mActiveTasks.erase(std::addressof(*it));
|
||||
it->onRemove();
|
||||
}
|
||||
}
|
||||
mLanes[id].head_task = nullptr;
|
||||
|
||||
const auto cancel_current_tasks_if_needed = [&] {
|
||||
ConditionalScopedLock lock{this};
|
||||
ScopedLock lock1{this};
|
||||
for (auto it = mThreads.begin(), end = mThreads.end(); it != end; ++it) {
|
||||
if (it->mTask && it->mTask->getLaneId() == id)
|
||||
it->cancelCurrentTask();
|
||||
}
|
||||
};
|
||||
|
||||
if (isProcessingTask(id)) {
|
||||
mLanes[id].lane_empty_event->resetSignal();
|
||||
signalEmptyEventsIfNeeded();
|
||||
cancel_current_tasks_if_needed();
|
||||
unlock();
|
||||
mLanes[id].lane_empty_event->wait();
|
||||
} else {
|
||||
signalEmptyEventsIfNeeded();
|
||||
cancel_current_tasks_if_needed();
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
bool TaskQueueBase::isProcessingTask(u8 id) const {
|
||||
ConditionalScopedLock lock{this};
|
||||
ScopedLock lock1{this};
|
||||
return std::any_of(mThreads.begin(), mThreads.end(), [id](const TaskThread& thread) {
|
||||
return thread.mTask && thread.mTask->getLaneId() == id;
|
||||
});
|
||||
}
|
||||
|
||||
void TaskQueueBase::signalEmptyEventsIfNeeded() {
|
||||
ScopedLock lock{this};
|
||||
|
||||
const bool is_any_thread_busy = isAnyThreadBusy();
|
||||
const bool has_no_tasks = mActiveTasks.isEmpty();
|
||||
if (!is_any_thread_busy && has_no_tasks)
|
||||
mQueueEmptyEvent.setSignal();
|
||||
|
||||
for (auto it = mLanes.begin(), end = mLanes.end(); it != end; ++it) {
|
||||
if (!isProcessingTask(it.getIndex()) && it->head_task == nullptr)
|
||||
it->lane_empty_event->setSignal();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskQueueBase::blockTasks(u8 id) {
|
||||
if (mLanes[id].blocked != 1)
|
||||
mLanes[id].blocked = true;
|
||||
}
|
||||
|
||||
// NON_MATCHING: the while (!areAllThreadsPaused()) loop generates weird code in the original
|
||||
void TaskQueueBase::blockTasksAndReloadThreads(u8 id) {
|
||||
blockTasks(id);
|
||||
|
||||
{
|
||||
ConditionalScopedLock lock{this};
|
||||
for (auto& thread : mThreads)
|
||||
thread.pause();
|
||||
}
|
||||
|
||||
const auto sleep_duration = sead::TickSpan::fromMilliSeconds(1);
|
||||
|
||||
while (!areAllThreadsPaused())
|
||||
sead::Thread::sleep(sleep_duration);
|
||||
|
||||
sead::Thread::sleep(sleep_duration);
|
||||
|
||||
{
|
||||
ConditionalScopedLock lock{this};
|
||||
for (auto& thread : mThreads)
|
||||
thread.resume();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskQueueBase::unblockTasks(u8 id) {
|
||||
if (mLanes[id].blocked) {
|
||||
mLanes[id].blocked = false;
|
||||
notifyThreadsForNewTasks();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskQueueBase::lock(TaskQueueLock* lock) {
|
||||
lock->lock(this);
|
||||
}
|
||||
|
||||
TaskThread* TaskQueueBase::getCurrentThread() const {
|
||||
const sead::Thread* current_thread = sead::ThreadMgr::instance()->getCurrentThread();
|
||||
lockIfNeeded();
|
||||
for (auto it = mThreads.begin(), end = mThreads.end(); it != end; ++it) {
|
||||
if (current_thread == std::addressof(*it)) {
|
||||
unlockIfNeeded();
|
||||
return std::addressof(*it);
|
||||
}
|
||||
}
|
||||
unlockIfNeeded();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sead::OffsetList<Task>::iterator TaskQueueBase::activeTasksBegin(TaskQueueLock* lock) {
|
||||
lock->lock(this);
|
||||
return mActiveTasks.begin();
|
||||
}
|
||||
|
||||
sead::OffsetList<Task>::robustIterator TaskQueueBase::activeTasksRobustBegin(TaskQueueLock* lock) {
|
||||
lock->lock(this);
|
||||
return mActiveTasks.robustBegin();
|
||||
}
|
||||
|
||||
sead::OffsetList<Task>::iterator TaskQueueBase::activeTasksEnd() const {
|
||||
return mActiveTasks.end();
|
||||
}
|
||||
|
||||
sead::OffsetList<Task>::robustIterator TaskQueueBase::activeTasksRobustEnd() const {
|
||||
return mActiveTasks.robustEnd();
|
||||
}
|
||||
|
||||
void TaskQueueBase::notifyThreadsForNewTasks() {
|
||||
s32 retry_count = 0;
|
||||
const sead::Thread* current_thread = sead::ThreadMgr::instance()->getCurrentThread();
|
||||
sead::BitFlag32 mask = 0;
|
||||
|
||||
while (true) {
|
||||
lockIfNeeded();
|
||||
bool done = true;
|
||||
s32 i = 0;
|
||||
auto* data = mThreads.data();
|
||||
for (auto& thread : mThreads) {
|
||||
static_cast<void>(thread);
|
||||
if (current_thread != data[i] && !data[i]->isLookingForTask() &&
|
||||
!data[i]->receivedQueueUpdatedMsg() && !data[i]->receivedPauseMsg() &&
|
||||
!data[i]->receivedResumeMsg() && !data[i]->receivedQuitMsg() && !mask.isOnBit(i)) {
|
||||
const bool send_ok = data[i]->sendMessage(
|
||||
TaskThread::cMessage_QueueUpdated, sead::MessageQueue::BlockType::NonBlocking);
|
||||
if (send_ok)
|
||||
mask.setBit(i);
|
||||
done &= send_ok;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
unlockIfNeeded();
|
||||
|
||||
if (done)
|
||||
break;
|
||||
|
||||
++retry_count;
|
||||
sead::Thread::sleep(cSleepSpan);
|
||||
}
|
||||
|
||||
if (retry_count >= 2)
|
||||
PrintDebug(sead::FormatFixedSafeString<128>("↓↓↓\nリトライ回数 %d 回\n↑↑↑\n", retry_count));
|
||||
}
|
||||
|
||||
// NON_MATCHING: regalloc for max_idx
|
||||
bool TaskQueueBase::push(const PushArg& arg) {
|
||||
lock();
|
||||
|
||||
if (!arg.task || mActiveTasks.isNodeLinked(arg.task)) {
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto num_tasks = mActiveTasks.size();
|
||||
const u8 max_idx = mLanes.size() - 1;
|
||||
const u8 id = arg.lane_id <= max_idx ? arg.lane_id : max_idx;
|
||||
arg.task->setLaneId(id);
|
||||
|
||||
bool added = false;
|
||||
for (u8 i = id - 1; i != 0xff; --i) {
|
||||
if (!mLanes[i].head_task)
|
||||
continue;
|
||||
|
||||
mActiveTasks.insertBefore(mLanes[i].head_task, arg.task);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!added) {
|
||||
mActiveTasks.pushBack(arg.task);
|
||||
mLanes[id].lane_empty_event->resetSignal();
|
||||
}
|
||||
|
||||
if (!mLanes[id].head_task)
|
||||
mLanes[id].head_task = arg.task;
|
||||
|
||||
arg.task->setStatusPushed();
|
||||
|
||||
if (num_tasks == 0)
|
||||
mQueueEmptyEvent.resetSignal();
|
||||
|
||||
unlock();
|
||||
notifyThreadsForNewTasks();
|
||||
return true;
|
||||
}
|
||||
|
||||
void TaskQueueBase::removeTask(Task* task, bool b) {
|
||||
if (!task)
|
||||
return;
|
||||
|
||||
lock();
|
||||
|
||||
if (!task->isInactive()) {
|
||||
if (task->getStatus() == Task::Status::Pushed) {
|
||||
const u8 id = task->getLaneId();
|
||||
if (mLanes[id].head_task == task) {
|
||||
auto* new_task = mActiveTasks.next(task);
|
||||
if (new_task && task->getLaneId() == new_task->getLaneId())
|
||||
mLanes[id].head_task = new_task;
|
||||
else
|
||||
mLanes[id].head_task = nullptr;
|
||||
}
|
||||
|
||||
mActiveTasks.erase(task);
|
||||
task->onRemove();
|
||||
signalEmptyEventsIfNeeded();
|
||||
} else if (b) {
|
||||
task->cancel();
|
||||
unlock();
|
||||
task->wait();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
// NON_MATCHING: regalloc inside the task lambda + reorderings for the loop counters.
|
||||
void TaskQueueBase::fetchTask(Task** out_task) {
|
||||
lock();
|
||||
|
||||
const auto check_state = [&] {
|
||||
if (!mActiveTasks.isEmpty() || isAnyThreadBusy())
|
||||
return true;
|
||||
mQueueEmptyEvent.setSignal();
|
||||
*out_task = nullptr;
|
||||
unlock();
|
||||
return false;
|
||||
};
|
||||
|
||||
if (!check_state())
|
||||
return;
|
||||
|
||||
auto* task = [&]() -> Task* {
|
||||
for (auto it = mLanes.rbegin(), end = mLanes.rend(); it != end; ++it) {
|
||||
if (it->blocked)
|
||||
continue;
|
||||
if (it->head_task == nullptr)
|
||||
continue;
|
||||
if (!mTaskSelectionDelegate)
|
||||
return it->head_task;
|
||||
|
||||
const auto it_begin = mActiveTasks.begin(it->head_task);
|
||||
Task* end_ptr = nullptr;
|
||||
for (auto it2 = it; it2 != mLanes.rbegin(0);) {
|
||||
// XXX: This looks really weird.
|
||||
auto* t = std::addressof(*it2)[-1].head_task;
|
||||
++it2;
|
||||
if (t) {
|
||||
if (it2->head_task)
|
||||
end_ptr = it2->head_task;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const auto it_end = end_ptr ? mActiveTasks.begin(end_ptr) : mActiveTasks.end();
|
||||
|
||||
TaskSelectionContext context;
|
||||
context.lane_id = it->head_task->getLaneId();
|
||||
context.it_begin = &it_begin;
|
||||
context.it_end = &it_end;
|
||||
Task* task = mTaskSelectionDelegate->invoke(context);
|
||||
if (task)
|
||||
return task;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
if (!check_state())
|
||||
return;
|
||||
|
||||
if (task) {
|
||||
for (u8 id = mLanes.size() - 1; id != 0xff; --id) {
|
||||
if (mLanes[id].head_task == task) {
|
||||
auto* new_task = mActiveTasks.next(task);
|
||||
if (new_task && task->getLaneId() == new_task->getLaneId())
|
||||
mLanes[id].head_task = new_task;
|
||||
else
|
||||
mLanes[id].head_task = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mActiveTasks.erase(task);
|
||||
task->setStatusFetched();
|
||||
*out_task = task;
|
||||
} else {
|
||||
*out_task = nullptr;
|
||||
}
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
} // namespace ksys::util
|
|
@ -0,0 +1,157 @@
|
|||
#pragma once
|
||||
|
||||
#include <basis/seadTypes.h>
|
||||
#include <container/seadBuffer.h>
|
||||
#include <container/seadOffsetList.h>
|
||||
#include <container/seadPtrArray.h>
|
||||
#include <prim/seadDelegate.h>
|
||||
#include <prim/seadRuntimeTypeInfo.h>
|
||||
#include <prim/seadTypedBitFlag.h>
|
||||
#include <time/seadTickSpan.h>
|
||||
#include "KingSystem/Utils/Thread/Event.h"
|
||||
#include "KingSystem/Utils/Types.h"
|
||||
|
||||
namespace ksys::util {
|
||||
|
||||
class Task;
|
||||
class TaskQueueLock;
|
||||
class TaskThread;
|
||||
|
||||
struct TaskSelectionContext {
|
||||
const auto& begin() const { return *it_begin; }
|
||||
const auto& end() const { return *it_end; }
|
||||
u8 lane_id;
|
||||
const sead::OffsetList<Task>::iterator* it_begin;
|
||||
const sead::OffsetList<Task>::iterator* it_end;
|
||||
};
|
||||
|
||||
using TaskSelectionDelegate = sead::IDelegate1R<const TaskSelectionContext&, Task*>;
|
||||
|
||||
class TaskQueueBase {
|
||||
SEAD_RTTI_BASE(TaskQueueBase)
|
||||
public:
|
||||
struct InitArg {
|
||||
bool enable_locks;
|
||||
/// Number of lanes.
|
||||
u16 num_lanes;
|
||||
/// Maximum number of threads that will be processing the queue.
|
||||
u16 max_num_threads;
|
||||
sead::Heap* heap;
|
||||
TaskSelectionDelegate* task_selection_delegate;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(InitArg, 0x18);
|
||||
|
||||
struct PushArg {
|
||||
u8 lane_id;
|
||||
Task* task;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(PushArg, 0x10);
|
||||
|
||||
explicit TaskQueueBase(sead::Heap* heap);
|
||||
virtual ~TaskQueueBase();
|
||||
|
||||
void clear();
|
||||
bool init(const InitArg& arg);
|
||||
|
||||
bool addThread(TaskThread* thread);
|
||||
void removeThread(TaskThread* thread);
|
||||
|
||||
s32 getNumActiveTasks() const;
|
||||
s32 countTasksInLane(u16 id) const;
|
||||
bool areNoThreadsBusy() const;
|
||||
bool isAnyThreadBusy() const;
|
||||
bool areAllThreadsPaused() const;
|
||||
|
||||
void waitForQueueToEmpty();
|
||||
void waitForLaneToEmpty(u8 id);
|
||||
|
||||
void cancelTasks(u8 id);
|
||||
bool isProcessingTask(u8 id) const;
|
||||
void signalEmptyEventsIfNeeded();
|
||||
|
||||
void blockTasks(u8 id);
|
||||
void blockTasksAndReloadThreads(u8 id);
|
||||
void unblockTasks(u8 id);
|
||||
|
||||
void lock(TaskQueueLock* lock);
|
||||
|
||||
/// @returns the current thread if it is in the thread pool and nullptr otherwise.
|
||||
TaskThread* getCurrentThread() const;
|
||||
|
||||
sead::OffsetList<Task>::iterator activeTasksBegin(TaskQueueLock* lock);
|
||||
sead::OffsetList<Task>::robustIterator activeTasksRobustBegin(TaskQueueLock* lock);
|
||||
sead::OffsetList<Task>::iterator activeTasksEnd() const;
|
||||
sead::OffsetList<Task>::robustIterator activeTasksRobustEnd() const;
|
||||
|
||||
bool push(const PushArg& arg);
|
||||
void removeTask(Task* task, bool b);
|
||||
void fetchTask(Task** out_task);
|
||||
|
||||
protected:
|
||||
enum class Flag {
|
||||
Lock = 0x1,
|
||||
PreventThreadPoolChanges = 0x2,
|
||||
};
|
||||
|
||||
struct Lane {
|
||||
/// If true, tasks in this lane are not allowed to be fetched by any thread.
|
||||
bool blocked = false;
|
||||
/// First task in the lane. Tasks are also added to a linked list (mActiveTasks).
|
||||
Task* head_task = nullptr;
|
||||
Event* lane_empty_event = nullptr;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(Lane, 0x18);
|
||||
|
||||
class ScopedLock {
|
||||
public:
|
||||
explicit ScopedLock(const TaskQueueBase* queue) : mQueue(queue) { mQueue->lock(); }
|
||||
ScopedLock(const ScopedLock&) = delete;
|
||||
~ScopedLock() { mQueue->unlock(); }
|
||||
ScopedLock& operator=(const ScopedLock&) = delete;
|
||||
|
||||
private:
|
||||
const TaskQueueBase* mQueue;
|
||||
};
|
||||
|
||||
class ConditionalScopedLock {
|
||||
public:
|
||||
explicit ConditionalScopedLock(const TaskQueueBase* queue) : mQueue(queue) {
|
||||
mQueue->lockIfNeeded();
|
||||
}
|
||||
ConditionalScopedLock(const ConditionalScopedLock&) = delete;
|
||||
~ConditionalScopedLock() { mQueue->unlockIfNeeded(); }
|
||||
ConditionalScopedLock& operator=(const ConditionalScopedLock&) = delete;
|
||||
|
||||
private:
|
||||
const TaskQueueBase* mQueue;
|
||||
};
|
||||
|
||||
friend class TaskQueueLock;
|
||||
|
||||
virtual void lock() const {}
|
||||
virtual void unlock() const {}
|
||||
|
||||
bool shouldLock() const { return mFlags.isOn(Flag::Lock); }
|
||||
|
||||
void lockIfNeeded() const {
|
||||
if (shouldLock())
|
||||
lock();
|
||||
}
|
||||
|
||||
void unlockIfNeeded() const {
|
||||
if (shouldLock())
|
||||
unlock();
|
||||
}
|
||||
|
||||
void notifyThreadsForNewTasks();
|
||||
|
||||
sead::TypedBitFlag<Flag, u8> mFlags;
|
||||
sead::OffsetList<Task> mActiveTasks;
|
||||
sead::Buffer<Lane> mLanes;
|
||||
Event mQueueEmptyEvent;
|
||||
sead::PtrArray<TaskThread> mThreads;
|
||||
TaskSelectionDelegate* mTaskSelectionDelegate = nullptr;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(TaskQueueBase, 0x90);
|
||||
|
||||
} // namespace ksys::util
|
|
@ -0,0 +1,26 @@
|
|||
#include "KingSystem/Utils/Thread/TaskQueueLock.h"
|
||||
#include "KingSystem/Utils/Thread/TaskQueueBase.h"
|
||||
|
||||
namespace ksys::util {
|
||||
|
||||
TaskQueueLock::TaskQueueLock() = default;
|
||||
|
||||
TaskQueueLock::~TaskQueueLock() {
|
||||
unlock();
|
||||
}
|
||||
|
||||
void TaskQueueLock::lock(TaskQueueBase* queue) {
|
||||
if (!mQueue) {
|
||||
mQueue = queue;
|
||||
mQueue->lock();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskQueueLock::unlock() {
|
||||
if (mQueue) {
|
||||
mQueue->unlock();
|
||||
mQueue = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ksys::util
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <prim/seadRuntimeTypeInfo.h>
|
||||
#include "KingSystem/Utils/Thread/TaskThread.h"
|
||||
#include "KingSystem/Utils/Types.h"
|
||||
|
||||
namespace ksys::util {
|
||||
|
||||
class TaskQueueBase;
|
||||
|
||||
class TaskQueueLock {
|
||||
SEAD_RTTI_BASE(TaskQueueLock)
|
||||
public:
|
||||
TaskQueueLock();
|
||||
explicit TaskQueueLock(TaskThread* thread) : TaskQueueLock() { thread->lock(this); }
|
||||
TaskQueueLock(const TaskQueueLock&) = delete;
|
||||
virtual ~TaskQueueLock();
|
||||
TaskQueueLock& operator=(const TaskQueueLock&) = delete;
|
||||
|
||||
void lock(TaskQueueBase* queue);
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
TaskQueueBase* mQueue = nullptr;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(TaskQueueLock, 0x10);
|
||||
|
||||
} // namespace ksys::util
|
|
@ -0,0 +1,261 @@
|
|||
#include "KingSystem/Utils/Thread/TaskThread.h"
|
||||
#include <thread/seadThread.h>
|
||||
#include "KingSystem/Utils/Thread/Task.h"
|
||||
#include "KingSystem/Utils/Thread/TaskQueue.h"
|
||||
#include "KingSystem/Utils/Thread/TaskQueueLock.h"
|
||||
|
||||
namespace ksys::util {
|
||||
|
||||
TaskThread::TaskThread(const sead::SafeString& name, sead::Heap* heap, s32 priority,
|
||||
sead::MessageQueue::BlockType block_type,
|
||||
sead::MessageQueue::Element quit_msg, s32 stack_size, s32 message_queue_size)
|
||||
: Thread(name, heap, priority, block_type, quit_msg, stack_size, message_queue_size),
|
||||
mPauseResumeEvent(heap), mTaskProcessedEvent(heap) {}
|
||||
|
||||
TaskThread::~TaskThread() {
|
||||
if (!mTaskQueue)
|
||||
return;
|
||||
|
||||
mTaskQueue->removeThread(this);
|
||||
if (mFlags.isOff(Flag::DoesNotOwnTaskQueue) && mTaskQueue) {
|
||||
delete mTaskQueue;
|
||||
mTaskQueue = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool TaskThread::init(const TaskThread::InitArg& arg) {
|
||||
if (arg.queue) {
|
||||
mTaskQueue = arg.queue;
|
||||
mFlags.set(Flag::DoesNotOwnTaskQueue);
|
||||
} else {
|
||||
mTaskQueue = new (arg.heap) TaskQueue(arg.heap);
|
||||
TaskQueueBase::InitArg queue_arg;
|
||||
queue_arg.enable_locks = false;
|
||||
queue_arg.task_selection_delegate = nullptr;
|
||||
queue_arg.heap = arg.heap;
|
||||
queue_arg.num_lanes = arg.num_lanes;
|
||||
queue_arg.max_num_threads = 1;
|
||||
mTaskQueue->init(queue_arg);
|
||||
}
|
||||
|
||||
mTaskQueue->addThread(this);
|
||||
|
||||
mPauseResumeEvent.initialize(true);
|
||||
mPauseResumeEvent.setSignal();
|
||||
|
||||
mTaskProcessedEvent.initialize(true);
|
||||
mTaskProcessedEvent.setSignal();
|
||||
|
||||
mBatchSize = arg.batch_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
s32 TaskThread::getNumActiveTasks() const {
|
||||
return mTaskQueue->getNumActiveTasks();
|
||||
}
|
||||
|
||||
void TaskThread::waitForQueueToEmpty() {
|
||||
mTaskQueue->waitForQueueToEmpty();
|
||||
}
|
||||
|
||||
void TaskThread::cancelTasks(u8 id) {
|
||||
mTaskQueue->cancelTasks(id);
|
||||
}
|
||||
|
||||
void TaskThread::clearQueue() {
|
||||
mTaskQueue->clear();
|
||||
}
|
||||
|
||||
void TaskThread::lock(TaskQueueLock* lock) {
|
||||
mTaskQueue->lock(lock);
|
||||
}
|
||||
|
||||
bool TaskThread::isActiveAndReceivedQueueUpdateMsg() const {
|
||||
if (mFlags.isOn(Flag::Paused))
|
||||
return false;
|
||||
if (mFlags.isOn(Flag::IsActive))
|
||||
return true;
|
||||
return receivedQueueUpdatedMsg();
|
||||
}
|
||||
|
||||
bool TaskThread::isPaused() const {
|
||||
return mFlags.isOn(Flag::Paused);
|
||||
}
|
||||
|
||||
bool TaskThread::receivedQueueUpdatedMsg() const {
|
||||
return mMessageQueue.peek(sead::MessageQueue::BlockType::NonBlocking) == cMessage_QueueUpdated;
|
||||
}
|
||||
|
||||
void TaskThread::pause() {
|
||||
if (sead::ThreadMgr::instance()->getCurrentThread() == this)
|
||||
return;
|
||||
|
||||
if (!mPauseResumeMsg.compareExchange(cMessage_Resume, cMessage_Pause))
|
||||
return;
|
||||
|
||||
mPauseResumeEvent.wait();
|
||||
mPauseResumeEvent.resetSignal();
|
||||
mMessageQueue.jam(cMessage_Pause, sead::MessageQueue::BlockType::Blocking);
|
||||
}
|
||||
|
||||
void TaskThread::pauseAndWaitForAck() {
|
||||
if (sead::ThreadMgr::instance()->getCurrentThread() == this)
|
||||
return;
|
||||
|
||||
pause();
|
||||
mPauseResumeEvent.wait();
|
||||
}
|
||||
|
||||
void TaskThread::resume() {
|
||||
if (sead::ThreadMgr::instance()->getCurrentThread() == this)
|
||||
return;
|
||||
|
||||
if (!mPauseResumeMsg.compareExchange(cMessage_Pause, cMessage_Resume))
|
||||
return;
|
||||
|
||||
mPauseResumeEvent.wait();
|
||||
mPauseResumeEvent.resetSignal();
|
||||
mMessageQueue.jam(cMessage_Resume, sead::MessageQueue::BlockType::Blocking);
|
||||
}
|
||||
|
||||
void TaskThread::resumeAndWaitForAck() {
|
||||
if (sead::ThreadMgr::instance()->getCurrentThread() == this)
|
||||
return;
|
||||
|
||||
resume();
|
||||
mPauseResumeEvent.wait();
|
||||
}
|
||||
|
||||
bool TaskThread::isBusyProcessingTask() const {
|
||||
return mFlags.isOn(Flag::IsBusyProcessingTask);
|
||||
}
|
||||
|
||||
bool TaskThread::isLookingForTask() const {
|
||||
return mFlags.isOn(Flag::IsLookingForTask);
|
||||
}
|
||||
|
||||
// NON_MATCHING: branching for `if (mTaskQueue->getNumActiveTasks() == 0)`:
|
||||
// Clang got rid of the branch and merged the two mFlags writes
|
||||
void TaskThread::calc_(sead::MessageQueue::Element msg) {
|
||||
if (mFlags.isOn(Flag::Paused)) {
|
||||
if (msg != cMessage_Resume)
|
||||
return;
|
||||
mFlags.reset(Flag::Paused);
|
||||
mPauseResumeEvent.setSignal();
|
||||
}
|
||||
|
||||
if (msg == cMessage_Pause) {
|
||||
mPauseResumeEvent.setSignal();
|
||||
mFlags.set(Flag::Paused);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mBatchSize >= 1)
|
||||
mNumRemainingTasksInBatch = mBatchSize;
|
||||
|
||||
while (true) {
|
||||
{
|
||||
TaskQueueLock lock{this};
|
||||
const auto latest_msg = mMessageQueue.peek(sead::MessageQueue::BlockType::NonBlocking);
|
||||
|
||||
if (latest_msg == mQuitMsg) {
|
||||
mFlags.reset(Flag::IsActive);
|
||||
mFlags.reset(Flag::IsLookingForTask);
|
||||
break;
|
||||
}
|
||||
|
||||
if (latest_msg == cMessage_Pause) {
|
||||
mFlags.reset(Flag::IsActive);
|
||||
mFlags.reset(Flag::IsLookingForTask);
|
||||
break;
|
||||
}
|
||||
|
||||
mFlags.set(Flag::IsActive);
|
||||
|
||||
mFlags.set(Flag::IsLookingForTask);
|
||||
mTaskQueue->fetchTask(&mTask);
|
||||
mFlags.reset(Flag::IsLookingForTask);
|
||||
|
||||
if (mTask == nullptr) {
|
||||
mFlags.reset(Flag::IsActive);
|
||||
mFlags.reset(Flag::IsLookingForTask);
|
||||
mTaskQueue->signalEmptyEventsIfNeeded();
|
||||
break;
|
||||
}
|
||||
|
||||
mFlags.reset(Flag::IsLookingForTask);
|
||||
|
||||
mFlags.set(Flag::IsBusyProcessingTask);
|
||||
mTask->setThread(this);
|
||||
}
|
||||
|
||||
mTask->run();
|
||||
|
||||
Task* task;
|
||||
{
|
||||
TaskQueueLock lock{this};
|
||||
mTask->onRunFinished();
|
||||
task = mTask;
|
||||
mTask = nullptr;
|
||||
}
|
||||
|
||||
if (task) {
|
||||
TaskPostRunResult result;
|
||||
task->invokePostRunCallback(&result);
|
||||
|
||||
TaskQueueLock lock{this};
|
||||
if (!result.getResult())
|
||||
task->finish();
|
||||
|
||||
mFlags.reset(Flag::IsBusyProcessingTask);
|
||||
mTaskProcessedEvent.setSignal();
|
||||
mTaskQueue->signalEmptyEventsIfNeeded();
|
||||
|
||||
if (mTaskQueue->getNumActiveTasks() == 0) {
|
||||
mFlags.reset(Flag::IsActive);
|
||||
#ifdef MATCHING_HACK_NX_CLANG
|
||||
// To make it easier to see what this function is functionally equivalent.
|
||||
// Does not fix the matching issue, but turns it into a 2-line reordering.
|
||||
asm("" ::: "memory");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
mFlags.set(Flag::IsLookingForTask);
|
||||
}
|
||||
|
||||
if (mBatchSize <= 0)
|
||||
continue;
|
||||
|
||||
--mNumRemainingTasksInBatch;
|
||||
if (mNumRemainingTasksInBatch == 0) {
|
||||
sead::Thread::yield();
|
||||
mNumRemainingTasksInBatch = mBatchSize;
|
||||
}
|
||||
}
|
||||
|
||||
mFlags.reset(Flag::IsActive);
|
||||
mFlags.reset(Flag::IsLookingForTask);
|
||||
}
|
||||
|
||||
bool TaskThread::receivedPauseMsg() const {
|
||||
return mMessageQueue.peek(sead::MessageQueue::BlockType::NonBlocking) == cMessage_Pause;
|
||||
}
|
||||
|
||||
bool TaskThread::receivedResumeMsg() const {
|
||||
return mMessageQueue.peek(sead::MessageQueue::BlockType::NonBlocking) == cMessage_Resume;
|
||||
}
|
||||
|
||||
bool TaskThread::receivedQuitMsg() const {
|
||||
const auto msg = static_cast<s32>(mQuitMsg);
|
||||
return mMessageQueue.peek(sead::MessageQueue::BlockType::NonBlocking) == msg;
|
||||
}
|
||||
|
||||
void TaskThread::cancelCurrentTask() {
|
||||
TaskQueueLock lock{this};
|
||||
if (mTask)
|
||||
mTask->cancel();
|
||||
}
|
||||
|
||||
} // namespace ksys::util
|
|
@ -0,0 +1,109 @@
|
|||
#pragma once
|
||||
|
||||
#include <prim/seadRuntimeTypeInfo.h>
|
||||
#include <prim/seadTypedBitFlag.h>
|
||||
#include <thread/seadAtomic.h>
|
||||
#include <thread/seadThread.h>
|
||||
#include "KingSystem/Utils/Thread/Event.h"
|
||||
#include "KingSystem/Utils/Types.h"
|
||||
|
||||
namespace ksys::util {
|
||||
|
||||
class Task;
|
||||
class TaskQueueBase;
|
||||
class TaskQueueLock;
|
||||
|
||||
class TaskThread : public sead::Thread {
|
||||
SEAD_RTTI_BASE(TaskThread)
|
||||
public:
|
||||
enum Message {
|
||||
cMessage_QueueUpdated = 1,
|
||||
cMessage_Pause = 2,
|
||||
cMessage_Resume = 3,
|
||||
};
|
||||
|
||||
struct InitArg {
|
||||
/// Number of lanes if a new queue is to be created.
|
||||
/// Only used if queue is nullptr.
|
||||
u16 num_lanes;
|
||||
/// Number of tasks to process in a row before yielding. Can be zero to disable the limit.
|
||||
u32 batch_size;
|
||||
/// Heap that will be used to allocate a new queue if necessary.
|
||||
/// Only used if queue is nullptr.
|
||||
sead::Heap* heap;
|
||||
/// Task queue. If null, a new queue will be created and owned by this thread.
|
||||
TaskQueueBase* queue;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(InitArg, 0x18);
|
||||
|
||||
TaskThread(const sead::SafeString& name, sead::Heap* heap, s32 priority,
|
||||
sead::MessageQueue::BlockType block_type, sead::MessageQueue::Element quit_msg,
|
||||
s32 stack_size, s32 message_queue_size);
|
||||
~TaskThread() override;
|
||||
|
||||
bool init(const InitArg& arg);
|
||||
|
||||
s32 getNumActiveTasks() const;
|
||||
|
||||
void waitForQueueToEmpty();
|
||||
void cancelTasks(u8 id);
|
||||
void clearQueue();
|
||||
void lock(TaskQueueLock* lock);
|
||||
|
||||
bool isActiveAndReceivedQueueUpdateMsg() const;
|
||||
bool isPaused() const;
|
||||
bool receivedQueueUpdatedMsg() const;
|
||||
|
||||
void pause();
|
||||
void pauseAndWaitForAck();
|
||||
void resume();
|
||||
void resumeAndWaitForAck();
|
||||
|
||||
bool isBusyProcessingTask() const;
|
||||
bool isLookingForTask() const;
|
||||
|
||||
bool receivedPauseMsg() const;
|
||||
bool receivedResumeMsg() const;
|
||||
bool receivedQuitMsg() const;
|
||||
|
||||
void cancelCurrentTask();
|
||||
|
||||
TaskQueueBase* getTaskQueue() const { return mTaskQueue; }
|
||||
|
||||
protected:
|
||||
friend class TaskQueueBase;
|
||||
|
||||
enum class Flag {
|
||||
_1 = 0x1,
|
||||
_2 = 0x2,
|
||||
_4 = 0x4,
|
||||
Paused = 0x8,
|
||||
/// The task queue is *not* owned by this TaskThread.
|
||||
DoesNotOwnTaskQueue = 0x10,
|
||||
/// A batch is being processed.
|
||||
IsActive = 0x20,
|
||||
/// This thread is looking for a task to process.
|
||||
IsLookingForTask = 0x40,
|
||||
/// A task is being processed.
|
||||
IsBusyProcessingTask = 0x80,
|
||||
};
|
||||
|
||||
void calc_(sead::MessageQueue::Element msg) override;
|
||||
|
||||
sead::TypedBitFlag<Flag, u8> mFlags = [] {
|
||||
decltype(mFlags) flags;
|
||||
flags.set(Flag::_2);
|
||||
flags.set(Flag::_4);
|
||||
return flags;
|
||||
}();
|
||||
Task* mTask = nullptr;
|
||||
s32 mBatchSize = 0;
|
||||
s32 mNumRemainingTasksInBatch = 0;
|
||||
sead::Atomic<Message> mPauseResumeMsg = cMessage_Resume;
|
||||
Event mPauseResumeEvent;
|
||||
Event mTaskProcessedEvent;
|
||||
TaskQueueBase* mTaskQueue = nullptr;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(TaskThread, 0x1a0);
|
||||
|
||||
} // namespace ksys::util
|
Loading…
Reference in New Issue