From 5f0779ce156487dd9891816e0f6a0eb3a74291d9 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 3 Dec 2019 19:26:43 -0800 Subject: [PATCH] os/dd: primitive fixes --- include/stratosphere/dd.hpp | 1 + include/stratosphere/dd/dd_io_mappings.hpp | 1 + include/stratosphere/dd/dd_process_handle.hpp | 24 +++++ include/stratosphere/os.hpp | 2 + include/stratosphere/os/os_common_types.hpp | 4 - include/stratosphere/os/os_condvar.hpp | 49 +++++----- include/stratosphere/os/os_message_queue.hpp | 11 +-- include/stratosphere/os/os_process_handle.hpp | 28 ++++++ include/stratosphere/os/os_rw_lock.hpp | 89 +++++++++++++++++++ include/stratosphere/os/os_timeout_helper.hpp | 4 +- source/map/map_api.cpp | 4 +- .../os_waitable_holder_of_message_queue.hpp | 20 ++++- source/os/impl/os_waitable_object_list.hpp | 2 +- source/os/os_event.cpp | 2 +- source/os/os_message_queue.cpp | 46 ++++++---- source/os/os_process_handle.cpp | 44 +++++++++ 16 files changed, 271 insertions(+), 60 deletions(-) create mode 100644 include/stratosphere/dd/dd_process_handle.hpp create mode 100644 include/stratosphere/os/os_process_handle.hpp create mode 100644 include/stratosphere/os/os_rw_lock.hpp create mode 100644 source/os/os_process_handle.cpp diff --git a/include/stratosphere/dd.hpp b/include/stratosphere/dd.hpp index 8d8c9bbe..d82cd874 100644 --- a/include/stratosphere/dd.hpp +++ b/include/stratosphere/dd.hpp @@ -17,3 +17,4 @@ #pragma once #include "dd/dd_io_mappings.hpp" +#include "dd/dd_process_handle.hpp" diff --git a/include/stratosphere/dd/dd_io_mappings.hpp b/include/stratosphere/dd/dd_io_mappings.hpp index 4105751d..22f3db41 100644 --- a/include/stratosphere/dd/dd_io_mappings.hpp +++ b/include/stratosphere/dd/dd_io_mappings.hpp @@ -32,4 +32,5 @@ namespace ams::dd { AMS_ASSERT(io_mapping); return io_mapping; } + } diff --git a/include/stratosphere/dd/dd_process_handle.hpp b/include/stratosphere/dd/dd_process_handle.hpp new file mode 100644 index 00000000..a3e7f8c0 --- /dev/null +++ b/include/stratosphere/dd/dd_process_handle.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2019 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 . + */ + +#pragma once +#include + +namespace ams::dd { + + ::Handle GetCurrentProcessHandle(); + +} diff --git a/include/stratosphere/os.hpp b/include/stratosphere/os.hpp index 04664b12..d899759b 100644 --- a/include/stratosphere/os.hpp +++ b/include/stratosphere/os.hpp @@ -18,8 +18,10 @@ #include "os/os_common_types.hpp" #include "os/os_managed_handle.hpp" +#include "os/os_process_handle.hpp" #include "os/os_mutex.hpp" #include "os/os_condvar.hpp" +#include "os/os_rw_lock.hpp" #include "os/os_semaphore.hpp" #include "os/os_timeout_helper.hpp" #include "os/os_event.hpp" diff --git a/include/stratosphere/os/os_common_types.hpp b/include/stratosphere/os/os_common_types.hpp index f88c3ba1..dc10018a 100644 --- a/include/stratosphere/os/os_common_types.hpp +++ b/include/stratosphere/os/os_common_types.hpp @@ -54,10 +54,6 @@ namespace ams::os { return process_id; } - NX_INLINE ProcessId GetCurrentProcessId() { - return GetProcessId(CUR_PROCESS_HANDLE); - } - inline constexpr bool operator==(const ProcessId &lhs, const ProcessId &rhs) { return lhs.value == rhs.value; } diff --git a/include/stratosphere/os/os_condvar.hpp b/include/stratosphere/os/os_condvar.hpp index 4e00ade0..26b18bf5 100644 --- a/include/stratosphere/os/os_condvar.hpp +++ b/include/stratosphere/os/os_condvar.hpp @@ -19,6 +19,11 @@ namespace ams::os { + enum class ConditionVariableStatus { + TimedOut = 0, + Success = 1, + }; + class ConditionVariable { NON_COPYABLE(ConditionVariable); NON_MOVEABLE(ConditionVariable); @@ -29,40 +34,36 @@ namespace ams::os { condvarInit(&cv); } - Result TimedWait(::Mutex *m, u64 timeout) { - return condvarWaitTimeout(&cv, m, timeout); + ConditionVariableStatus TimedWait(::Mutex *m, u64 timeout) { + if (timeout > 0) { + /* Abort on any error other than timed out/success. */ + R_TRY_CATCH(condvarWaitTimeout(&this->cv, m, timeout)) { + R_CATCH(svc::ResultTimedOut) { return ConditionVariableStatus::TimedOut; } + } R_END_TRY_CATCH_WITH_ASSERT; + + return ConditionVariableStatus::Success; + } + return ConditionVariableStatus::TimedOut; } - Result Wait(::Mutex *m) { - return condvarWait(&cv, m); + void Wait(::Mutex *m) { + R_ASSERT(condvarWait(&this->cv, m)); } - Result TimedWait(os::Mutex *m, u64 timeout) { - return TimedWait(m->GetMutex(), timeout); + ConditionVariableStatus TimedWait(os::Mutex *m, u64 timeout) { + return this->TimedWait(m->GetMutex(), timeout); } - Result Wait(os::Mutex *m) { - return Wait(m->GetMutex()); + void Wait(os::Mutex *m) { + return this->Wait(m->GetMutex()); } - Result Wake(int num) { - return condvarWake(&cv, num); + void Signal() { + condvarWakeOne(&this->cv); } - Result WakeOne() { - return condvarWakeOne(&cv); - } - - Result WakeAll() { - return condvarWakeAll(&cv); - } - - Result Signal() { - return this->WakeOne(); - } - - Result Broadcast() { - return this->WakeAll(); + void Broadcast() { + condvarWakeAll(&this->cv); } }; diff --git a/include/stratosphere/os/os_message_queue.hpp b/include/stratosphere/os/os_message_queue.hpp index 5c24bf5e..ab0733e3 100644 --- a/include/stratosphere/os/os_message_queue.hpp +++ b/include/stratosphere/os/os_message_queue.hpp @@ -35,21 +35,22 @@ namespace ams::os { NON_COPYABLE(MessageQueue); NON_MOVEABLE(MessageQueue); private: - util::TypedStorage waitable_object_list_storage; + util::TypedStorage waitlist_not_empty; + util::TypedStorage waitlist_not_full; Mutex queue_lock; ConditionVariable cv_not_full; ConditionVariable cv_not_empty; std::unique_ptr buffer; size_t capacity; - size_t count = 0; - size_t offset = 0; + size_t count; + size_t offset; private: - bool IsFull() const { + constexpr inline bool IsFull() const { return this->count >= this->capacity; } - bool IsEmpty() const { + constexpr inline bool IsEmpty() const { return this->count == 0; } diff --git a/include/stratosphere/os/os_process_handle.hpp b/include/stratosphere/os/os_process_handle.hpp new file mode 100644 index 00000000..1b5eac0a --- /dev/null +++ b/include/stratosphere/os/os_process_handle.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2019 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 . + */ + +#pragma once +#include "os_managed_handle.hpp" + +namespace ams::os { + + ::Handle GetCurrentProcessHandle(); + + NX_INLINE ProcessId GetCurrentProcessId() { + return GetProcessId(GetCurrentProcessHandle()); + } + +} diff --git a/include/stratosphere/os/os_rw_lock.hpp b/include/stratosphere/os/os_rw_lock.hpp new file mode 100644 index 00000000..674d056e --- /dev/null +++ b/include/stratosphere/os/os_rw_lock.hpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018-2019 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 . + */ + +#pragma once +#include "os_common_types.hpp" + +namespace ams::os { + + class ReadWriteLock { + NON_COPYABLE(ReadWriteLock); + NON_MOVEABLE(ReadWriteLock); + private: + ::RwLock r; + public: + ReadWriteLock() { + rwlockInit(&this->r); + } + + bool IsWriteLockHeldByCurrentThread() const { + return rwlockIsWriteLockHeldByCurrentThread(const_cast<::RwLock *>(&this->r)); + } + + bool IsLockOwner() const { + return rwlockIsOwnedByCurrentThread(const_cast<::RwLock *>(&this->r)); + } + + void AcquireReadLock() { + rwlockReadLock(&this->r); + } + + void ReleaseReadLock() { + rwlockReadUnlock(&this->r); + } + + bool TryAcquireReadLock() { + return rwlockTryReadLock(&this->r); + } + + void AcquireWriteLock() { + rwlockWriteLock(&this->r); + } + + void ReleaseWriteLock() { + rwlockWriteUnlock(&this->r); + } + + bool TryAcquireWriteLock() { + return rwlockTryWriteLock(&this->r); + } + + void lock_shared() { + this->AcquireReadLock(); + } + + void unlock_shared() { + this->ReleaseReadLock(); + } + + bool try_lock_shared() { + return this->TryAcquireReadLock(); + } + + void lock() { + this->AcquireWriteLock(); + } + + void unlock() { + this->ReleaseWriteLock(); + } + + bool try_lock() { + return this->TryAcquireWriteLock(); + } + }; + +} \ No newline at end of file diff --git a/include/stratosphere/os/os_timeout_helper.hpp b/include/stratosphere/os/os_timeout_helper.hpp index 650a3e3e..5f5bf408 100644 --- a/include/stratosphere/os/os_timeout_helper.hpp +++ b/include/stratosphere/os/os_timeout_helper.hpp @@ -41,7 +41,7 @@ namespace ams::os { return (tick * 625) / 12; } - bool TimedOut() const { + inline bool TimedOut() const { if (this->end_tick == 0) { return true; } @@ -49,7 +49,7 @@ namespace ams::os { return armGetSystemTick() >= this->end_tick; } - u64 NsUntilTimeout() const { + inline u64 NsUntilTimeout() const { u64 diff = TickToNs(this->end_tick - armGetSystemTick()); if (this->TimedOut()) { diff --git a/source/map/map_api.cpp b/source/map/map_api.cpp index ca239272..1e8614d8 100644 --- a/source/map/map_api.cpp +++ b/source/map/map_api.cpp @@ -31,7 +31,7 @@ namespace ams::map { uintptr_t cur_base = 0; AddressSpaceInfo address_space; - R_TRY(GetProcessAddressSpaceInfo(&address_space, CUR_PROCESS_HANDLE)); + R_TRY(GetProcessAddressSpaceInfo(&address_space, dd::GetCurrentProcessHandle())); cur_base = address_space.aslr_base; do { @@ -57,7 +57,7 @@ namespace ams::map { uintptr_t cur_base = 0, cur_end = 0; AddressSpaceInfo address_space; - R_TRY(GetProcessAddressSpaceInfo(&address_space, CUR_PROCESS_HANDLE)); + R_TRY(GetProcessAddressSpaceInfo(&address_space, dd::GetCurrentProcessHandle())); cur_base = address_space.aslr_base; cur_end = cur_base + size; diff --git a/source/os/impl/os_waitable_holder_of_message_queue.hpp b/source/os/impl/os_waitable_holder_of_message_queue.hpp index ff5aefa6..cba2b099 100644 --- a/source/os/impl/os_waitable_holder_of_message_queue.hpp +++ b/source/os/impl/os_waitable_holder_of_message_queue.hpp @@ -25,13 +25,25 @@ namespace ams::os::impl { private: MessageQueue *message_queue; private: - TriBool IsSignaledImpl() const { + constexpr inline TriBool IsSignaledImpl() const { if constexpr (WaitKind == MessageQueueWaitKind::ForNotEmpty) { /* ForNotEmpty. */ return this->message_queue->IsEmpty() ? TriBool::False : TriBool::True; - } else /* if constexpr (WaitKind == MessageQueueWaitKind::ForNotFull) */ { + } else if constexpr (WaitKind == MessageQueueWaitKind::ForNotFull) { /* ForNotFull */ return this->message_queue->IsFull() ? TriBool::False : TriBool::True; + } else { + static_assert(WaitKind != WaitKind); + } + } + + constexpr inline WaitableObjectList &GetObjectList() const { + if constexpr (WaitKind == MessageQueueWaitKind::ForNotEmpty) { + return GetReference(this->message_queue->waitlist_not_empty); + } else if constexpr (WaitKind == MessageQueueWaitKind::ForNotFull) { + return GetReference(this->message_queue->waitlist_not_full); + } else { + static_assert(WaitKind != WaitKind); } } public: @@ -46,14 +58,14 @@ namespace ams::os::impl { virtual TriBool LinkToObjectList() override { std::scoped_lock lk(this->message_queue->queue_lock); - GetReference(this->message_queue->waitable_object_list_storage).LinkWaitableHolder(*this); + this->GetObjectList().LinkWaitableHolder(*this); return this->IsSignaledImpl(); } virtual void UnlinkFromObjectList() override { std::scoped_lock lk(this->message_queue->queue_lock); - GetReference(this->message_queue->waitable_object_list_storage).UnlinkWaitableHolder(*this); + this->GetObjectList().UnlinkWaitableHolder(*this); } }; diff --git a/source/os/impl/os_waitable_object_list.hpp b/source/os/impl/os_waitable_object_list.hpp index 71660ad3..ec0a83cc 100644 --- a/source/os/impl/os_waitable_object_list.hpp +++ b/source/os/impl/os_waitable_object_list.hpp @@ -31,7 +31,7 @@ namespace ams::os::impl { } } - void WakeupAllThreads() { + void BroadcastAllThreads() { for (WaitableHolderBase &holder_base : this->object_list) { holder_base.GetManager()->SignalAndWakeupThread(nullptr); } diff --git a/source/os/os_event.cpp b/source/os/os_event.cpp index 1e146b9e..fdb61a22 100644 --- a/source/os/os_event.cpp +++ b/source/os/os_event.cpp @@ -90,7 +90,7 @@ namespace ams::os { if (this->counter != cur_counter) { break; } - if (R_FAILED(this->cv.TimedWait(&this->lock, timeout_helper.NsUntilTimeout()))) { + if (this->cv.TimedWait(&this->lock, timeout_helper.NsUntilTimeout()) == ConditionVariableStatus::TimedOut) { return false; } } diff --git a/source/os/os_message_queue.cpp b/source/os/os_message_queue.cpp index 17accc8a..ab38b891 100644 --- a/source/os/os_message_queue.cpp +++ b/source/os/os_message_queue.cpp @@ -17,12 +17,14 @@ namespace ams::os { - MessageQueue::MessageQueue(std::unique_ptr buf, size_t c): buffer(std::move(buf)), capacity(c) { - new (GetPointer(this->waitable_object_list_storage)) impl::WaitableObjectList(); + MessageQueue::MessageQueue(std::unique_ptr buf, size_t c): buffer(std::move(buf)), capacity(c), count(0), offset(0) { + new (GetPointer(this->waitlist_not_empty)) impl::WaitableObjectList(); + new (GetPointer(this->waitlist_not_full)) impl::WaitableObjectList(); } MessageQueue::~MessageQueue() { - GetReference(this->waitable_object_list_storage).~WaitableObjectList(); + GetReference(this->waitlist_not_empty).~WaitableObjectList(); + GetReference(this->waitlist_not_full).~WaitableObjectList(); } void MessageQueue::SendInternal(uintptr_t data) { @@ -53,7 +55,7 @@ namespace ams::os { return data; } - uintptr_t MessageQueue::PeekInternal() { + inline uintptr_t MessageQueue::PeekInternal() { /* Ensure we don't corrupt the queue, but this should never happen. */ AMS_ASSERT(this->count > 0); @@ -70,7 +72,8 @@ namespace ams::os { /* Send, signal. */ this->SendInternal(data); - this->cv_not_empty.WakeAll(); + this->cv_not_empty.Broadcast(); + GetReference(this->waitlist_not_empty).SignalAllThreads(); } bool MessageQueue::TrySend(uintptr_t data) { @@ -81,7 +84,8 @@ namespace ams::os { /* Send, signal. */ this->SendInternal(data); - this->cv_not_empty.WakeAll(); + this->cv_not_empty.Broadcast(); + GetReference(this->waitlist_not_empty).SignalAllThreads(); return true; } @@ -94,12 +98,13 @@ namespace ams::os { return false; } - this->cv_not_full.TimedWait(&this->queue_lock, timeout); + this->cv_not_full.TimedWait(&this->queue_lock, timeout_helper.NsUntilTimeout()); } /* Send, signal. */ this->SendInternal(data); - this->cv_not_empty.WakeAll(); + this->cv_not_empty.Broadcast(); + GetReference(this->waitlist_not_empty).SignalAllThreads(); return true; } @@ -113,7 +118,8 @@ namespace ams::os { /* Send, signal. */ this->SendNextInternal(data); - this->cv_not_empty.WakeAll(); + this->cv_not_empty.Broadcast(); + GetReference(this->waitlist_not_empty).SignalAllThreads(); } bool MessageQueue::TrySendNext(uintptr_t data) { @@ -124,7 +130,8 @@ namespace ams::os { /* Send, signal. */ this->SendNextInternal(data); - this->cv_not_empty.WakeAll(); + this->cv_not_empty.Broadcast(); + GetReference(this->waitlist_not_empty).SignalAllThreads(); return true; } @@ -137,12 +144,13 @@ namespace ams::os { return false; } - this->cv_not_full.TimedWait(&this->queue_lock, timeout); + this->cv_not_full.TimedWait(&this->queue_lock, timeout_helper.NsUntilTimeout()); } /* Send, signal. */ this->SendNextInternal(data); - this->cv_not_empty.WakeAll(); + this->cv_not_empty.Broadcast(); + GetReference(this->waitlist_not_empty).SignalAllThreads(); return true; } @@ -156,8 +164,10 @@ namespace ams::os { /* Receive, signal. */ *out = this->ReceiveInternal(); - this->cv_not_full.WakeAll(); + this->cv_not_full.Broadcast(); + GetReference(this->waitlist_not_full).SignalAllThreads(); } + bool MessageQueue::TryReceive(uintptr_t *out) { /* Acquire mutex, wait receivable. */ std::scoped_lock lock(this->queue_lock); @@ -168,7 +178,8 @@ namespace ams::os { /* Receive, signal. */ *out = this->ReceiveInternal(); - this->cv_not_full.WakeAll(); + this->cv_not_full.Broadcast(); + GetReference(this->waitlist_not_full).SignalAllThreads(); return true; } @@ -181,12 +192,13 @@ namespace ams::os { return false; } - this->cv_not_empty.TimedWait(&this->queue_lock, timeout); + this->cv_not_empty.TimedWait(&this->queue_lock, timeout_helper.NsUntilTimeout()); } /* Receive, signal. */ *out = this->ReceiveInternal(); - this->cv_not_full.WakeAll(); + this->cv_not_full.Broadcast(); + GetReference(this->waitlist_not_full).SignalAllThreads(); return true; } @@ -224,7 +236,7 @@ namespace ams::os { return false; } - this->cv_not_empty.TimedWait(&this->queue_lock, timeout); + this->cv_not_empty.TimedWait(&this->queue_lock, timeout_helper.NsUntilTimeout()); } /* Peek. */ diff --git a/source/os/os_process_handle.cpp b/source/os/os_process_handle.cpp new file mode 100644 index 00000000..c5d3736c --- /dev/null +++ b/source/os/os_process_handle.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#include + +namespace ams { + + namespace { + + constexpr inline ::Handle GetCurrentProcessHandleImpl() { + return CUR_PROCESS_HANDLE; + } + + } + + namespace os { + + ::Handle __attribute__((const)) GetCurrentProcessHandle() { + return GetCurrentProcessHandleImpl(); + } + + } + + namespace dd { + + ::Handle __attribute__((const)) GetCurrentProcessHandle() { + return GetCurrentProcessHandleImpl(); + } + + } + +}