os: implement ReadWriteLock

This commit is contained in:
Michael Scire 2020-04-13 17:07:06 -07:00
parent 6eb77e69c4
commit 97cba5e881
20 changed files with 941 additions and 66 deletions

View file

@ -26,7 +26,7 @@ namespace ams::exosphere {
R_ABORT_UNLESS(ResultNotPresent());
}
return ApiInfo{ util::BitPack64(exosphere_cfg) };
return ApiInfo{ util::BitPack64{exosphere_cfg} };
}
void ForceRebootToRcm() {

View file

@ -0,0 +1,185 @@
/*
* Copyright (c) 2018-2020 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/>.
*/
#pragma once
#include <stratosphere.hpp>
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_rw_lock_target_impl.os.horizon.hpp"
#else
#error "Unknown OS for os::ReadWriteLockTargetImpl"
#endif
namespace ams::os::impl {
static_assert(alignof(ReadWriteLockType) == sizeof(u64) || alignof(ReadWriteLockType) == sizeof(u32));
constexpr inline bool IsReadWriteLockGuaranteedAlignment = alignof(ReadWriteLockType) == sizeof(u64);
struct ReadWriteLockCounter {
using ReadLockCount = util::BitPack32::Field< 0, BITSIZEOF(u16) - 1, u32>;
using WriteLocked = util::BitPack32::Field< ReadLockCount::Next, 1, u32>;
using ReadLockWaiterCount = util::BitPack32::Field< WriteLocked::Next, BITSIZEOF(u8), u32>;
using WriteLockWaiterCount = util::BitPack32::Field<ReadLockWaiterCount::Next, BITSIZEOF(u8), u32>;
};
static_assert(ReadWriteLockCounter::WriteLockWaiterCount::Next == BITSIZEOF(u32));
ALWAYS_INLINE void ClearReadLockCount(ReadWriteLockType::LockCount &lc) {
lc.counter.Set<ReadWriteLockCounter::ReadLockCount>(0);
}
ALWAYS_INLINE void ClearWriteLocked(ReadWriteLockType::LockCount &lc) {
lc.counter.Set<ReadWriteLockCounter::WriteLocked>(0);
}
ALWAYS_INLINE void ClearReadLockWaiterCount(ReadWriteLockType::LockCount &lc) {
lc.counter.Set<ReadWriteLockCounter::ReadLockWaiterCount>(0);
}
ALWAYS_INLINE void ClearWriteLockWaiterCount(ReadWriteLockType::LockCount &lc) {
lc.counter.Set<ReadWriteLockCounter::WriteLockWaiterCount>(0);
}
ALWAYS_INLINE void ClearWriteLockCount(ReadWriteLockType *rw_lock) {
if constexpr (IsReadWriteLockGuaranteedAlignment) {
rw_lock->lock_count.aligned.write_lock_count = 0;
} else {
if (reinterpret_cast<uintptr_t>(std::addressof(rw_lock->lock_count)) & sizeof(u32)) {
rw_lock->lock_count.not_aligned.write_lock_count = 0;
} else {
rw_lock->lock_count.aligned.write_lock_count = 0;
}
}
}
ALWAYS_INLINE ReadWriteLockType::LockCount &GetLockCount(ReadWriteLockType *rw_lock) {
if constexpr (IsReadWriteLockGuaranteedAlignment) {
return rw_lock->lock_count.aligned.c;
} else {
if (reinterpret_cast<uintptr_t>(std::addressof(rw_lock->lock_count)) & sizeof(u32)) {
return rw_lock->lock_count.not_aligned.c;
} else {
return rw_lock->lock_count.aligned.c;
}
}
}
ALWAYS_INLINE const ReadWriteLockType::LockCount &GetLockCount(const ReadWriteLockType *rw_lock) {
if constexpr (IsReadWriteLockGuaranteedAlignment) {
return rw_lock->lock_count.aligned.c;
} else {
if (reinterpret_cast<uintptr_t>(std::addressof(rw_lock->lock_count)) & sizeof(u32)) {
return rw_lock->lock_count.not_aligned.c;
} else {
return rw_lock->lock_count.aligned.c;
}
}
}
ALWAYS_INLINE u32 GetReadLockCount(const ReadWriteLockType::LockCount &lc) {
return lc.counter.Get<ReadWriteLockCounter::ReadLockCount>();
}
ALWAYS_INLINE u32 GetWriteLocked(const ReadWriteLockType::LockCount &lc) {
return lc.counter.Get<ReadWriteLockCounter::WriteLocked>();
}
ALWAYS_INLINE u32 GetReadLockWaiterCount(const ReadWriteLockType::LockCount &lc) {
return lc.counter.Get<ReadWriteLockCounter::ReadLockWaiterCount>();
}
ALWAYS_INLINE u32 GetWriteLockWaiterCount(const ReadWriteLockType::LockCount &lc) {
return lc.counter.Get<ReadWriteLockCounter::WriteLockWaiterCount>();
}
ALWAYS_INLINE u32 &GetWriteLockCount(ReadWriteLockType &rw_lock) {
if constexpr (IsReadWriteLockGuaranteedAlignment) {
return rw_lock.lock_count.aligned.write_lock_count;
} else {
if (reinterpret_cast<uintptr_t>(std::addressof(rw_lock.lock_count)) & sizeof(u32)) {
return rw_lock.lock_count.not_aligned.write_lock_count;
} else {
return rw_lock.lock_count.aligned.write_lock_count;
}
}
}
ALWAYS_INLINE const u32 &GetWriteLockCount(const ReadWriteLockType &rw_lock) {
if constexpr (IsReadWriteLockGuaranteedAlignment) {
return rw_lock.lock_count.aligned.write_lock_count;
} else {
if (reinterpret_cast<uintptr_t>(std::addressof(rw_lock.lock_count)) & sizeof(u32)) {
return rw_lock.lock_count.not_aligned.write_lock_count;
} else {
return rw_lock.lock_count.aligned.write_lock_count;
}
}
}
ALWAYS_INLINE void IncReadLockCount(ReadWriteLockType::LockCount &lc) {
const u32 read_lock_count = lc.counter.Get<ReadWriteLockCounter::ReadLockCount>();
AMS_ASSERT(read_lock_count < ReadWriteLockCountMax);
lc.counter.Set<ReadWriteLockCounter::ReadLockCount>(read_lock_count + 1);
}
ALWAYS_INLINE void DecReadLockCount(ReadWriteLockType::LockCount &lc) {
const u32 read_lock_count = lc.counter.Get<ReadWriteLockCounter::ReadLockCount>();
AMS_ASSERT(read_lock_count > 0);
lc.counter.Set<ReadWriteLockCounter::ReadLockCount>(read_lock_count - 1);
}
ALWAYS_INLINE void IncReadLockWaiterCount(ReadWriteLockType::LockCount &lc) {
const u32 read_lock_waiter_count = lc.counter.Get<ReadWriteLockCounter::ReadLockWaiterCount>();
AMS_ASSERT(read_lock_waiter_count < ReadWriteLockWaiterCountMax);
lc.counter.Set<ReadWriteLockCounter::ReadLockWaiterCount>(read_lock_waiter_count + 1);
}
ALWAYS_INLINE void DecReadLockWaiterCount(ReadWriteLockType::LockCount &lc) {
const u32 read_lock_waiter_count = lc.counter.Get<ReadWriteLockCounter::ReadLockWaiterCount>();
AMS_ASSERT(read_lock_waiter_count > 0);
lc.counter.Set<ReadWriteLockCounter::ReadLockWaiterCount>(read_lock_waiter_count - 1);
}
ALWAYS_INLINE void IncWriteLockWaiterCount(ReadWriteLockType::LockCount &lc) {
const u32 write_lock_waiter_count = lc.counter.Get<ReadWriteLockCounter::WriteLockWaiterCount>();
AMS_ASSERT(write_lock_waiter_count < ReadWriteLockWaiterCountMax);
lc.counter.Set<ReadWriteLockCounter::WriteLockWaiterCount>(write_lock_waiter_count + 1);
}
ALWAYS_INLINE void DecWriteLockWaiterCount(ReadWriteLockType::LockCount &lc) {
const u32 write_lock_waiter_count = lc.counter.Get<ReadWriteLockCounter::WriteLockWaiterCount>();
AMS_ASSERT(write_lock_waiter_count > 0);
lc.counter.Set<ReadWriteLockCounter::WriteLockWaiterCount>(write_lock_waiter_count - 1);
}
ALWAYS_INLINE void IncWriteLockCount(ReadWriteLockType &rw_lock) {
u32 &write_lock_count = GetWriteLockCount(rw_lock);
AMS_ASSERT(write_lock_count < ReadWriteLockCountMax);
++write_lock_count;
}
ALWAYS_INLINE void DecWriteLockCount(ReadWriteLockType &rw_lock) {
u32 &write_lock_count = GetWriteLockCount(rw_lock);
AMS_ASSERT(write_lock_count > 0);
--write_lock_count;
}
ALWAYS_INLINE void SetWriteLocked(ReadWriteLockType::LockCount &lc) {
lc.counter.Set<ReadWriteLockCounter::WriteLocked>(1);
}
using ReadWriteLockImpl = ReadWriteLockTargetImpl;
}

View file

@ -0,0 +1,349 @@
/*
* Copyright (c) 2018-2020 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 {
#define ATOMIC_COMPARE_EXCHANGE_LOCK_COUNT(dst_ref, expected_ref, desired_ref, success, fail) \
(__atomic_compare_exchange(reinterpret_cast<u64 *>(&dst_ref), reinterpret_cast<u64 *>(&expected_ref), reinterpret_cast<u64 *>(&desired_ref), true, success, fail))
void ReadWriteLockHorizonImpl::AcquireReadLockWriteLocked(os::ReadWriteLockType *rw_lock) {
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 1);
AMS_ASSERT((GetThreadHandle(GetLockCount(rw_lock)) | svc::HandleWaitMask) == (impl::GetCurrentThreadHandle() | svc::HandleWaitMask));
alignas(alignof(u64)) LockCount expected = GetLockCount(rw_lock);
alignas(alignof(u64)) LockCount lock_count;
do {
lock_count = expected;
IncReadLockCount(lock_count);
} while (!ATOMIC_COMPARE_EXCHANGE_LOCK_COUNT(GetLockCount(rw_lock), expected, lock_count, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
}
void ReadWriteLockHorizonImpl::ReleaseReadLockWriteLocked(os::ReadWriteLockType *rw_lock) {
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 1);
AMS_ASSERT((GetThreadHandle(GetLockCount(rw_lock)) | svc::HandleWaitMask) == (impl::GetCurrentThreadHandle() | svc::HandleWaitMask));
alignas(alignof(u64)) LockCount expected = GetLockCount(rw_lock);
alignas(alignof(u64)) LockCount lock_count;
do {
lock_count = expected;
DecReadLockCount(lock_count);
} while (!ATOMIC_COMPARE_EXCHANGE_LOCK_COUNT(GetLockCount(rw_lock), expected, lock_count, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
return ReleaseWriteLockImpl(rw_lock);
}
void ReadWriteLockHorizonImpl::ReleaseWriteLockImpl(os::ReadWriteLockType *rw_lock) {
alignas(alignof(u64)) LockCount expected = GetLockCount(rw_lock);
alignas(alignof(u64)) LockCount lock_count = expected;
AMS_ASSERT(GetWriteLocked(lock_count) == 1);
if (GetReadLockCount(lock_count) == 0 && GetWriteLockCount(*rw_lock) == 0) {
rw_lock->owner_thread = nullptr;
if (GetWriteLockWaiterCount(lock_count) > 0) {
GetReference(rw_lock->cv_write_lock._storage).Signal();
} else if (GetReadLockWaiterCount(lock_count) > 0) {
GetReference(rw_lock->cv_read_lock._storage).Broadcast();
}
do {
ClearWriteLocked(lock_count);
SetThreadHandle(lock_count, (GetThreadHandle(lock_count) & svc::HandleWaitMask) ? GetThreadHandle(lock_count) : svc::InvalidHandle);
} while(!ATOMIC_COMPARE_EXCHANGE_LOCK_COUNT(GetLockCount(rw_lock), expected, lock_count, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
if (GetThreadHandle(lock_count)) {
R_ABORT_UNLESS(svc::ArbitrateUnlock(GetThreadHandleAddress(GetLockCount(rw_lock))));
}
}
}
void ReadWriteLockHorizonImpl::AcquireReadLock(os::ReadWriteLockType *rw_lock) {
AMS_ASSERT(::ams::svc::GetThreadLocalRegion()->disable_count == 0);
auto *cur_thread = impl::GetCurrentThread();
if (rw_lock->owner_thread == cur_thread) {
return AcquireReadLockWriteLocked(rw_lock);
} else {
const u32 cur_handle = cur_thread->thread_impl->handle;
bool arbitrated = false;
bool got_read_lock, needs_arbitrate_lock;
alignas(alignof(u64)) LockCount lock_count;
alignas(alignof(u64)) LockCount expected;
while (true) {
expected = GetLockCount(rw_lock);
do {
lock_count = expected;
if (GetWriteLocked(lock_count) != 0 || GetWriteLockWaiterCount(lock_count) != 0) {
if (!arbitrated) {
IncReadLockWaiterCount(lock_count);
}
const u32 h = GetThreadHandle(lock_count);
got_read_lock = false;
needs_arbitrate_lock = h != 0;
SetThreadHandle(lock_count, needs_arbitrate_lock ? (h | svc::HandleWaitMask) : cur_handle);
} else {
if (arbitrated) {
DecReadLockWaiterCount(lock_count);
}
IncReadLockCount(lock_count);
got_read_lock = true;
needs_arbitrate_lock = false;
}
} while (!ATOMIC_COMPARE_EXCHANGE_LOCK_COUNT(GetLockCount(rw_lock), expected, lock_count, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
if (!needs_arbitrate_lock) {
break;
}
R_ABORT_UNLESS(svc::ArbitrateLock(GetThreadHandle(lock_count) & ~svc::HandleWaitMask, GetThreadHandleAddress(GetLockCount(rw_lock)), cur_handle));
const u32 new_handle = GetThreadHandle(GetLockCount(rw_lock));
if ((new_handle | svc::HandleWaitMask) == (cur_handle | svc::HandleWaitMask)) {
SetThreadHandle(lock_count, new_handle);
break;
}
arbitrated = true;
}
if (got_read_lock) {
return;
}
expected = lock_count;
do {
while (GetWriteLockWaiterCount(lock_count)) {
GetReference(rw_lock->cv_read_lock._storage).Wait(GetPointer(GetLockCount(rw_lock).cs_storage));
expected = GetLockCount(rw_lock);
lock_count = expected;
}
DecReadLockWaiterCount(lock_count);
IncReadLockCount(lock_count);
SetThreadHandle(lock_count, (GetThreadHandle(lock_count) & svc::HandleWaitMask) ? GetThreadHandle(lock_count) : svc::InvalidHandle);
} while(!ATOMIC_COMPARE_EXCHANGE_LOCK_COUNT(GetLockCount(rw_lock), expected, lock_count, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
if (GetThreadHandle(lock_count)) {
R_ABORT_UNLESS(svc::ArbitrateUnlock(GetThreadHandleAddress(GetLockCount(rw_lock))));
}
}
}
bool ReadWriteLockHorizonImpl::TryAcquireReadLock(os::ReadWriteLockType *rw_lock) {
AMS_ASSERT(::ams::svc::GetThreadLocalRegion()->disable_count == 0);
auto *cur_thread = impl::GetCurrentThread();
if (rw_lock->owner_thread == cur_thread) {
AcquireReadLockWriteLocked(rw_lock);
return true;
} else {
alignas(alignof(u64)) LockCount expected = GetLockCount(rw_lock);
alignas(alignof(u64)) LockCount lock_count;
while (true) {
lock_count = expected;
AMS_ASSERT(GetWriteLocked(lock_count) == 0 || GetThreadHandle(lock_count) != 0);
if (GetWriteLocked(lock_count) == 0 && GetWriteLockWaiterCount(lock_count) == 0) {
IncReadLockCount(lock_count);
if (ATOMIC_COMPARE_EXCHANGE_LOCK_COUNT(GetLockCount(rw_lock), expected, lock_count, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
return true;
}
} else {
return false;
}
}
}
}
void ReadWriteLockHorizonImpl::ReleaseReadLock(os::ReadWriteLockType *rw_lock) {
AMS_ASSERT(::ams::svc::GetThreadLocalRegion()->disable_count == 0);
AMS_ASSERT(GetReadLockCount(GetLockCount(rw_lock)) > 0);
auto *cur_thread = impl::GetCurrentThread();
if (rw_lock->owner_thread == cur_thread) {
return ReleaseReadLockWriteLocked(rw_lock);
} else {
alignas(alignof(u64)) LockCount expected = GetLockCount(rw_lock);
alignas(alignof(u64)) LockCount lock_count;
while (true) {
lock_count = expected;
DecReadLockCount(lock_count);
if (GetReadLockCount(lock_count) == 0 && GetWriteLockWaiterCount(lock_count) != 0) {
break;
}
if (ATOMIC_COMPARE_EXCHANGE_LOCK_COUNT(GetLockCount(rw_lock), expected, lock_count, __ATOMIC_RELEASE, __ATOMIC_RELAXED)) {
return;
}
}
AMS_ASSERT(GetWriteLocked(lock_count) == 0);
const u32 cur_handle = cur_thread->thread_impl->handle;
do {
do {
const u32 h = GetThreadHandle(lock_count);
SetThreadHandle(lock_count, h != 0 ? (h | svc::HandleWaitMask) : cur_handle);
} while (!ATOMIC_COMPARE_EXCHANGE_LOCK_COUNT(GetLockCount(rw_lock), expected, lock_count, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
if (GetThreadHandle(lock_count) == cur_handle) {
break;
}
R_ABORT_UNLESS(svc::ArbitrateLock(GetThreadHandle(lock_count) & ~svc::HandleWaitMask, GetThreadHandleAddress(GetLockCount(rw_lock)), cur_handle));
expected = GetLockCount(rw_lock);
} while ((GetThreadHandle(GetLockCount(rw_lock)) | svc::HandleWaitMask) != (GetThreadHandle(lock_count) | svc::HandleWaitMask));
do {
lock_count = expected;
AMS_ASSERT(GetReadLockCount(lock_count) == 1 && GetWriteLockWaiterCount(lock_count) > 0);
DecReadLockCount(lock_count);
} while (!ATOMIC_COMPARE_EXCHANGE_LOCK_COUNT(GetLockCount(rw_lock), expected, lock_count, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
GetReference(rw_lock->cv_write_lock._storage).Signal();
do {
lock_count = expected;
AMS_ASSERT(GetReadLockCount(lock_count) == 0 && GetWriteLockWaiterCount(lock_count) > 0);
SetThreadHandle(lock_count, (GetThreadHandle(lock_count) & svc::HandleWaitMask) ? GetThreadHandle(lock_count) : svc::InvalidHandle);
} while(!ATOMIC_COMPARE_EXCHANGE_LOCK_COUNT(GetLockCount(rw_lock), expected, lock_count, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
if (GetThreadHandle(lock_count) & svc::HandleWaitMask) {
R_ABORT_UNLESS(svc::ArbitrateUnlock(GetThreadHandleAddress(GetLockCount(rw_lock))));
}
}
}
void ReadWriteLockHorizonImpl::AcquireWriteLock(os::ReadWriteLockType *rw_lock) {
AMS_ASSERT(::ams::svc::GetThreadLocalRegion()->disable_count == 0);
auto *cur_thread = impl::GetCurrentThread();
if (rw_lock->owner_thread == cur_thread) {
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 1);
IncWriteLockCount(*rw_lock);
} else {
const u32 cur_handle = cur_thread->thread_impl->handle;
alignas(alignof(u64)) LockCount expected;
alignas(alignof(u64)) LockCount lock_count;
bool arbitrated = false;
bool got_write_lock, needs_arbitrate_lock;
while (true) {
expected = GetLockCount(rw_lock);
do {
lock_count = expected;
AMS_ASSERT(GetWriteLocked(lock_count) == 0 || GetThreadHandle(lock_count) != svc::InvalidHandle);
if (GetReadLockCount(lock_count) > 0 || GetThreadHandle(lock_count) != svc::InvalidHandle) {
if (!arbitrated) {
IncWriteLockWaiterCount(lock_count);
}
const u32 h = GetThreadHandle(lock_count);
got_write_lock = false;
needs_arbitrate_lock = h != 0;
SetThreadHandle(lock_count, needs_arbitrate_lock ? (h | svc::HandleWaitMask) : cur_handle);
} else {
if (arbitrated) {
DecWriteLockWaiterCount(lock_count);
}
SetWriteLocked(lock_count);
got_write_lock = true;
needs_arbitrate_lock = false;
}
} while (!ATOMIC_COMPARE_EXCHANGE_LOCK_COUNT(GetLockCount(rw_lock), expected, lock_count, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
if (got_write_lock) {
break;
}
if (needs_arbitrate_lock) {
R_ABORT_UNLESS(svc::ArbitrateLock(GetThreadHandle(lock_count) & ~svc::HandleWaitMask, GetThreadHandleAddress(GetLockCount(rw_lock)), cur_handle));
arbitrated = true;
expected = GetLockCount(rw_lock);
if ((GetThreadHandle(lock_count) | svc::HandleWaitMask) != (cur_handle | svc::HandleWaitMask)) {
continue;
}
}
do {
lock_count = expected;
AMS_ASSERT(GetWriteLocked(lock_count) == 0);
while (GetReadLockCount(lock_count) > 0) {
GetReference(rw_lock->cv_write_lock._storage).Wait(GetPointer(GetLockCount(rw_lock).cs_storage));
expected = GetLockCount(rw_lock);
lock_count = expected;
if (GetWriteLocked(lock_count) == 1) {
break;
}
}
if (GetWriteLocked(lock_count) == 1) {
continue;
}
DecWriteLockWaiterCount(lock_count);
SetWriteLocked(lock_count);
} while (!ATOMIC_COMPARE_EXCHANGE_LOCK_COUNT(GetLockCount(rw_lock), expected, lock_count, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
break;
}
AMS_ASSERT(GetWriteLockCount(*rw_lock) == 0);
IncWriteLockCount(*rw_lock);
rw_lock->owner_thread = cur_thread;
}
}
bool ReadWriteLockHorizonImpl::TryAcquireWriteLock(os::ReadWriteLockType *rw_lock) {
AMS_ASSERT(::ams::svc::GetThreadLocalRegion()->disable_count == 0);
auto *cur_thread = impl::GetCurrentThread();
if (rw_lock->owner_thread == cur_thread) {
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 1);
IncWriteLockCount(*rw_lock);
return true;
} else {
const u32 cur_handle = cur_thread->thread_impl->handle;
alignas(alignof(u64)) LockCount expected = GetLockCount(rw_lock);
alignas(alignof(u64)) LockCount lock_count;
do {
lock_count = expected;
AMS_ASSERT(GetWriteLocked(lock_count) == 0 || GetThreadHandle(lock_count) != svc::InvalidHandle);
if (GetReadLockCount(lock_count) > 0 || GetThreadHandle(lock_count) != svc::InvalidHandle) {
return false;
}
SetWriteLocked(lock_count);
SetThreadHandle(lock_count, cur_handle);
} while (!ATOMIC_COMPARE_EXCHANGE_LOCK_COUNT(GetLockCount(rw_lock), expected, lock_count, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
AMS_ASSERT(GetWriteLockCount(*rw_lock) == 0);
IncWriteLockCount(*rw_lock);
rw_lock->owner_thread = cur_thread;
return true;
}
}
void ReadWriteLockHorizonImpl::ReleaseWriteLock(os::ReadWriteLockType *rw_lock) {
AMS_ASSERT(::ams::svc::GetThreadLocalRegion()->disable_count == 0);
AMS_ASSERT(GetWriteLockCount(*rw_lock) > 0);
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) != 0);
DecWriteLockCount(*rw_lock);
return ReleaseWriteLockImpl(rw_lock);
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2018-2020 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/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
class ReadWriteLockHorizonImpl {
private:
using LockCount = os::ReadWriteLockType::LockCount;
private:
static ALWAYS_INLINE u32 GetThreadHandle(const LockCount &lc) {
return GetReference(lc.cs_storage).Get()->thread_handle;
}
static ALWAYS_INLINE uintptr_t GetThreadHandleAddress(LockCount &lc) {
return reinterpret_cast<uintptr_t>(std::addressof(GetReference(lc.cs_storage).Get()->thread_handle));
}
static ALWAYS_INLINE void SetThreadHandle(LockCount &lc, u32 handle) {
GetReference(lc.cs_storage).Get()->thread_handle = handle;
}
static void AcquireReadLockWriteLocked(os::ReadWriteLockType *rw_lock);
static void ReleaseReadLockWriteLocked(os::ReadWriteLockType *rw_lock);
static void ReleaseWriteLockImpl(os::ReadWriteLockType *rw_lock);
public:
static void AcquireReadLock(os::ReadWriteLockType *rw_lock);
static bool TryAcquireReadLock(os::ReadWriteLockType *rw_lock);
static void ReleaseReadLock(os::ReadWriteLockType *rw_lock);
static void AcquireWriteLock(os::ReadWriteLockType *rw_lock);
static bool TryAcquireWriteLock(os::ReadWriteLockType *rw_lock);
static void ReleaseWriteLock(os::ReadWriteLockType *rw_lock);
};
using ReadWriteLockTargetImpl = ReadWriteLockHorizonImpl;
}

View file

@ -27,4 +27,8 @@ namespace ams::os::impl {
return GetTickManager().GetTick();
}
ALWAYS_INLINE Tick GetCurrentTickOrdered() {
return GetTickManager().GetSystemTickOrdered();
}
}

View file

@ -37,6 +37,10 @@ namespace ams::os::impl {
return this->impl.GetTick();
}
ALWAYS_INLINE Tick GetSystemTickOrdered() const {
return this->impl.GetSystemTickOrdered();
}
ALWAYS_INLINE s64 GetTickFrequency() const {
return this->impl.GetTickFrequency();
}

View file

@ -32,6 +32,22 @@ namespace ams::os::impl {
return Tick(tick);
}
ALWAYS_INLINE Tick GetSystemTickOrdered() const {
s64 tick;
#if defined(ATMOSPHERE_ARCH_ARM64)
__asm__ __volatile__("dsb ish\n"
"isb\n"
"mrs %[tick], cntpct_el0\n"
"isb"
: [tick]"=&r"(tick)
:
: "memory");
#else
#error "Unknown Architecture for TickManagerImpl::GetSystemTickOrdered"
#endif
return Tick(tick);
}
static constexpr ALWAYS_INLINE s64 GetTickFrequency() {
return static_cast<s64>(::ams::svc::TicksPerSecond);
}

View file

@ -0,0 +1,102 @@
/*
* Copyright (c) 2018-2020 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 "impl/os_thread_manager.hpp"
#include "impl/os_rw_lock_impl.hpp"
namespace ams::os {
void InitalizeReadWriteLock(ReadWriteLockType *rw_lock) {
/* Create objects. */
new (GetPointer(impl::GetLockCount(rw_lock).cs_storage)) impl::InternalCriticalSection;
new (GetPointer(rw_lock->cv_read_lock._storage)) impl::InternalConditionVariable;
new (GetPointer(rw_lock->cv_write_lock._storage)) impl::InternalConditionVariable;
/* Set member variables. */
impl::ClearReadLockCount(impl::GetLockCount(rw_lock));
impl::ClearWriteLocked(impl::GetLockCount(rw_lock));
impl::ClearReadLockWaiterCount(impl::GetLockCount(rw_lock));
impl::ClearWriteLockWaiterCount(impl::GetLockCount(rw_lock));
impl::ClearWriteLockCount(rw_lock);
rw_lock->owner_thread = nullptr;
/* Mark initialized. */
rw_lock->state = ReadWriteLockType::State_Initialized;
}
void FinalizeReadWriteLock(ReadWriteLockType *rw_lock) {
AMS_ASSERT(rw_lock->state == ReadWriteLockType::State_Initialized);
/* Don't allow finalizing a locked lock. */
AMS_ASSERT(impl::GetReadLockCount(impl::GetLockCount(rw_lock)) == 0);
AMS_ASSERT(impl::GetWriteLocked(impl::GetLockCount(rw_lock)) == 0);
/* Mark not initialized. */
rw_lock->state = ReadWriteLockType::State_NotInitialized;
/* Destroy objects. */
GetReference(rw_lock->cv_write_lock._storage).~InternalConditionVariable();
GetReference(rw_lock->cv_read_lock._storage).~InternalConditionVariable();
GetReference(impl::GetLockCount(rw_lock).cs_storage).~InternalCriticalSection();
}
void AcquireReadLock(ReadWriteLockType *rw_lock) {
AMS_ASSERT(rw_lock->state == ReadWriteLockType::State_Initialized);
return impl::ReadWriteLockImpl::AcquireReadLock(rw_lock);
}
bool TryAcquireReadLock(ReadWriteLockType *rw_lock) {
AMS_ASSERT(rw_lock->state == ReadWriteLockType::State_Initialized);
return impl::ReadWriteLockImpl::TryAcquireReadLock(rw_lock);
}
void ReleaseReadLock(ReadWriteLockType *rw_lock) {
AMS_ASSERT(rw_lock->state == ReadWriteLockType::State_Initialized);
return impl::ReadWriteLockImpl::ReleaseReadLock(rw_lock);
}
void AcquireWriteLock(ReadWriteLockType *rw_lock) {
AMS_ASSERT(rw_lock->state == ReadWriteLockType::State_Initialized);
return impl::ReadWriteLockImpl::AcquireWriteLock(rw_lock);
}
bool TryAcquireWriteLock(ReadWriteLockType *rw_lock) {
AMS_ASSERT(rw_lock->state == ReadWriteLockType::State_Initialized);
return impl::ReadWriteLockImpl::TryAcquireWriteLock(rw_lock);
}
void ReleaseWriteLock(ReadWriteLockType *rw_lock) {
AMS_ASSERT(rw_lock->state == ReadWriteLockType::State_Initialized);
AMS_ABORT_UNLESS(rw_lock->owner_thread == impl::GetCurrentThread());
return impl::ReadWriteLockImpl::ReleaseWriteLock(rw_lock);
}
bool IsReadLockHeld(const ReadWriteLockType *rw_lock) {
AMS_ASSERT(rw_lock->state == ReadWriteLockType::State_Initialized);
return impl::GetReadLockCount(impl::GetLockCount(rw_lock)) != 0;
}
bool IsWriteLockHeldByCurrentThread(const ReadWriteLockType *rw_lock) {
AMS_ASSERT(rw_lock->state == ReadWriteLockType::State_Initialized);
return rw_lock->owner_thread == impl::GetCurrentThread() && impl::GetWriteLockCount(*rw_lock) != 0;
}
bool IsReadWriteLockOwnerThread(const ReadWriteLockType *rw_lock) {
AMS_ASSERT(rw_lock->state == ReadWriteLockType::State_Initialized);
return rw_lock->owner_thread == impl::GetCurrentThread();
}
}

