diff --git a/Userland/Libraries/LibThreading/RWLock.h b/Userland/Libraries/LibThreading/RWLock.h new file mode 100644 index 00000000000..afdbb7b32f2 --- /dev/null +++ b/Userland/Libraries/LibThreading/RWLock.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024, Ali Mohammad Pur + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Threading { + +class RWLock { + AK_MAKE_NONCOPYABLE(RWLock); + AK_MAKE_NONMOVABLE(RWLock); + +public: + RWLock() + { + pthread_rwlock_init(&m_rwlock, nullptr); + } + + ~RWLock() + { + VERIFY(!m_write_locked); + pthread_rwlock_destroy(&m_rwlock); + } + + void lock_read(); + void lock_write(); + + void unlock(); + +private: + pthread_rwlock_t m_rwlock; + bool m_write_locked { false }; + bool m_read_locked_with_write_lock { false }; +}; + +enum class LockMode { + Read, + Write, +}; +template +class RWLockLocker { + AK_MAKE_NONCOPYABLE(RWLockLocker); + AK_MAKE_NONMOVABLE(RWLockLocker); + +public: + ALWAYS_INLINE explicit RWLockLocker(RWLock& l) + : m_lock(l) + { + lock(); + } + + ALWAYS_INLINE ~RWLockLocker() + { + unlock(); + } + + ALWAYS_INLINE void unlock() + { + m_lock.unlock(); + } + + ALWAYS_INLINE void lock() + { + if constexpr (mode == LockMode::Read) + m_lock.lock_read(); + else + m_lock.lock_write(); + } + +private: + RWLock& m_lock; +}; + +ALWAYS_INLINE void RWLock::lock_read() +{ + auto rc = pthread_rwlock_rdlock(&m_rwlock); + if (rc == EDEADLK) { + // We're already holding the write lock, so we can just return. + m_read_locked_with_write_lock = true; + } else { + VERIFY(rc == 0); + } +} + +ALWAYS_INLINE void RWLock::lock_write() +{ + auto rc = pthread_rwlock_wrlock(&m_rwlock); + VERIFY(rc == 0); + m_write_locked = true; +} + +ALWAYS_INLINE void RWLock::unlock() +{ + m_write_locked = false; + auto needs_unlock = !m_read_locked_with_write_lock; + m_read_locked_with_write_lock = false; + if (needs_unlock) + pthread_rwlock_unlock(&m_rwlock); +} + +} diff --git a/Userland/Libraries/LibThreading/RWLockProtected.h b/Userland/Libraries/LibThreading/RWLockProtected.h new file mode 100644 index 00000000000..16d2d86b9d4 --- /dev/null +++ b/Userland/Libraries/LibThreading/RWLockProtected.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024, Ali Mohammad Pur + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Threading { + +template +class RWLockProtected { + AK_MAKE_NONCOPYABLE(RWLockProtected); + AK_MAKE_NONMOVABLE(RWLockProtected); + +public: + using ProtectedType = T; + + ALWAYS_INLINE RWLockProtected() = default; + ALWAYS_INLINE RWLockProtected(T&& value) + : m_value(move(value)) + { + } + ALWAYS_INLINE explicit RWLockProtected(T& value) + : m_value(value) + { + } + + template + requires(requires { declval()(declval()); }) + decltype(auto) with_read_locked(Callback callback) const + { + auto lock = this->lock_read(); + return callback(m_value); + } + + template + decltype(auto) with_write_locked(Callback callback) + { + auto lock = this->lock_write(); + return callback(m_value); + } + + template Callback> + void for_each_locked(Callback callback) + { + with_read_locked([&](auto const& value) { + for (auto& item : value) + callback(item); + }); + } + +private: + [[nodiscard]] ALWAYS_INLINE RWLockLocker lock_read() const + { + return RWLockLocker { m_lock }; + } + + [[nodiscard]] ALWAYS_INLINE RWLockLocker lock_write() + { + return RWLockLocker { m_lock }; + } + + T m_value; + mutable RWLock m_lock {}; +}; + +}