diff --git a/Libraries/LibSync/CMakeLists.txt b/Libraries/LibSync/CMakeLists.txt index 32f379c6cf..c1ec331041 100644 --- a/Libraries/LibSync/CMakeLists.txt +++ b/Libraries/LibSync/CMakeLists.txt @@ -1,7 +1,7 @@ if (WIN32) - set(SOURCES MutexWindows.cpp ConditionVariableWindows.cpp) + set(SOURCES MutexWindows.cpp ConditionVariableWindows.cpp RWLockWindows.cpp) else() - set(SOURCES MutexPOSIX.cpp ConditionVariablePOSIX.cpp) + set(SOURCES MutexPOSIX.cpp ConditionVariablePOSIX.cpp RWLockPOSIX.cpp) endif() ladybird_lib(LibSync sync EXPLICIT_SYMBOL_EXPORT) diff --git a/Libraries/LibSync/RWLock.h b/Libraries/LibSync/RWLock.h index 0bf8c8c229..aa9b4b8112 100644 --- a/Libraries/LibSync/RWLock.h +++ b/Libraries/LibSync/RWLock.h @@ -1,52 +1,59 @@ /* * Copyright (c) 2024, Ali Mohammad Pur + * Copyright (c) 2025, Ryszard Goc * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once -#include #include +#include #include -#include +#include +#if !defined(AK_OS_WINDOWS) +# include +#endif namespace Sync { -class RWLock { +// TODO: Implement interprocess RWLocks. This needs a hand-rolled implementation for win32. +class SYNC_API RWLock { AK_MAKE_NONCOPYABLE(RWLock); AK_MAKE_NONMOVABLE(RWLock); public: - RWLock() - { - pthread_rwlock_init(&m_rwlock, nullptr); - } + RWLock(); + ~RWLock(); - ~RWLock() - { - VERIFY(!m_write_locked); - pthread_rwlock_destroy(&m_rwlock); - } + bool try_lock_read(); + bool try_lock_write(); + // Recursively acquiring a RWLock is not supported void lock_read(); void lock_write(); + // NOTE: While the pthread api has one unlock method, the Win32 api has separate ones per lock mode void unlock_read(); void unlock_write(); private: - pthread_rwlock_t m_rwlock; - bool m_write_locked { false }; - bool m_read_locked_with_write_lock { false }; +#ifdef AK_OS_WINDOWS + using StorageType = void*; +#else + using StorageType = pthread_rwlock_t; +#endif + + alignas(StorageType) unsigned char m_storage[sizeof(StorageType)]; }; -enum class LockMode { +enum class LockMode : u8 { Read, Write, }; + template -class RWLockLocker { +class SYNC_API RWLockLocker { AK_MAKE_NONCOPYABLE(RWLockLocker); AK_MAKE_NONMOVABLE(RWLockLocker); @@ -82,37 +89,4 @@ 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_read() -{ - if (m_read_locked_with_write_lock) { - m_read_locked_with_write_lock = false; - return; - } - pthread_rwlock_unlock(&m_rwlock); -} - -ALWAYS_INLINE void RWLock::unlock_write() -{ - m_write_locked = false; - pthread_rwlock_unlock(&m_rwlock); -} - } diff --git a/Libraries/LibSync/RWLockPOSIX.cpp b/Libraries/LibSync/RWLockPOSIX.cpp new file mode 100644 index 0000000000..61ce88e7c3 --- /dev/null +++ b/Libraries/LibSync/RWLockPOSIX.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2024, Ali Mohammad Pur + * Copyright (c) 2025, Ryszard Goc + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Sync { + +namespace { + +ALWAYS_INLINE pthread_rwlock_t* to_impl(void* ptr) +{ + return reinterpret_cast(ptr); +} + +} + +RWLock::RWLock() +{ + static_assert(sizeof(pthread_rwlock_t) == sizeof(m_storage)); + auto* rwlock_ptr = new (m_storage) pthread_rwlock_t; + int result = pthread_rwlock_init(rwlock_ptr, nullptr); + if (result != 0) { + warnln("pthread_rwlock_unlock failed with: {}", Error::from_errno(result)); + VERIFY_NOT_REACHED(); + } +} + +RWLock::~RWLock() +{ + int result = pthread_rwlock_destroy(to_impl(m_storage)); + if (result != 0) { + warnln("pthread_rwlock_destroy failed with: {}", Error::from_errno(result)); + VERIFY_NOT_REACHED(); + } +} + +bool RWLock::try_lock_read() +{ + int result = pthread_rwlock_tryrdlock(to_impl(m_storage)); + if (result == 0) + return true; + if (result == EBUSY) + return false; + warnln("pthread_rwlock_trywrlock failed with: {}", Error::from_errno(result)); + VERIFY_NOT_REACHED(); +} + +bool RWLock::try_lock_write() +{ + int result = pthread_rwlock_trywrlock(to_impl(m_storage)); + if (result == 0) + return true; + if (result == EBUSY) + return false; + warnln("pthread_rwlock_trywrlock failed with: {}", Error::from_errno(result)); + VERIFY_NOT_REACHED(); +} +void RWLock::lock_read() +{ + int result = pthread_rwlock_rdlock(to_impl(m_storage)); + if (result != 0) { + warnln("pthread_rwlock_rdlock failed with: {}", Error::from_errno(result)); + VERIFY_NOT_REACHED(); + } +} + +void RWLock::lock_write() +{ + int result = pthread_rwlock_wrlock(to_impl(m_storage)); + if (result != 0) { + warnln("pthread_rwlock_wrlock failed with: {}", Error::from_errno(result)); + VERIFY_NOT_REACHED(); + } +} + +void RWLock::unlock_read() +{ + int result = pthread_rwlock_unlock(to_impl(m_storage)); + if (result != 0) { + warnln("pthread_rwlock_unlock failed with: {}", Error::from_errno(result)); + VERIFY_NOT_REACHED(); + } +} + +void RWLock::unlock_write() +{ + int result = pthread_rwlock_unlock(to_impl(m_storage)); + if (result != 0) { + warnln("pthread_rwlock_unlock failed with: {}", Error::from_errno(result)); + VERIFY_NOT_REACHED(); + } +} + +} diff --git a/Libraries/LibSync/RWLockWindows.cpp b/Libraries/LibSync/RWLockWindows.cpp new file mode 100644 index 0000000000..dc4b907c32 --- /dev/null +++ b/Libraries/LibSync/RWLockWindows.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025, Ryszard Goc + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Sync { + +namespace { + +ALWAYS_INLINE PSRWLOCK to_impl(void* ptr) +{ + return reinterpret_cast(ptr); +} + +} + +RWLock::RWLock() +{ + static_assert(sizeof(SRWLOCK) == sizeof(m_storage)); + PSRWLOCK rwlock_ptr = new (m_storage) SRWLOCK; + InitializeSRWLock(rwlock_ptr); +} + +RWLock::~RWLock() = default; + +bool RWLock::try_lock_read() +{ + return TryAcquireSRWLockShared(to_impl(m_storage)); +} + +bool RWLock::try_lock_write() +{ + return TryAcquireSRWLockExclusive(to_impl(m_storage)); +} + +void RWLock::lock_read() +{ + AcquireSRWLockShared(to_impl(m_storage)); +} + +void RWLock::lock_write() +{ + AcquireSRWLockExclusive(to_impl(m_storage)); +} + +void RWLock::unlock_read() +{ + ReleaseSRWLockShared(to_impl(m_storage)); +} + +void RWLock::unlock_write() +{ + ReleaseSRWLockExclusive(to_impl(m_storage)); +} + +}