| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- // Copyright 2021 yuzu Emulator Project
- // Licensed under GPLv2 or any later version
- // Refer to the license.txt file included.
- #include "common/assert.h"
- #include "common/common_types.h"
- #include "core/hle/kernel/k_scheduler.h"
- #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
- #include "core/hle/kernel/k_synchronization_object.h"
- #include "core/hle/kernel/k_thread.h"
- #include "core/hle/kernel/kernel.h"
- #include "core/hle/kernel/svc_results.h"
- namespace Kernel {
- ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
- KSynchronizationObject** objects, const s32 num_objects,
- s64 timeout) {
- // Allocate space on stack for thread nodes.
- std::vector<ThreadListNode> thread_nodes(num_objects);
- // Prepare for wait.
- KThread* thread = kernel.CurrentScheduler()->GetCurrentThread();
- Handle timer = InvalidHandle;
- {
- // Setup the scheduling lock and sleep.
- KScopedSchedulerLockAndSleep slp(kernel, timer, thread, timeout);
- // Check if any of the objects are already signaled.
- for (auto i = 0; i < num_objects; ++i) {
- ASSERT(objects[i] != nullptr);
- if (objects[i]->IsSignaled()) {
- *out_index = i;
- slp.CancelSleep();
- return RESULT_SUCCESS;
- }
- }
- // Check if the timeout is zero.
- if (timeout == 0) {
- slp.CancelSleep();
- return Svc::ResultTimedOut;
- }
- // Check if the thread should terminate.
- if (thread->IsTerminationRequested()) {
- slp.CancelSleep();
- return Svc::ResultTerminationRequested;
- }
- // Check if waiting was canceled.
- if (thread->IsWaitCancelled()) {
- slp.CancelSleep();
- thread->ClearWaitCancelled();
- return Svc::ResultCancelled;
- }
- // Add the waiters.
- for (auto i = 0; i < num_objects; ++i) {
- thread_nodes[i].thread = thread;
- thread_nodes[i].next = nullptr;
- if (objects[i]->thread_list_tail == nullptr) {
- objects[i]->thread_list_head = std::addressof(thread_nodes[i]);
- } else {
- objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]);
- }
- objects[i]->thread_list_tail = std::addressof(thread_nodes[i]);
- }
- // For debugging only
- thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)});
- // Mark the thread as waiting.
- thread->SetCancellable();
- thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
- thread->SetState(ThreadState::Waiting);
- thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
- }
- // The lock/sleep is done, so we should be able to get our result.
- // Thread is no longer cancellable.
- thread->ClearCancellable();
- // For debugging only
- thread->SetWaitObjectsForDebugging({});
- // Cancel the timer as needed.
- if (timer != InvalidHandle) {
- auto& time_manager = kernel.TimeManager();
- time_manager.UnscheduleTimeEvent(timer);
- }
- // Get the wait result.
- ResultCode wait_result{RESULT_SUCCESS};
- s32 sync_index = -1;
- {
- KScopedSchedulerLock lock(kernel);
- KSynchronizationObject* synced_obj;
- wait_result = thread->GetWaitResult(std::addressof(synced_obj));
- for (auto i = 0; i < num_objects; ++i) {
- // Unlink the object from the list.
- ThreadListNode* prev_ptr =
- reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head));
- ThreadListNode* prev_val = nullptr;
- ThreadListNode *prev, *tail_prev;
- do {
- prev = prev_ptr;
- prev_ptr = prev_ptr->next;
- tail_prev = prev_val;
- prev_val = prev_ptr;
- } while (prev_ptr != std::addressof(thread_nodes[i]));
- if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) {
- objects[i]->thread_list_tail = tail_prev;
- }
- prev->next = thread_nodes[i].next;
- if (objects[i] == synced_obj) {
- sync_index = i;
- }
- }
- }
- // Set output.
- *out_index = sync_index;
- return wait_result;
- }
- KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : Object{kernel} {}
- KSynchronizationObject::~KSynchronizationObject() = default;
- void KSynchronizationObject::NotifyAvailable(ResultCode result) {
- KScopedSchedulerLock lock(kernel);
- // If we're not signaled, we've nothing to notify.
- if (!this->IsSignaled()) {
- return;
- }
- // Iterate over each thread.
- for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
- KThread* thread = cur_node->thread;
- if (thread->GetState() == ThreadState::Waiting) {
- thread->SetSyncedObject(this, result);
- thread->SetState(ThreadState::Runnable);
- }
- }
- }
- std::vector<KThread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
- std::vector<KThread*> threads;
- // If debugging, dump the list of waiters.
- {
- KScopedSchedulerLock lock(kernel);
- for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
- threads.emplace_back(cur_node->thread);
- }
- }
- return threads;
- }
- } // namespace Kernel
|