@ -249,27 +249,30 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) {
auto object = Kernel : : g_handle_table . GetWaitObject ( handle ) ;
Kernel : : Thread * thread = Kernel : : GetCurrentThread ( ) ;
thread - > waitsynch_waited = false ;
if ( object = = nullptr )
return ERR_INVALID_HANDLE ;
LOG_TRACE ( Kernel_SVC , " called handle=0x%08X(%s:%s), nanoseconds=%lld " , handle ,
object - > GetTypeName ( ) . c_str ( ) , object - > GetName ( ) . c_str ( ) , nano_seconds ) ;
HLE : : Reschedule ( __func__ ) ;
// Check for next thread to schedule
if ( object - > ShouldWait ( ) ) {
if ( nano_seconds = = 0 )
return ResultCode ( ErrorDescription : : Timeout , ErrorModule : : OS ,
ErrorSummary : : StatusChanged ,
ErrorLevel : : Info ) ;
object - > AddWaitingThread ( thread ) ;
Kernel : : WaitCurrentThread_WaitSynchronization ( { object } , false , false ) ;
thread - > status = THREADSTATUS_WAIT_SYNCH ;
// Create an event to wake the thread up after the specified nanosecond delay has passed
thread - > WakeAfterDelay ( nano_seconds ) ;
// NOTE: output of this SVC will be set later depending on how the thread resumes
return HLE : : RESULT_INVALID ;
// Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a signal in one of its wait objects.
// Otherwise we retain the default value of timeout.
return ResultCode ( ErrorDescription : : Timeout , ErrorModule : : OS ,
ErrorSummary : : StatusChanged ,
ErrorLevel : : Info ) ;
}
object - > Acquire ( ) ;
@ -283,8 +286,6 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou
bool wait_thread = ! wait_all ;
int handle_index = 0 ;
Kernel : : Thread * thread = Kernel : : GetCurrentThread ( ) ;
bool was_waiting = thread - > waitsynch_waited ;
thread - > waitsynch_waited = false ;
// Check if 'handles' is invalid
if ( handles = = nullptr )
@ -300,90 +301,113 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou
return ResultCode ( ErrorDescription : : OutOfRange , ErrorModule : : OS ,
ErrorSummary : : InvalidArgument , ErrorLevel : : Usage ) ;
// If 'handle_count' is non-zero, iterate through each handle and wait the current thread if
// necessary
if ( handle_count ! = 0 ) {
bool selected = false ; // True once an object has been selected
Kernel : : SharedPtr < Kernel : : WaitObject > wait_object ;
for ( int i = 0 ; i < handle_count ; + + i ) {
auto object = Kernel : : g_handle_table . GetWaitObject ( handles [ i ] ) ;
if ( object = = nullptr )
return ERR_INVALID_HANDLE ;
// Check if the current thread should wait on this object...
if ( object - > ShouldWait ( ) ) {
// Check we are waiting on all objects...
if ( wait_all )
// Wait the thread
wait_thread = true ;
} else {
// Do not wait on this object, check if this object should be selected...
if ( ! wait_all & & ( ! selected | | ( wait_object = = object & & was_waiting ) ) ) {
// Do not wait the thread
wait_thread = false ;
handle_index = i ;
wait_object = object ;
selected = true ;
}
}
}
} else {
// If no handles were passed in, put the thread to sleep only when 'wait_all' is false
// NOTE: This should deadlock the current thread if no timeout was specified
if ( ! wait_all ) {
wait_thread = true ;
}
using ObjectPtr = Kernel : : SharedPtr < Kernel : : WaitObject > ;
std : : vector < ObjectPtr > objects ( handle_count ) ;
for ( int i = 0 ; i < handle_count ; + + i ) {
auto object = Kernel : : g_handle_table . GetWaitObject ( handles [ i ] ) ;
if ( object = = nullptr )
return ERR_INVALID_HANDLE ;
objects [ i ] = object ;
}
SCOPE_EXIT ( {
HLE : : Reschedule ( " WaitSynchronizationN " ) ;
} ) ; // Reschedule after putting the threads to sleep.
// Clear the mapping of wait object indices
thread - > wait_objects_index . clear ( ) ;
if ( ! wait_all ) {
// Find the first object that is acquireable in the provided list of objects
auto itr = std : : find_if ( objects . begin ( ) , objects . end ( ) , [ ] ( const ObjectPtr & object ) {
return ! object - > ShouldWait ( ) ;
} ) ;
if ( itr ! = objects . end ( ) ) {
// We found a ready object, acquire it and set the result value
ObjectPtr object = * itr ;
object - > Acquire ( ) ;
* out = std : : distance ( objects . begin ( ) , itr ) ;
return RESULT_SUCCESS ;
}
// No objects were ready to be acquired, prepare to suspend the thread.
// If a timeout value of 0 was provided, just return the Timeout error code instead of suspending the thread.
if ( nano_seconds = = 0 ) {
return ResultCode ( ErrorDescription : : Timeout , ErrorModule : : OS ,
ErrorSummary : : StatusChanged ,
ErrorLevel : : Info ) ;
}
// If thread should wait, then set its state to waiting
if ( wait_thread ) {
// Put the thread to sleep
thread - > status = THREADSTATUS_WAIT_SYNCH ;
// Actually wait the current thread on each object if we decided to wait...
std : : vector < SharedPtr < Kernel : : WaitObject > > wait_objects ;
wait_objects . reserve ( handle_count ) ;
// Clear the thread's waitlist, we won't use it for wait_all = false
thread - > wait_objects . clear ( ) ;
for ( int i = 0 ; i < handle_count ; + + i ) {
auto object = Kernel : : g_handle_table . GetWaitObject ( handles [ i ] ) ;
object - > AddWaitingThread ( Kernel : : GetCurrentThread ( ) ) ;
wait_objects . push_back ( object ) ;
// Add the thread to each of the objects' waiting threads.
for ( int i = 0 ; i < objects . size ( ) ; + + i ) {
ObjectPtr object = objects [ i ] ;
// Set the index of this object in the mapping of Objects -> index for this thread.
thread - > wait_objects_index [ object - > GetObjectId ( ) ] = i ;
object - > AddWaitingThread ( thread ) ;
// TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion.
}
Kernel : : WaitCurrentThread_WaitSynchronization ( std : : move ( wait_objects ) , true , wait_all ) ;
// Note: If no handles and no timeout were given, then the thread will deadlock, this is consistent with hardware behavior.
// Create an event to wake the thread up after the specified nanosecond delay has passed
Kernel : : GetCurrentThread ( ) - > WakeAfterDelay ( nano_seconds ) ;
thread - > WakeAfterDelay ( nano_seconds ) ;
// NOTE: output of this SVC will be set later depending on how the thread resumes
return HLE : : RESULT_INVALID ;
}
// Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a signal in one of its wait objects.
// Otherwise we retain the default value of timeout, and -1 in the out parameter
thread - > wait_set_output = true ;
* out = - 1 ;
return ResultCode ( ErrorDescription : : Timeout , ErrorModule : : OS ,
ErrorSummary : : StatusChanged ,
ErrorLevel : : Info ) ;
} else {
bool all_available = std : : all_of ( objects . begin ( ) , objects . end ( ) , [ ] ( const ObjectPtr & object ) {
return ! object - > ShouldWait ( ) ;
} ) ;
if ( all_available ) {
// We can acquire all objects right now, do so.
for ( auto object : objects )
object - > Acquire ( ) ;
// Note: In this case, the `out` parameter is not set, and retains whatever value it had before.
return RESULT_SUCCESS ;
}
// Acquire objects if we did not wait...
for ( int i = 0 ; i < handle_count ; + + i ) {
auto object = Kernel : : g_handle_table . GetWaitObject ( handles [ i ] ) ;
// Not all objects were available right now, prepare to suspend the thread.
// Acquire the object if it is not waiting...
if ( ! object - > ShouldWait ( ) ) {
object - > Acquire ( ) ;
// If a timeout value of 0 was provided, just return the Timeout error code instead of suspending the thread.
if ( nano_seconds = = 0 ) {
return ResultCode ( ErrorDescription : : Timeout , ErrorModule : : OS ,
ErrorSummary : : StatusChanged ,
ErrorLevel : : Info ) ;
}
// Put the thread to sleep
thread - > status = THREADSTATUS_WAIT_SYNCH ;
// If this was the first non-waiting object and 'wait_all' is false, don't acquire
// any other objects
if ( ! wait_all )
break ;
// Set the thread's waitlist to the list of objects passed to WaitSynchronizationN
thread - > wait_objects = objects ;
// Add the thread to each of the objects' waiting threads.
for ( auto object : objects ) {
object - > AddWaitingThread ( thread ) ;
// TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion.
}
}
// TODO(bunnei): If 'wait_all' is true, this is probably wrong. However, real hardware does
// not seem to set it to any meaningful value.
* out = handle_count ! = 0 ? ( wait_all ? - 1 : handle_index ) : 0 ;
// Create an event to wake the thread up after the specified nanosecond delay has passed
thread - > WakeAfterDelay ( nano_seconds ) ;
return RESULT_SUCCESS ;
// This value gets set to -1 by default in this case, it is not modified after this.
* out = - 1 ;
// Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a signal in one of its wait objects.
return ResultCode ( ErrorDescription : : Timeout , ErrorModule : : OS ,
ErrorSummary : : StatusChanged ,
ErrorLevel : : Info ) ;
}
}
/// Create an address arbiter (to allocate access to shared resources)
@ -1148,6 +1172,7 @@ void CallSVC(u32 immediate) {
if ( info ) {
if ( info - > func ) {
info - > func ( ) ;
HLE : : Reschedule ( __func__ ) ;
} else {
LOG_ERROR ( Kernel_SVC , " unimplemented SVC function %s(..) " , info - > name ) ;
}