k_synchronization_object.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "common/assert.h"
  4. #include "common/common_types.h"
  5. #include "core/hle/kernel/k_scheduler.h"
  6. #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
  7. #include "core/hle/kernel/k_synchronization_object.h"
  8. #include "core/hle/kernel/k_thread.h"
  9. #include "core/hle/kernel/k_thread_queue.h"
  10. #include "core/hle/kernel/kernel.h"
  11. #include "core/hle/kernel/svc_results.h"
  12. namespace Kernel {
  13. namespace {
  14. class ThreadQueueImplForKSynchronizationObjectWait final : public KThreadQueueWithoutEndWait {
  15. public:
  16. ThreadQueueImplForKSynchronizationObjectWait(KernelCore& kernel_, KSynchronizationObject** o,
  17. KSynchronizationObject::ThreadListNode* n, s32 c)
  18. : KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {}
  19. void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
  20. Result wait_result) override {
  21. // Determine the sync index, and unlink all nodes.
  22. s32 sync_index = -1;
  23. for (auto i = 0; i < m_count; ++i) {
  24. // Check if this is the signaled object.
  25. if (m_objects[i] == signaled_object && sync_index == -1) {
  26. sync_index = i;
  27. }
  28. // Unlink the current node from the current object.
  29. m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
  30. }
  31. // Set the waiting thread's sync index.
  32. waiting_thread->SetSyncedIndex(sync_index);
  33. // Set the waiting thread as not cancellable.
  34. waiting_thread->ClearCancellable();
  35. // Invoke the base end wait handler.
  36. KThreadQueue::EndWait(waiting_thread, wait_result);
  37. }
  38. void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
  39. // Remove all nodes from our list.
  40. for (auto i = 0; i < m_count; ++i) {
  41. m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
  42. }
  43. // Set the waiting thread as not cancellable.
  44. waiting_thread->ClearCancellable();
  45. // Invoke the base cancel wait handler.
  46. KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
  47. }
  48. private:
  49. KSynchronizationObject** m_objects;
  50. KSynchronizationObject::ThreadListNode* m_nodes;
  51. s32 m_count;
  52. };
  53. } // namespace
  54. void KSynchronizationObject::Finalize() {
  55. this->OnFinalizeSynchronizationObject();
  56. KAutoObject::Finalize();
  57. }
  58. Result KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
  59. KSynchronizationObject** objects, const s32 num_objects,
  60. s64 timeout) {
  61. // Allocate space on stack for thread nodes.
  62. std::vector<ThreadListNode> thread_nodes(num_objects);
  63. // Prepare for wait.
  64. KThread* thread = GetCurrentThreadPointer(kernel);
  65. KHardwareTimer* timer{};
  66. ThreadQueueImplForKSynchronizationObjectWait wait_queue(kernel, objects, thread_nodes.data(),
  67. num_objects);
  68. {
  69. // Setup the scheduling lock and sleep.
  70. KScopedSchedulerLockAndSleep slp(kernel, std::addressof(timer), thread, timeout);
  71. // Check if the thread should terminate.
  72. if (thread->IsTerminationRequested()) {
  73. slp.CancelSleep();
  74. R_THROW(ResultTerminationRequested);
  75. }
  76. // Check if any of the objects are already signaled.
  77. for (auto i = 0; i < num_objects; ++i) {
  78. ASSERT(objects[i] != nullptr);
  79. if (objects[i]->IsSignaled()) {
  80. *out_index = i;
  81. slp.CancelSleep();
  82. R_THROW(ResultSuccess);
  83. }
  84. }
  85. // Check if the timeout is zero.
  86. if (timeout == 0) {
  87. slp.CancelSleep();
  88. R_THROW(ResultTimedOut);
  89. }
  90. // Check if waiting was canceled.
  91. if (thread->IsWaitCancelled()) {
  92. slp.CancelSleep();
  93. thread->ClearWaitCancelled();
  94. R_THROW(ResultCancelled);
  95. }
  96. // Add the waiters.
  97. for (auto i = 0; i < num_objects; ++i) {
  98. thread_nodes[i].thread = thread;
  99. thread_nodes[i].next = nullptr;
  100. objects[i]->LinkNode(std::addressof(thread_nodes[i]));
  101. }
  102. // Mark the thread as cancellable.
  103. thread->SetCancellable();
  104. // Clear the thread's synced index.
  105. thread->SetSyncedIndex(-1);
  106. // Wait for an object to be signaled.
  107. wait_queue.SetHardwareTimer(timer);
  108. thread->BeginWait(std::addressof(wait_queue));
  109. thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
  110. }
  111. // Set the output index.
  112. *out_index = thread->GetSyncedIndex();
  113. // Get the wait result.
  114. R_RETURN(thread->GetWaitResult());
  115. }
  116. KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
  117. : KAutoObjectWithList{kernel_} {}
  118. KSynchronizationObject::~KSynchronizationObject() = default;
  119. void KSynchronizationObject::NotifyAvailable(Result result) {
  120. KScopedSchedulerLock sl(kernel);
  121. // If we're not signaled, we've nothing to notify.
  122. if (!this->IsSignaled()) {
  123. return;
  124. }
  125. // Iterate over each thread.
  126. for (auto* cur_node = m_thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
  127. cur_node->thread->NotifyAvailable(this, result);
  128. }
  129. }
  130. std::vector<KThread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
  131. std::vector<KThread*> threads;
  132. // If debugging, dump the list of waiters.
  133. {
  134. KScopedSchedulerLock lock(kernel);
  135. for (auto* cur_node = m_thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
  136. threads.emplace_back(cur_node->thread);
  137. }
  138. }
  139. return threads;
  140. }
  141. } // namespace Kernel