mirror of
https://github.com/Atmosphere-NX/Atmosphere-libs.git
synced 2025-06-21 19:12:42 +02:00
239 lines
10 KiB
C++
239 lines
10 KiB
C++
/*
|
|
* Copyright (c) Atmosphère-NX
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <stratosphere.hpp>
|
|
#include "os_rw_lock_impl.hpp"
|
|
#include "os_thread_manager.hpp"
|
|
|
|
namespace ams::os::impl {
|
|
|
|
void ReaderWriterLockHorizonImpl::AcquireReadLock(os::ReaderWriterLockType *rw_lock) {
|
|
/* Acquire exclusive access. */
|
|
std::scoped_lock lk(util::GetReference(GetLockCount(rw_lock).cs_storage));
|
|
|
|
/* Get the lock into a state where we can safely increment the read lock count. */
|
|
if (rw_lock->owner_thread == impl::GetCurrentThread()) {
|
|
/* If we're the owner thread, we should hold the write lock. */
|
|
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 1);
|
|
} else {
|
|
/* Wait until we're not write locked, and there are no write lock waiters. */
|
|
while (true) {
|
|
/* Check if we're write locked. */
|
|
if (GetWriteLocked(GetLockCount(rw_lock)) != 1) {
|
|
/* We're not, so check if we have write lock waiters. */
|
|
if (GetWriteLockWaiterCount(GetLockCount(rw_lock)) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* We're write locked, or we have write lock waiters. */
|
|
IncReadLockWaiterCount(GetLockCount(rw_lock));
|
|
util::GetReference(rw_lock->cv_read_lock._storage).Wait(util::GetPointer(GetLockCount(rw_lock).cs_storage));
|
|
DecReadLockWaiterCount(GetLockCount(rw_lock));
|
|
}
|
|
|
|
/* Verify we're in the desired state. */
|
|
AMS_ASSERT(GetWriteLockCount(*rw_lock) == 0);
|
|
AMS_ASSERT(rw_lock->owner_thread == nullptr);
|
|
}
|
|
|
|
/* Increment the read lock count. */
|
|
IncReadLockCount(GetLockCount(rw_lock));
|
|
}
|
|
|
|
bool ReaderWriterLockHorizonImpl::TryAcquireReadLock(os::ReaderWriterLockType *rw_lock) {
|
|
/* Acquire exclusive access. */
|
|
std::scoped_lock lk(util::GetReference(GetLockCount(rw_lock).cs_storage));
|
|
|
|
/* Try to get the lock into a state where we can safely increment the read lock count. */
|
|
if (rw_lock->owner_thread == impl::GetCurrentThread()) {
|
|
/* If we're the owner thread, we should hold the write lock. */
|
|
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 1);
|
|
} else {
|
|
/* We can only read lock if we're not write locked, and there are no write lock waiters. */
|
|
if (GetWriteLocked(GetLockCount(rw_lock)) == 1) {
|
|
return false;
|
|
}
|
|
if (GetWriteLockWaiterCount(GetLockCount(rw_lock)) != 0) {
|
|
return false;
|
|
}
|
|
|
|
/* Verify we're in the desired state. */
|
|
AMS_ASSERT(GetWriteLockCount(*rw_lock) == 0);
|
|
AMS_ASSERT(rw_lock->owner_thread == nullptr);
|
|
}
|
|
|
|
/* Increment the read lock count. */
|
|
IncReadLockCount(GetLockCount(rw_lock));
|
|
return true;
|
|
}
|
|
|
|
void ReaderWriterLockHorizonImpl::ReleaseReadLock(os::ReaderWriterLockType *rw_lock) {
|
|
/* Acquire exclusive access. */
|
|
std::scoped_lock lk(util::GetReference(GetLockCount(rw_lock).cs_storage));
|
|
|
|
/* Sanity check that we hold the read lock. */
|
|
AMS_ASSERT(GetReadLockCount(GetLockCount(rw_lock)) > 0);
|
|
|
|
/* Decrement the read lock count. */
|
|
DecReadLockCount(GetLockCount(rw_lock));
|
|
|
|
/* If we're the owner of the write lock, we may need to check if this causes a full release/signal to waiters. */
|
|
if (rw_lock->owner_thread == impl::GetCurrentThread()) {
|
|
/* Sanity check that we're write locked. */
|
|
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 1);
|
|
|
|
/* If we've called ReleaseWriteLock and have no remaining read locks, we may need to signal/broadcast. */
|
|
if (GetWriteLockCount(*rw_lock) == 0 && GetReadLockCount(GetLockCount(rw_lock)) == 0) {
|
|
/* Clear lock owner. */
|
|
rw_lock->owner_thread = nullptr;
|
|
|
|
/* Clear write locked. */
|
|
ClearWriteLocked(GetLockCount(rw_lock));
|
|
|
|
/* If we have lock waiters, signal them. */
|
|
if (GetWriteLockWaiterCount(GetLockCount(rw_lock)) != 0) {
|
|
util::GetReference(rw_lock->cv_write_lock._storage).Signal();
|
|
} else if (GetReadLockWaiterCount(GetLockCount(rw_lock)) != 0) {
|
|
util::GetReference(rw_lock->cv_read_lock._storage).Broadcast();
|
|
}
|
|
}
|
|
} else {
|
|
/* Sanity check that our read lock release was fine. */
|
|
AMS_ASSERT(GetWriteLockCount(*rw_lock) == 0);
|
|
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 0);
|
|
AMS_ASSERT(rw_lock->owner_thread == nullptr);
|
|
|
|
/* We need to signal if there are no remaining read locks. */
|
|
if (GetReadLockCount(GetLockCount(rw_lock)) == 0) {
|
|
if (GetWriteLockWaiterCount(GetLockCount(rw_lock)) != 0) {
|
|
util::GetReference(rw_lock->cv_write_lock._storage).Signal();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReaderWriterLockHorizonImpl::AcquireWriteLock(os::ReaderWriterLockType *rw_lock) {
|
|
/* Acquire exclusive access. */
|
|
std::scoped_lock lk(util::GetReference(GetLockCount(rw_lock).cs_storage));
|
|
|
|
/* Get the lock into a state where we can safely increment the read lock count. */
|
|
if (rw_lock->owner_thread == impl::GetCurrentThread()) {
|
|
/* If we're the owner thread, we should hold the write lock. */
|
|
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 1);
|
|
|
|
/* Increment the write lock count. */
|
|
IncWriteLockCount(*rw_lock);
|
|
} else {
|
|
/* Wait until we're not read locked and not write locked. */
|
|
while (true) {
|
|
/* Check if we're read locked. */
|
|
if (GetReadLockCount(GetLockCount(rw_lock)) == 0) {
|
|
/* We're not, so check if we're write locked. */
|
|
if (GetWriteLocked(GetLockCount(rw_lock)) != 1) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* We're write locked or read locked. */
|
|
IncWriteLockWaiterCount(GetLockCount(rw_lock));
|
|
util::GetReference(rw_lock->cv_write_lock._storage).Wait(util::GetPointer(GetLockCount(rw_lock).cs_storage));
|
|
DecWriteLockWaiterCount(GetLockCount(rw_lock));
|
|
}
|
|
|
|
/* Verify we're in the desired state. */
|
|
AMS_ASSERT(GetWriteLockCount(*rw_lock) == 0);
|
|
AMS_ASSERT(rw_lock->owner_thread == nullptr);
|
|
|
|
/* Increment the write lock count. */
|
|
IncWriteLockCount(*rw_lock);
|
|
|
|
/* Set write locked. */
|
|
SetWriteLocked(GetLockCount(rw_lock));
|
|
|
|
/* Set ourselves as the owner. */
|
|
rw_lock->owner_thread = impl::GetCurrentThread();
|
|
}
|
|
}
|
|
|
|
bool ReaderWriterLockHorizonImpl::TryAcquireWriteLock(os::ReaderWriterLockType *rw_lock) {
|
|
/* Acquire exclusive access. */
|
|
std::scoped_lock lk(util::GetReference(GetLockCount(rw_lock).cs_storage));
|
|
|
|
/* Get the lock into a state where we can safely increment the read lock count. */
|
|
if (rw_lock->owner_thread == impl::GetCurrentThread()) {
|
|
/* If we're the owner thread, we should hold the write lock. */
|
|
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 1);
|
|
|
|
/* Increment the write lock count. */
|
|
IncWriteLockCount(*rw_lock);
|
|
} else {
|
|
/* Check if we're read locked. */
|
|
if (GetReadLockCount(GetLockCount(rw_lock)) != 0) {
|
|
return false;
|
|
}
|
|
/* We're not, so check if we're write locked. */
|
|
if (GetWriteLocked(GetLockCount(rw_lock)) == 1) {
|
|
return false;
|
|
}
|
|
|
|
/* Verify we're in the desired state. */
|
|
AMS_ASSERT(GetWriteLockCount(*rw_lock) == 0);
|
|
AMS_ASSERT(rw_lock->owner_thread == nullptr);
|
|
|
|
/* Increment the write lock count. */
|
|
IncWriteLockCount(*rw_lock);
|
|
|
|
/* Set write locked. */
|
|
SetWriteLocked(GetLockCount(rw_lock));
|
|
|
|
/* Set ourselves as the owner. */
|
|
rw_lock->owner_thread = impl::GetCurrentThread();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ReaderWriterLockHorizonImpl::ReleaseWriteLock(os::ReaderWriterLockType *rw_lock) {
|
|
/* Acquire exclusive access. */
|
|
std::scoped_lock lk(util::GetReference(GetLockCount(rw_lock).cs_storage));
|
|
|
|
/* Sanity check our state. */
|
|
AMS_ASSERT(GetWriteLockCount(*rw_lock) > 0);
|
|
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) != 0);
|
|
AMS_ASSERT(rw_lock->owner_thread == impl::GetCurrentThread());
|
|
|
|
/* Decrement the write lock count. */
|
|
DecWriteLockCount(*rw_lock);
|
|
|
|
/* If we have no remaining write locks, we may need to signal/release. */
|
|
if (GetWriteLockCount(*rw_lock) == 0 && GetReadLockCount(GetLockCount(rw_lock)) == 0) {
|
|
/* Clear lock owner. */
|
|
rw_lock->owner_thread = nullptr;
|
|
|
|
/* Clear write locked. */
|
|
ClearWriteLocked(GetLockCount(rw_lock));
|
|
|
|
/* If we have lock waiters, signal them. */
|
|
if (GetWriteLockWaiterCount(GetLockCount(rw_lock)) != 0) {
|
|
util::GetReference(rw_lock->cv_write_lock._storage).Signal();
|
|
} else if (GetReadLockWaiterCount(GetLockCount(rw_lock)) != 0) {
|
|
util::GetReference(rw_lock->cv_read_lock._storage).Broadcast();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|