Multi-threading: Difference between revisions

Neobrain (talk | contribs)
Kynex7510 (talk | contribs)
m Fake news
 
(19 intermediate revisions by 7 users not shown)
Line 1: Line 1:
This page is a work in progress. Put everything related to multi-threading here, threads, synchronization, multi-core support, etc.
This page documents all kernel functionality for managing multiple processes and threads as well as handling synchronization between them.
 
The Nintendo 3DS offers support for threading through use of [[SVC]] calls.


= Processes =
= Processes =
Line 11: Line 9:
Each process has a separate handle-table, the size of which is stored in the kernel capability descriptor. The handles in a handle-table can't be used in the context of other processes, since those handles don't exist in other handle-tables.
Each process has a separate handle-table, the size of which is stored in the kernel capability descriptor. The handles in a handle-table can't be used in the context of other processes, since those handles don't exist in other handle-tables.


0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for the current KThread.
0xFFFF8001 is a handle alias for the current process.


Calling svcBreak on retail will only terminate the process which called this SVC.
Calling svcBreak on retail will only terminate the process which called this SVC.
Line 100: Line 98:
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.
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.
The [[Glossary#appcore|appcore]] thread scheduler, in typical real-time operating system fashion, implements a simple preemptive algorithm based around multiple thread priority levels. This algorithm guarantees that the currently executing thread is always the highest priority runnable thread (also known as SCHED_FIFO). In other words, a thread will be interrupted (preempted) if and only if a higher priority thread is woken up, by means of an event (i.e. svcSendSyncRequest) or similar. Contrary to typical desktop operating systems, no timeslice-based scheduling is performed, which means that if a thread uses up all available CPU time (for example if it enters an endless loop), all other threads with equal or lower priority that run on the same CPU core won't get a chance to run. Yielding to other threads is otherwise done by means of synchronization primitives (thread sleep, mutex, address arbiter, etc.). Address arbiters can be used to implement process-local synchronization primitives.
 
0xFFFF8000 is a handle alias for the currently active thread.


== Usage ==
== Usage ==
Line 128: Line 128:
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 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 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.
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. On New3DS, IDs 2 and 3 are also valid, referring to the 2 additional CPU cores) 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.


The thread priority value must be in the range 0x0..0x3F. Otherwise, error 0xE0E01BFD is returned.
The thread priority value must be in the range 0x0..0x3F. Otherwise, error 0xE0E01BFD is returned.
Line 139: Line 139:
'''Signature'''
'''Signature'''
  void ExitThread(void);
  void ExitThread(void);
'''Details'''
Makes the currently running thread exit. When a thread exits, all mutex objects it owns are released and made available to other threads.


=== SleepThread  ===
=== SleepThread  ===
Line 185: Line 189:
'''Signature'''
'''Signature'''
  Result GetThreadId(u32* threadId, Handle thread);
  Result GetThreadId(u32* threadId, Handle thread);
'''Details'''
It seems that only the thread itself or one of its parent can get the ID. Calling this on the handle of a sibling or parent seems to always yield the ID 0.


=== GetThreadInfo ===
=== GetThreadInfo ===
Line 263: Line 264:
Synchronization can be performed via WaitSynchronization on any handles deriving from [[KSynchronizationObject]]. The semantic meaning of the call depends on the particular object type referred to by the given handle:
Synchronization can be performed via WaitSynchronization on any handles deriving from [[KSynchronizationObject]]. The semantic meaning of the call depends on the particular object type referred to by the given handle:


* KClientPort: ???
* KClientPort: Wakes if max sessions not reached (free session available)
* KClientSession: ???
* KClientSession: Always false?
* KDebug: ???
* KDebug: Waits until a debug event is signaled (the user should then use svcGetProcessDebugEvent to get the debug event info)
* KDmaObject: ???
* KDmaObject: ???
* KEvent: Waits until the event is signaled
* KEvent: Waits until the event is signaled
* KInterruptEvent: ???
* KMutex: Acquires a lock on the mutex (blocks until this succeeds)
* KMutex: Acquires a lock on the mutex (blocks until this succeeds)
* KProcess: Waits until the process exits
* KProcess: Waits until the process exits/is terminated
* KSemaphore: This consumes a value from the semaphore count, if possible, otherwise continues to wait
* KSemaphore: This consumes a value from the semaphore count, if possible, otherwise continues to wait
* KServerPort: Waits for a new client connection, upon which svcAcceptSession is ready to be called
* KServerPort: Waits for a new client connection, upon which svcAcceptSession is ready to be called
* KServerSession: Waits for an IPC command to be submitted to the server process
* KServerSession: Waits for an IPC command to be submitted to the server process
* KThread: Waits until the thread terminates
* KThread: Waits until the thread terminates
* KTimer: ???
* KTimer: Wakes when timer activates (this also clears the timer if it is oneshot)


Most synchronization systems seem to have both a "normal" and "light-weight" version
Most synchronization systems seem to have both a "normal" and "light-weight" version


== Mutex (normal) ==
== Mutex ==


For Kernel implementation details, see [[KMutex]]
For Kernel implementation details, see [[KMutex]]
Line 288: Line 288:


=== ReleaseMutex ===
=== ReleaseMutex ===
== Ciritical Section (light-weight mutex) ==
== CriticalSection::Initialize ==
Same thread ownership as a mutex ?
=== CriticalSection::Enter ===
=== CriticalSection::Leave ===


== Semaphore ==
== Semaphore ==
== Light Semaphore ? ==
Does it exist ?


== Event ==
== Event ==
== Light Event ==


== Address Arbiters ==
== Address Arbiters ==