LibThreading: Fix data race in RWLock::unlock()

The generic unlock() wrote to m_write_locked from every thread
regardless of whether a read or write lock was held. When multiple
threads held concurrent read locks, their unlock() calls would race
on the non-atomic m_write_locked and m_read_locked_with_write_lock
fields.

Split unlock() into unlock_read() and unlock_write() so that read
unlocks never touch the write-lock tracking fields. The RWLockLocker
template dispatches at compile time based on LockMode.
This commit is contained in:
Andreas Kling
2026-03-07 11:57:09 +01:00
committed by Andreas Kling
parent 8a6d902d4c
commit 656a84e180
Notes: github-actions[bot] 2026-03-07 12:11:04 +00:00
2 changed files with 18 additions and 8 deletions

View File

@@ -180,7 +180,7 @@ StrongEventLoopReference::StrongEventLoopReference(WeakEventLoopReference& event
StrongEventLoopReference::~StrongEventLoopReference()
{
m_event_loop_weak->m_lock.unlock();
m_event_loop_weak->m_lock.unlock_read();
}
bool StrongEventLoopReference::is_alive() const

View File

@@ -32,7 +32,8 @@ public:
void lock_read();
void lock_write();
void unlock();
void unlock_read();
void unlock_write();
private:
pthread_rwlock_t m_rwlock;
@@ -63,7 +64,10 @@ public:
ALWAYS_INLINE void unlock()
{
m_lock.unlock();
if constexpr (mode == LockMode::Read)
m_lock.unlock_read();
else
m_lock.unlock_write();
}
ALWAYS_INLINE void lock()
@@ -96,13 +100,19 @@ ALWAYS_INLINE void RWLock::lock_write()
m_write_locked = true;
}
ALWAYS_INLINE void RWLock::unlock()
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;
auto needs_unlock = !m_read_locked_with_write_lock;
m_read_locked_with_write_lock = false;
if (needs_unlock)
pthread_rwlock_unlock(&m_rwlock);
pthread_rwlock_unlock(&m_rwlock);
}
}