mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-09 22:56:35 +00:00
os: implement ReadWriteLock
This commit is contained in:
parent
6eb77e69c4
commit
97cba5e881
20 changed files with 941 additions and 66 deletions
|
@ -19,12 +19,18 @@
|
|||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class ReadWriteLockImpl;
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
class ReadWriteLockHorizonImpl;
|
||||
#endif
|
||||
|
||||
class InternalConditionVariableImpl;
|
||||
|
||||
class InternalCriticalSectionImpl {
|
||||
private:
|
||||
friend class ReadWriteLockImpl;
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
friend class ReadWriteLockHorizonImpl;
|
||||
#endif
|
||||
|
||||
friend class InternalConditionVariableImpl;
|
||||
private:
|
||||
u32 thread_handle;
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
#include "os_common_types.hpp"
|
||||
#include <stratosphere/os/os_rw_lock_common.hpp>
|
||||
#include <stratosphere/os/os_rw_lock_types.hpp>
|
||||
#include <stratosphere/os/os_rw_lock_api.hpp>
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
|
@ -23,67 +25,83 @@ namespace ams::os {
|
|||
NON_COPYABLE(ReadWriteLock);
|
||||
NON_MOVEABLE(ReadWriteLock);
|
||||
private:
|
||||
::RwLock r;
|
||||
ReadWriteLockType rw_lock;
|
||||
public:
|
||||
ReadWriteLock() {
|
||||
rwlockInit(&this->r);
|
||||
}
|
||||
constexpr explicit ReadWriteLock() : rw_lock{{}, 0, ::ams::os::ReadWriteLockType::State_Initialized, nullptr, 0, {}, {}} { /* ... */ }
|
||||
|
||||
bool IsWriteLockHeldByCurrentThread() const {
|
||||
return rwlockIsWriteLockHeldByCurrentThread(const_cast<::RwLock *>(&this->r));
|
||||
}
|
||||
|
||||
bool IsLockOwner() const {
|
||||
return rwlockIsOwnedByCurrentThread(const_cast<::RwLock *>(&this->r));
|
||||
}
|
||||
~ReadWriteLock() { os::FinalizeReadWriteLock(std::addressof(this->rw_lock)); }
|
||||
|
||||
void AcquireReadLock() {
|
||||
rwlockReadLock(&this->r);
|
||||
}
|
||||
|
||||
void ReleaseReadLock() {
|
||||
rwlockReadUnlock(&this->r);
|
||||
return os::AcquireReadLock(std::addressof(this->rw_lock));
|
||||
}
|
||||
|
||||
bool TryAcquireReadLock() {
|
||||
return rwlockTryReadLock(&this->r);
|
||||
return os::TryAcquireReadLock(std::addressof(this->rw_lock));
|
||||
}
|
||||
|
||||
void ReleaseReadLock() {
|
||||
return os::ReleaseReadLock(std::addressof(this->rw_lock));
|
||||
}
|
||||
|
||||
void AcquireWriteLock() {
|
||||
rwlockWriteLock(&this->r);
|
||||
}
|
||||
|
||||
void ReleaseWriteLock() {
|
||||
rwlockWriteUnlock(&this->r);
|
||||
return os::AcquireWriteLock(std::addressof(this->rw_lock));
|
||||
}
|
||||
|
||||
bool TryAcquireWriteLock() {
|
||||
return rwlockTryWriteLock(&this->r);
|
||||
return os::TryAcquireWriteLock(std::addressof(this->rw_lock));
|
||||
}
|
||||
|
||||
void ReleaseWriteLock() {
|
||||
return os::ReleaseWriteLock(std::addressof(this->rw_lock));
|
||||
}
|
||||
|
||||
bool IsReadLockHeld() const {
|
||||
return os::IsReadLockHeld(std::addressof(this->rw_lock));
|
||||
}
|
||||
|
||||
bool IsWriteLockHeldByCurrentThread() const {
|
||||
return os::IsWriteLockHeldByCurrentThread(std::addressof(this->rw_lock));
|
||||
}
|
||||
|
||||
bool IsLockOwner() const {
|
||||
return os::IsReadWriteLockOwnerThread(std::addressof(this->rw_lock));
|
||||
}
|
||||
|
||||
void lock_shared() {
|
||||
this->AcquireReadLock();
|
||||
}
|
||||
|
||||
void unlock_shared() {
|
||||
this->ReleaseReadLock();
|
||||
return this->AcquireReadLock();
|
||||
}
|
||||
|
||||
bool try_lock_shared() {
|
||||
return this->TryAcquireReadLock();
|
||||
}
|
||||
|
||||
void lock() {
|
||||
this->AcquireWriteLock();
|
||||
void unlock_shared() {
|
||||
return this->ReleaseReadLock();
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
this->ReleaseWriteLock();
|
||||
void lock() {
|
||||
return this->AcquireWriteLock();
|
||||
}
|
||||
|
||||
bool try_lock() {
|
||||
return this->TryAcquireWriteLock();
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
return this->ReleaseWriteLock();
|
||||
}
|
||||
|
||||
operator ReadWriteLockType &() {
|
||||
return this->rw_lock;
|
||||
}
|
||||
|
||||
operator const ReadWriteLockType &() const {
|
||||
return this->rw_lock;
|
||||
}
|
||||
|
||||
ReadWriteLockType *GetBase() {
|
||||
return std::addressof(this->rw_lock);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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 <vapours.hpp>
|
||||
#include <stratosphere/os/os_rw_lock_common.hpp>
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
struct ReadWriteLockType;
|
||||
|
||||
void InitalizeReadWriteLock(ReadWriteLockType *rw_lock);
|
||||
void FinalizeReadWriteLock(ReadWriteLockType *rw_lock);
|
||||
|
||||
void AcquireReadLock(ReadWriteLockType *rw_lock);
|
||||
bool TryAcquireReadLock(ReadWriteLockType *rw_lock);
|
||||
void ReleaseReadLock(ReadWriteLockType *rw_lock);
|
||||
|
||||
void AcquireWriteLock(ReadWriteLockType *rw_lock);
|
||||
bool TryAcquireWriteLock(ReadWriteLockType *rw_lock);
|
||||
void ReleaseWriteLock(ReadWriteLockType *rw_lock);
|
||||
|
||||
bool IsReadLockHeld(const ReadWriteLockType *rw_lock);
|
||||
bool IsWriteLockHeldByCurrentThread(const ReadWriteLockType *rw_lock);
|
||||
bool IsReadWriteLockOwnerThread(const ReadWriteLockType *rw_lock);
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 <vapours.hpp>
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
constexpr inline s32 ReadWriteLockCountMax = (1 << (BITSIZEOF(u16) - 1)) - 1;
|
||||
constexpr inline s32 ReadWriteLockWaiterCountMax = (1 << BITSIZEOF(u8)) - 1;
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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 <vapours.hpp>
|
||||
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
|
||||
#include <stratosphere/os/impl/os_internal_condition_variable.hpp>
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
struct ThreadType;
|
||||
|
||||
struct ReadWriteLockType {
|
||||
enum State {
|
||||
State_NotInitialized = 0,
|
||||
State_Initialized = 1,
|
||||
};
|
||||
|
||||
struct LockCount {
|
||||
union {
|
||||
s32 _arr[sizeof(impl::InternalCriticalSectionStorage) / sizeof(s32)];
|
||||
impl::InternalCriticalSectionStorage cs_storage;
|
||||
};
|
||||
util::BitPack32 counter;
|
||||
};
|
||||
static_assert(std::is_pod<LockCount>::value);
|
||||
static_assert(std::is_trivial<LockCount>::value);
|
||||
|
||||
union {
|
||||
struct {
|
||||
LockCount c;
|
||||
u32 write_lock_count;
|
||||
} aligned;
|
||||
struct {
|
||||
u32 write_lock_count;
|
||||
LockCount c;
|
||||
} not_aligned;
|
||||
} lock_count;
|
||||
|
||||
u32 reserved_1;
|
||||
|
||||
u8 state;
|
||||
ThreadType *owner_thread;
|
||||
u32 reserved_2;
|
||||
|
||||
union {
|
||||
s32 _arr[sizeof(impl::InternalConditionVariableStorage) / sizeof(s32)];
|
||||
impl::InternalConditionVariableStorage _storage;
|
||||
} cv_read_lock;
|
||||
union {
|
||||
s32 _arr[sizeof(impl::InternalConditionVariableStorage) / sizeof(s32)];
|
||||
impl::InternalConditionVariableStorage _storage;
|
||||
} cv_write_lock;
|
||||
};
|
||||
static_assert(std::is_trivial<ReadWriteLockType>::value);
|
||||
|
||||
}
|
|
@ -22,6 +22,7 @@ namespace ams::os {
|
|||
|
||||
/* Tick API. */
|
||||
Tick GetSystemTick();
|
||||
Tick GetSystemTickOrdered();
|
||||
s64 GetSystemTickFrequency();
|
||||
TimeSpan ConvertToTimeSpan(Tick tick);
|
||||
Tick ConvertToTick(TimeSpan ts);
|
||||
|
|
|
@ -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() {
|
||||
|
|
185
libraries/libstratosphere/source/os/impl/os_rw_lock_impl.hpp
Normal file
185
libraries/libstratosphere/source/os/impl/os_rw_lock_impl.hpp
Normal 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;
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -27,4 +27,8 @@ namespace ams::os::impl {
|
|||
return GetTickManager().GetTick();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Tick GetCurrentTickOrdered() {
|
||||
return GetTickManager().GetSystemTickOrdered();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
102
libraries/libstratosphere/source/os/os_rw_lock.cpp
Normal file
102
libraries/libstratosphere/source/os/os_rw_lock.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
|
@ -22,6 +22,10 @@ namespace ams::os {
|
|||
return impl::GetTickManager().GetTick();
|
||||
}
|
||||
|
||||
Tick GetSystemTickOrdered() {
|
||||
return impl::GetTickManager().GetSystemTickOrdered();
|
||||
}
|
||||
|
||||
s64 GetSystemTickFrequency() {
|
||||
return impl::GetTickManager().GetTickFrequency();
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -23,8 +23,8 @@ namespace ams::svc::arch::arm64 {
|
|||
|
||||
struct ThreadLocalRegion {
|
||||
u32 message_buffer[MessageBufferSize / sizeof(u32)];
|
||||
u16 disable_count;
|
||||
u16 preemption_state;
|
||||
volatile u16 disable_count;
|
||||
volatile u16 interrupt_flag;
|
||||
/* TODO: How should we handle libnx vs Nintendo user thread local space? */
|
||||
uintptr_t TODO[(0x200 - 0x108) / sizeof(uintptr_t)];
|
||||
};
|
||||
|
|
|
@ -58,11 +58,11 @@ namespace ams::svc::ipc {
|
|||
private:
|
||||
util::BitPack32 header[2];
|
||||
public:
|
||||
constexpr ALWAYS_INLINE MessageHeader() : header({util::BitPack32(0), util::BitPack32(0)}) {
|
||||
constexpr ALWAYS_INLINE MessageHeader() : header({util::BitPack32{0}, util::BitPack32{0}}) {
|
||||
this->header[0].Set<Tag>(NullTag);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE MessageHeader(u16 tag, bool special, s32 ptr, s32 send, s32 recv, s32 exch, s32 raw, s32 recv_list) : header({util::BitPack32(0), util::BitPack32(0)}) {
|
||||
constexpr ALWAYS_INLINE MessageHeader(u16 tag, bool special, s32 ptr, s32 send, s32 recv, s32 exch, s32 raw, s32 recv_list) : header({util::BitPack32{0}, util::BitPack32{0}}) {
|
||||
this->header[0].Set<Tag>(tag);
|
||||
this->header[0].Set<PointerCount>(ptr);
|
||||
this->header[0].Set<SendCount>(send);
|
||||
|
@ -74,11 +74,11 @@ namespace ams::svc::ipc {
|
|||
this->header[1].Set<HasSpecialHeader>(special);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE explicit MessageHeader(const MessageBuffer &buf) : header({util::BitPack32(0), util::BitPack32(0)}) {
|
||||
ALWAYS_INLINE explicit MessageHeader(const MessageBuffer &buf) : header({util::BitPack32{0}, util::BitPack32{0}}) {
|
||||
buf.Get(0, this->header, util::size(this->header));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE explicit MessageHeader(const u32 *msg) : header({util::BitPack32(msg[0]), util::BitPack32(msg[1])}) { /* ... */ }
|
||||
ALWAYS_INLINE explicit MessageHeader(const u32 *msg) : header({util::BitPack32{msg[0]}, util::BitPack32{msg[1]}}) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE u16 GetTag() const {
|
||||
return this->header[0].Get<Tag>();
|
||||
|
@ -143,13 +143,13 @@ namespace ams::svc::ipc {
|
|||
util::BitPack32 header;
|
||||
bool has_header;
|
||||
public:
|
||||
constexpr ALWAYS_INLINE explicit SpecialHeader(bool pid, s32 copy, s32 move) : header(0), has_header(true) {
|
||||
constexpr ALWAYS_INLINE explicit SpecialHeader(bool pid, s32 copy, s32 move) : header{0}, has_header(true) {
|
||||
this->header.Set<HasProcessId>(pid);
|
||||
this->header.Set<CopyHandleCount>(copy);
|
||||
this->header.Set<MoveHandleCount>(move);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE explicit SpecialHeader(const MessageBuffer &buf, const MessageHeader &hdr) : header(0), has_header(hdr.GetHasSpecialHeader()) {
|
||||
ALWAYS_INLINE explicit SpecialHeader(const MessageBuffer &buf, const MessageHeader &hdr) : header{0}, has_header(hdr.GetHasSpecialHeader()) {
|
||||
if (this->has_header) {
|
||||
buf.Get(MessageHeader::GetDataSize() / sizeof(util::BitPack32), std::addressof(this->header), sizeof(this->header) / sizeof(util::BitPack32));
|
||||
}
|
||||
|
@ -219,9 +219,9 @@ namespace ams::svc::ipc {
|
|||
private:
|
||||
util::BitPack32 data[3];
|
||||
public:
|
||||
constexpr ALWAYS_INLINE MapAliasDescriptor() : data({util::BitPack32(0), util::BitPack32(0), util::BitPack32(0)}) { /* ... */ }
|
||||
constexpr ALWAYS_INLINE MapAliasDescriptor() : data({util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}}) { /* ... */ }
|
||||
|
||||
ALWAYS_INLINE MapAliasDescriptor(const void *buffer, size_t _size, Attribute attr = Attribute_Ipc) : data({util::BitPack32(0), util::BitPack32(0), util::BitPack32(0)}) {
|
||||
ALWAYS_INLINE MapAliasDescriptor(const void *buffer, size_t _size, Attribute attr = Attribute_Ipc) : data({util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}}) {
|
||||
const u64 address = reinterpret_cast<u64>(buffer);
|
||||
const u64 size = static_cast<u64>(_size);
|
||||
this->data[0] = { static_cast<u32>(size) };
|
||||
|
@ -233,7 +233,7 @@ namespace ams::svc::ipc {
|
|||
this->data[2].Set<AddressHigh>(GetAddressHigh(address));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE MapAliasDescriptor(const MessageBuffer &buf, s32 index) : data({util::BitPack32(0), util::BitPack32(0), util::BitPack32(0)}) {
|
||||
ALWAYS_INLINE MapAliasDescriptor(const MessageBuffer &buf, s32 index) : data({util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}}) {
|
||||
buf.Get(index, this->data, util::size(this->data));
|
||||
}
|
||||
|
||||
|
@ -283,9 +283,9 @@ namespace ams::svc::ipc {
|
|||
private:
|
||||
util::BitPack32 data[2];
|
||||
public:
|
||||
constexpr ALWAYS_INLINE PointerDescriptor() : data({util::BitPack32(0), util::BitPack32(0)}) { /* ... */ }
|
||||
constexpr ALWAYS_INLINE PointerDescriptor() : data({util::BitPack32{0}, util::BitPack32{0}}) { /* ... */ }
|
||||
|
||||
ALWAYS_INLINE PointerDescriptor(const void *buffer, size_t size, s32 index) : data({util::BitPack32(0), util::BitPack32(0)}) {
|
||||
ALWAYS_INLINE PointerDescriptor(const void *buffer, size_t size, s32 index) : data({util::BitPack32{0}, util::BitPack32{0}}) {
|
||||
const u64 address = reinterpret_cast<u64>(buffer);
|
||||
|
||||
this->data[0].Set<Index>(index);
|
||||
|
@ -296,7 +296,7 @@ namespace ams::svc::ipc {
|
|||
this->data[1] = { static_cast<u32>(address) };
|
||||
}
|
||||
|
||||
ALWAYS_INLINE PointerDescriptor(const MessageBuffer &buf, s32 index) : data({util::BitPack32(0), util::BitPack32(0)}) {
|
||||
ALWAYS_INLINE PointerDescriptor(const MessageBuffer &buf, s32 index) : data({util::BitPack32{0}, util::BitPack32{0}}) {
|
||||
buf.Get(index, this->data, util::size(this->data));
|
||||
}
|
||||
|
||||
|
@ -338,9 +338,9 @@ namespace ams::svc::ipc {
|
|||
private:
|
||||
util::BitPack32 data[2];
|
||||
public:
|
||||
constexpr ALWAYS_INLINE ReceiveListEntry() : data({util::BitPack32(0), util::BitPack32(0)}) { /* ... */ }
|
||||
constexpr ALWAYS_INLINE ReceiveListEntry() : data({util::BitPack32{0}, util::BitPack32{0}}) { /* ... */ }
|
||||
|
||||
ALWAYS_INLINE ReceiveListEntry(const void *buffer, size_t size) : data({util::BitPack32(0), util::BitPack32(0)}) {
|
||||
ALWAYS_INLINE ReceiveListEntry(const void *buffer, size_t size) : data({util::BitPack32{0}, util::BitPack32{0}}) {
|
||||
const u64 address = reinterpret_cast<u64>(buffer);
|
||||
|
||||
this->data[0] = { static_cast<u32>(address) };
|
||||
|
@ -349,7 +349,7 @@ namespace ams::svc::ipc {
|
|||
this->data[1].Set<Size>(size);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ReceiveListEntry(u32 a, u32 b) : data({util::BitPack32(a), util::BitPack32(b)}) { /* ... */ }
|
||||
ALWAYS_INLINE ReceiveListEntry(u32 a, u32 b) : data({util::BitPack32{a}, util::BitPack32{b}}) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE uintptr_t GetAddress() {
|
||||
const u64 address = (static_cast<u64>(this->data[1].Get<AddressHigh>()) << AddressLow::Count) | this->data[0].Get<AddressLow>();
|
||||
|
|
|
@ -23,7 +23,8 @@ namespace ams::util {
|
|||
namespace impl {
|
||||
|
||||
template<typename IntegralStorageType>
|
||||
class BitPack {
|
||||
struct BitPack {
|
||||
IntegralStorageType value;
|
||||
private:
|
||||
static_assert(std::is_integral<IntegralStorageType>::value);
|
||||
static_assert(std::is_unsigned<IntegralStorageType>::value);
|
||||
|
@ -49,16 +50,13 @@ namespace ams::util {
|
|||
static constexpr size_t Next = Index + Count;
|
||||
|
||||
using BitPackType = BitPack<IntegralStorageType>;
|
||||
static_assert(std::is_pod<BitPackType>::value);
|
||||
|
||||
static_assert(Mask<Index, Count> != 0);
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value);
|
||||
static_assert(!std::is_same<T, bool>::value || Count == 1);
|
||||
};
|
||||
private:
|
||||
IntegralStorageType value;
|
||||
public:
|
||||
constexpr ALWAYS_INLINE BitPack(IntegralStorageType v) : value(v) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE void Clear() {
|
||||
constexpr IntegralStorageType Zero = IntegralStorageType(0);
|
||||
this->value = Zero;
|
||||
|
@ -86,6 +84,10 @@ namespace ams::util {
|
|||
using BitPack32 = impl::BitPack<u32>;
|
||||
using BitPack64 = impl::BitPack<u64>;
|
||||
|
||||
static_assert(std::is_pod<BitPack8>::value);
|
||||
static_assert(std::is_pod<BitPack16>::value);
|
||||
static_assert(std::is_pod<BitPack32>::value);
|
||||
static_assert(std::is_pod<BitPack64>::value);
|
||||
static_assert(std::is_trivially_destructible<BitPack8 >::value);
|
||||
static_assert(std::is_trivially_destructible<BitPack16>::value);
|
||||
static_assert(std::is_trivially_destructible<BitPack32>::value);
|
||||
|
|
|
@ -42,8 +42,7 @@ namespace ams::ldr::caps {
|
|||
constexpr ALWAYS_INLINE typename name::Type Get##name() const { return this->Get<name>(); }
|
||||
|
||||
constexpr ALWAYS_INLINE CapabilityId GetCapabilityId(util::BitPack32 cap) {
|
||||
using RawValue = CapabilityField<0, BITSIZEOF(u32)>;
|
||||
return static_cast<CapabilityId>(__builtin_ctz(~cap.Get<RawValue>()));
|
||||
return static_cast<CapabilityId>(__builtin_ctz(~cap.value));
|
||||
}
|
||||
|
||||
#define CAPABILITY_CLASS_NAME(id) Capability##id
|
||||
|
@ -53,7 +52,6 @@ namespace ams::ldr::caps {
|
|||
public: \
|
||||
static constexpr CapabilityId Id = CapabilityId::id; \
|
||||
using IdBits = CapabilityField<0, static_cast<size_t>(Id) + 1>; \
|
||||
using RawValue = CapabilityField<IdBits::Next, BITSIZEOF(u32) - IdBits::Next>; \
|
||||
static constexpr u32 IdBitsValue = (static_cast<u32>(1) << static_cast<size_t>(Id)) - 1; \
|
||||
private: \
|
||||
util::BitPack32 value; \
|
||||
|
@ -62,9 +60,9 @@ namespace ams::ldr::caps {
|
|||
constexpr ALWAYS_INLINE typename FieldType::Type Get() const { return this->value.Get<FieldType>(); } \
|
||||
template<typename FieldType> \
|
||||
constexpr ALWAYS_INLINE void Set(typename FieldType::Type fv) { this->value.Set<FieldType>(fv); } \
|
||||
constexpr ALWAYS_INLINE u32 GetValue() const { return this->Get<RawValue>(); } \
|
||||
constexpr ALWAYS_INLINE u32 GetValue() const { return this->value.value; } \
|
||||
public: \
|
||||
constexpr ALWAYS_INLINE CAPABILITY_CLASS_NAME(id)(util::BitPack32 v) : value(v) { /* ... */ } \
|
||||
constexpr ALWAYS_INLINE CAPABILITY_CLASS_NAME(id)(util::BitPack32 v) : value{v} { /* ... */ } \
|
||||
\
|
||||
static constexpr CAPABILITY_CLASS_NAME(id) Decode(util::BitPack32 v) { return CAPABILITY_CLASS_NAME(id)(v); } \
|
||||
\
|
||||
|
|
Loading…
Reference in a new issue