diff options
Diffstat (limited to 'src/core/hle/kernel')
| -rw-r--r-- | src/core/hle/kernel/event.cpp | 3 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 22 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.h | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 9 | ||||
| -rw-r--r-- | src/core/hle/kernel/semaphore.cpp | 8 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 76 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 14 |
7 files changed, 37 insertions, 101 deletions
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index e45deb1c6..f338f3266 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp | |||
| @@ -41,10 +41,7 @@ void Event::Acquire() { | |||
| 41 | 41 | ||
| 42 | void Event::Signal() { | 42 | void Event::Signal() { |
| 43 | signaled = true; | 43 | signaled = true; |
| 44 | |||
| 45 | WakeupAllWaitingThreads(); | 44 | WakeupAllWaitingThreads(); |
| 46 | |||
| 47 | HLE::Reschedule(__func__); | ||
| 48 | } | 45 | } |
| 49 | 46 | ||
| 50 | void Event::Clear() { | 47 | void Event::Clear() { |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 726e4d2ff..20e11da16 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -32,27 +32,13 @@ void WaitObject::RemoveWaitingThread(Thread* thread) { | |||
| 32 | waiting_threads.erase(itr); | 32 | waiting_threads.erase(itr); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | SharedPtr<Thread> WaitObject::WakeupNextThread() { | ||
| 36 | if (waiting_threads.empty()) | ||
| 37 | return nullptr; | ||
| 38 | |||
| 39 | auto next_thread = std::move(waiting_threads.front()); | ||
| 40 | waiting_threads.erase(waiting_threads.begin()); | ||
| 41 | |||
| 42 | next_thread->ReleaseWaitObject(this); | ||
| 43 | |||
| 44 | return next_thread; | ||
| 45 | } | ||
| 46 | |||
| 47 | void WaitObject::WakeupAllWaitingThreads() { | 35 | void WaitObject::WakeupAllWaitingThreads() { |
| 48 | auto waiting_threads_copy = waiting_threads; | 36 | for (auto thread : waiting_threads) |
| 37 | thread->ResumeFromWait(); | ||
| 49 | 38 | ||
| 50 | // We use a copy because ReleaseWaitObject will remove the thread from this object's | 39 | waiting_threads.clear(); |
| 51 | // waiting_threads list | ||
| 52 | for (auto thread : waiting_threads_copy) | ||
| 53 | thread->ReleaseWaitObject(this); | ||
| 54 | 40 | ||
| 55 | ASSERT_MSG(waiting_threads.empty(), "failed to awaken all waiting threads!"); | 41 | HLE::Reschedule(__func__); |
| 56 | } | 42 | } |
| 57 | 43 | ||
| 58 | HandleTable::HandleTable() { | 44 | HandleTable::HandleTable() { |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index a5a0f4800..64595f758 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -140,12 +140,6 @@ public: | |||
| 140 | */ | 140 | */ |
| 141 | void RemoveWaitingThread(Thread* thread); | 141 | void RemoveWaitingThread(Thread* thread); |
| 142 | 142 | ||
| 143 | /** | ||
| 144 | * Wake up the next thread waiting on this object | ||
| 145 | * @return Pointer to the thread that was resumed, nullptr if no threads are waiting | ||
| 146 | */ | ||
| 147 | SharedPtr<Thread> WakeupNextThread(); | ||
| 148 | |||
| 149 | /// Wake up all threads waiting on this object | 143 | /// Wake up all threads waiting on this object |
| 150 | void WakeupAllWaitingThreads(); | 144 | void WakeupAllWaitingThreads(); |
| 151 | 145 | ||
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 6aa73df86..edb97d324 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -23,12 +23,7 @@ static void ResumeWaitingThread(Mutex* mutex) { | |||
| 23 | // Reset mutex lock thread handle, nothing is waiting | 23 | // Reset mutex lock thread handle, nothing is waiting |
| 24 | mutex->lock_count = 0; | 24 | mutex->lock_count = 0; |
| 25 | mutex->holding_thread = nullptr; | 25 | mutex->holding_thread = nullptr; |
| 26 | 26 | mutex->WakeupAllWaitingThreads(); | |
| 27 | // Find the next waiting thread for the mutex... | ||
| 28 | auto next_thread = mutex->WakeupNextThread(); | ||
| 29 | if (next_thread != nullptr) { | ||
| 30 | mutex->Acquire(next_thread); | ||
| 31 | } | ||
| 32 | } | 27 | } |
| 33 | 28 | ||
| 34 | void ReleaseThreadMutexes(Thread* thread) { | 29 | void ReleaseThreadMutexes(Thread* thread) { |
| @@ -94,8 +89,6 @@ void Mutex::Release() { | |||
| 94 | ResumeWaitingThread(this); | 89 | ResumeWaitingThread(this); |
| 95 | } | 90 | } |
| 96 | } | 91 | } |
| 97 | |||
| 98 | HLE::Reschedule(__func__); | ||
| 99 | } | 92 | } |
| 100 | 93 | ||
| 101 | } // namespace | 94 | } // namespace |
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 96d61ed3a..4b359ed07 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp | |||
| @@ -48,13 +48,7 @@ ResultVal<s32> Semaphore::Release(s32 release_count) { | |||
| 48 | s32 previous_count = available_count; | 48 | s32 previous_count = available_count; |
| 49 | available_count += release_count; | 49 | available_count += release_count; |
| 50 | 50 | ||
| 51 | // Notify some of the threads that the semaphore has been released | 51 | WakeupAllWaitingThreads(); |
| 52 | // stop once the semaphore is full again or there are no more waiting threads | ||
| 53 | while (!ShouldWait() && WakeupNextThread() != nullptr) { | ||
| 54 | Acquire(); | ||
| 55 | } | ||
| 56 | |||
| 57 | HLE::Reschedule(__func__); | ||
| 58 | 52 | ||
| 59 | return MakeResult<s32>(previous_count); | 53 | return MakeResult<s32>(previous_count); |
| 60 | } | 54 | } |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 22c795ad4..4729a7fe0 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include "common/thread_queue_list.h" | 13 | #include "common/thread_queue_list.h" |
| 14 | 14 | ||
| 15 | #include "core/arm/arm_interface.h" | 15 | #include "core/arm/arm_interface.h" |
| 16 | #include "core/arm/skyeye_common/armdefs.h" | ||
| 16 | #include "core/core.h" | 17 | #include "core/core.h" |
| 17 | #include "core/core_timing.h" | 18 | #include "core/core_timing.h" |
| 18 | #include "core/hle/hle.h" | 19 | #include "core/hle/hle.h" |
| @@ -193,8 +194,22 @@ static void SwitchContext(Thread* new_thread) { | |||
| 193 | if (new_thread) { | 194 | if (new_thread) { |
| 194 | DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running."); | 195 | DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running."); |
| 195 | 196 | ||
| 197 | // Cancel any outstanding wakeup events for this thread | ||
| 198 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); | ||
| 199 | |||
| 196 | current_thread = new_thread; | 200 | current_thread = new_thread; |
| 197 | 201 | ||
| 202 | // If the thread was waited by a svcWaitSynch call, step back PC by one instruction to rerun | ||
| 203 | // the SVC when the thread wakes up. This is necessary to ensure that the thread can acquire | ||
| 204 | // the requested wait object(s) before continuing. | ||
| 205 | if (new_thread->waitsynch_waited) { | ||
| 206 | // CPSR flag indicates CPU mode | ||
| 207 | bool thumb_mode = (new_thread->context.cpsr & TBIT) != 0; | ||
| 208 | |||
| 209 | // SVC instruction is 2 bytes for THUMB, 4 bytes for ARM | ||
| 210 | new_thread->context.pc -= thumb_mode ? 2 : 4; | ||
| 211 | } | ||
| 212 | |||
| 198 | ready_queue.remove(new_thread->current_priority, new_thread); | 213 | ready_queue.remove(new_thread->current_priority, new_thread); |
| 199 | new_thread->status = THREADSTATUS_RUNNING; | 214 | new_thread->status = THREADSTATUS_RUNNING; |
| 200 | 215 | ||
| @@ -243,6 +258,7 @@ void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wa | |||
| 243 | thread->wait_set_output = wait_set_output; | 258 | thread->wait_set_output = wait_set_output; |
| 244 | thread->wait_all = wait_all; | 259 | thread->wait_all = wait_all; |
| 245 | thread->wait_objects = std::move(wait_objects); | 260 | thread->wait_objects = std::move(wait_objects); |
| 261 | thread->waitsynch_waited = true; | ||
| 246 | thread->status = THREADSTATUS_WAIT_SYNCH; | 262 | thread->status = THREADSTATUS_WAIT_SYNCH; |
| 247 | } | 263 | } |
| 248 | 264 | ||
| @@ -268,6 +284,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { | |||
| 268 | return; | 284 | return; |
| 269 | } | 285 | } |
| 270 | 286 | ||
| 287 | thread->waitsynch_waited = false; | ||
| 288 | |||
| 271 | if (thread->status == THREADSTATUS_WAIT_SYNCH) { | 289 | if (thread->status == THREADSTATUS_WAIT_SYNCH) { |
| 272 | thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | 290 | thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, |
| 273 | ErrorSummary::StatusChanged, ErrorLevel::Info)); | 291 | ErrorSummary::StatusChanged, ErrorLevel::Info)); |
| @@ -288,63 +306,20 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { | |||
| 288 | CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle); | 306 | CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle); |
| 289 | } | 307 | } |
| 290 | 308 | ||
| 291 | void Thread::ReleaseWaitObject(WaitObject* wait_object) { | ||
| 292 | if (status != THREADSTATUS_WAIT_SYNCH || wait_objects.empty()) { | ||
| 293 | LOG_CRITICAL(Kernel, "thread is not waiting on any objects!"); | ||
| 294 | return; | ||
| 295 | } | ||
| 296 | |||
| 297 | // Remove this thread from the waiting object's thread list | ||
| 298 | wait_object->RemoveWaitingThread(this); | ||
| 299 | |||
| 300 | unsigned index = 0; | ||
| 301 | bool wait_all_failed = false; // Will be set to true if any object is unavailable | ||
| 302 | |||
| 303 | // Iterate through all waiting objects to check availability... | ||
| 304 | for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) { | ||
| 305 | if ((*itr)->ShouldWait()) | ||
| 306 | wait_all_failed = true; | ||
| 307 | |||
| 308 | // The output should be the last index of wait_object | ||
| 309 | if (*itr == wait_object) | ||
| 310 | index = itr - wait_objects.begin(); | ||
| 311 | } | ||
| 312 | |||
| 313 | // If we are waiting on all objects... | ||
| 314 | if (wait_all) { | ||
| 315 | // Resume the thread only if all are available... | ||
| 316 | if (!wait_all_failed) { | ||
| 317 | SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 318 | SetWaitSynchronizationOutput(-1); | ||
| 319 | |||
| 320 | ResumeFromWait(); | ||
| 321 | } | ||
| 322 | } else { | ||
| 323 | // Otherwise, resume | ||
| 324 | SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 325 | |||
| 326 | if (wait_set_output) | ||
| 327 | SetWaitSynchronizationOutput(index); | ||
| 328 | |||
| 329 | ResumeFromWait(); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | void Thread::ResumeFromWait() { | 309 | void Thread::ResumeFromWait() { |
| 334 | // Cancel any outstanding wakeup events for this thread | ||
| 335 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); | ||
| 336 | |||
| 337 | switch (status) { | 310 | switch (status) { |
| 338 | case THREADSTATUS_WAIT_SYNCH: | 311 | case THREADSTATUS_WAIT_SYNCH: |
| 339 | // Remove this thread from all other WaitObjects | ||
| 340 | for (auto wait_object : wait_objects) | ||
| 341 | wait_object->RemoveWaitingThread(this); | ||
| 342 | break; | ||
| 343 | case THREADSTATUS_WAIT_ARB: | 312 | case THREADSTATUS_WAIT_ARB: |
| 344 | case THREADSTATUS_WAIT_SLEEP: | 313 | case THREADSTATUS_WAIT_SLEEP: |
| 345 | break; | 314 | break; |
| 346 | case THREADSTATUS_RUNNING: | 315 | |
| 347 | case THREADSTATUS_READY: | 316 | case THREADSTATUS_READY: |
| 317 | // If the thread is waiting on multiple wait objects, it might be awoken more than once | ||
| 318 | // before actually resuming. We can ignore subsequent wakeups if the thread status has | ||
| 319 | // already been set to THREADSTATUS_READY. | ||
| 320 | return; | ||
| 321 | |||
| 322 | case THREADSTATUS_RUNNING: | ||
| 348 | DEBUG_ASSERT_MSG(false, "Thread with object id %u has already resumed.", GetObjectId()); | 323 | DEBUG_ASSERT_MSG(false, "Thread with object id %u has already resumed.", GetObjectId()); |
| 349 | return; | 324 | return; |
| 350 | case THREADSTATUS_DEAD: | 325 | case THREADSTATUS_DEAD: |
| @@ -415,6 +390,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 415 | thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); | 390 | thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); |
| 416 | thread->owner_process = g_current_process; | 391 | thread->owner_process = g_current_process; |
| 417 | thread->tls_index = -1; | 392 | thread->tls_index = -1; |
| 393 | thread->waitsynch_waited = false; | ||
| 418 | 394 | ||
| 419 | // Find the next available TLS index, and mark it as used | 395 | // Find the next available TLS index, and mark it as used |
| 420 | auto& used_tls_slots = Kernel::g_current_process->used_tls_slots; | 396 | auto& used_tls_slots = Kernel::g_current_process->used_tls_slots; |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 2c65419c3..b8160bb2c 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -96,12 +96,6 @@ public: | |||
| 96 | u32 GetThreadId() const { return thread_id; } | 96 | u32 GetThreadId() const { return thread_id; } |
| 97 | 97 | ||
| 98 | /** | 98 | /** |
| 99 | * Release an acquired wait object | ||
| 100 | * @param wait_object WaitObject to release | ||
| 101 | */ | ||
| 102 | void ReleaseWaitObject(WaitObject* wait_object); | ||
| 103 | |||
| 104 | /** | ||
| 105 | * Resumes a thread from waiting | 99 | * Resumes a thread from waiting |
| 106 | */ | 100 | */ |
| 107 | void ResumeFromWait(); | 101 | void ResumeFromWait(); |
| @@ -152,6 +146,8 @@ public: | |||
| 152 | 146 | ||
| 153 | s32 tls_index; ///< Index of the Thread Local Storage of the thread | 147 | s32 tls_index; ///< Index of the Thread Local Storage of the thread |
| 154 | 148 | ||
| 149 | bool waitsynch_waited; ///< Set to true if the last svcWaitSynch call caused the thread to wait | ||
| 150 | |||
| 155 | /// Mutexes currently held by this thread, which will be released when it exits. | 151 | /// Mutexes currently held by this thread, which will be released when it exits. |
| 156 | boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; | 152 | boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; |
| 157 | 153 | ||
| @@ -163,12 +159,12 @@ public: | |||
| 163 | 159 | ||
| 164 | std::string name; | 160 | std::string name; |
| 165 | 161 | ||
| 162 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. | ||
| 163 | Handle callback_handle; | ||
| 164 | |||
| 166 | private: | 165 | private: |
| 167 | Thread(); | 166 | Thread(); |
| 168 | ~Thread() override; | 167 | ~Thread() override; |
| 169 | |||
| 170 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. | ||
| 171 | Handle callback_handle; | ||
| 172 | }; | 168 | }; |
| 173 | 169 | ||
| 174 | /** | 170 | /** |