| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #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/k_thread_queue.h"
- #include "core/hle/kernel/kernel.h"
- #include "core/hle/kernel/svc_results.h"
- namespace Kernel {
- namespace {
- class ThreadQueueImplForKSynchronizationObjectWait final : public KThreadQueueWithoutEndWait {
- public:
- ThreadQueueImplForKSynchronizationObjectWait(KernelCore& kernel_, KSynchronizationObject** o,
- KSynchronizationObject::ThreadListNode* n, s32 c)
- : KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {}
- void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
- Result wait_result) override {
- // Determine the sync index, and unlink all nodes.
- s32 sync_index = -1;
- for (auto i = 0; i < m_count; ++i) {
- // Check if this is the signaled object.
- if (m_objects[i] == signaled_object && sync_index == -1) {
- sync_index = i;
- }
- // Unlink the current node from the current object.
- m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
- }
- // Set the waiting thread's sync index.
- waiting_thread->SetSyncedIndex(sync_index);
- // Set the waiting thread as not cancellable.
- waiting_thread->ClearCancellable();
- // Invoke the base end wait handler.
- KThreadQueue::EndWait(waiting_thread, wait_result);
- }
- void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
- // Remove all nodes from our list.
- for (auto i = 0; i < m_count; ++i) {
- m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
- }
- // Set the waiting thread as not cancellable.
- waiting_thread->ClearCancellable();
- // Invoke the base cancel wait handler.
- KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
- }
- private:
- KSynchronizationObject** m_objects;
- KSynchronizationObject::ThreadListNode* m_nodes;
- s32 m_count;
- };
- } // namespace
- void KSynchronizationObject::Finalize() {
- this->OnFinalizeSynchronizationObject();
- KAutoObject::Finalize();
- }
- Result 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 = GetCurrentThreadPointer(kernel);
- KHardwareTimer* timer{};
- ThreadQueueImplForKSynchronizationObjectWait wait_queue(kernel, objects, thread_nodes.data(),
- num_objects);
- {
- // Setup the scheduling lock and sleep.
- KScopedSchedulerLockAndSleep slp(kernel, std::addressof(timer), thread, timeout);
- // Check if the thread should terminate.
- if (thread->IsTerminationRequested()) {
- slp.CancelSleep();
- R_THROW(ResultTerminationRequested);
- }
- // 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();
- R_THROW(ResultSuccess);
- }
- }
- // Check if the timeout is zero.
- if (timeout == 0) {
- slp.CancelSleep();
- R_THROW(ResultTimedOut);
- }
- // Check if waiting was canceled.
- if (thread->IsWaitCancelled()) {
- slp.CancelSleep();
- thread->ClearWaitCancelled();
- R_THROW(ResultCancelled);
- }
- // Add the waiters.
- for (auto i = 0; i < num_objects; ++i) {
- thread_nodes[i].thread = thread;
- thread_nodes[i].next = nullptr;
- objects[i]->LinkNode(std::addressof(thread_nodes[i]));
- }
- // Mark the thread as cancellable.
- thread->SetCancellable();
- // Clear the thread's synced index.
- thread->SetSyncedIndex(-1);
- // Wait for an object to be signaled.
- wait_queue.SetHardwareTimer(timer);
- thread->BeginWait(std::addressof(wait_queue));
- thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
- }
- // Set the output index.
- *out_index = thread->GetSyncedIndex();
- // Get the wait result.
- R_RETURN(thread->GetWaitResult());
- }
- KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
- : KAutoObjectWithList{kernel_} {}
- KSynchronizationObject::~KSynchronizationObject() = default;
- void KSynchronizationObject::NotifyAvailable(Result result) {
- KScopedSchedulerLock sl(kernel);
- // If we're not signaled, we've nothing to notify.
- if (!this->IsSignaled()) {
- return;
- }
- // Iterate over each thread.
- for (auto* cur_node = m_thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
- cur_node->thread->NotifyAvailable(this, result);
- }
- }
- std::vector<KThread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
- std::vector<KThread*> threads;
- // If debugging, dump the list of waiters.
- {
- KScopedSchedulerLock lock(kernel);
- for (auto* cur_node = m_thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
- threads.emplace_back(cur_node->thread);
- }
- }
- return threads;
- }
- } // namespace Kernel
|