2020-04-19 23:04:05 +02:00
// SPDX-License-Identifier: MPL-2.0
2020-03-27 20:36:02 +01:00
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
2020-12-05 18:41:52 +01:00
# include <cxxabi.h>
2020-10-07 17:41:13 +02:00
# include <unistd.h>
2020-11-03 10:44:09 +01:00
# include <common/signal.h>
2021-03-20 17:52:08 +01:00
# include <common/trace.h>
2020-03-26 15:33:19 +01:00
# include <nce.h>
2020-10-07 17:41:13 +02:00
# include <os.h>
2019-09-24 22:54:27 +02:00
# include "KProcess.h"
2020-12-08 11:43:54 +01:00
# include "KThread.h"
2019-09-24 22:54:27 +02:00
namespace skyline : : kernel : : type {
2021-10-30 21:30:34 +02:00
KThread : : KThread ( const DeviceState & state , KHandle handle , KProcess * parent , size_t id , void * entry , u64 argument , void * stackTop , i8 priority , u8 idealCore )
: handle ( handle ) ,
parent ( parent ) ,
id ( id ) ,
entry ( entry ) ,
entryArgument ( argument ) ,
stackTop ( stackTop ) ,
priority ( priority ) ,
basePriority ( priority ) ,
idealCore ( idealCore ) ,
coreId ( idealCore ) ,
KSyncObject ( state , KType : : KThread ) {
2020-10-21 19:09:35 +02:00
affinityMask . set ( coreId ) ;
2019-09-24 22:54:27 +02:00
}
KThread : : ~ KThread ( ) {
2021-01-11 20:41:21 +01:00
Kill ( true ) ;
2020-11-17 01:48:41 +01:00
if ( thread . joinable ( ) )
thread . join ( ) ;
2020-12-08 11:43:54 +01:00
if ( preemptionTimer )
2021-01-11 20:41:21 +01:00
timer_delete ( preemptionTimer ) ;
2020-10-17 13:38:27 +02:00
}
2020-10-07 17:41:13 +02:00
void KThread : : StartThread ( ) {
2022-06-09 18:40:44 +02:00
std : : array < char , 16 > threadName { } ;
if ( int result { pthread_getname_np ( pthread , threadName . data ( ) , threadName . size ( ) ) } )
Logger : : Warn ( " Failed to get the thread name: {} " , strerror ( result ) ) ;
if ( int result { pthread_setname_np ( pthread , fmt : : format ( " HOS-{} " , id ) . c_str ( ) ) } )
Logger : : Warn ( " Failed to set the thread name: {} " , strerror ( result ) ) ;
2021-11-10 23:21:43 +01:00
Logger : : UpdateTag ( ) ;
2020-03-25 19:57:05 +01:00
2020-10-07 17:41:13 +02:00
if ( ! ctx . tpidrroEl0 )
ctx . tpidrroEl0 = parent - > AllocateTlsSlot ( ) ;
2020-10-10 17:53:14 +02:00
ctx . state = & state ;
2020-10-07 17:41:13 +02:00
state . ctx = & ctx ;
2020-10-10 17:53:14 +02:00
state . thread = shared_from_this ( ) ;
2020-10-07 17:41:13 +02:00
2020-11-03 10:44:09 +01:00
if ( setjmp ( originalCtx ) ) { // Returns 1 if it's returning from guest, 0 otherwise
2020-12-05 18:41:52 +01:00
state . scheduler - > RemoveThread ( ) ;
2021-01-11 20:41:21 +01:00
{
2022-04-25 16:00:30 +02:00
std : : scoped_lock lock { statusMutex } ;
2021-01-11 20:41:21 +01:00
running = false ;
ready = false ;
statusCondition . notify_all ( ) ;
}
2020-11-03 10:44:09 +01:00
Signal ( ) ;
2020-11-17 01:48:41 +01:00
if ( threadName [ 0 ] ! = ' H ' | | threadName [ 1 ] ! = ' O ' | | threadName [ 2 ] ! = ' S ' | | threadName [ 3 ] ! = ' - ' ) {
2022-06-09 18:40:44 +02:00
if ( int result { pthread_setname_np ( pthread , threadName . data ( ) ) } )
Logger : : Warn ( " Failed to set the thread name: {} " , strerror ( result ) ) ;
2021-11-10 23:21:43 +01:00
Logger : : UpdateTag ( ) ;
2020-11-17 01:48:41 +01:00
}
2020-11-03 10:44:09 +01:00
return ;
}
2021-01-11 20:41:21 +01:00
struct sigevent event {
2021-03-05 10:06:41 +01:00
. sigev_signo = Scheduler : : PreemptionSignal ,
2021-01-11 20:41:21 +01:00
. sigev_notify = SIGEV_THREAD_ID ,
. sigev_notify_thread_id = gettid ( ) ,
} ;
if ( timer_create ( CLOCK_THREAD_CPUTIME_ID , & event , & preemptionTimer ) )
throw exception ( " timer_create has failed with '{}' " , strerror ( errno ) ) ;
2020-11-03 10:44:09 +01:00
signal : : SetSignalHandler ( { SIGINT , SIGILL , SIGTRAP , SIGBUS , SIGFPE , SIGSEGV } , nce : : NCE : : SignalHandler ) ;
2021-03-05 10:06:41 +01:00
signal : : SetSignalHandler ( { Scheduler : : YieldSignal , Scheduler : : PreemptionSignal } , Scheduler : : SignalHandler , false ) ; // We want futexes to fail and their predicates rechecked
2021-01-11 20:41:21 +01:00
{
2022-04-25 16:00:30 +02:00
std : : scoped_lock lock { statusMutex } ;
2021-01-11 20:41:21 +01:00
ready = true ;
statusCondition . notify_all ( ) ;
}
2020-10-07 17:41:13 +02:00
2020-12-05 18:41:52 +01:00
try {
2021-01-11 20:41:21 +01:00
if ( ! Scheduler : : YieldPending )
state . scheduler - > WaitSchedule ( ) ;
while ( Scheduler : : YieldPending ) {
// If there is a yield pending on us after thread creation
state . scheduler - > Rotate ( ) ;
Scheduler : : YieldPending = false ;
state . scheduler - > WaitSchedule ( ) ;
}
2020-12-05 18:41:52 +01:00
2021-03-11 19:41:12 +01:00
TRACE_EVENT_BEGIN ( " guest " , " Guest " ) ;
2020-12-05 18:41:52 +01:00
asm volatile (
" MRS X0, TPIDR_EL0 \n \t "
" MSR TPIDR_EL0, %x0 \n \t " // Set TLS to ThreadContext
" STR X0, [%x0, #0x2A0] \n \t " // Write ThreadContext::hostTpidrEl0
" MOV X0, SP \n \t "
" STR X0, [%x0, #0x2A8] \n \t " // Write ThreadContext::hostSp
" MOV SP, %x1 \n \t " // Replace SP with guest stack
2021-03-04 14:30:14 +01:00
" MOV LR, %x2 \n \t " // Store entry in Link Register so it's jumped to on return
2020-12-05 18:41:52 +01:00
" MOV X0, %x3 \n \t " // Store the argument in X0
" MOV X1, %x4 \n \t " // Store the thread handle in X1, NCA applications require this
" MOV X2, XZR \n \t " // Zero out other GP and SIMD registers, not doing this will break applications
" MOV X3, XZR \n \t "
" MOV X4, XZR \n \t "
" MOV X5, XZR \n \t "
" MOV X6, XZR \n \t "
" MOV X7, XZR \n \t "
" MOV X8, XZR \n \t "
" MOV X9, XZR \n \t "
" MOV X10, XZR \n \t "
" MOV X11, XZR \n \t "
" MOV X12, XZR \n \t "
" MOV X13, XZR \n \t "
" MOV X14, XZR \n \t "
" MOV X15, XZR \n \t "
" MOV X16, XZR \n \t "
" MOV X17, XZR \n \t "
" MOV X18, XZR \n \t "
" MOV X19, XZR \n \t "
" MOV X20, XZR \n \t "
" MOV X21, XZR \n \t "
" MOV X22, XZR \n \t "
" MOV X23, XZR \n \t "
" MOV X24, XZR \n \t "
" MOV X25, XZR \n \t "
" MOV X26, XZR \n \t "
" MOV X27, XZR \n \t "
" MOV X28, XZR \n \t "
" MOV X29, XZR \n \t "
" MSR FPSR, XZR \n \t "
" MSR FPCR, XZR \n \t "
" DUP V0.16B, WZR \n \t "
" DUP V1.16B, WZR \n \t "
" DUP V2.16B, WZR \n \t "
" DUP V3.16B, WZR \n \t "
" DUP V4.16B, WZR \n \t "
" DUP V5.16B, WZR \n \t "
" DUP V6.16B, WZR \n \t "
" DUP V7.16B, WZR \n \t "
" DUP V8.16B, WZR \n \t "
" DUP V9.16B, WZR \n \t "
" DUP V10.16B, WZR \n \t "
" DUP V11.16B, WZR \n \t "
" DUP V12.16B, WZR \n \t "
" DUP V13.16B, WZR \n \t "
" DUP V14.16B, WZR \n \t "
" DUP V15.16B, WZR \n \t "
" DUP V16.16B, WZR \n \t "
" DUP V17.16B, WZR \n \t "
" DUP V18.16B, WZR \n \t "
" DUP V19.16B, WZR \n \t "
" DUP V20.16B, WZR \n \t "
" DUP V21.16B, WZR \n \t "
" DUP V22.16B, WZR \n \t "
" DUP V23.16B, WZR \n \t "
" DUP V24.16B, WZR \n \t "
" DUP V25.16B, WZR \n \t "
" DUP V26.16B, WZR \n \t "
" DUP V27.16B, WZR \n \t "
" DUP V28.16B, WZR \n \t "
" DUP V29.16B, WZR \n \t "
" DUP V30.16B, WZR \n \t "
" DUP V31.16B, WZR \n \t "
" RET "
:
: " r " ( & ctx ) , " r " ( stackTop ) , " r " ( entry ) , " r " ( entryArgument ) , " r " ( handle )
: " x0 " , " x1 " , " lr "
) ;
__builtin_unreachable ( ) ;
} catch ( const std : : exception & e ) {
2021-11-10 23:21:43 +01:00
Logger : : Error ( e . what ( ) ) ;
2022-04-10 09:49:50 +02:00
Logger : : EmulationContext . Flush ( ) ;
2020-12-05 18:41:52 +01:00
if ( id ) {
signal : : BlockSignal ( { SIGINT } ) ;
state . process - > Kill ( false ) ;
}
abi : : __cxa_end_catch ( ) ;
std : : longjmp ( originalCtx , true ) ;
} catch ( const signal : : SignalException & e ) {
if ( e . signal ! = SIGINT ) {
2021-11-10 23:21:43 +01:00
Logger : : Error ( e . what ( ) ) ;
2022-04-10 09:49:50 +02:00
Logger : : EmulationContext . Flush ( ) ;
2020-12-05 18:41:52 +01:00
if ( id ) {
signal : : BlockSignal ( { SIGINT } ) ;
state . process - > Kill ( false ) ;
}
}
abi : : __cxa_end_catch ( ) ;
std : : longjmp ( originalCtx , true ) ;
}
2020-10-07 17:41:13 +02:00
}
void KThread : : Start ( bool self ) {
2021-01-11 20:41:21 +01:00
std : : unique_lock lock ( statusMutex ) ;
2020-10-07 17:41:13 +02:00
if ( ! running ) {
2021-03-04 14:30:14 +01:00
{
2022-04-25 16:00:30 +02:00
std : : scoped_lock migrationLock { coreMigrationMutex } ;
2021-03-04 14:30:14 +01:00
auto thisShared { shared_from_this ( ) } ;
coreId = state . scheduler - > GetOptimalCoreForThread ( thisShared ) . id ;
state . scheduler - > InsertThread ( thisShared ) ;
}
2021-01-11 20:41:21 +01:00
running = true ;
killed = false ;
statusCondition . notify_all ( ) ;
2020-11-03 10:44:09 +01:00
if ( self ) {
pthread = pthread_self ( ) ;
lock . unlock ( ) ;
2020-10-07 17:41:13 +02:00
StartThread ( ) ;
2020-11-03 10:44:09 +01:00
} else {
2020-11-17 01:48:41 +01:00
thread = std : : thread ( & KThread : : StartThread , this ) ;
pthread = thread . native_handle ( ) ;
2020-11-03 10:44:09 +01:00
}
2019-11-17 21:19:01 +01:00
}
2019-09-24 22:54:27 +02:00
}
2020-11-03 10:44:09 +01:00
void KThread : : Kill ( bool join ) {
2021-01-11 20:41:21 +01:00
std : : unique_lock lock ( statusMutex ) ;
if ( ! killed & & running ) {
statusCondition . wait ( lock , [ this ] ( ) { return ready | | killed ; } ) ;
if ( ! killed ) {
pthread_kill ( pthread , SIGINT ) ;
killed = true ;
statusCondition . notify_all ( ) ;
2020-11-17 01:48:41 +01:00
}
2019-11-22 15:59:50 +01:00
}
2021-01-11 20:41:21 +01:00
if ( join )
statusCondition . wait ( lock , [ this ] ( ) { return ! running ; } ) ;
2019-11-22 15:59:50 +01:00
}
2020-12-08 11:43:54 +01:00
void KThread : : SendSignal ( int signal ) {
2021-01-11 20:41:21 +01:00
std : : unique_lock lock ( statusMutex ) ;
statusCondition . wait ( lock , [ this ] ( ) { return ready | | killed ; } ) ;
if ( ! killed & & running )
2020-12-08 11:43:54 +01:00
pthread_kill ( pthread , signal ) ;
}
2021-03-05 10:06:41 +01:00
void KThread : : ArmPreemptionTimer ( std : : chrono : : nanoseconds timeToFire ) {
2021-05-30 19:15:04 +02:00
std : : unique_lock lock ( statusMutex ) ;
statusCondition . wait ( lock , [ this ] ( ) { return ready | | killed ; } ) ;
if ( ! killed & & running ) {
struct itimerspec spec { . it_value = {
2021-10-24 21:45:29 +02:00
. tv_nsec = std : : min ( static_cast < i64 > ( timeToFire . count ( ) ) , constant : : NsInSecond ) ,
2021-05-30 19:15:04 +02:00
. tv_sec = std : : max ( std : : chrono : : duration_cast < std : : chrono : : seconds > ( timeToFire ) . count ( ) - 1 , 0LL ) ,
} } ;
timer_settime ( preemptionTimer , 0 , & spec , nullptr ) ;
isPreempted = true ;
}
2021-03-05 10:06:41 +01:00
}
void KThread : : DisarmPreemptionTimer ( ) {
2021-06-18 18:24:31 +02:00
if ( ! isPreempted ) [[unlikely]]
2021-05-30 19:15:04 +02:00
return ;
std : : unique_lock lock ( statusMutex ) ;
statusCondition . wait ( lock , [ this ] ( ) { return ready | | killed ; } ) ;
if ( ! killed & & running ) {
2021-03-05 10:06:41 +01:00
struct itimerspec spec { } ;
timer_settime ( preemptionTimer , 0 , & spec , nullptr ) ;
isPreempted = false ;
}
}
void KThread : : UpdatePriorityInheritance ( ) {
2022-08-04 13:45:13 +02:00
std : : unique_lock lock { waiterMutex } ;
std : : shared_ptr < KThread > waitingOn { waitThread } ;
2021-10-24 21:45:29 +02:00
i8 currentPriority { priority . load ( ) } ;
2021-03-05 10:06:41 +01:00
while ( waitingOn ) {
2021-10-24 21:45:29 +02:00
i8 ownerPriority ;
2021-03-05 10:06:41 +01:00
do {
// Try to CAS the priority of the owner with the current thread
// If the new priority is equivalent to the current priority then we don't need to CAS
ownerPriority = waitingOn - > priority . load ( ) ;
if ( ownerPriority < = currentPriority )
return ;
2022-07-14 17:00:04 +02:00
} while ( ! waitingOn - > priority . compare_exchange_strong ( ownerPriority , currentPriority ) ) ;
2021-03-05 10:06:41 +01:00
if ( ownerPriority ! = currentPriority ) {
2022-08-04 13:45:13 +02:00
std : : unique_lock waiterLock { waitingOn - > waiterMutex , std : : try_to_lock } ;
if ( ! waiterLock ) {
// We want to avoid a deadlock here from the thread holding waitingOn->waiterMutex waiting for waiterMutex
// We use a fallback mechanism to avoid this, resetting the state and trying again after being able to successfully acquire waitingOn->waiterMutex once
waitingOn - > priority = ownerPriority ;
lock . unlock ( ) ;
2022-08-28 16:45:08 +02:00
2022-08-04 13:45:13 +02:00
waiterLock . lock ( ) ;
2022-08-28 16:45:08 +02:00
waiterLock . unlock ( ) ;
2022-08-04 13:45:13 +02:00
lock . lock ( ) ;
waitingOn = waitThread ;
continue ;
}
2021-03-05 10:06:41 +01:00
auto nextThread { waitingOn - > waitThread } ;
2021-02-06 13:36:58 +01:00
if ( nextThread ) {
2021-03-05 10:06:41 +01:00
// We need to update the location of the owner thread in the waiter queue of the thread it's waiting on
2022-08-28 16:45:08 +02:00
std : : unique_lock nextWaiterLock { nextThread - > waiterMutex , std : : try_to_lock } ;
if ( ! nextWaiterLock ) {
// We want to avoid a deadlock here from the thread holding nextThread->waiterMutex waiting for waiterMutex or waitingOn->waiterMutex
waitingOn - > priority = ownerPriority ;
lock . unlock ( ) ;
waiterLock . unlock ( ) ;
nextWaiterLock . lock ( ) ;
nextWaiterLock . unlock ( ) ;
lock . lock ( ) ;
waitingOn = waitThread ;
continue ;
}
2021-03-05 10:06:41 +01:00
auto & piWaiters { nextThread - > waiters } ;
piWaiters . erase ( std : : find ( piWaiters . begin ( ) , piWaiters . end ( ) , waitingOn ) ) ;
piWaiters . insert ( std : : upper_bound ( piWaiters . begin ( ) , piWaiters . end ( ) , currentPriority , KThread : : IsHigherPriority ) , waitingOn ) ;
break ;
}
state . scheduler - > UpdatePriority ( waitingOn ) ;
waitingOn = nextThread ;
} else {
break ;
}
}
}
2019-09-24 22:54:27 +02:00
}