summaryrefslogtreecommitdiffstats
path: root/src/core/hle/svc.cpp
diff options
context:
space:
mode:
authorSubv <subv2112@gmail.com>2016-12-04 04:38:14 +0100
committerSubv <subv2112@gmail.com>2016-12-04 04:38:14 +0100
commit8634b8cb83755b6c6554faa11c0e488d2ad21f90 (patch)
tree93c2e91659ccd2925210dcffb559213edbd2a64a /src/core/hle/svc.cpp
parentMerge pull request #2251 from JayFoxRox/remove-version (diff)
downloadyuzu-8634b8cb83755b6c6554faa11c0e488d2ad21f90.tar
yuzu-8634b8cb83755b6c6554faa11c0e488d2ad21f90.tar.gz
yuzu-8634b8cb83755b6c6554faa11c0e488d2ad21f90.tar.bz2
yuzu-8634b8cb83755b6c6554faa11c0e488d2ad21f90.tar.lz
yuzu-8634b8cb83755b6c6554faa11c0e488d2ad21f90.tar.xz
yuzu-8634b8cb83755b6c6554faa11c0e488d2ad21f90.tar.zst
yuzu-8634b8cb83755b6c6554faa11c0e488d2ad21f90.zip
Diffstat (limited to 'src/core/hle/svc.cpp')
-rw-r--r--src/core/hle/svc.cpp181
1 files changed, 103 insertions, 78 deletions
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<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);
}