k_synchronization_object.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "common/assert.h"
  5. #include "common/common_types.h"
  6. #include "core/hle/kernel/k_scheduler.h"
  7. #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
  8. #include "core/hle/kernel/k_synchronization_object.h"
  9. #include "core/hle/kernel/k_thread.h"
  10. #include "core/hle/kernel/kernel.h"
  11. #include "core/hle/kernel/svc_results.h"
  12. namespace Kernel {
  13. void KSynchronizationObject::Finalize() {
  14. this->OnFinalizeSynchronizationObject();
  15. KAutoObject::Finalize();
  16. }
  17. ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
  18. KSynchronizationObject** objects, const s32 num_objects,
  19. s64 timeout) {
  20. // Allocate space on stack for thread nodes.
  21. std::vector<ThreadListNode> thread_nodes(num_objects);
  22. // Prepare for wait.
  23. KThread* thread = kernel_ctx.CurrentScheduler()->GetCurrentThread();
  24. {
  25. // Setup the scheduling lock and sleep.
  26. KScopedSchedulerLockAndSleep slp{kernel_ctx, thread, timeout};
  27. // Check if any of the objects are already signaled.
  28. for (auto i = 0; i < num_objects; ++i) {
  29. ASSERT(objects[i] != nullptr);
  30. if (objects[i]->IsSignaled()) {
  31. *out_index = i;
  32. slp.CancelSleep();
  33. return RESULT_SUCCESS;
  34. }
  35. }
  36. // Check if the timeout is zero.
  37. if (timeout == 0) {
  38. slp.CancelSleep();
  39. return ResultTimedOut;
  40. }
  41. // Check if the thread should terminate.
  42. if (thread->IsTerminationRequested()) {
  43. slp.CancelSleep();
  44. return ResultTerminationRequested;
  45. }
  46. // Check if waiting was canceled.
  47. if (thread->IsWaitCancelled()) {
  48. slp.CancelSleep();
  49. thread->ClearWaitCancelled();
  50. return ResultCancelled;
  51. }
  52. // Add the waiters.
  53. for (auto i = 0; i < num_objects; ++i) {
  54. thread_nodes[i].thread = thread;
  55. thread_nodes[i].next = nullptr;
  56. if (objects[i]->thread_list_tail == nullptr) {
  57. objects[i]->thread_list_head = std::addressof(thread_nodes[i]);
  58. } else {
  59. objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]);
  60. }
  61. objects[i]->thread_list_tail = std::addressof(thread_nodes[i]);
  62. }
  63. // For debugging only
  64. thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)});
  65. // Mark the thread as waiting.
  66. thread->SetCancellable();
  67. thread->SetSyncedObject(nullptr, ResultTimedOut);
  68. thread->SetState(ThreadState::Waiting);
  69. thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
  70. }
  71. // The lock/sleep is done, so we should be able to get our result.
  72. // Thread is no longer cancellable.
  73. thread->ClearCancellable();
  74. // For debugging only
  75. thread->SetWaitObjectsForDebugging({});
  76. // Cancel the timer as needed.
  77. kernel_ctx.TimeManager().UnscheduleTimeEvent(thread);
  78. // Get the wait result.
  79. ResultCode wait_result{RESULT_SUCCESS};
  80. s32 sync_index = -1;
  81. {
  82. KScopedSchedulerLock lock(kernel_ctx);
  83. KSynchronizationObject* synced_obj;
  84. wait_result = thread->GetWaitResult(std::addressof(synced_obj));
  85. for (auto i = 0; i < num_objects; ++i) {
  86. // Unlink the object from the list.
  87. ThreadListNode* prev_ptr =
  88. reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head));
  89. ThreadListNode* prev_val = nullptr;
  90. ThreadListNode *prev, *tail_prev;
  91. do {
  92. prev = prev_ptr;
  93. prev_ptr = prev_ptr->next;
  94. tail_prev = prev_val;
  95. prev_val = prev_ptr;
  96. } while (prev_ptr != std::addressof(thread_nodes[i]));
  97. if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) {
  98. objects[i]->thread_list_tail = tail_prev;
  99. }
  100. prev->next = thread_nodes[i].next;
  101. if (objects[i] == synced_obj) {
  102. sync_index = i;
  103. }
  104. }
  105. }
  106. // Set output.
  107. *out_index = sync_index;
  108. return wait_result;
  109. }
  110. KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
  111. : KAutoObjectWithList{kernel_} {}
  112. KSynchronizationObject::~KSynchronizationObject() = default;
  113. void KSynchronizationObject::NotifyAvailable(ResultCode result) {
  114. KScopedSchedulerLock lock(kernel);
  115. // If we're not signaled, we've nothing to notify.
  116. if (!this->IsSignaled()) {
  117. return;
  118. }
  119. // Iterate over each thread.
  120. for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
  121. KThread* thread = cur_node->thread;
  122. if (thread->GetState() == ThreadState::Waiting) {
  123. thread->SetSyncedObject(this, result);
  124. thread->SetState(ThreadState::Runnable);
  125. }
  126. }
  127. }
  128. std::vector<KThread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
  129. std::vector<KThread*> threads;
  130. // If debugging, dump the list of waiters.
  131. {
  132. KScopedSchedulerLock lock(kernel);
  133. for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
  134. threads.emplace_back(cur_node->thread);
  135. }
  136. }
  137. return threads;
  138. }
  139. } // namespace Kernel