Multi-threading: Difference between revisions
Created page with "This page is a work in progress. Put everything related to multi-threading here, threads, synchronization, multi-core support, etc. The Nintendo 3DS offers support for threading..." |
No edit summary |
||
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) === | |||
'''Definition''' | |||
Result GetThreadPriority(s32* priority, Handle thread); | |||
'''asm''' | |||
.global svcGetThreadPriority | |||
.type svcGetThreadPriority, %function | |||
svcGetThreadPriority: | |||
str r0, [sp, #-0x4]! | |||
svc 0x0B | |||
ldr r3, [sp], #4 | |||
str r1, [r3] | |||
bx lr | |||
=== | === SetThreadPriority (svc 0x0C) === | ||
'''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 === |