LibThreading: Add RWLock and RWLockedProtected

This commit is contained in:
Ali Mohammad Pur 2024-05-13 17:04:40 +02:00 committed by Andreas Kling
parent 06d522f017
commit 5cc90f848f
Notes: sideshowbarker 2024-07-16 23:17:55 +09:00
2 changed files with 180 additions and 0 deletions

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2024, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Assertions.h>
#include <AK/Noncopyable.h>
#include <AK/Types.h>
#include <pthread.h>
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<LockMode mode>
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);
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2024, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Concepts.h>
#include <AK/Noncopyable.h>
#include <LibThreading/RWLock.h>
namespace Threading {
template<typename T>
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<typename Callback>
requires(requires { declval<Callback>()(declval<T const&>()); })
decltype(auto) with_read_locked(Callback callback) const
{
auto lock = this->lock_read();
return callback(m_value);
}
template<typename Callback>
decltype(auto) with_write_locked(Callback callback)
{
auto lock = this->lock_write();
return callback(m_value);
}
template<VoidFunction<T> Callback>
void for_each_locked(Callback callback)
{
with_read_locked([&](auto const& value) {
for (auto& item : value)
callback(item);
});
}
private:
[[nodiscard]] ALWAYS_INLINE RWLockLocker<LockMode::Read> lock_read() const
{
return RWLockLocker<LockMode::Read> { m_lock };
}
[[nodiscard]] ALWAYS_INLINE RWLockLocker<LockMode::Write> lock_write()
{
return RWLockLocker<LockMode::Write> { m_lock };
}
T m_value;
mutable RWLock m_lock {};
};
}