Difference between revisions of "Multi-threading"
(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...") |
|||
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 === |
Revision as of 20:20, 25 May 2015
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 through use of SVC calls.
Threads
For Kernel implementation details, see KThread.
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
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 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
Definition
Result OpenThread(Handle* thread, Handle process, u32 threadId);
GetProcessIdOfThread
Definition
Result GetProcessIdOfThread(u32* processId, Handle thread);
GetThreadId
Definition
Result GetThreadId(u32* threadId, Handle thread);
GetThreadInfo
Definition
Result GetThreadInfo(s64* out, Handle thread, ThreadInfoType type);
ThreadInfoType value | Description |
---|---|
? | ? |
GetThreadContext
Definition
Result GetThreadContext(ThreadContext* context, Handle thread);
Details Stubbed?
Core affinity
GetThreadAffinityMask
Definition
Result GetThreadAffinityMask(u8* affinitymask, Handle thread, s32 processorcount);
SetThreadAffinityMask
Definition
Result SetThreadAffinityMask(Handle thread, u8* affinitymask, s32 processorcount);
GetThreadIdealProcessor
Definition
Result GetThreadIdealProcessor(s32* processorid, Handle thread);
SetThreadIdealProcessor
Debug
GetThreadList
GetDebugThreadContext
SetDebugThreadContext
GetDebugThreadParam
Synchronization
Most synchronization systems seem to have both a "normal" and "light-weight" version
Mutex (normal)
For Kernel implementation details, see KMutex
CreateMutex
/!\ It seems that the mutex will not be available once the thread that created it is destroyed
ReleaseMutex
Ciritical Section (light-weight mutex)
CriticalSection::Initialize
Same thread ownership as a mutex ?
CriticalSection::Enter
CriticalSection::Leave
Semaphore
Light Semaphore ?
Does it exist ?