mutex.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <utility>
  5. #include <vector>
  6. #include "common/assert.h"
  7. #include "core/core.h"
  8. #include "core/hle/kernel/errors.h"
  9. #include "core/hle/kernel/handle_table.h"
  10. #include "core/hle/kernel/kernel.h"
  11. #include "core/hle/kernel/mutex.h"
  12. #include "core/hle/kernel/object.h"
  13. #include "core/hle/kernel/process.h"
  14. #include "core/hle/kernel/scheduler.h"
  15. #include "core/hle/kernel/thread.h"
  16. #include "core/hle/result.h"
  17. #include "core/memory.h"
  18. namespace Kernel {
  19. /// Returns the number of threads that are waiting for a mutex, and the highest priority one among
  20. /// those.
  21. static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
  22. const std::shared_ptr<Thread>& current_thread, VAddr mutex_addr) {
  23. std::shared_ptr<Thread> highest_priority_thread;
  24. u32 num_waiters = 0;
  25. for (const auto& thread : current_thread->GetMutexWaitingThreads()) {
  26. if (thread->GetMutexWaitAddress() != mutex_addr)
  27. continue;
  28. ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
  29. ++num_waiters;
  30. if (highest_priority_thread == nullptr ||
  31. thread->GetPriority() < highest_priority_thread->GetPriority()) {
  32. highest_priority_thread = thread;
  33. }
  34. }
  35. return {highest_priority_thread, num_waiters};
  36. }
  37. /// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
  38. static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread,
  39. std::shared_ptr<Thread> new_owner) {
  40. const auto threads = current_thread->GetMutexWaitingThreads();
  41. for (const auto& thread : threads) {
  42. if (thread->GetMutexWaitAddress() != mutex_addr)
  43. continue;
  44. ASSERT(thread->GetLockOwner() == current_thread.get());
  45. current_thread->RemoveMutexWaiter(thread);
  46. if (new_owner != thread)
  47. new_owner->AddMutexWaiter(thread);
  48. }
  49. }
  50. Mutex::Mutex(Core::System& system) : system{system} {}
  51. Mutex::~Mutex() = default;
  52. ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
  53. Handle requesting_thread_handle) {
  54. // The mutex address must be 4-byte aligned
  55. if ((address % sizeof(u32)) != 0) {
  56. return ERR_INVALID_ADDRESS;
  57. }
  58. const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
  59. std::shared_ptr<Thread> current_thread =
  60. SharedFrom(system.CurrentScheduler().GetCurrentThread());
  61. std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
  62. std::shared_ptr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle);
  63. // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
  64. // thread.
  65. ASSERT(requesting_thread == current_thread);
  66. const u32 addr_value = Memory::Read32(address);
  67. // If the mutex isn't being held, just return success.
  68. if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
  69. return RESULT_SUCCESS;
  70. }
  71. if (holding_thread == nullptr) {
  72. return ERR_INVALID_HANDLE;
  73. }
  74. // Wait until the mutex is released
  75. current_thread->SetMutexWaitAddress(address);
  76. current_thread->SetWaitHandle(requesting_thread_handle);
  77. current_thread->SetStatus(ThreadStatus::WaitMutex);
  78. current_thread->InvalidateWakeupCallback();
  79. // Update the lock holder thread's priority to prevent priority inversion.
  80. holding_thread->AddMutexWaiter(current_thread);
  81. system.PrepareReschedule();
  82. return RESULT_SUCCESS;
  83. }
  84. ResultCode Mutex::Release(VAddr address) {
  85. // The mutex address must be 4-byte aligned
  86. if ((address % sizeof(u32)) != 0) {
  87. return ERR_INVALID_ADDRESS;
  88. }
  89. std::shared_ptr<Thread> current_thread =
  90. SharedFrom(system.CurrentScheduler().GetCurrentThread());
  91. auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address);
  92. // There are no more threads waiting for the mutex, release it completely.
  93. if (thread == nullptr) {
  94. Memory::Write32(address, 0);
  95. return RESULT_SUCCESS;
  96. }
  97. // Transfer the ownership of the mutex from the previous owner to the new one.
  98. TransferMutexOwnership(address, current_thread, thread);
  99. u32 mutex_value = thread->GetWaitHandle();
  100. if (num_waiters >= 2) {
  101. // Notify the guest that there are still some threads waiting for the mutex
  102. mutex_value |= Mutex::MutexHasWaitersFlag;
  103. }
  104. // Grant the mutex to the next waiting thread and resume it.
  105. Memory::Write32(address, mutex_value);
  106. ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
  107. thread->ResumeFromWait();
  108. thread->SetLockOwner(nullptr);
  109. thread->SetCondVarWaitAddress(0);
  110. thread->SetMutexWaitAddress(0);
  111. thread->SetWaitHandle(0);
  112. thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
  113. system.PrepareReschedule();
  114. return RESULT_SUCCESS;
  115. }
  116. } // namespace Kernel