From 8634b8cb83755b6c6554faa11c0e488d2ad21f90 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 3 Dec 2016 22:38:14 -0500 Subject: Threading: Reworked the way our scheduler works. Threads will now be awakened when the objects they're waiting on are signaled, instead of repeating the WaitSynchronization call every now and then. The scheduler is now called once after every SVC call, and once after a thread is awakened from sleep by its timeout callback. This new implementation is based off reverse-engineering of the real kernel. See https://gist.github.com/Subv/02f29bd9f1e5deb7aceea1e8f019c8f4 for a more detailed description of how the real kernel handles rescheduling. --- src/core/hle/svc.cpp | 181 +++++++++++++++++++++++++++++---------------------- 1 file changed, 103 insertions(+), 78 deletions(-) (limited to 'src/core/hle/svc.cpp') diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index c6b80dc50..061692af8 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -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 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; + + std::vector 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> 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); } -- cgit v1.2.3 From bdad00c73f46106ba78995bdde1b50349e940b09 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 4 Dec 2016 09:58:36 -0500 Subject: Threading: Added some utility functions and const correctness. --- src/core/hle/svc.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'src/core/hle/svc.cpp') diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 061692af8..c06df84b3 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -257,18 +257,21 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { if (object->ShouldWait()) { - if (nano_seconds == 0) + if (nano_seconds == 0) { return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, ErrorSummary::StatusChanged, ErrorLevel::Info); + } object->AddWaitingThread(thread); + // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. + // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. 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: 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. + // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a signal in its wait objects. // Otherwise we retain the default value of timeout. return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, ErrorSummary::StatusChanged, @@ -312,7 +315,9 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou objects[i] = object; } - // Clear the mapping of wait object indices + // Clear the mapping of wait object indices. + // We don't want any lingering state in this map. + // It will be repopulated later in the wait_all = false case. thread->wait_objects_index.clear(); if (!wait_all) { @@ -345,12 +350,13 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou thread->wait_objects.clear(); // Add the thread to each of the objects' waiting threads. - for (int i = 0; i < objects.size(); ++i) { + for (size_t 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; + thread->wait_objects_index[object->GetObjectId()] = static_cast(i); object->AddWaitingThread(thread); // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. + // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. } // Note: If no handles and no timeout were given, then the thread will deadlock, this is consistent with hardware behavior. @@ -396,6 +402,7 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou for (auto object : objects) { object->AddWaitingThread(thread); // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. + // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. } // Create an event to wake the thread up after the specified nanosecond delay has passed @@ -1172,6 +1179,7 @@ void CallSVC(u32 immediate) { if (info) { if (info->func) { info->func(); + // TODO(Subv): Not all service functions should cause a reschedule in all cases. HLE::Reschedule(__func__); } else { LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); -- cgit v1.2.3 From 17b29d8865ea4d96c18f7e1671bd6d0f01eab95f Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 8 Dec 2016 10:34:53 -0500 Subject: WaitSynch: Removed unused variables and reduced SharedPtr copies. Define a variable with the value of the sync timeout error code. Use a boost::flat_map instead of an unordered_map to hold the equivalence of objects and wait indices in a WaitSynchN call. --- src/core/hle/svc.cpp | 105 ++++++++++++++++++++++----------------------------- 1 file changed, 45 insertions(+), 60 deletions(-) (limited to 'src/core/hle/svc.cpp') diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index c06df84b3..14da09883 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -41,6 +41,9 @@ const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E0181E +const ResultCode ERR_SYNC_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS, + ErrorSummary::StatusChanged, ErrorLevel::Info); + const ResultCode ERR_MISALIGNED_ADDRESS{// 0xE0E01BF1 ErrorDescription::MisalignedAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage}; @@ -257,11 +260,8 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { if (object->ShouldWait()) { - if (nano_seconds == 0) { - return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, - ErrorSummary::StatusChanged, - ErrorLevel::Info); - } + if (nano_seconds == 0) + return ERR_SYNC_TIMEOUT; object->AddWaitingThread(thread); // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. @@ -273,9 +273,7 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a signal in its wait objects. // Otherwise we retain the default value of timeout. - return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, - ErrorSummary::StatusChanged, - ErrorLevel::Info); + return ERR_SYNC_TIMEOUT; } object->Acquire(); @@ -286,8 +284,6 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { /// Wait for the given handles to synchronize, timeout after the specified nanoseconds static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) { - bool wait_thread = !wait_all; - int handle_index = 0; Kernel::Thread* thread = Kernel::GetCurrentThread(); // Check if 'handles' is invalid @@ -305,7 +301,6 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou ErrorSummary::InvalidArgument, ErrorLevel::Usage); using ObjectPtr = Kernel::SharedPtr; - std::vector objects(handle_count); for (int i = 0; i < handle_count; ++i) { @@ -320,100 +315,90 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou // It will be repopulated later in the wait_all = false case. 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) { + if (wait_all) { + bool all_available = std::all_of(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); + 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; } - // No objects were ready to be acquired, prepare to suspend the thread. + // Not all objects were available right now, 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 (nano_seconds == 0) + return ERR_SYNC_TIMEOUT; // Put the thread to sleep thread->status = THREADSTATUS_WAIT_SYNCH; - // Clear the thread's waitlist, we won't use it for wait_all = false - thread->wait_objects.clear(); - // Add the thread to each of the objects' waiting threads. - for (size_t 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()] = static_cast(i); + for (auto& object : objects) { object->AddWaitingThread(thread); // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. } - // Note: If no handles and no timeout were given, then the thread will deadlock, this is consistent with hardware behavior. + // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN + thread->wait_objects = std::move(objects); // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); - // 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; + // This value gets set to -1 by default in this case, it is not modified after this. *out = -1; - return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, - ErrorSummary::StatusChanged, - ErrorLevel::Info); + // 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 ERR_SYNC_TIMEOUT; } else { - bool all_available = std::all_of(objects.begin(), objects.end(), [](const ObjectPtr& object) { + // Find the first object that is acquirable in the provided list of objects + auto itr = std::find_if(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. + + if (itr != objects.end()) { + // We found a ready object, acquire it and set the result value + Kernel::WaitObject* object = itr->get(); + object->Acquire(); + *out = std::distance(objects.begin(), itr); return RESULT_SUCCESS; } - // Not all objects were available right now, prepare to suspend the thread. + // 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 (nano_seconds == 0) + return ERR_SYNC_TIMEOUT; // Put the thread to sleep thread->status = THREADSTATUS_WAIT_SYNCH; - // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN - thread->wait_objects = objects; + // Clear the thread's waitlist, we won't use it for wait_all = false + thread->wait_objects.clear(); // Add the thread to each of the objects' waiting threads. - for (auto object : objects) { + for (size_t i = 0; i < objects.size(); ++i) { + Kernel::WaitObject* object = objects[i].get(); + // Set the index of this object in the mapping of Objects -> index for this thread. + thread->wait_objects_index[object->GetObjectId()] = static_cast(i); object->AddWaitingThread(thread); // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. } + // 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 thread->WakeAfterDelay(nano_seconds); - // 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); + // Otherwise we retain the default value of timeout, and -1 in the out parameter + thread->wait_set_output = true; + *out = -1; + return ERR_SYNC_TIMEOUT; } } -- cgit v1.2.3 From 406907d57055965780e04769482556995de8c50a Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 10 Dec 2016 13:29:31 -0500 Subject: Properly remove a thread from its wait_objects' waitlist when it is awoken by a timeout. --- src/core/hle/svc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/core/hle/svc.cpp') diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 14da09883..c81c14443 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -321,7 +321,7 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou }); if (all_available) { // We can acquire all objects right now, do so. - for (auto object : objects) + 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; -- cgit v1.2.3 From 5b1edc6ae70972d4a11eee1f1ff8fdff2122b5a2 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 14 Dec 2016 12:13:02 -0500 Subject: Fixed the codestyle to match our clang-format rules. --- src/core/hle/svc.cpp | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) (limited to 'src/core/hle/svc.cpp') diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index c81c14443..a4a00d9b2 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -264,14 +264,16 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { return ERR_SYNC_TIMEOUT; object->AddWaitingThread(thread); - // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. - // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. + // TODO(Subv): Perform things like update the mutex lock owner's priority to + // prevent priority inversion. Currently this is done in Mutex::ShouldWait, + // but it should be moved to a function that is called from here. 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: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a signal in its wait objects. + // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread + // resumes due to a signal in its wait objects. // Otherwise we retain the default value of timeout. return ERR_SYNC_TIMEOUT; } @@ -316,20 +318,22 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou thread->wait_objects_index.clear(); if (wait_all) { - bool all_available = std::all_of(objects.begin(), objects.end(), [](const ObjectPtr& object) { - return !object->ShouldWait(); - }); + 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. + // Note: In this case, the `out` parameter is not set, + // and retains whatever value it had before. return RESULT_SUCCESS; } // Not all objects were available right now, 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 a timeout value of 0 was provided, just return the Timeout error code instead of + // suspending the thread. if (nano_seconds == 0) return ERR_SYNC_TIMEOUT; @@ -339,8 +343,9 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou // 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. - // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. + // TODO(Subv): Perform things like update the mutex lock owner's priority to + // prevent priority inversion. Currently this is done in Mutex::ShouldWait, + // but it should be moved to a function that is called from here. } // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN @@ -351,13 +356,13 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou // 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. + // 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 ERR_SYNC_TIMEOUT; } else { // Find the first object that is acquirable in the provided list of objects - auto itr = std::find_if(objects.begin(), objects.end(), [](const ObjectPtr& object) { - return !object->ShouldWait(); - }); + 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 @@ -369,7 +374,8 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou // 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 a timeout value of 0 was provided, just return the Timeout error code instead of + // suspending the thread. if (nano_seconds == 0) return ERR_SYNC_TIMEOUT; @@ -385,16 +391,19 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou // Set the index of this object in the mapping of Objects -> index for this thread. thread->wait_objects_index[object->GetObjectId()] = static_cast(i); object->AddWaitingThread(thread); - // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. - // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. + // TODO(Subv): Perform things like update the mutex lock owner's priority to + // prevent priority inversion. Currently this is done in Mutex::ShouldWait, + // but it should be moved to a function that is called from here. } - // Note: If no handles and no timeout were given, then the thread will deadlock, this is consistent with hardware behavior. + // 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 thread->WakeAfterDelay(nano_seconds); - // 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. + // 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; -- cgit v1.2.3