Mutex.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <Kernel/Debug.h>
  8. #include <Kernel/KSyms.h>
  9. #include <Kernel/Locking/LockLocation.h>
  10. #include <Kernel/Locking/Mutex.h>
  11. #include <Kernel/Locking/Spinlock.h>
  12. #include <Kernel/Thread.h>
  13. namespace Kernel {
  14. void Mutex::lock(Mode mode, [[maybe_unused]] LockLocation const& location)
  15. {
  16. // NOTE: This may be called from an interrupt handler (not an IRQ handler)
  17. // and also from within critical sections!
  18. VERIFY(!Processor::current_in_irq());
  19. if constexpr (LOCK_IN_CRITICAL_DEBUG)
  20. VERIFY_INTERRUPTS_ENABLED();
  21. VERIFY(mode != Mode::Unlocked);
  22. auto* current_thread = Thread::current();
  23. SpinlockLocker lock(m_lock);
  24. bool did_block = false;
  25. Mode current_mode = m_mode;
  26. switch (current_mode) {
  27. case Mode::Unlocked: {
  28. dbgln_if(LOCK_TRACE_DEBUG, "Mutex::lock @ ({}) {}: acquire {}, currently unlocked", this, m_name, mode_to_string(mode));
  29. m_mode = mode;
  30. VERIFY(!m_holder);
  31. VERIFY(m_shared_holders == 0);
  32. if (mode == Mode::Exclusive) {
  33. m_holder = current_thread;
  34. } else {
  35. VERIFY(mode == Mode::Shared);
  36. ++m_shared_holders;
  37. #if LOCK_SHARED_UPGRADE_DEBUG
  38. m_shared_holders_map.set(current_thread, 1);
  39. #endif
  40. }
  41. VERIFY(m_times_locked == 0);
  42. m_times_locked++;
  43. #if LOCK_DEBUG
  44. if (current_thread) {
  45. current_thread->holding_lock(*this, 1, location);
  46. }
  47. #endif
  48. return;
  49. }
  50. case Mode::Exclusive: {
  51. VERIFY(m_holder);
  52. if (m_holder != current_thread) {
  53. block(*current_thread, mode, lock, 1);
  54. did_block = true;
  55. // If we blocked then m_mode should have been updated to what we requested
  56. VERIFY(m_mode == mode);
  57. }
  58. if (m_mode == Mode::Exclusive) {
  59. VERIFY(m_holder == current_thread);
  60. VERIFY(m_shared_holders == 0);
  61. } else if (did_block && mode == Mode::Shared) {
  62. // Only if we blocked trying to acquire a shared lock the lock would have been converted
  63. VERIFY(!m_holder);
  64. VERIFY(m_shared_holders > 0);
  65. }
  66. if constexpr (LOCK_TRACE_DEBUG) {
  67. if (mode == Mode::Exclusive)
  68. dbgln("Mutex::lock @ {} ({}): acquire {}, currently exclusive, holding: {}", this, m_name, mode_to_string(mode), m_times_locked);
  69. else
  70. dbgln("Mutex::lock @ {} ({}): acquire exclusive (requested {}), currently exclusive, holding: {}", this, m_name, mode_to_string(mode), m_times_locked);
  71. }
  72. VERIFY(m_times_locked > 0);
  73. if (!did_block) {
  74. // if we didn't block we must still be an exclusive lock
  75. VERIFY(m_mode == Mode::Exclusive);
  76. m_times_locked++;
  77. }
  78. #if LOCK_DEBUG
  79. current_thread->holding_lock(*this, 1, location);
  80. #endif
  81. return;
  82. }
  83. case Mode::Shared: {
  84. VERIFY(!m_holder);
  85. if (mode == Mode::Exclusive) {
  86. dbgln_if(LOCK_TRACE_DEBUG, "Mutex::lock @ {} ({}): blocking for exclusive access, currently shared, locks held {}", this, m_name, m_times_locked);
  87. #if LOCK_SHARED_UPGRADE_DEBUG
  88. VERIFY(m_shared_holders_map.size() != 1 || m_shared_holders_map.begin()->key != current_thread);
  89. #endif
  90. // WARNING: The following block will deadlock if the current thread is the only shared locker of this Mutex
  91. // and is asking to upgrade the lock to be exclusive without first releasing the shared lock. We have no
  92. // allocation-free way to detect such a scenario, so if you suspect that this is the cause of your deadlock,
  93. // try turning on LOCK_SHARED_UPGRADE_DEBUG.
  94. block(*current_thread, mode, lock, 1);
  95. did_block = true;
  96. VERIFY(m_mode == mode);
  97. }
  98. dbgln_if(LOCK_TRACE_DEBUG, "Mutex::lock @ {} ({}): acquire {}, currently shared, locks held {}", this, m_name, mode_to_string(mode), m_times_locked);
  99. VERIFY(m_times_locked > 0);
  100. if (m_mode == Mode::Shared) {
  101. VERIFY(!m_holder);
  102. VERIFY(!did_block);
  103. } else if (did_block) {
  104. VERIFY(mode == Mode::Exclusive);
  105. VERIFY(m_holder == current_thread);
  106. VERIFY(m_shared_holders == 0);
  107. }
  108. if (!did_block) {
  109. // if we didn't block we must still be a shared lock
  110. VERIFY(m_mode == Mode::Shared);
  111. m_times_locked++;
  112. VERIFY(m_shared_holders > 0);
  113. ++m_shared_holders;
  114. #if LOCK_SHARED_UPGRADE_DEBUG
  115. auto it = m_shared_holders_map.find(current_thread);
  116. if (it != m_shared_holders_map.end())
  117. it->value++;
  118. else
  119. m_shared_holders_map.set(current_thread, 1);
  120. #endif
  121. }
  122. #if LOCK_DEBUG
  123. current_thread->holding_lock(*this, 1, location);
  124. #endif
  125. return;
  126. }
  127. default:
  128. VERIFY_NOT_REACHED();
  129. }
  130. }
  131. void Mutex::unlock()
  132. {
  133. // NOTE: This may be called from an interrupt handler (not an IRQ handler)
  134. // and also from within critical sections!
  135. if constexpr (LOCK_IN_CRITICAL_DEBUG)
  136. VERIFY_INTERRUPTS_ENABLED();
  137. VERIFY(!Processor::current_in_irq());
  138. auto* current_thread = Thread::current();
  139. SpinlockLocker lock(m_lock);
  140. Mode current_mode = m_mode;
  141. if constexpr (LOCK_TRACE_DEBUG) {
  142. if (current_mode == Mode::Shared)
  143. dbgln("Mutex::unlock @ {} ({}): release {}, locks held: {}", this, m_name, mode_to_string(current_mode), m_times_locked);
  144. else
  145. dbgln("Mutex::unlock @ {} ({}): release {}, holding: {}", this, m_name, mode_to_string(current_mode), m_times_locked);
  146. }
  147. VERIFY(current_mode != Mode::Unlocked);
  148. VERIFY(m_times_locked > 0);
  149. m_times_locked--;
  150. switch (current_mode) {
  151. case Mode::Exclusive:
  152. VERIFY(m_holder == current_thread);
  153. VERIFY(m_shared_holders == 0);
  154. if (m_times_locked == 0)
  155. m_holder = nullptr;
  156. break;
  157. case Mode::Shared: {
  158. VERIFY(!m_holder);
  159. VERIFY(m_shared_holders > 0);
  160. --m_shared_holders;
  161. #if LOCK_SHARED_UPGRADE_DEBUG
  162. auto it = m_shared_holders_map.find(current_thread);
  163. if (it->value > 1)
  164. it->value--;
  165. else
  166. m_shared_holders_map.remove(it);
  167. #endif
  168. break;
  169. }
  170. default:
  171. VERIFY_NOT_REACHED();
  172. }
  173. #if LOCK_DEBUG
  174. if (current_thread) {
  175. current_thread->holding_lock(*this, -1, {});
  176. }
  177. #endif
  178. if (m_times_locked == 0) {
  179. VERIFY(current_mode == Mode::Exclusive ? !m_holder : m_shared_holders == 0);
  180. m_mode = Mode::Unlocked;
  181. unblock_waiters(current_mode);
  182. }
  183. }
  184. void Mutex::block(Thread& current_thread, Mode mode, SpinlockLocker<Spinlock>& lock, u32 requested_locks)
  185. {
  186. if constexpr (LOCK_IN_CRITICAL_DEBUG)
  187. VERIFY_INTERRUPTS_ENABLED();
  188. auto& blocked_thread_list = thread_list_for_mode(mode);
  189. VERIFY(!blocked_thread_list.contains(current_thread));
  190. blocked_thread_list.append(current_thread);
  191. dbgln_if(LOCK_TRACE_DEBUG, "Mutex::lock @ {} ({}) waiting...", this, m_name);
  192. current_thread.block(*this, lock, requested_locks);
  193. dbgln_if(LOCK_TRACE_DEBUG, "Mutex::lock @ {} ({}) waited", this, m_name);
  194. VERIFY(blocked_thread_list.contains(current_thread));
  195. blocked_thread_list.remove(current_thread);
  196. }
  197. void Mutex::unblock_waiters(Mode previous_mode)
  198. {
  199. VERIFY(m_times_locked == 0);
  200. VERIFY(m_mode == Mode::Unlocked);
  201. if (m_blocked_threads_list_exclusive.is_empty() && m_blocked_threads_list_shared.is_empty())
  202. return;
  203. auto unblock_shared = [&]() {
  204. if (m_blocked_threads_list_shared.is_empty())
  205. return false;
  206. m_mode = Mode::Shared;
  207. for (auto& thread : m_blocked_threads_list_shared) {
  208. auto requested_locks = thread.unblock_from_mutex(*this);
  209. m_shared_holders += requested_locks;
  210. #if LOCK_SHARED_UPGRADE_DEBUG
  211. auto set_result = m_shared_holders_map.set(&thread, requested_locks);
  212. VERIFY(set_result == AK::HashSetResult::InsertedNewEntry);
  213. #endif
  214. m_times_locked += requested_locks;
  215. }
  216. return true;
  217. };
  218. auto unblock_exclusive = [&]() {
  219. if (auto* next_exclusive_thread = m_blocked_threads_list_exclusive.first()) {
  220. m_mode = Mode::Exclusive;
  221. m_times_locked = next_exclusive_thread->unblock_from_mutex(*this);
  222. m_holder = next_exclusive_thread;
  223. return true;
  224. }
  225. return false;
  226. };
  227. if (previous_mode == Mode::Exclusive) {
  228. if (!unblock_shared())
  229. unblock_exclusive();
  230. } else {
  231. if (!unblock_exclusive())
  232. unblock_shared();
  233. }
  234. }
  235. auto Mutex::force_unlock_exclusive_if_locked(u32& lock_count_to_restore) -> Mode
  236. {
  237. // NOTE: This may be called from an interrupt handler (not an IRQ handler)
  238. // and also from within critical sections!
  239. VERIFY(!Processor::current_in_irq());
  240. auto* current_thread = Thread::current();
  241. SpinlockLocker lock(m_lock);
  242. auto current_mode = m_mode;
  243. switch (current_mode) {
  244. case Mode::Exclusive: {
  245. if (m_holder != current_thread) {
  246. lock_count_to_restore = 0;
  247. return Mode::Unlocked;
  248. }
  249. dbgln_if(LOCK_RESTORE_DEBUG, "Mutex::force_unlock_exclusive_if_locked @ {}: unlocking exclusive with lock count: {}", this, m_times_locked);
  250. #if LOCK_DEBUG
  251. m_holder->holding_lock(*this, -(int)m_times_locked, {});
  252. #endif
  253. m_holder = nullptr;
  254. VERIFY(m_times_locked > 0);
  255. lock_count_to_restore = m_times_locked;
  256. m_times_locked = 0;
  257. m_mode = Mode::Unlocked;
  258. unblock_waiters(Mode::Exclusive);
  259. break;
  260. }
  261. case Mode::Unlocked: {
  262. lock_count_to_restore = 0;
  263. break;
  264. }
  265. default:
  266. VERIFY_NOT_REACHED();
  267. }
  268. return current_mode;
  269. }
  270. void Mutex::restore_exclusive_lock(u32 lock_count, [[maybe_unused]] LockLocation const& location)
  271. {
  272. VERIFY(lock_count > 0);
  273. VERIFY(!Processor::current_in_irq());
  274. auto* current_thread = Thread::current();
  275. bool did_block = false;
  276. SpinlockLocker lock(m_lock);
  277. [[maybe_unused]] auto previous_mode = m_mode;
  278. if (m_mode == Mode::Exclusive && m_holder != current_thread) {
  279. block(*current_thread, Mode::Exclusive, lock, lock_count);
  280. did_block = true;
  281. // If we blocked then m_mode should have been updated to what we requested
  282. VERIFY(m_mode == Mode::Exclusive);
  283. }
  284. dbgln_if(LOCK_RESTORE_DEBUG, "Mutex::restore_exclusive_lock @ {}: restoring exclusive with lock count {}, was {}", this, lock_count, mode_to_string(previous_mode));
  285. VERIFY(m_mode != Mode::Shared);
  286. VERIFY(m_shared_holders == 0);
  287. if (did_block) {
  288. VERIFY(m_times_locked > 0);
  289. VERIFY(m_holder == current_thread);
  290. } else {
  291. if (m_mode == Mode::Unlocked) {
  292. m_mode = Mode::Exclusive;
  293. VERIFY(m_times_locked == 0);
  294. m_times_locked = lock_count;
  295. VERIFY(!m_holder);
  296. m_holder = current_thread;
  297. } else {
  298. VERIFY(m_mode == Mode::Exclusive);
  299. VERIFY(m_holder == current_thread);
  300. VERIFY(m_times_locked > 0);
  301. m_times_locked += lock_count;
  302. }
  303. }
  304. #if LOCK_DEBUG
  305. m_holder->holding_lock(*this, (int)lock_count, location);
  306. #endif
  307. }
  308. }