mutex.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <map>
  5. #include <vector>
  6. #include "common/common.h"
  7. #include "core/hle/kernel/kernel.h"
  8. #include "core/hle/kernel/mutex.h"
  9. #include "core/hle/kernel/thread.h"
  10. namespace Kernel {
  11. class Mutex : public Object {
  12. public:
  13. std::string GetTypeName() const override { return "Mutex"; }
  14. std::string GetName() const override { return name; }
  15. static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; }
  16. Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Mutex; }
  17. bool initial_locked; ///< Initial lock state when mutex was created
  18. bool locked; ///< Current locked state
  19. Handle lock_thread; ///< Handle to thread that currently has mutex
  20. std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
  21. std::string name; ///< Name of mutex (optional)
  22. ResultVal<bool> WaitSynchronization() override;
  23. };
  24. ////////////////////////////////////////////////////////////////////////////////////////////////////
  25. typedef std::multimap<Handle, Handle> MutexMap;
  26. static MutexMap g_mutex_held_locks;
  27. /**
  28. * Acquires the specified mutex for the specified thread
  29. * @param mutex Mutex that is to be acquired
  30. * @param thread Thread that will acquired
  31. */
  32. void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) {
  33. g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
  34. mutex->lock_thread = thread;
  35. }
  36. bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
  37. MutexAcquireLock(mutex, thread);
  38. Kernel::ResumeThreadFromWait(thread);
  39. return true;
  40. }
  41. /**
  42. * Resumes a thread waiting for the specified mutex
  43. * @param mutex The mutex that some thread is waiting on
  44. */
  45. void ResumeWaitingThread(Mutex* mutex) {
  46. // Find the next waiting thread for the mutex...
  47. if (mutex->waiting_threads.empty()) {
  48. // Reset mutex lock thread handle, nothing is waiting
  49. mutex->locked = false;
  50. mutex->lock_thread = -1;
  51. }
  52. else {
  53. // Resume the next waiting thread and re-lock the mutex
  54. std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
  55. ReleaseMutexForThread(mutex, *iter);
  56. mutex->waiting_threads.erase(iter);
  57. }
  58. }
  59. void MutexEraseLock(Mutex* mutex) {
  60. Handle handle = mutex->GetHandle();
  61. auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread);
  62. for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
  63. if (iter->second == handle) {
  64. g_mutex_held_locks.erase(iter);
  65. break;
  66. }
  67. }
  68. mutex->lock_thread = -1;
  69. }
  70. void ReleaseThreadMutexes(Handle thread) {
  71. auto locked = g_mutex_held_locks.equal_range(thread);
  72. // Release every mutex that the thread holds, and resume execution on the waiting threads
  73. for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
  74. Mutex* mutex = g_handle_table.GetFast<Mutex>(iter->second);
  75. ResumeWaitingThread(mutex);
  76. }
  77. // Erase all the locks that this thread holds
  78. g_mutex_held_locks.erase(thread);
  79. }
  80. bool LockMutex(Mutex* mutex) {
  81. // Mutex alread locked?
  82. if (mutex->locked) {
  83. return false;
  84. }
  85. MutexAcquireLock(mutex);
  86. return true;
  87. }
  88. bool ReleaseMutex(Mutex* mutex) {
  89. MutexEraseLock(mutex);
  90. ResumeWaitingThread(mutex);
  91. return true;
  92. }
  93. /**
  94. * Releases a mutex
  95. * @param handle Handle to mutex to release
  96. */
  97. ResultCode ReleaseMutex(Handle handle) {
  98. Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle);
  99. if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel);
  100. if (!ReleaseMutex(mutex)) {
  101. // TODO(yuriks): Verify error code, this one was pulled out of thin air. I'm not even sure
  102. // what error condition this is supposed to be signaling.
  103. return ResultCode(ErrorDescription::AlreadyDone, ErrorModule::Kernel,
  104. ErrorSummary::NothingHappened, ErrorLevel::Temporary);
  105. }
  106. return RESULT_SUCCESS;
  107. }
  108. /**
  109. * Creates a mutex
  110. * @param handle Reference to handle for the newly created mutex
  111. * @param initial_locked Specifies if the mutex should be locked initially
  112. * @param name Optional name of mutex
  113. * @return Pointer to new Mutex object
  114. */
  115. Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) {
  116. Mutex* mutex = new Mutex;
  117. handle = Kernel::g_handle_table.Create(mutex);
  118. mutex->locked = mutex->initial_locked = initial_locked;
  119. mutex->name = name;
  120. // Acquire mutex with current thread if initialized as locked...
  121. if (mutex->locked) {
  122. MutexAcquireLock(mutex);
  123. // Otherwise, reset lock thread handle
  124. } else {
  125. mutex->lock_thread = -1;
  126. }
  127. return mutex;
  128. }
  129. /**
  130. * Creates a mutex
  131. * @param initial_locked Specifies if the mutex should be locked initially
  132. * @param name Optional name of mutex
  133. * @return Handle to newly created object
  134. */
  135. Handle CreateMutex(bool initial_locked, const std::string& name) {
  136. Handle handle;
  137. Mutex* mutex = CreateMutex(handle, initial_locked, name);
  138. return handle;
  139. }
  140. ResultVal<bool> Mutex::WaitSynchronization() {
  141. bool wait = locked;
  142. if (locked) {
  143. Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
  144. }
  145. else {
  146. // Lock the mutex when the first thread accesses it
  147. locked = true;
  148. MutexAcquireLock(this);
  149. }
  150. return MakeResult<bool>(wait);
  151. }
  152. } // namespace