View file

@ -22,6 +22,10 @@ namespace ams::os {
return impl::GetTickManager().GetTick();
}
Tick GetSystemTickOrdered() {
return impl::GetTickManager().GetSystemTickOrdered();
}
s64 GetSystemTickFrequency() {
return impl::GetTickManager().GetTickFrequency();
}

View file

@ -42,7 +42,7 @@ namespace ams::util {
inline Uuid Convert() const {
/* Convert the fields from native endian to big endian. */
util::BitPack32 converted[4] = {util::BitPack32(0), util::BitPack32(0), util::BitPack32(0), util::BitPack32(0)};
util::BitPack32 converted[4] = {util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}};
converted[0].Set<TimeLow>(util::ConvertToBigEndian(this->data[0].Get<TimeLow>()));
@ -75,7 +75,7 @@ namespace ams::util {
constexpr u8 Reserved = 0x1;
/* Generate a random uuid. */
UuidImpl uuid = {util::BitPack32(0), util::BitPack32(0), util::BitPack32(0), util::BitPack32(0)};
UuidImpl uuid = {util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}};
os::GenerateRandomBytes(uuid.data, sizeof(uuid.data));
/* Set version and reserved. */
@ -97,7 +97,7 @@ namespace ams::util {
constexpr u8 Reserved = 0x1;
/* Generate a uuid from a SHA1 hash. */
UuidImpl uuid = {util::BitPack32(0), util::BitPack32(0), util::BitPack32(0), util::BitPack32(0)};
UuidImpl uuid = {util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}};
std::memcpy(uuid.data, sha1_hash, sizeof(uuid.data));
/* Set version and reserved. */