Line 8: |
Line 8: |
| | | |
| Though it is possible to run multi-threaded programs, running those on different cores is not possible "as-is". One core is always dedicated to the OS, hence you will never get 100% of both cores. | | Though it is possible to run multi-threaded programs, running those on different cores is not possible "as-is". One core is always dedicated to the OS, hence you will never get 100% of both cores. |
| + | |
| + | Using CloseHandle() with a KThread handle will terminate the specified thread, only if the reference count reaches 0. |
| + | |
| + | Lower priority values give the thread higher priority. For userland apps, priorities between 0x18 and 0x3F are allowed. The priority of the app's main thread seems to be 0x30. |
| + | |
| + | The thread scheduler is cooperative, therefore if a thread takes up all the CPU time (for example if it enters an endless loop), all the other threads that run on the same CPU core won't get a chance to run. The main way of yielding another thread is using an address arbiter. |
| | | |
| == Usage == | | == Usage == |
| | | |
− | === CreateThread === | + | === CreateThread (svc 0x08) === |
| + | |
| + | '''Definition''' |
| + | Result CreateThread(Handle* thread, func entrypoint, u32 arg, u32 stacktop, s32 threadpriority, s32 processorid); |
| + | |
| + | '''Configuration''' |
| + | R0=s32 threadpriority |
| + | R1=func entrypoint |
| + | R2=u32 arg |
| + | R3=u32 stacktop |
| + | R4=s32 processorid |
| + | |
| + | Result result=R0 |
| + | Handle* thread=R1 |
| + | |
| + | '''Details''' |
| + | The processorid parameter specifies which processor the thread can run on. Non-negative values correspond to a specific CPU. (e.g. 0 for the Appcore and 1 for the Syscore on Old3DS) Special value -1 means all CPUs, and -2 means the default CPU for the process (Read from the [[NCCH/Extended Header|Exheader]], usually 0 for applications, 1 for system services). Games usually create threads using -2. |
| + | |
| + | With the Old3DS kernel, the s32 processorid must be <=2. |
| + | |
| + | With the New3DS kernel: processorid must be <= <total cores(MPCore "SCU Configuration Register" CPU number value + 1)>. When processorid==0x2 and the process is not an APPLICATION mem-region process, exheader kernel-flags bitmask 0x2000 must be set otherwise error 0xD9001BEA is returned. When processorid==0x3 and the process is not an APPLICATION mem-region process, error 0xD9001BEA is returned. These are the only restriction checks done by the kernel for processorid. |
| + | |
| + | The thread priority value must be in the following range: 0x0..0x3F. |
| + | |
| + | The stacktop must be aligned to 0x8-bytes, otherwise when not aligned to 0x8-bytes the ARM11 kernel clears the low 3-bits of the stacktop address. |
| + | |
| + | The input address used for Entrypoint_Param and StackTop are normally the same, however these can be arbitrary. For the main thread the Entrypoint_Param is value 0. |
| + | |
| + | === ExitThread (svc 0x09)=== |
| + | |
| + | '''Definition''' |
| + | void ExitThread(void); |
| + | |
| + | === SleepThread (svc 0x0A)=== |
| + | |
| + | '''Definition''' |
| + | void SleepThread(s64 nanoseconds); |
| + | |
| + | === GetThreadPriority (svc 0x0B) === |
| | | |
− | === ExitThread ===
| + | '''Definition''' |
| + | Result GetThreadPriority(s32* priority, Handle thread); |
| | | |
− | === SleepThread ===
| + | '''asm''' |
| + | .global svcGetThreadPriority |
| + | .type svcGetThreadPriority, %function |
| + | svcGetThreadPriority: |
| + | str r0, [sp, #-0x4]! |
| + | svc 0x0B |
| + | ldr r3, [sp], #4 |
| + | str r1, [r3] |
| + | bx lr |
| | | |
− | === GetThreadPriority === | + | === SetThreadPriority (svc 0x0C) === |
| | | |
− | === SetThreadPriority ===
| + | '''Definition''' |
| + | Result SetThreadPriority(Handle thread, s32 priority); |
| | | |
| === OpenThread === | | === OpenThread === |
| + | |
| + | '''Definition''' |
| + | Result OpenThread(Handle* thread, Handle process, u32 threadId); |
| | | |
| === GetProcessIdOfThread === | | === GetProcessIdOfThread === |
| + | |
| + | '''Definition''' |
| + | Result GetProcessIdOfThread(u32* processId, Handle thread); |
| | | |
| === GetThreadId === | | === GetThreadId === |
| + | |
| + | '''Definition''' |
| + | Result GetThreadId(u32* threadId, Handle thread); |
| | | |
| === GetThreadInfo === | | === GetThreadInfo === |
| + | |
| + | '''Definition''' |
| + | Result GetThreadInfo(s64* out, Handle thread, ThreadInfoType type); |
| + | |
| + | {| class="wikitable" border="1" |
| + | ! ThreadInfoType value |
| + | ! Description |
| + | |- |
| + | | ? |
| + | | ? |
| + | |} |
| | | |
| === GetThreadContext === | | === GetThreadContext === |
| + | |
| + | '''Definition''' |
| + | Result GetThreadContext(ThreadContext* context, Handle thread); |
| + | |
| + | '''Details''' |
| + | Stubbed? |
| | | |
| == Core affinity == | | == Core affinity == |
| | | |
| === GetThreadAffinityMask === | | === GetThreadAffinityMask === |
| + | |
| + | '''Definition''' |
| + | Result GetThreadAffinityMask(u8* affinitymask, Handle thread, s32 processorcount); |
| | | |
| === SetThreadAffinityMask === | | === SetThreadAffinityMask === |
| + | |
| + | '''Definition''' |
| + | Result SetThreadAffinityMask(Handle thread, u8* affinitymask, s32 processorcount); |
| | | |
| === GetThreadIdealProcessor === | | === GetThreadIdealProcessor === |
| + | |
| + | '''Definition''' |
| + | Result GetThreadIdealProcessor(s32* processorid, Handle thread); |
| | | |
| === SetThreadIdealProcessor === | | === SetThreadIdealProcessor === |
Line 56: |
Line 145: |
| | | |
| == Mutex (normal) == | | == Mutex (normal) == |
| + | |
| + | For Kernel implementation details, see [[KMutex]] |
| | | |
| === CreateMutex === | | === CreateMutex === |