diff options
| -rw-r--r-- | src/citra_qt/debugger/wait_tree.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 13 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 19 | ||||
| -rw-r--r-- | src/core/hle/svc.cpp | 18 |
4 files changed, 36 insertions, 16 deletions
diff --git a/src/citra_qt/debugger/wait_tree.cpp b/src/citra_qt/debugger/wait_tree.cpp index 8fc3e37e0..829ac7dd6 100644 --- a/src/citra_qt/debugger/wait_tree.cpp +++ b/src/citra_qt/debugger/wait_tree.cpp | |||
| @@ -230,7 +230,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { | |||
| 230 | list.push_back(std::make_unique<WaitTreeMutexList>(thread.held_mutexes)); | 230 | list.push_back(std::make_unique<WaitTreeMutexList>(thread.held_mutexes)); |
| 231 | } | 231 | } |
| 232 | if (thread.status == THREADSTATUS_WAIT_SYNCH) { | 232 | if (thread.status == THREADSTATUS_WAIT_SYNCH) { |
| 233 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, !thread.wait_objects.empty())); | 233 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, thread.IsWaitingAll())); |
| 234 | } | 234 | } |
| 235 | 235 | ||
| 236 | return list; | 236 | return list; |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index be7a5a6d8..6d358def7 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -33,7 +33,7 @@ void WaitObject::RemoveWaitingThread(Thread* thread) { | |||
| 33 | 33 | ||
| 34 | SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { | 34 | SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { |
| 35 | // Remove the threads that are ready or already running from our waitlist | 35 | // Remove the threads that are ready or already running from our waitlist |
| 36 | waiting_threads.erase(std::remove_if(waiting_threads.begin(), waiting_threads.end(), [](SharedPtr<Thread> thread) -> bool { | 36 | waiting_threads.erase(std::remove_if(waiting_threads.begin(), waiting_threads.end(), [](const SharedPtr<Thread>& thread) -> bool { |
| 37 | return thread->status == THREADSTATUS_RUNNING || thread->status == THREADSTATUS_READY; | 37 | return thread->status == THREADSTATUS_RUNNING || thread->status == THREADSTATUS_READY; |
| 38 | }), waiting_threads.end()); | 38 | }), waiting_threads.end()); |
| 39 | 39 | ||
| @@ -42,12 +42,11 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { | |||
| 42 | 42 | ||
| 43 | auto candidate_threads = waiting_threads; | 43 | auto candidate_threads = waiting_threads; |
| 44 | 44 | ||
| 45 | // Eliminate all threads that are waiting on more than one object, and not all of them are ready | 45 | // Eliminate all threads that are waiting on more than one object, and not all of said objects are ready |
| 46 | candidate_threads.erase(std::remove_if(candidate_threads.begin(), candidate_threads.end(), [](SharedPtr<Thread> thread) -> bool { | 46 | candidate_threads.erase(std::remove_if(candidate_threads.begin(), candidate_threads.end(), [](const SharedPtr<Thread>& thread) -> bool { |
| 47 | for (auto object : thread->wait_objects) | 47 | return std::any_of(thread->wait_objects.begin(), thread->wait_objects.end(), [](const SharedPtr<WaitObject>& object) -> bool { |
| 48 | if (object->ShouldWait()) | 48 | return object->ShouldWait(); |
| 49 | return true; | 49 | }); |
| 50 | return false; | ||
| 51 | }), candidate_threads.end()); | 50 | }), candidate_threads.end()); |
| 52 | 51 | ||
| 53 | // Return the thread with the lowest priority value (The one with the highest priority) | 52 | // Return the thread with the lowest priority value (The one with the highest priority) |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 63b97b74f..1b29fb3a3 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -131,8 +131,8 @@ public: | |||
| 131 | * It is used to set the output value of WaitSynchronizationN when the thread is awakened. | 131 | * It is used to set the output value of WaitSynchronizationN when the thread is awakened. |
| 132 | * @param object Object to query the index of. | 132 | * @param object Object to query the index of. |
| 133 | */ | 133 | */ |
| 134 | s32 GetWaitObjectIndex(WaitObject* object) { | 134 | s32 GetWaitObjectIndex(const WaitObject* object) const { |
| 135 | return wait_objects_index[object->GetObjectId()]; | 135 | return wait_objects_index.at(object->GetObjectId()); |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | /** | 138 | /** |
| @@ -148,6 +148,15 @@ public: | |||
| 148 | return tls_address; | 148 | return tls_address; |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | /** | ||
| 152 | * Returns whether this thread is waiting for all the objects in | ||
| 153 | * its wait list to become ready, as a result of a WaitSynchronizationN call | ||
| 154 | * with wait_all = true, or a ReplyAndReceive call. | ||
| 155 | */ | ||
| 156 | bool IsWaitingAll() const { | ||
| 157 | return !wait_objects.empty(); | ||
| 158 | } | ||
| 159 | |||
| 151 | Core::ThreadContext context; | 160 | Core::ThreadContext context; |
| 152 | 161 | ||
| 153 | u32 thread_id; | 162 | u32 thread_id; |
| @@ -169,7 +178,11 @@ public: | |||
| 169 | boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; | 178 | boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; |
| 170 | 179 | ||
| 171 | SharedPtr<Process> owner_process; ///< Process that owns this thread | 180 | SharedPtr<Process> owner_process; ///< Process that owns this thread |
| 172 | std::vector<SharedPtr<WaitObject>> wait_objects; ///< Objects that the thread is waiting on | 181 | |
| 182 | /// Objects that the thread is waiting on. | ||
| 183 | /// This is only populated when the thread should wait for all the objects to become ready. | ||
| 184 | std::vector<SharedPtr<WaitObject>> wait_objects; | ||
| 185 | |||
| 173 | std::unordered_map<int, s32> wait_objects_index; ///< Mapping of Object ids to their position in the last waitlist that this object waited on. | 186 | std::unordered_map<int, s32> wait_objects_index; ///< Mapping of Object ids to their position in the last waitlist that this object waited on. |
| 174 | 187 | ||
| 175 | VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address | 188 | VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address |
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) { | |||
| 257 | 257 | ||
| 258 | if (object->ShouldWait()) { | 258 | if (object->ShouldWait()) { |
| 259 | 259 | ||
| 260 | if (nano_seconds == 0) | 260 | if (nano_seconds == 0) { |
| 261 | return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | 261 | return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, |
| 262 | ErrorSummary::StatusChanged, | 262 | ErrorSummary::StatusChanged, |
| 263 | ErrorLevel::Info); | 263 | ErrorLevel::Info); |
| 264 | } | ||
| 264 | 265 | ||
| 265 | object->AddWaitingThread(thread); | 266 | object->AddWaitingThread(thread); |
| 267 | // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. | ||
| 268 | // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. | ||
| 266 | thread->status = THREADSTATUS_WAIT_SYNCH; | 269 | thread->status = THREADSTATUS_WAIT_SYNCH; |
| 267 | 270 | ||
| 268 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 271 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 269 | thread->WakeAfterDelay(nano_seconds); | 272 | thread->WakeAfterDelay(nano_seconds); |
| 270 | 273 | ||
| 271 | // 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. | 274 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a signal in its wait objects. |
| 272 | // Otherwise we retain the default value of timeout. | 275 | // Otherwise we retain the default value of timeout. |
| 273 | return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | 276 | return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, |
| 274 | ErrorSummary::StatusChanged, | 277 | ErrorSummary::StatusChanged, |
| @@ -312,7 +315,9 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou | |||
| 312 | objects[i] = object; | 315 | objects[i] = object; |
| 313 | } | 316 | } |
| 314 | 317 | ||
| 315 | // Clear the mapping of wait object indices | 318 | // Clear the mapping of wait object indices. |
| 319 | // We don't want any lingering state in this map. | ||
| 320 | // It will be repopulated later in the wait_all = false case. | ||
| 316 | thread->wait_objects_index.clear(); | 321 | thread->wait_objects_index.clear(); |
| 317 | 322 | ||
| 318 | if (!wait_all) { | 323 | if (!wait_all) { |
| @@ -345,12 +350,13 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou | |||
| 345 | thread->wait_objects.clear(); | 350 | thread->wait_objects.clear(); |
| 346 | 351 | ||
| 347 | // Add the thread to each of the objects' waiting threads. | 352 | // Add the thread to each of the objects' waiting threads. |
| 348 | for (int i = 0; i < objects.size(); ++i) { | 353 | for (size_t i = 0; i < objects.size(); ++i) { |
| 349 | ObjectPtr object = objects[i]; | 354 | ObjectPtr object = objects[i]; |
| 350 | // Set the index of this object in the mapping of Objects -> index for this thread. | 355 | // Set the index of this object in the mapping of Objects -> index for this thread. |
| 351 | thread->wait_objects_index[object->GetObjectId()] = i; | 356 | thread->wait_objects_index[object->GetObjectId()] = static_cast<int>(i); |
| 352 | object->AddWaitingThread(thread); | 357 | object->AddWaitingThread(thread); |
| 353 | // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. | 358 | // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. |
| 359 | // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. | ||
| 354 | } | 360 | } |
| 355 | 361 | ||
| 356 | // Note: If no handles and no timeout were given, then the thread will deadlock, this is consistent with hardware behavior. | 362 | // 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 | |||
| 396 | for (auto object : objects) { | 402 | for (auto object : objects) { |
| 397 | object->AddWaitingThread(thread); | 403 | object->AddWaitingThread(thread); |
| 398 | // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. | 404 | // TODO(Subv): Perform things like update the mutex lock owner's priority to prevent priority inversion. |
| 405 | // Currently this is done in Mutex::ShouldWait, but it should be moved to a function that is called from here. | ||
| 399 | } | 406 | } |
| 400 | 407 | ||
| 401 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 408 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| @@ -1172,6 +1179,7 @@ void CallSVC(u32 immediate) { | |||
| 1172 | if (info) { | 1179 | if (info) { |
| 1173 | if (info->func) { | 1180 | if (info->func) { |
| 1174 | info->func(); | 1181 | info->func(); |
| 1182 | // TODO(Subv): Not all service functions should cause a reschedule in all cases. | ||
| 1175 | HLE::Reschedule(__func__); | 1183 | HLE::Reschedule(__func__); |
| 1176 | } else { | 1184 | } else { |
| 1177 | LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); | 1185 | LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); |