mutex.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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 WaitObject {
  12. public:
  13. std::string GetTypeName() const override { return "Mutex"; }
  14. std::string GetName() const override { return name; }
  15. static const HandleType HANDLE_TYPE = HandleType::Mutex;
  16. HandleType GetHandleType() const override { return HANDLE_TYPE; }
  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::string name; ///< Name of mutex (optional)
  21. ResultVal<bool> WaitSynchronization(unsigned index) override;
  22. };
  23. ////////////////////////////////////////////////////////////////////////////////////////////////////
  24. typedef std::multimap<Handle, Handle> MutexMap;
  25. static MutexMap g_mutex_held_locks;
  26. /**
  27. * Acquires the specified mutex for the specified thread
  28. * @param mutex Mutex that is to be acquired
  29. * @param thread Thread that will acquired
  30. */
  31. void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandle()) {
  32. g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
  33. mutex->lock_thread = thread;
  34. }
  35. /**
  36. * Resumes a thread waiting for the specified mutex
  37. * @param mutex The mutex that some thread is waiting on
  38. */
  39. void ResumeWaitingThread(Mutex* mutex) {
  40. // Find the next waiting thread for the mutex...
  41. auto next_thread = mutex->ReleaseNextThread();
  42. if (next_thread != nullptr) {
  43. MutexAcquireLock(mutex, next_thread->GetHandle());
  44. } else {
  45. // Reset mutex lock thread handle, nothing is waiting
  46. mutex->locked = false;
  47. mutex->lock_thread = -1;
  48. }
  49. }
  50. void MutexEraseLock(Mutex* mutex) {
  51. Handle handle = mutex->GetHandle();
  52. auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread);
  53. for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
  54. if (iter->second == handle) {
  55. g_mutex_held_locks.erase(iter);
  56. break;
  57. }
  58. }
  59. mutex->lock_thread = -1;
  60. }
  61. void ReleaseThreadMutexes(Handle thread) {
  62. auto locked = g_mutex_held_locks.equal_range(thread);
  63. // Release every mutex that the thread holds, and resume execution on the waiting threads
  64. for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
  65. Mutex* mutex = g_handle_table.Get<Mutex>(iter->second).get();
  66. ResumeWaitingThread(mutex);
  67. }
  68. // Erase all the locks that this thread holds
  69. g_mutex_held_locks.erase(thread);
  70. }
  71. bool LockMutex(Mutex* mutex) {
  72. // Mutex alread locked?
  73. if (mutex->locked) {
  74. return false;
  75. }
  76. MutexAcquireLock(mutex);
  77. return true;
  78. }
  79. bool ReleaseMutex(Mutex* mutex) {
  80. MutexEraseLock(mutex);
  81. ResumeWaitingThread(mutex);
  82. return true;
  83. }
  84. /**
  85. * Releases a mutex
  86. * @param handle Handle to mutex to release
  87. */
  88. ResultCode ReleaseMutex(Handle handle) {
  89. Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle).get();
  90. if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel);
  91. if (!ReleaseMutex(mutex)) {
  92. // TODO(yuriks): Verify error code, this one was pulled out of thin air. I'm not even sure
  93. // what error condition this is supposed to be signaling.
  94. return ResultCode(ErrorDescription::AlreadyDone, ErrorModule::Kernel,
  95. ErrorSummary::NothingHappened, ErrorLevel::Temporary);
  96. }
  97. return RESULT_SUCCESS;
  98. }
  99. /**
  100. * Creates a mutex
  101. * @param handle Reference to handle for the newly created mutex
  102. * @param initial_locked Specifies if the mutex should be locked initially
  103. * @param name Optional name of mutex
  104. * @return Pointer to new Mutex object
  105. */
  106. Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) {
  107. Mutex* mutex = new Mutex;
  108. // TODO(yuriks): Fix error reporting
  109. handle = Kernel::g_handle_table.Create(mutex).ValueOr(INVALID_HANDLE);
  110. mutex->locked = mutex->initial_locked = initial_locked;
  111. mutex->name = name;
  112. // Acquire mutex with current thread if initialized as locked...
  113. if (mutex->locked) {
  114. MutexAcquireLock(mutex);
  115. // Otherwise, reset lock thread handle
  116. } else {
  117. mutex->lock_thread = -1;
  118. }
  119. return mutex;
  120. }
  121. /**
  122. * Creates a mutex
  123. * @param initial_locked Specifies if the mutex should be locked initially
  124. * @param name Optional name of mutex
  125. * @return Handle to newly created object
  126. */
  127. Handle CreateMutex(bool initial_locked, const std::string& name) {
  128. Handle handle;
  129. Mutex* mutex = CreateMutex(handle, initial_locked, name);
  130. return handle;
  131. }
  132. ResultVal<bool> Mutex::WaitSynchronization(unsigned index) {
  133. bool wait = locked;
  134. if (locked) {
  135. AddWaitingThread(GetCurrentThread());
  136. Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_MUTEX, this, index);
  137. } else {
  138. // Lock the mutex when the first thread accesses it
  139. locked = true;
  140. MutexAcquireLock(this);
  141. }
  142. return MakeResult<bool>(wait);
  143. }
  144. } // namespace