Serenity Operating System
at master 184 lines 5.2 kB view raw
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 8#pragma once 9 10#include <AK/Assertions.h> 11#include <AK/Atomic.h> 12#include <AK/HashMap.h> 13#include <AK/Types.h> 14#include <Kernel/Forward.h> 15#include <Kernel/Locking/LockLocation.h> 16#include <Kernel/Locking/LockMode.h> 17#include <Kernel/WaitQueue.h> 18 19namespace Kernel { 20 21class Mutex { 22 friend class Thread; 23 24 AK_MAKE_NONCOPYABLE(Mutex); 25 AK_MAKE_NONMOVABLE(Mutex); 26 27public: 28 using Mode = LockMode; 29 30 // FIXME: remove this after annihilating Process::m_big_lock 31 enum class MutexBehavior { 32 Regular, 33 BigLock, 34 }; 35 36 Mutex(StringView name = {}, MutexBehavior behavior = MutexBehavior::Regular) 37 : m_name(name) 38 , m_behavior(behavior) 39 { 40 } 41 ~Mutex() = default; 42 43 void lock(Mode mode = Mode::Exclusive, LockLocation const& location = LockLocation::current()); 44 void restore_exclusive_lock(u32, LockLocation const& location = LockLocation::current()); 45 46 void unlock(); 47 [[nodiscard]] Mode force_unlock_exclusive_if_locked(u32&); 48 [[nodiscard]] bool is_locked() const 49 { 50 SpinlockLocker lock(m_lock); 51 return m_mode != Mode::Unlocked; 52 } 53 54 [[nodiscard]] bool is_exclusively_locked_by_current_thread() const 55 { 56 SpinlockLocker lock(m_lock); 57 VERIFY(m_mode != Mode::Shared); // This method should only be used on exclusively-held locks 58 if (m_mode == Mode::Unlocked) 59 return false; 60 return m_holder == Thread::current(); 61 } 62 63 [[nodiscard]] StringView name() const { return m_name; } 64 65 static StringView mode_to_string(Mode mode) 66 { 67 switch (mode) { 68 case Mode::Unlocked: 69 return "unlocked"sv; 70 case Mode::Exclusive: 71 return "exclusive"sv; 72 case Mode::Shared: 73 return "shared"sv; 74 default: 75 return "invalid"sv; 76 } 77 } 78 79private: 80 using BlockedThreadList = IntrusiveList<&Thread::m_blocked_threads_list_node>; 81 82 // FIXME: remove this after annihilating Process::m_big_lock 83 using BigLockBlockedThreadList = IntrusiveList<&Thread::m_big_lock_blocked_threads_list_node>; 84 85 // FIXME: Allow any lock rank. 86 void block(Thread&, Mode, SpinlockLocker<Spinlock<LockRank::None>>&, u32); 87 void unblock_waiters(Mode); 88 89 StringView m_name; 90 Mode m_mode { Mode::Unlocked }; 91 92 // FIXME: remove this after annihilating Process::m_big_lock 93 MutexBehavior m_behavior; 94 95 // When locked exclusively, only the thread already holding the lock can 96 // lock it again. When locked in shared mode, any thread can do that. 97 u32 m_times_locked { 0 }; 98 99 // One of the threads that hold this lock, or nullptr. When locked in shared 100 // mode, this is stored on best effort basis: nullptr value does *not* mean 101 // the lock is unlocked, it just means we don't know which threads hold it. 102 // When locked exclusively, this is always the one thread that holds the 103 // lock. 104 LockRefPtr<Thread> m_holder; 105 size_t m_shared_holders { 0 }; 106 107 struct BlockedThreadLists { 108 BlockedThreadList exclusive; 109 BlockedThreadList shared; 110 111 // FIXME: remove this after annihilating Process::m_big_lock 112 BigLockBlockedThreadList exclusive_big_lock; 113 114 ALWAYS_INLINE BlockedThreadList& list_for_mode(Mode mode) 115 { 116 VERIFY(mode == Mode::Exclusive || mode == Mode::Shared); 117 return mode == Mode::Exclusive ? exclusive : shared; 118 } 119 }; 120 // FIXME: Use a specific lock rank passed by constructor. 121 SpinlockProtected<BlockedThreadLists, LockRank::None> m_blocked_thread_lists {}; 122 123 // FIXME: See above. 124 mutable Spinlock<LockRank::None> m_lock {}; 125 126#if LOCK_SHARED_UPGRADE_DEBUG 127 HashMap<Thread*, u32> m_shared_holders_map; 128#endif 129}; 130 131class MutexLocker { 132 AK_MAKE_NONCOPYABLE(MutexLocker); 133 134public: 135 ALWAYS_INLINE explicit MutexLocker() 136 : m_lock(nullptr) 137 , m_locked(false) 138 { 139 } 140 141 ALWAYS_INLINE explicit MutexLocker(Mutex& l, Mutex::Mode mode = Mutex::Mode::Exclusive, LockLocation const& location = LockLocation::current()) 142 : m_lock(&l) 143 { 144 m_lock->lock(mode, location); 145 } 146 147 ALWAYS_INLINE ~MutexLocker() 148 { 149 if (m_locked) 150 unlock(); 151 } 152 153 ALWAYS_INLINE void unlock() 154 { 155 VERIFY(m_lock); 156 VERIFY(m_locked); 157 m_locked = false; 158 m_lock->unlock(); 159 } 160 161 ALWAYS_INLINE void attach_and_lock(Mutex& lock, Mutex::Mode mode = Mutex::Mode::Exclusive, LockLocation const& location = LockLocation::current()) 162 { 163 VERIFY(!m_locked); 164 m_lock = &lock; 165 m_locked = true; 166 167 m_lock->lock(mode, location); 168 } 169 170 ALWAYS_INLINE void lock(Mutex::Mode mode = Mutex::Mode::Exclusive, LockLocation const& location = LockLocation::current()) 171 { 172 VERIFY(m_lock); 173 VERIFY(!m_locked); 174 m_locked = true; 175 176 m_lock->lock(mode, location); 177 } 178 179private: 180 Mutex* m_lock; 181 bool m_locked { true }; 182}; 183 184}