mirror of
https://github.com/switchbrew/libnx.git
synced 2025-06-21 12:32:40 +02:00
rwlock: revamp completely (#350)
Implements rwlockTryReadLock and rwlockTryWriteLock. Also implements rwlockIsWriteLockHeldByCurrentThread and rwlockIsOwnedByCurrentThread. Also re-designs RwLock to have semantics identical to Nintendo's (nn::os::ReaderWriterLock). The upshot is mostly that the lock is now fully recursive/write-preferring.
This commit is contained in:
parent
4626b50341
commit
afe030f08b
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file rwlock.h
|
||||
* @brief Read/write lock synchronization primitive.
|
||||
* @author plutoo
|
||||
* @author plutoo, SciresM
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
@ -11,10 +11,13 @@
|
||||
/// Read/write lock structure.
|
||||
typedef struct {
|
||||
Mutex mutex;
|
||||
CondVar condvar_readers;
|
||||
CondVar condvar_writer;
|
||||
u32 readers : 31;
|
||||
bool writer : 1;
|
||||
CondVar condvar_reader_wait;
|
||||
CondVar condvar_writer_wait;
|
||||
u32 read_lock_count;
|
||||
u32 read_waiter_count;
|
||||
u32 write_lock_count;
|
||||
u32 write_waiter_count;
|
||||
u32 write_owner_tag;
|
||||
} RwLock;
|
||||
|
||||
/**
|
||||
@ -29,6 +32,13 @@ void rwlockInit(RwLock* r);
|
||||
*/
|
||||
void rwlockReadLock(RwLock* r);
|
||||
|
||||
/**
|
||||
* @brief Attempts to lock the read/write lock for reading without waiting.
|
||||
* @param r Read/write lock object.
|
||||
* @return 1 if the mutex has been acquired successfully, and 0 on contention.
|
||||
*/
|
||||
bool rwlockTryReadLock(RwLock* r);
|
||||
|
||||
/**
|
||||
* @brief Unlocks the read/write lock for reading.
|
||||
* @param r Read/write lock object.
|
||||
@ -41,8 +51,30 @@ void rwlockReadUnlock(RwLock* r);
|
||||
*/
|
||||
void rwlockWriteLock(RwLock* r);
|
||||
|
||||
/**
|
||||
* @brief Attempts to lock the read/write lock for writing without waiting.
|
||||
* @param r Read/write lock object.
|
||||
* @return 1 if the mutex has been acquired successfully, and 0 on contention.
|
||||
*/
|
||||
bool rwlockTryWriteLock(RwLock* r);
|
||||
|
||||
/**
|
||||
* @brief Unlocks the read/write lock for writing.
|
||||
* @param r Read/write lock object.
|
||||
*/
|
||||
void rwlockWriteUnlock(RwLock* r);
|
||||
|
||||
/**
|
||||
* @brief Checks if the write lock is held by the current thread.
|
||||
* @param r Read/write lock object.
|
||||
* @return 1 if the current hold holds the write lock, and 0 if it does not.
|
||||
*/
|
||||
bool rwlockIsWriteLockHeldByCurrentThread(RwLock* r);
|
||||
|
||||
/**
|
||||
* @brief Checks if the read/write lock is owned by the current thread.
|
||||
* @param r Read/write lock object.
|
||||
* @return 1 if the current hold holds the write lock or if it holds read locks acquired
|
||||
* while it held the write lock, and 0 if it does not.
|
||||
*/
|
||||
bool rwlockIsOwnedByCurrentThread(RwLock* r);
|
||||
|
@ -1,59 +1,170 @@
|
||||
// Copyright 2018 plutoo
|
||||
#include "kernel/mutex.h"
|
||||
#include "kernel/rwlock.h"
|
||||
#include "../internal.h"
|
||||
|
||||
NX_INLINE u32 _GetCurrentThreadTag(void) {
|
||||
return getThreadVars()->handle;
|
||||
}
|
||||
|
||||
void rwlockInit(RwLock* r) {
|
||||
mutexInit(&r->mutex);
|
||||
condvarInit(&r->condvar_readers);
|
||||
condvarInit(&r->condvar_writer);
|
||||
condvarInit(&r->condvar_reader_wait);
|
||||
condvarInit(&r->condvar_writer_wait);
|
||||
|
||||
r->readers = 0;
|
||||
r->writer = false;
|
||||
r->read_lock_count = 0;
|
||||
r->read_waiter_count = 0;
|
||||
r->write_lock_count = 0;
|
||||
r->write_waiter_count = 0;
|
||||
r->write_owner_tag = 0;
|
||||
}
|
||||
|
||||
void rwlockReadLock(RwLock* r) {
|
||||
mutexLock(&r->mutex);
|
||||
const u32 cur_tag = _GetCurrentThreadTag();
|
||||
|
||||
while (r->writer) {
|
||||
condvarWait(&r->condvar_writer, &r->mutex);
|
||||
if (r->write_owner_tag == cur_tag) {
|
||||
r->read_lock_count++;
|
||||
return;
|
||||
}
|
||||
|
||||
r->readers++;
|
||||
mutexLock(&r->mutex);
|
||||
|
||||
while (r->write_waiter_count > 0) {
|
||||
r->read_waiter_count++;
|
||||
condvarWait(&r->condvar_reader_wait, &r->mutex);
|
||||
r->read_waiter_count--;
|
||||
}
|
||||
|
||||
r->read_lock_count++;
|
||||
|
||||
mutexUnlock(&r->mutex);
|
||||
}
|
||||
|
||||
bool rwlockTryReadLock(RwLock* r) {
|
||||
const u32 cur_tag = _GetCurrentThreadTag();
|
||||
|
||||
if (r->write_owner_tag == cur_tag) {
|
||||
r->read_lock_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mutexTryLock(&r->mutex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool got_lock = r->write_waiter_count == 0;
|
||||
if (got_lock) {
|
||||
r->read_lock_count++;
|
||||
}
|
||||
|
||||
mutexUnlock(&r->mutex);
|
||||
return got_lock;
|
||||
}
|
||||
|
||||
void rwlockReadUnlock(RwLock* r) {
|
||||
mutexLock(&r->mutex);
|
||||
const u32 cur_tag = _GetCurrentThreadTag();
|
||||
|
||||
if (--r->readers == 0) {
|
||||
condvarWakeAll(&r->condvar_readers);
|
||||
if (r->write_owner_tag == cur_tag) {
|
||||
// Write lock is owned by this thread.
|
||||
r->read_lock_count--;
|
||||
if (r->read_lock_count == 0 && r->write_lock_count == 0) {
|
||||
// Relinquish control of the lock.
|
||||
r->write_owner_tag = 0;
|
||||
|
||||
if (r->write_waiter_count > 0) {
|
||||
condvarWakeOne(&r->condvar_writer_wait);
|
||||
} else if (r->read_waiter_count > 0) {
|
||||
condvarWakeAll(&r->condvar_reader_wait);
|
||||
}
|
||||
|
||||
// Corresponding mutexLock was called in WriteLock/WriteTryLock.
|
||||
mutexUnlock(&r->mutex);
|
||||
}
|
||||
} else {
|
||||
// Write lock isn't owned by this thread.
|
||||
mutexLock(&r->mutex);
|
||||
r->read_lock_count--;
|
||||
if (r->read_lock_count == 0 && r->write_waiter_count > 0) {
|
||||
condvarWakeOne(&r->condvar_writer_wait);
|
||||
}
|
||||
mutexUnlock(&r->mutex);
|
||||
}
|
||||
|
||||
mutexUnlock(&r->mutex);
|
||||
}
|
||||
|
||||
void rwlockWriteLock(RwLock* r) {
|
||||
const u32 cur_tag = _GetCurrentThreadTag();
|
||||
|
||||
if (r->write_owner_tag == cur_tag) {
|
||||
r->write_lock_count++;
|
||||
return;
|
||||
}
|
||||
|
||||
mutexLock(&r->mutex);
|
||||
|
||||
while (r->writer) {
|
||||
condvarWait(&r->condvar_writer, &r->mutex);
|
||||
while (r->read_lock_count > 0) {
|
||||
r->write_waiter_count++;
|
||||
condvarWait(&r->condvar_writer_wait, &r->mutex);
|
||||
r->write_waiter_count--;
|
||||
}
|
||||
|
||||
r->writer = true;
|
||||
r->write_lock_count = 1;
|
||||
r->write_owner_tag = cur_tag;
|
||||
|
||||
while (r->readers > 0) {
|
||||
condvarWait(&r->condvar_readers, &r->mutex);
|
||||
// mutexUnlock(&r->mutex) is intentionally not called here.
|
||||
// The mutex will be unlocked by a call to ReadUnlock or WriteUnlock.
|
||||
}
|
||||
|
||||
bool rwlockTryWriteLock(RwLock* r) {
|
||||
const u32 cur_tag = _GetCurrentThreadTag();
|
||||
|
||||
if (r->write_owner_tag == cur_tag) {
|
||||
r->write_lock_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
mutexUnlock(&r->mutex);
|
||||
if (!mutexTryLock(&r->mutex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (r->read_lock_count > 0) {
|
||||
mutexUnlock(&r->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
r->write_lock_count = 1;
|
||||
r->write_owner_tag = cur_tag;
|
||||
|
||||
// mutexUnlock(&r->mutex) is intentionally not called here.
|
||||
// The mutex will be unlocked by a call to ReadUnlock or WriteUnlock.
|
||||
return true;
|
||||
}
|
||||
|
||||
void rwlockWriteUnlock(RwLock* r) {
|
||||
mutexLock(&r->mutex);
|
||||
// This function assumes the write lock is held.
|
||||
// This means that r->mutex is locked, and r->write_owner_tag == cur_tag.
|
||||
// if (r->write_owner_tag == cur_tag)
|
||||
{
|
||||
r->write_lock_count--;
|
||||
if (r->write_lock_count == 0 && r->read_lock_count == 0) {
|
||||
// Relinquish control of the lock.
|
||||
r->write_owner_tag = 0;
|
||||
|
||||
r->writer = false;
|
||||
condvarWakeAll(&r->condvar_writer);
|
||||
if (r->write_waiter_count > 0) {
|
||||
condvarWakeOne(&r->condvar_writer_wait);
|
||||
} else if (r->read_waiter_count > 0) {
|
||||
condvarWakeAll(&r->condvar_reader_wait);
|
||||
}
|
||||
|
||||
mutexUnlock(&r->mutex);
|
||||
// Corresponding mutexLock was called in WriteLock/WriteTryLock.
|
||||
mutexUnlock(&r->mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool rwlockIsWriteLockHeldByCurrentThread(RwLock* r) {
|
||||
return r->write_owner_tag == _GetCurrentThreadTag() && r->write_lock_count > 0;
|
||||
}
|
||||
|
||||
bool rwlockIsOwnedByCurrentThread(RwLock* r) {
|
||||
return r->write_owner_tag == _GetCurrentThreadTag();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user