From b52d630cf0d363c344539d6202517bb1adebebee Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 27 Sep 2019 18:04:58 -0700 Subject: [PATCH] os: implement waitable management. This implements waitable management for Events (and implements Events). It also refactors PM to use new Event/Waitable semantics, and also adds STS_ASSERT as a macro for asserting a boolean expression. The rest of stratosphere has been refactored to use STS_ASSERT whenever possible. --- Makefile | 2 +- include/stratosphere.hpp | 4 - include/stratosphere/auto_handle.hpp | 80 -------- include/stratosphere/defines.hpp | 25 +-- include/stratosphere/event.hpp | 139 ------------- .../stratosphere/kvdb/kvdb_auto_buffer.hpp | 4 +- .../stratosphere/kvdb/kvdb_bounded_string.hpp | 9 +- .../kvdb/kvdb_file_key_value_cache.hpp | 25 +-- .../kvdb/kvdb_file_key_value_store.hpp | 4 +- .../kvdb/kvdb_memory_key_value_store.hpp | 11 +- include/stratosphere/os.hpp | 8 +- include/stratosphere/os/os_common_types.hpp | 32 +++ include/stratosphere/os/os_event.hpp | 96 ++------- .../stratosphere/os/os_interrupt_event.hpp | 50 +++++ include/stratosphere/os/os_managed_handle.hpp | 84 ++++++++ include/stratosphere/os/os_message_queue.hpp | 21 +- include/stratosphere/os/os_system_event.hpp | 81 ++++++++ include/stratosphere/os/os_thread.hpp | 4 + .../stratosphere/os/os_waitable_holder.hpp | 66 ++++++ .../stratosphere/os/os_waitable_manager.hpp | 53 +++++ include/stratosphere/results.hpp | 1 + include/stratosphere/results/os_results.hpp | 23 +++ include/stratosphere/results/utilities.h | 16 ++ include/stratosphere/ro/ro_types.hpp | 5 +- include/stratosphere/scope_guard.hpp | 62 ------ include/stratosphere/sm/sm_types.hpp | 8 +- include/stratosphere/svc/svc_types.hpp | 6 + include/stratosphere/util.hpp | 8 +- include/stratosphere/util/util_alignment.hpp | 47 +++++ .../stratosphere/util/util_intrusive_list.hpp | 42 ++-- .../stratosphere/util/util_scope_guard.hpp | 59 ++++++ include/stratosphere/util/util_size.hpp | 37 ++++ .../stratosphere/util/util_typed_storage.hpp | 51 +++++ include/stratosphere/waitable_manager.hpp | 3 +- source/emummc_utilities.cpp | 4 +- source/firmware_version.cpp | 4 +- source/kvdb/kvdb_archive.cpp | 25 +-- source/kvdb/kvdb_file_key_value_store.cpp | 8 +- source/os/impl/os_inter_process_event.cpp | 182 +++++++++++++++++ source/os/impl/os_inter_process_event.hpp | 55 +++++ source/os/impl/os_waitable_holder_base.hpp | 77 +++++++ source/os/impl/os_waitable_holder_impl.hpp | 53 +++++ .../os/impl/os_waitable_holder_of_event.hpp | 52 +++++ .../os/impl/os_waitable_holder_of_handle.hpp | 37 ++++ ...waitable_holder_of_inter_process_event.hpp | 39 ++++ .../os_waitable_holder_of_interrupt_event.hpp | 38 ++++ .../os_waitable_holder_of_message_queue.hpp | 63 ++++++ .../os/impl/os_waitable_holder_of_thread.hpp | 39 ++++ source/os/impl/os_waitable_manager_impl.cpp | 177 ++++++++++++++++ source/os/impl/os_waitable_manager_impl.hpp | 94 +++++++++ source/os/impl/os_waitable_object_list.hpp | 53 +++++ source/os/os_event.cpp | 106 ++++++++++ source/os/os_interrupt_event.cpp | 112 +++++++++++ source/os/os_message_queue.cpp | 25 +-- source/os/os_system_event.cpp | 189 ++++++++++++++++++ source/os/os_waitable_holder.cpp | 111 ++++++++++ source/os/os_waitable_manager.cpp | 89 +++++++++ source/patcher/patcher_api.cpp | 24 +-- source/updater/updater_api.cpp | 20 +- source/updater/updater_bis_management.cpp | 37 +--- source/updater/updater_bis_management.hpp | 10 +- source/updater/updater_bis_save.cpp | 6 +- source/updater/updater_paths.cpp | 4 +- source/util/util_compression.cpp | 10 +- source/util/util_ini.cpp | 4 +- 65 files changed, 2333 insertions(+), 580 deletions(-) delete mode 100644 include/stratosphere/auto_handle.hpp delete mode 100644 include/stratosphere/event.hpp create mode 100644 include/stratosphere/os/os_common_types.hpp create mode 100644 include/stratosphere/os/os_interrupt_event.hpp create mode 100644 include/stratosphere/os/os_managed_handle.hpp create mode 100644 include/stratosphere/os/os_system_event.hpp create mode 100644 include/stratosphere/os/os_waitable_holder.hpp create mode 100644 include/stratosphere/os/os_waitable_manager.hpp create mode 100644 include/stratosphere/results/os_results.hpp delete mode 100644 include/stratosphere/scope_guard.hpp create mode 100644 include/stratosphere/util/util_alignment.hpp create mode 100644 include/stratosphere/util/util_scope_guard.hpp create mode 100644 include/stratosphere/util/util_size.hpp create mode 100644 include/stratosphere/util/util_typed_storage.hpp create mode 100644 source/os/impl/os_inter_process_event.cpp create mode 100644 source/os/impl/os_inter_process_event.hpp create mode 100644 source/os/impl/os_waitable_holder_base.hpp create mode 100644 source/os/impl/os_waitable_holder_impl.hpp create mode 100644 source/os/impl/os_waitable_holder_of_event.hpp create mode 100644 source/os/impl/os_waitable_holder_of_handle.hpp create mode 100644 source/os/impl/os_waitable_holder_of_inter_process_event.hpp create mode 100644 source/os/impl/os_waitable_holder_of_interrupt_event.hpp create mode 100644 source/os/impl/os_waitable_holder_of_message_queue.hpp create mode 100644 source/os/impl/os_waitable_holder_of_thread.hpp create mode 100644 source/os/impl/os_waitable_manager_impl.cpp create mode 100644 source/os/impl/os_waitable_manager_impl.hpp create mode 100644 source/os/impl/os_waitable_object_list.hpp create mode 100644 source/os/os_event.cpp create mode 100644 source/os/os_interrupt_event.cpp create mode 100644 source/os/os_system_event.cpp create mode 100644 source/os/os_waitable_holder.cpp create mode 100644 source/os/os_waitable_manager.cpp diff --git a/Makefile b/Makefile index c3f1c90d..5a8943af 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ include $(DEVKITPRO)/libnx/switch_rules # INCLUDES is a list of directories containing header files #--------------------------------------------------------------------------------- TARGET := $(notdir $(CURDIR)) -SOURCES := source source/os source/spl source/spl/smc source/updater source/patcher source/map source/rnd source/util source/sm source/cfg source/pm source/hid source/ldr source/kvdb +SOURCES := source source/os source/os/impl source/spl source/spl/smc source/updater source/patcher source/map source/rnd source/util source/sm source/cfg source/pm source/hid source/ldr source/kvdb DATA := data INCLUDES := include diff --git a/include/stratosphere.hpp b/include/stratosphere.hpp index 2192ff1d..5c3d899e 100644 --- a/include/stratosphere.hpp +++ b/include/stratosphere.hpp @@ -21,13 +21,9 @@ #include "stratosphere/utilities.hpp" #include "stratosphere/emummc_utilities.hpp" -#include "stratosphere/scope_guard.hpp" - #include "stratosphere/version_check.hpp" -#include "stratosphere/auto_handle.hpp" #include "stratosphere/iwaitable.hpp" -#include "stratosphere/event.hpp" #include "stratosphere/waitable_manager.hpp" diff --git a/include/stratosphere/auto_handle.hpp b/include/stratosphere/auto_handle.hpp deleted file mode 100644 index 77846547..00000000 --- a/include/stratosphere/auto_handle.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 -#include "defines.hpp" - -class AutoHandle { - NON_COPYABLE(AutoHandle); - private: - Handle hnd; - public: - AutoHandle() : hnd(INVALID_HANDLE) { /* ... */ } - AutoHandle(Handle h) : hnd(h) { /* ... */ } - ~AutoHandle() { - if (this->hnd != INVALID_HANDLE) { - svcCloseHandle(this->hnd); - this->hnd = INVALID_HANDLE; - } - } - - AutoHandle(AutoHandle&& rhs) { - this->hnd = rhs.hnd; - rhs.hnd = INVALID_HANDLE; - } - - AutoHandle& operator=(AutoHandle&& rhs) { - rhs.Swap(*this); - return *this; - } - - explicit operator bool() const { - return this->hnd != INVALID_HANDLE; - } - - void Swap(AutoHandle& rhs) { - std::swap(this->hnd, rhs.hnd); - } - - Handle Get() const { - return this->hnd; - } - - Handle *GetPointer() { - return &this->hnd; - } - - Handle *GetPointerAndClear() { - this->Clear(); - return this->GetPointer(); - } - - Handle Move() { - const Handle h = this->hnd; - this->hnd = INVALID_HANDLE; - return h; - } - - void Reset(Handle h) { - AutoHandle(h).Swap(*this); - } - - void Clear() { - this->Reset(INVALID_HANDLE); - } -}; diff --git a/include/stratosphere/defines.hpp b/include/stratosphere/defines.hpp index 5df9d676..aecf6aa7 100644 --- a/include/stratosphere/defines.hpp +++ b/include/stratosphere/defines.hpp @@ -19,6 +19,8 @@ /* Any broadly useful language defines should go here. */ +#define STS_ASSERT(expr) do { if (!(expr)) { std::abort(); } } while (0) + #define NON_COPYABLE(cls) \ cls(const cls&) = delete; \ cls& operator=(const cls&) = delete @@ -30,21 +32,12 @@ #define ALIGNED(algn) __attribute__((aligned(algn))) #define WEAK __attribute__((weak)) -namespace sts::util { - /* std::size() does not support zero-size C arrays. We're fixing that. */ - template - constexpr auto size(const C& c) -> decltype(c.size()) { - return std::size(c); - } +#define CONCATENATE_IMPL(S1, s2) s1##s2 +#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2) - template - constexpr std::size_t size(const C& c) { - if constexpr (sizeof(C) == 0) { - return 0; - } else { - return std::size(c); - } - } - -} \ No newline at end of file +#ifdef __COUNTER__ +#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __COUNTER__) +#else +#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __LINE__) +#endif \ No newline at end of file diff --git a/include/stratosphere/event.hpp b/include/stratosphere/event.hpp deleted file mode 100644 index 326bf935..00000000 --- a/include/stratosphere/event.hpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * 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 -#include -#include - -#include "iwaitable.hpp" -#include "results.hpp" - -class IEvent : public IWaitable { - public: - /* Information members. */ - Handle r_h; - Handle w_h; - bool autoclear; - public: - IEvent(bool a = false) : r_h(INVALID_HANDLE), w_h(INVALID_HANDLE), autoclear(a) { } - IEvent(Handle r, bool a = false) : r_h(r), w_h(INVALID_HANDLE), autoclear(a) { } - IEvent(Handle r, Handle w, bool a = false) : r_h(r), w_h(w), autoclear(a) { } - - ~IEvent() { - if (r_h != INVALID_HANDLE) { - svcCloseHandle(r_h); - } - if (w_h != INVALID_HANDLE) { - svcCloseHandle(w_h); - } - } - - /* Make it non-copyable */ - IEvent() = delete; - IEvent(const IEvent &) = delete; - IEvent& operator=(const IEvent&) = delete; - - - bool IsAutoClear() { - return this->autoclear; - } - - void Clear() { - std::scoped_lock lock(this->sig_lock); - this->is_signaled = false; - if (this->w_h != INVALID_HANDLE) { - svcClearEvent(this->w_h); - } else if (this->r_h != INVALID_HANDLE) { - svcResetSignal(this->r_h); - } - } - - void Signal() { - std::scoped_lock lock(this->sig_lock); - - if (this->w_h == INVALID_HANDLE && this->r_h != INVALID_HANDLE) { - /* We can't signal an event if we only have a read handle. */ - std::abort(); - } - - if (this->w_h == INVALID_HANDLE && this->is_signaled) { - return; - } - - this->is_signaled = true; - - if (this->w_h != INVALID_HANDLE) { - svcSignalEvent(this->w_h); - } else { - this->NotifyManagerSignaled(); - } - } - - virtual Result HandleSignaled(u64 timeout) = 0; - - /* IWaitable */ - virtual Handle GetHandle() override { - return this->r_h; - } -}; - -template -class HosEvent : public IEvent { - private: - F callback; - public: - HosEvent(F f, bool a = false) : IEvent(a), callback(std::move(f)) { } - HosEvent(Handle r, F f, bool a = false) : IEvent(r, a), callback(std::move(f)) { } - HosEvent(Handle r, Handle w, F f, bool a = false) : IEvent(r, w, a), callback(std::move(f)) { } - - virtual Result HandleSignaled(u64 timeout) override { - if (this->IsAutoClear()) { - this->Clear(); - } - return this->callback(timeout); - } -}; - -template -static IEvent *CreateHosEvent(F f, bool autoclear = false) { - return new HosEvent(INVALID_HANDLE, INVALID_HANDLE, std::move(f), autoclear); -} - -template -static IEvent *CreateSystemEvent(F f, bool autoclear = false) { - Handle w_h, r_h; - R_ASSERT(svcCreateEvent(&w_h, &r_h)); - return new HosEvent(r_h, w_h, std::move(f), autoclear); -} - -template -static IEvent *CreateInterruptEvent(F f, u64 irq, bool autoclear = false) { - Handle r_h; - /* flag is "rising edge vs level". */ - R_ASSERT(svcCreateInterruptEvent(&r_h, irq, autoclear ? 0 : 1)); - return new HosEvent(r_h, INVALID_HANDLE, std::move(f), autoclear); -} - -template -static IEvent *CreateWriteOnlySystemEvent() { - return CreateSystemEvent([](u64 timeout) -> Result { std::abort(); }, a); -} - -template -static IEvent *LoadReadOnlySystemEvent(Handle r_h, F f, bool autoclear = false) { - return new HosEvent(r_h, f, autoclear); -} diff --git a/include/stratosphere/kvdb/kvdb_auto_buffer.hpp b/include/stratosphere/kvdb/kvdb_auto_buffer.hpp index 269abcce..f731f8f7 100644 --- a/include/stratosphere/kvdb/kvdb_auto_buffer.hpp +++ b/include/stratosphere/kvdb/kvdb_auto_buffer.hpp @@ -68,9 +68,7 @@ namespace sts::kvdb { Result Initialize(size_t size) { /* Check that we're not already initialized. */ - if (this->buffer != nullptr) { - std::abort(); - } + STS_ASSERT(this->buffer == nullptr); /* Allocate a buffer. */ this->buffer = static_cast(std::malloc(size)); diff --git a/include/stratosphere/kvdb/kvdb_bounded_string.hpp b/include/stratosphere/kvdb/kvdb_bounded_string.hpp index 89272dfb..8ab11dc8 100644 --- a/include/stratosphere/kvdb/kvdb_bounded_string.hpp +++ b/include/stratosphere/kvdb/kvdb_bounded_string.hpp @@ -32,9 +32,7 @@ namespace sts::kvdb { private: /* Utility. */ static inline void CheckLength(size_t len) { - if (len >= N) { - std::abort(); - } + STS_ASSERT(len < N); } public: /* Constructors. */ @@ -115,9 +113,8 @@ namespace sts::kvdb { /* Substring utilities. */ void GetSubstring(char *dst, size_t dst_size, size_t offset, size_t length) const { /* Make sure output buffer can hold the substring. */ - if (offset + length > GetLength() || dst_size <= length) { - std::abort(); - } + STS_ASSERT(offset + length <= GetLength()); + STS_ASSERT(dst_size > length); /* Copy substring to dst. */ std::strncpy(dst, this->buffer + offset, length); dst[length] = 0; diff --git a/include/stratosphere/kvdb/kvdb_file_key_value_cache.hpp b/include/stratosphere/kvdb/kvdb_file_key_value_cache.hpp index 64a6037b..920bddb8 100644 --- a/include/stratosphere/kvdb/kvdb_file_key_value_cache.hpp +++ b/include/stratosphere/kvdb/kvdb_file_key_value_cache.hpp @@ -60,9 +60,7 @@ namespace sts::kvdb { } private: void RemoveIndex(size_t i) { - if (i >= this->GetCount()) { - std::abort(); - } + STS_ASSERT(i < this->GetCount()); std::memmove(this->keys + i, this->keys + i + 1, sizeof(*this->keys) * (this->GetCount() - (i + 1))); this->DecrementCount(); } @@ -79,9 +77,8 @@ namespace sts::kvdb { Result Initialize(const char *path, void *buf, size_t size) { /* Only initialize once, and ensure we have sufficient memory. */ - if (this->keys != nullptr || size < BufferSize) { - std::abort(); - } + STS_ASSERT(this->keys == nullptr); + SSS_ASSERT(size >= BufferSize); /* Setup member variables. */ this->keys = static_cast(buf); @@ -148,31 +145,23 @@ namespace sts::kvdb { } Key Get(size_t i) const { - if (i >= this->GetCount()) { - std::abort(); - } + STS_ASSERT(i < this->GetCount()); return this->keys[i]; } Key Peek() const { - if (this->IsEmpty()) { - std::abort(); - } + STS_ASSERT(!this->IsEmpty()); return this->Get(0); } void Push(const Key &key) { - if (this->IsFull()) { - std::abort(); - } + STS_ASSERT(!this->IsFull()); this->keys[this->GetCount()] = key; this->IncrementCount(); } Key Pop() { - if (this->IsEmpty()) { - std::abort(); - } + STS_ASSERT(!this->IsEmpty()); this->RemoveIndex(0); } diff --git a/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp b/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp index ea06860b..745bcdb4 100644 --- a/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp +++ b/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp @@ -96,9 +96,7 @@ namespace sts::kvdb { static_assert(std::is_pod::value && !std::is_pointer::value, "Invalid FileKeyValueStore Value!"); size_t size = 0; R_TRY(this->Get(&size, out_value, sizeof(Value), key)); - if (size < sizeof(Value)) { - std::abort(); - } + STS_ASSERT(size >= sizeof(Value)); return ResultSuccess; } diff --git a/include/stratosphere/kvdb/kvdb_memory_key_value_store.hpp b/include/stratosphere/kvdb/kvdb_memory_key_value_store.hpp index d44dd8b9..926a2d1e 100644 --- a/include/stratosphere/kvdb/kvdb_memory_key_value_store.hpp +++ b/include/stratosphere/kvdb/kvdb_memory_key_value_store.hpp @@ -18,6 +18,9 @@ #include #include #include +#include "../defines.hpp" +#include "../results.hpp" +#include "../util.hpp" #include "kvdb_auto_buffer.hpp" #include "kvdb_archive.hpp" #include "kvdb_bounded_string.hpp" @@ -47,9 +50,7 @@ namespace sts::kvdb { Value *GetValuePointer() { /* Size check. Note: Nintendo does not size check. */ if constexpr (!std::is_same::value) { - if (sizeof(Value) > this->value_size) { - std::abort(); - } + STS_ASSERT(sizeof(Value) <= this->value_size); /* Ensure we only get pod. */ static_assert(std::is_pod::value, "KeyValueStore Values must be pod"); } @@ -60,9 +61,7 @@ namespace sts::kvdb { const Value *GetValuePointer() const { /* Size check. Note: Nintendo does not size check. */ if constexpr (!std::is_same::value) { - if (sizeof(Value) > this->value_size) { - std::abort(); - } + STS_ASSERT(sizeof(Value) <= this->value_size); /* Ensure we only get pod. */ static_assert(std::is_pod::value, "KeyValueStore Values must be pod"); } diff --git a/include/stratosphere/os.hpp b/include/stratosphere/os.hpp index b3a53469..6d009ee0 100644 --- a/include/stratosphere/os.hpp +++ b/include/stratosphere/os.hpp @@ -17,10 +17,16 @@ #pragma once #include +#include "os/os_common_types.hpp" +#include "os/os_managed_handle.hpp" #include "os/os_mutex.hpp" #include "os/os_condvar.hpp" #include "os/os_semaphore.hpp" #include "os/os_timeout_helper.hpp" #include "os/os_event.hpp" +#include "os/os_system_event.hpp" +#include "os/os_interrupt_event.hpp" #include "os/os_thread.hpp" -#include "os/os_message_queue.hpp" \ No newline at end of file +#include "os/os_message_queue.hpp" +#include "os/os_waitable_holder.hpp" +#include "os/os_waitable_manager.hpp" diff --git a/include/stratosphere/os/os_common_types.hpp b/include/stratosphere/os/os_common_types.hpp new file mode 100644 index 00000000..b6d36df9 --- /dev/null +++ b/include/stratosphere/os/os_common_types.hpp @@ -0,0 +1,32 @@ +/* + * 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 sts::os { + + enum class TriBool { + False = 0, + True = 1, + Undefined = 2, + }; + + enum class MessageQueueWaitKind { + ForNotEmpty, + ForNotFull, + }; + +} diff --git a/include/stratosphere/os/os_event.hpp b/include/stratosphere/os/os_event.hpp index e287db6c..461e0a23 100644 --- a/include/stratosphere/os/os_event.hpp +++ b/include/stratosphere/os/os_event.hpp @@ -21,93 +21,33 @@ namespace sts::os { + namespace impl { + + class WaitableObjectList; + class WaitableHolderOfEvent; + + } + class Event { + friend class impl::WaitableHolderOfEvent; NON_COPYABLE(Event); NON_MOVEABLE(Event); private: - Mutex m; + util::TypedStorage waitable_object_list_storage; + Mutex lock; ConditionVariable cv; + u64 counter = 0; bool auto_clear; bool signaled; - u64 counter = 0; public: - Event(bool a = true, bool s = false) : auto_clear(a), signaled(s) {} + Event(bool a = true, bool s = false); + ~Event(); - void Signal() { - std::scoped_lock lk(this->m); - - /* If we're already signaled, nothing more to do. */ - if (this->signaled) { - return; - } - - this->signaled = true; - - /* Signal! */ - if (this->auto_clear) { - /* If we're auto clear, signal one thread, which will clear. */ - this->cv.Signal(); - } else { - /* If we're manual clear, increment counter and wake all. */ - this->counter++; - this->cv.Broadcast(); - } - - /* TODO: Waitable auto-wakeup. */ - } - - void Reset() { - std::scoped_lock lk(this->m); - this->signaled = false; - } - - void Wait() { - std::scoped_lock lk(this->m); - - u64 cur_counter = this->counter; - while (!this->signaled) { - if (this->counter != cur_counter) { - break; - } - this->cv.Wait(&this->m); - } - - if (this->auto_clear) { - this->signaled = false; - } - } - - bool TryWait() { - std::scoped_lock lk(this->m); - - const bool success = this->signaled; - if (this->auto_clear) { - this->signaled = false; - } - - return success; - } - - bool TimedWait(u64 ns) { - TimeoutHelper timeout_helper(ns); - std::scoped_lock lk(this->m); - - u64 cur_counter = this->counter; - while (!this->signaled) { - if (this->counter != cur_counter) { - break; - } - if (R_FAILED(this->cv.TimedWait(&this->m, timeout_helper.NsUntilTimeout()))) { - return false; - } - } - - if (this->auto_clear) { - this->signaled = false; - } - - return true; - } + void Signal(); + void Reset(); + void Wait(); + bool TryWait(); + bool TimedWait(u64 ns); }; } diff --git a/include/stratosphere/os/os_interrupt_event.hpp b/include/stratosphere/os/os_interrupt_event.hpp new file mode 100644 index 00000000..d1fd1592 --- /dev/null +++ b/include/stratosphere/os/os_interrupt_event.hpp @@ -0,0 +1,50 @@ +/* + * 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" +#include "os_managed_handle.hpp" + +namespace sts::os { + + namespace impl { + + class WaitableHolderOfInterruptEvent; + + } + + class InterruptEvent { + friend class impl::WaitableHolderOfInterruptEvent; + NON_COPYABLE(InterruptEvent); + NON_MOVEABLE(InterruptEvent); + private: + ManagedHandle handle; + bool auto_clear; + bool is_initialized; + public: + InterruptEvent() : auto_clear(true), is_initialized(false) { } + InterruptEvent(u32 interrupt_id, bool autoclear = true); + + Result Initialize(u32 interrupt_id, bool autoclear = true); + void Finalize(); + + void Reset(); + void Wait(); + bool TryWait(); + bool TimedWait(u64 ns); + }; + +} diff --git a/include/stratosphere/os/os_managed_handle.hpp b/include/stratosphere/os/os_managed_handle.hpp new file mode 100644 index 00000000..0d2d91ff --- /dev/null +++ b/include/stratosphere/os/os_managed_handle.hpp @@ -0,0 +1,84 @@ +/* + * 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 +#include "../defines.hpp" +#include "../results.hpp" + +namespace sts::os { + + class ManagedHandle { + NON_COPYABLE(ManagedHandle); + private: + Handle hnd; + public: + ManagedHandle() : hnd(INVALID_HANDLE) { /* ... */ } + ManagedHandle(Handle h) : hnd(h) { /* ... */ } + ~ManagedHandle() { + if (this->hnd != INVALID_HANDLE) { + R_ASSERT(svcCloseHandle(this->hnd)); + this->hnd = INVALID_HANDLE; + } + } + + ManagedHandle(ManagedHandle&& rhs) { + this->hnd = rhs.hnd; + rhs.hnd = INVALID_HANDLE; + } + + ManagedHandle& operator=(ManagedHandle&& rhs) { + rhs.Swap(*this); + return *this; + } + + explicit operator bool() const { + return this->hnd != INVALID_HANDLE; + } + + void Swap(ManagedHandle& rhs) { + std::swap(this->hnd, rhs.hnd); + } + + Handle Get() const { + return this->hnd; + } + + Handle *GetPointer() { + return &this->hnd; + } + + Handle *GetPointerAndClear() { + this->Clear(); + return this->GetPointer(); + } + + Handle Move() { + const Handle h = this->hnd; + this->hnd = INVALID_HANDLE; + return h; + } + + void Reset(Handle h) { + ManagedHandle(h).Swap(*this); + } + + void Clear() { + this->Reset(INVALID_HANDLE); + } + }; + +} diff --git a/include/stratosphere/os/os_message_queue.hpp b/include/stratosphere/os/os_message_queue.hpp index 7c9f8bad..a320e29c 100644 --- a/include/stratosphere/os/os_message_queue.hpp +++ b/include/stratosphere/os/os_message_queue.hpp @@ -16,15 +16,28 @@ #pragma once #include +#include "os_common_types.hpp" #include "os_mutex.hpp" #include "os_condvar.hpp" namespace sts::os { + namespace impl { + + class WaitableObjectList; + + template + class WaitableHolderOfMessageQueue; + + } + class MessageQueue { + template + friend class impl::WaitableHolderOfMessageQueue; NON_COPYABLE(MessageQueue); NON_MOVEABLE(MessageQueue); private: + util::TypedStorage waitable_object_list_storage; Mutex queue_lock; ConditionVariable cv_not_full; ConditionVariable cv_not_empty; @@ -47,11 +60,11 @@ namespace sts::os { uintptr_t ReceiveInternal(); uintptr_t PeekInternal(); public: - MessageQueue(size_t c) : capacity(c) { - this->buffer = std::make_unique(this->capacity); - } + MessageQueue(std::unique_ptr buf, size_t c); + ~MessageQueue(); - MessageQueue(std::unique_ptr buf, size_t c) : buffer(std::move(buf)), capacity(c) { } + /* For convenience. */ + MessageQueue(size_t c) : MessageQueue(std::make_unique(c), c) { /* ... */ } /* Sending (FIFO functionality) */ void Send(uintptr_t data); diff --git a/include/stratosphere/os/os_system_event.hpp b/include/stratosphere/os/os_system_event.hpp new file mode 100644 index 00000000..50654455 --- /dev/null +++ b/include/stratosphere/os/os_system_event.hpp @@ -0,0 +1,81 @@ +/* + * 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" +#include "os_event.hpp" + +namespace sts::os { + + class WaitableHolder; + + namespace impl { + + class InterProcessEvent; + + } + + enum class SystemEventState { + Uninitialized, + Event, + InterProcessEvent, + }; + + class SystemEvent { + friend class WaitableHolder; + NON_COPYABLE(SystemEvent); + NON_MOVEABLE(SystemEvent); + private: + union { + util::TypedStorage storage_for_event; + util::TypedStorage storage_for_inter_process_event; + }; + SystemEventState state; + private: + Event &GetEvent(); + const Event &GetEvent() const; + impl::InterProcessEvent &GetInterProcessEvent(); + const impl::InterProcessEvent &GetInterProcessEvent() const; + public: + SystemEvent() : state(SystemEventState::Uninitialized) { /* ... */ } + SystemEvent(bool inter_process, bool autoclear = true); + SystemEvent(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear = true); + SystemEvent(Handle read_handle, bool manage_read_handle, bool autoclear = true) : SystemEvent(read_handle, manage_read_handle, INVALID_HANDLE, false, autoclear) { /* ... */ } + ~SystemEvent(); + + Result InitializeAsEvent(bool autoclear = true); + Result InitializeAsInterProcessEvent(bool autoclear = true); + void AttachHandles(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear = true); + void AttachReadableHandle(Handle read_handle, bool manage_read_handle, bool autoclear = true); + void AttachWritableHandle(Handle write_handle, bool manage_write_handle, bool autoclear = true); + Handle DetachReadableHandle(); + Handle DetachWritableHandle(); + Handle GetReadableHandle() const; + Handle GetWritableHandle() const; + void Finalize(); + + SystemEventState GetState() const { + return this->state; + } + + void Signal(); + void Reset(); + void Wait(); + bool TryWait(); + bool TimedWait(u64 ns); + }; + +} diff --git a/include/stratosphere/os/os_thread.hpp b/include/stratosphere/os/os_thread.hpp index e659ac3c..b0ab77f4 100644 --- a/include/stratosphere/os/os_thread.hpp +++ b/include/stratosphere/os/os_thread.hpp @@ -37,6 +37,10 @@ namespace sts::os { return threadStart(&this->thr); } + Result Wait() { + return threadWaitForExit(&this->thr); + } + Result Join() { R_TRY(threadWaitForExit(&this->thr)); R_TRY(threadClose(&this->thr)); diff --git a/include/stratosphere/os/os_waitable_holder.hpp b/include/stratosphere/os/os_waitable_holder.hpp new file mode 100644 index 00000000..6118a3a2 --- /dev/null +++ b/include/stratosphere/os/os_waitable_holder.hpp @@ -0,0 +1,66 @@ +/* + * 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 "../util.hpp" +#include "os_common_types.hpp" + +namespace sts::os { + + class WaitableManager; + + class Event; + class SystemEvent; + class InterruptEvent; + class Thread; + class MessageQueue; + + namespace impl { + + class WaitableHolderImpl; + + } + + class WaitableHolder { + friend class WaitableManager; + NON_COPYABLE(WaitableHolder); + NON_MOVEABLE(WaitableHolder); + private: + util::TypedStorage impl_storage; + uintptr_t user_data; + public: + static constexpr size_t ImplStorageSize = sizeof(impl_storage); + public: + WaitableHolder(Handle handle); + WaitableHolder(Event *event); + WaitableHolder(SystemEvent *event); + WaitableHolder(InterruptEvent *event); + WaitableHolder(Thread *thread); + WaitableHolder(MessageQueue *message_queue, MessageQueueWaitKind wait_kind); + + ~WaitableHolder(); + + void SetUserData(uintptr_t data) { + this->user_data = data; + } + + uintptr_t GetUserData() const { + return this->user_data; + } + + void UnlinkFromWaitableManager(); + }; + +} diff --git a/include/stratosphere/os/os_waitable_manager.hpp b/include/stratosphere/os/os_waitable_manager.hpp new file mode 100644 index 00000000..c4eaf721 --- /dev/null +++ b/include/stratosphere/os/os_waitable_manager.hpp @@ -0,0 +1,53 @@ +/* + * 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 "../util.hpp" +#include "os_common_types.hpp" +#include "os_mutex.hpp" + +namespace sts::os { + + class WaitableHolder; + + namespace impl { + + class WaitableManagerImpl; + + } + + class WaitableManager { + NON_COPYABLE(WaitableManager); + NON_MOVEABLE(WaitableManager); + private: + util::TypedStorage impl_storage; + public: + static constexpr size_t ImplStorageSize = sizeof(impl_storage); + public: + WaitableManager(); + ~WaitableManager(); + + /* Wait. */ + WaitableHolder *WaitAny(); + WaitableHolder *TryWaitAny(); + WaitableHolder *TimedWaitAny(u64 timeout); + + /* Link. */ + void LinkWaitableHolder(WaitableHolder *holder); + void UnlinkAll(); + void MoveAllFrom(WaitableManager *other); + }; + +} diff --git a/include/stratosphere/results.hpp b/include/stratosphere/results.hpp index 5eddf72f..ef1a7c11 100644 --- a/include/stratosphere/results.hpp +++ b/include/stratosphere/results.hpp @@ -32,6 +32,7 @@ #include "results/kvdb_results.hpp" #include "results/loader_results.hpp" #include "results/lr_results.hpp" +#include "results/os_results.hpp" #include "results/ncm_results.hpp" #include "results/pm_results.hpp" #include "results/ro_results.hpp" diff --git a/include/stratosphere/results/os_results.hpp b/include/stratosphere/results/os_results.hpp new file mode 100644 index 00000000..81acaf05 --- /dev/null +++ b/include/stratosphere/results/os_results.hpp @@ -0,0 +1,23 @@ +/* + * 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 + +static constexpr u32 Module_Os = 3; + +static constexpr Result ResultOsOutOfMemory = MAKERESULT(Module_Os, 8); +static constexpr Result ResultOsResourceExhausted = MAKERESULT(Module_Os, 9); diff --git a/include/stratosphere/results/utilities.h b/include/stratosphere/results/utilities.h index da499496..7768d486 100644 --- a/include/stratosphere/results/utilities.h +++ b/include/stratosphere/results/utilities.h @@ -28,6 +28,14 @@ extern "C" { #define R_ASSERT_IMPL(res) fatalSimple(res) #endif +/// Evaluates a boolean expression, and returns a result unless that expression is true. +#define R_UNLESS(expr, res) \ + ({ \ + if (!(expr)) { \ + return static_cast(res); \ + } \ + }) + #define R_ASSERT(res_expr) \ ({ \ const Result _tmp_r_assert_rc = res_expr; \ @@ -83,6 +91,14 @@ extern "C" { } \ }) +#define R_END_TRY_CATCH_WITH_ASSERT \ + else { \ + R_ASSERT(R_TRY_CATCH_RESULT); \ + } \ + } \ + }) + + /// Evaluates an expression that returns a result, and returns the result (after evaluating a cleanup expression) if it would fail. #define R_CLEANUP_RESULT _tmp_r_try_cleanup_rc diff --git a/include/stratosphere/ro/ro_types.hpp b/include/stratosphere/ro/ro_types.hpp index 0a6a0189..7c092159 100644 --- a/include/stratosphere/ro/ro_types.hpp +++ b/include/stratosphere/ro/ro_types.hpp @@ -17,6 +17,7 @@ #pragma once #include #include +#include "../defines.hpp" namespace sts::ro { @@ -61,9 +62,7 @@ namespace sts::ro { ModuleType GetType() const { const ModuleType type = static_cast(this->type); - if (type >= ModuleType::Count) { - std::abort(); - } + STS_ASSERT(type < ModuleType::Count); return type; } diff --git a/include/stratosphere/scope_guard.hpp b/include/stratosphere/scope_guard.hpp deleted file mode 100644 index 9ff4be70..00000000 --- a/include/stratosphere/scope_guard.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 . - */ - - /* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */ -#pragma once - -#include - -template -class ScopeGuard { -private: - F f; - bool active; -public: - ScopeGuard(F f) : f(std::move(f)), active(true) { } - ~ScopeGuard() { if (active) { f(); } } - void Cancel() { active = false; } - - ScopeGuard() = delete; - ScopeGuard(const ScopeGuard &) = delete; - ScopeGuard& operator=(const ScopeGuard&) = delete; - ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) { - rhs.Cancel(); - } -}; - -template -ScopeGuard MakeScopeGuard(F f) { - return ScopeGuard(std::move(f)); -} - -enum class ScopeGuardOnExit {}; - -template -ScopeGuard operator+(ScopeGuardOnExit, F&& f) { - return ScopeGuard(std::forward(f)); -} - -#define CONCATENATE_IMPL(S1, s2) s1##s2 -#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2) - -#ifdef __COUNTER__ -#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __COUNTER__) -#else -#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __LINE__) -#endif - -#define SCOPE_GUARD ScopeGuardOnExit() + [&]() -#define ON_SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD diff --git a/include/stratosphere/sm/sm_types.hpp b/include/stratosphere/sm/sm_types.hpp index c831d913..68a9091a 100644 --- a/include/stratosphere/sm/sm_types.hpp +++ b/include/stratosphere/sm/sm_types.hpp @@ -114,9 +114,7 @@ namespace sts::sm { } Result Initialize() { - if (this->has_initialized) { - std::abort(); - } + STS_ASSERT(!this->has_initialized); DoWithSmSession([&]() { this->result = Initializer(); @@ -127,9 +125,7 @@ namespace sts::sm { } void Finalize() { - if (!this->has_initialized) { - std::abort(); - } + STS_ASSERT(this->has_initialized); Finalizer(); this->has_initialized = false; } diff --git a/include/stratosphere/svc/svc_types.hpp b/include/stratosphere/svc/svc_types.hpp index 98855abd..74612d49 100644 --- a/include/stratosphere/svc/svc_types.hpp +++ b/include/stratosphere/svc/svc_types.hpp @@ -202,4 +202,10 @@ namespace sts::svc { CreateProcessFlag_OptimizeMemoryAllocation = (1 << 11), }; + /* Type for svcCreateInterruptEvent. */ + enum InterruptType : u32 { + InterruptType_Edge = 0, + InterruptType_Level = 1, + }; + } diff --git a/include/stratosphere/util.hpp b/include/stratosphere/util.hpp index d33453d6..a8b21abe 100644 --- a/include/stratosphere/util.hpp +++ b/include/stratosphere/util.hpp @@ -17,7 +17,11 @@ #pragma once #include -#include "util/util_compression.hpp" -#include "util/util_ini.hpp" +#include "util/util_alignment.hpp" +#include "util/util_size.hpp" +#include "util/util_scope_guard.hpp" +#include "util/util_typed_storage.hpp" #include "util/util_intrusive_list.hpp" #include "util/util_intrusive_red_black_tree.hpp" +#include "util/util_compression.hpp" +#include "util/util_ini.hpp" diff --git a/include/stratosphere/util/util_alignment.hpp b/include/stratosphere/util/util_alignment.hpp new file mode 100644 index 00000000..c7d9b4c0 --- /dev/null +++ b/include/stratosphere/util/util_alignment.hpp @@ -0,0 +1,47 @@ +/* + * 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 +#include "../defines.hpp" +#include + +namespace sts::util { + + /* Utilities for alignment to power of two. */ + + template + static constexpr inline T AlignUp(T value, size_t alignment) { + using U = typename std::make_unsigned::type; + const U invmask = static_cast(alignment - 1); + return static_cast((value + invmask) & ~invmask); + } + + template + static constexpr inline T AlignDown(T value, size_t alignment) { + using U = typename std::make_unsigned::type; + const U invmask = static_cast(alignment - 1); + return static_cast(value & ~invmask); + } + + template + static constexpr inline bool IsAligned(T value, size_t alignment) { + using U = typename std::make_unsigned::type; + const U invmask = static_cast(alignment - 1); + return (value & invmask) == 0; + } + +} \ No newline at end of file diff --git a/include/stratosphere/util/util_intrusive_list.hpp b/include/stratosphere/util/util_intrusive_list.hpp index 708dfbfc..01762584 100644 --- a/include/stratosphere/util/util_intrusive_list.hpp +++ b/include/stratosphere/util/util_intrusive_list.hpp @@ -49,10 +49,8 @@ namespace sts::util { private: void LinkPrev(IntrusiveListNode *node) { /* We can't link an already linked node. */ - if (node->IsLinked()) { - std::abort(); - } - return this->SplicePrev(node, node); + STS_ASSERT(!node->IsLinked()); + this->SplicePrev(node, node); } void SplicePrev(IntrusiveListNode *first, IntrusiveListNode *last) { @@ -66,9 +64,7 @@ namespace sts::util { void LinkNext(IntrusiveListNode *node) { /* We can't link an already linked node. */ - if (node->IsLinked()) { - std::abort(); - } + STS_ASSERT(!node->IsLinked()); return this->SpliceNext(node, node); } @@ -214,17 +210,13 @@ namespace sts::util { iterator iterator_to(reference v) { /* Only allow iterator_to for values in lists. */ - if (!v.IsLinked()) { - std::abort(); - } + STS_ASSERT(v.IsLinked()); return iterator(&v); } const_iterator iterator_to(const_reference v) const { /* Only allow iterator_to for values in lists. */ - if (!v.IsLinked()) { - std::abort(); - } + STS_ASSERT(v.IsLinked()); return const_iterator(&v); } @@ -488,23 +480,23 @@ namespace sts::util { } reference back() { - if (this->impl.empty()) { std::abort(); } - return this->impl.back(); + STS_ASSERT(!this->impl.empty()); + return GetParent(this->impl.back()); } const_reference back() const { - if (this->impl.empty()) { std::abort(); } - return this->impl.back(); + STS_ASSERT(!this->impl.empty()); + return GetParent(this->impl.back()); } reference front() { - if (this->impl.empty()) { std::abort(); } - return this->impl.front(); + STS_ASSERT(!this->impl.empty()); + return GetParent(this->impl.front()); } const_reference front() const { - if (this->impl.empty()) { std::abort(); } - return this->impl.front(); + STS_ASSERT(!this->impl.empty()); + return GetParent(this->impl.front()); } void push_back(reference ref) { @@ -516,13 +508,13 @@ namespace sts::util { } void pop_back() { - if (this->impl.empty()) { std::abort(); } - return this->impl.pop_back(); + STS_ASSERT(!this->impl.empty()); + this->impl.pop_back(); } void pop_front() { - if (this->impl.empty()) { std::abort(); } - return this->impl.pop_front(); + STS_ASSERT(!this->impl.empty()); + this->impl.pop_front(); } iterator insert(const_iterator pos, reference ref) { diff --git a/include/stratosphere/util/util_scope_guard.hpp b/include/stratosphere/util/util_scope_guard.hpp new file mode 100644 index 00000000..275c1368 --- /dev/null +++ b/include/stratosphere/util/util_scope_guard.hpp @@ -0,0 +1,59 @@ +/* + * 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 . + */ + + /* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */ +#pragma once +#include +#include "../defines.hpp" + +namespace sts::util { + + namespace impl { + + template + class ScopeGuard { + NON_COPYABLE(ScopeGuard); + private: + F f; + bool active; + public: + constexpr ScopeGuard(F f) : f(std::move(f)), active(true) { } + ~ScopeGuard() { if (active) { f(); } } + void Cancel() { active = false; } + + ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) { + rhs.Cancel(); + } + }; + + template + constexpr ScopeGuard MakeScopeGuard(F f) { + return ScopeGuard(std::move(f)); + } + + enum class ScopeGuardOnExit {}; + + template + constexpr ScopeGuard operator+(ScopeGuardOnExit, F&& f) { + return ScopeGuard(std::forward(f)); + } + + } + +} + +#define SCOPE_GUARD ::sts::util::impl::ScopeGuardOnExit() + [&]() +#define ON_SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD diff --git a/include/stratosphere/util/util_size.hpp b/include/stratosphere/util/util_size.hpp new file mode 100644 index 00000000..8f1785c5 --- /dev/null +++ b/include/stratosphere/util/util_size.hpp @@ -0,0 +1,37 @@ +/* + * 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 sts::util { + + /* std::size() does not support zero-size C arrays. We're fixing that. */ + template + constexpr auto size(const C& c) -> decltype(c.size()) { + return std::size(c); + } + + template + constexpr std::size_t size(const C& c) { + if constexpr (sizeof(C) == 0) { + return 0; + } else { + return std::size(c); + } + } + +} \ No newline at end of file diff --git a/include/stratosphere/util/util_typed_storage.hpp b/include/stratosphere/util/util_typed_storage.hpp new file mode 100644 index 00000000..54999a87 --- /dev/null +++ b/include/stratosphere/util/util_typed_storage.hpp @@ -0,0 +1,51 @@ +/* + * 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 +#include "../defines.hpp" +#include + +namespace sts::util { + + template + struct TypedStorage { + typename std::aligned_storage::type _storage; + }; + + #define TYPED_STORAGE(T) ::sts::util::TypedStorage + + template + static constexpr inline __attribute__((always_inline)) T *GetPointer(TYPED_STORAGE(T) &ts) { + return reinterpret_cast(&ts._storage); + } + + template + static constexpr inline __attribute__((always_inline)) const T *GetPointer(const TYPED_STORAGE(T) &ts) { + return reinterpret_cast(&ts._storage); + } + + template + static constexpr inline __attribute__((always_inline)) T &GetReference(TYPED_STORAGE(T) &ts) { + return *GetPointer(ts); + } + + template + static constexpr inline __attribute__((always_inline)) const T &GetReference(const TYPED_STORAGE(T) &ts) { + return *GetPointer(ts); + } + +} \ No newline at end of file diff --git a/include/stratosphere/waitable_manager.hpp b/include/stratosphere/waitable_manager.hpp index 7edc4b52..e27bac9f 100644 --- a/include/stratosphere/waitable_manager.hpp +++ b/include/stratosphere/waitable_manager.hpp @@ -23,11 +23,10 @@ #include "results.hpp" #include "os.hpp" #include "waitable_manager_base.hpp" -#include "event.hpp" #include "ipc.hpp" #include "servers.hpp" -#include "scope_guard.hpp" +#include "util.hpp" static inline Handle GetCurrentThreadHandle() { return threadGetCurHandle(); diff --git a/source/emummc_utilities.cpp b/source/emummc_utilities.cpp index ba73ae13..59c4267c 100644 --- a/source/emummc_utilities.cpp +++ b/source/emummc_utilities.cpp @@ -81,9 +81,7 @@ static void _CacheValues(void) args.X[1] = 0; /* NAND */ args.X[2] = reinterpret_cast(&paths); /* path output */ R_ASSERT(svcCallSecureMonitor(&args)); - if (args.X[0] != 0) { - std::abort(); - } + STS_ASSERT(args.X[0] == 0); std::memcpy(&g_exo_emummc_config, &args.X[1], sizeof(args) - sizeof(args.X[0])); } diff --git a/source/firmware_version.cpp b/source/firmware_version.cpp index 4723872c..c444ad49 100644 --- a/source/firmware_version.cpp +++ b/source/firmware_version.cpp @@ -40,9 +40,7 @@ static void _CacheValues(void) args.X[0] = 0xC3000002; /* smcGetConfig */ args.X[1] = 65000; /* ConfigItem_ExosphereVersion */ R_ASSERT(svcCallSecureMonitor(&args)); - if (args.X[0] != 0) { - std::abort(); - } + STS_ASSERT(args.X[0] == 0); target_fw = (args.X[1] >> 0x08) & 0xFF; } diff --git a/source/kvdb/kvdb_archive.cpp b/source/kvdb/kvdb_archive.cpp index 503f155f..be3cd6e2 100644 --- a/source/kvdb/kvdb_archive.cpp +++ b/source/kvdb/kvdb_archive.cpp @@ -90,9 +90,7 @@ namespace sts::kvdb { Result ArchiveReader::ReadEntryCount(size_t *out) { /* This should only be called at the start of reading stream. */ - if (this->offset != 0) { - std::abort(); - } + STS_ASSERT(this->offset == 0); /* Read and validate header. */ ArchiveHeader header; @@ -105,9 +103,7 @@ namespace sts::kvdb { Result ArchiveReader::GetEntrySize(size_t *out_key_size, size_t *out_value_size) { /* This should only be called after ReadEntryCount. */ - if (this->offset == 0) { - std::abort(); - } + STS_ASSERT(this->offset != 0); /* Peek the next entry header. */ ArchiveEntryHeader header; @@ -121,9 +117,7 @@ namespace sts::kvdb { Result ArchiveReader::ReadEntry(void *out_key, size_t key_size, void *out_value, size_t value_size) { /* This should only be called after ReadEntryCount. */ - if (this->offset == 0) { - std::abort(); - } + STS_ASSERT(this->offset != 0); /* Read the next entry header. */ ArchiveEntryHeader header; @@ -131,9 +125,8 @@ namespace sts::kvdb { R_TRY(header.Validate()); /* Key size and Value size must be correct. */ - if (key_size != header.key_size || value_size != header.value_size) { - std::abort(); - } + STS_ASSERT(key_size == header.key_size); + STS_ASSERT(value_size == header.value_size); R_ASSERT(this->Read(out_key, key_size)); R_ASSERT(this->Read(out_value, value_size)); @@ -154,9 +147,7 @@ namespace sts::kvdb { void ArchiveWriter::WriteHeader(size_t entry_count) { /* This should only be called at start of write. */ - if (this->offset != 0) { - std::abort(); - } + STS_ASSERT(this->offset == 0); ArchiveHeader header = ArchiveHeader::Make(entry_count); R_ASSERT(this->Write(&header, sizeof(header))); @@ -164,9 +155,7 @@ namespace sts::kvdb { void ArchiveWriter::WriteEntry(const void *key, size_t key_size, const void *value, size_t value_size) { /* This should only be called after writing header. */ - if (this->offset == 0) { - std::abort(); - } + STS_ASSERT(this->offset != 0); ArchiveEntryHeader header = ArchiveEntryHeader::Make(key_size, value_size); R_ASSERT(this->Write(&header, sizeof(header))); diff --git a/source/kvdb/kvdb_file_key_value_store.cpp b/source/kvdb/kvdb_file_key_value_store.cpp index e1f77068..3853c2f6 100644 --- a/source/kvdb/kvdb_file_key_value_store.cpp +++ b/source/kvdb/kvdb_file_key_value_store.cpp @@ -57,9 +57,7 @@ namespace sts::kvdb { this->backing_buffer_free_offset = 0; this->count = 0; this->entries = static_castentries)>(this->Allocate(sizeof(*this->entries) * this->capacity)); - if (this->entries == nullptr) { - std::abort(); - } + STS_ASSERT(this->entries != nullptr); } std::optional FileKeyValueStore::Cache::TryGet(void *out_value, size_t max_out_size, const void *key, size_t key_size) { @@ -106,9 +104,7 @@ namespace sts::kvdb { } /* Ensure key size is small enough. */ - if (key_size > MaxKeySize) { - std::abort(); - } + STS_ASSERT(key_size <= MaxKeySize); /* If we're at capacity, invalidate the cache. */ if (this->count == this->capacity) { diff --git a/source/os/impl/os_inter_process_event.cpp b/source/os/impl/os_inter_process_event.cpp new file mode 100644 index 00000000..2b7377f6 --- /dev/null +++ b/source/os/impl/os_inter_process_event.cpp @@ -0,0 +1,182 @@ +/* + * 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 +#include "os_inter_process_event.hpp" + +namespace sts::os::impl { + + namespace { + + Result CreateEventHandles(Handle *out_readable, Handle *out_writable) { + /* Create the event handles. */ + R_TRY_CATCH(svcCreateEvent(out_writable, out_readable)) { + R_CATCH(ResultKernelResourceExhausted) { + return ResultOsResourceExhausted; + } + } R_END_TRY_CATCH_WITH_ASSERT; + + return ResultSuccess; + } + + } + + InterProcessEvent::InterProcessEvent(bool autoclear) : is_initialized(false) { + R_ASSERT(this->Initialize(autoclear)); + } + + InterProcessEvent::~InterProcessEvent() { + this->Finalize(); + } + + Result InterProcessEvent::Initialize(bool autoclear) { + STS_ASSERT(!this->is_initialized); + Handle rh, wh; + R_TRY(CreateEventHandles(&rh, &wh)); + this->Initialize(rh, true, wh, true, autoclear); + return ResultSuccess; + } + + void InterProcessEvent::Initialize(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) { + STS_ASSERT(!this->is_initialized); + STS_ASSERT(read_handle != INVALID_HANDLE || write_handle != INVALID_HANDLE); + this->read_handle = read_handle; + this->manage_read_handle = manage_read_handle; + this->write_handle = write_handle; + this->manage_write_handle = manage_write_handle; + this->auto_clear = autoclear; + this->is_initialized = true; + } + + Handle InterProcessEvent::DetachReadableHandle() { + STS_ASSERT(this->is_initialized); + const Handle handle = this->read_handle; + STS_ASSERT(handle != INVALID_HANDLE); + this->read_handle = INVALID_HANDLE; + this->manage_read_handle = false; + return handle; + } + + Handle InterProcessEvent::DetachWritableHandle() { + STS_ASSERT(this->is_initialized); + const Handle handle = this->write_handle; + STS_ASSERT(handle != INVALID_HANDLE); + this->write_handle = INVALID_HANDLE; + this->manage_write_handle = false; + return handle; + } + + Handle InterProcessEvent::GetReadableHandle() const { + STS_ASSERT(this->is_initialized); + return this->read_handle; + } + + Handle InterProcessEvent::GetWritableHandle() const { + STS_ASSERT(this->is_initialized); + return this->write_handle; + } + + void InterProcessEvent::Finalize() { + if (this->is_initialized) { + if (this->manage_read_handle && this->read_handle != INVALID_HANDLE) { + R_ASSERT(svcCloseHandle(this->read_handle)); + } + if (this->manage_write_handle && this->write_handle != INVALID_HANDLE) { + R_ASSERT(svcCloseHandle(this->write_handle)); + } + } + this->read_handle = INVALID_HANDLE; + this->manage_read_handle = false; + this->write_handle = INVALID_HANDLE; + this->manage_write_handle = false; + this->is_initialized = false; + } + + void InterProcessEvent::Signal() { + R_ASSERT(svcSignalEvent(this->GetWritableHandle())); + } + + void InterProcessEvent::Reset() { + Handle handle = this->GetReadableHandle(); + if (handle == INVALID_HANDLE) { + handle = this->GetWritableHandle(); + } + R_ASSERT(svcClearEvent(handle)); + } + + void InterProcessEvent::Wait() { + const Handle handle = this->GetReadableHandle(); + + while (true) { + /* Continuously wait, until success. */ + R_TRY_CATCH(svcWaitSynchronizationSingle(handle, U64_MAX)) { + R_CATCH(ResultKernelCancelled) { continue; } + } R_END_TRY_CATCH_WITH_ASSERT; + + /* Clear, if we must. */ + if (this->auto_clear) { + R_TRY_CATCH(svcResetSignal(handle)) { + /* Some other thread might have caught this before we did. */ + R_CATCH(ResultKernelInvalidState) { continue; } + } R_END_TRY_CATCH_WITH_ASSERT; + } + return; + } + } + + bool InterProcessEvent::TryWait() { + const Handle handle = this->GetReadableHandle(); + + if (this->auto_clear) { + /* Auto-clear. Just try to reset. */ + return R_SUCCEEDED(svcResetSignal(handle)); + } else { + /* Not auto-clear. */ + while (true) { + /* Continuously wait, until success or timeout. */ + R_TRY_CATCH(svcWaitSynchronizationSingle(handle, 0)) { + R_CATCH(ResultKernelTimedOut) { return false; } + R_CATCH(ResultKernelCancelled) { continue; } + } R_END_TRY_CATCH_WITH_ASSERT; + + /* We succeeded, so we're signaled. */ + return true; + } + } + } + + bool InterProcessEvent::TimedWait(u64 ns) { + const Handle handle = this->GetReadableHandle(); + + TimeoutHelper timeout_helper(ns); + while (true) { + /* Continuously wait, until success or timeout. */ + R_TRY_CATCH(svcWaitSynchronizationSingle(handle, timeout_helper.NsUntilTimeout())) { + R_CATCH(ResultKernelTimedOut) { return false; } + R_CATCH(ResultKernelCancelled) { continue; } + } R_END_TRY_CATCH_WITH_ASSERT; + + /* Clear, if we must. */ + if (this->auto_clear) { + R_TRY_CATCH(svcResetSignal(handle)) { + /* Some other thread might have caught this before we did. */ + R_CATCH(ResultKernelInvalidState) { continue; } + } R_END_TRY_CATCH_WITH_ASSERT; + } + + return true; + } + } +} diff --git a/source/os/impl/os_inter_process_event.hpp b/source/os/impl/os_inter_process_event.hpp new file mode 100644 index 00000000..98f5d974 --- /dev/null +++ b/source/os/impl/os_inter_process_event.hpp @@ -0,0 +1,55 @@ +/* + * 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 sts::os::impl { + + class WaitableHolderOfInterProcessEvent; + + class InterProcessEvent { + friend class WaitableHolderOfInterProcessEvent; + NON_COPYABLE(InterProcessEvent); + NON_MOVEABLE(InterProcessEvent); + private: + Handle read_handle; + Handle write_handle; + bool manage_read_handle; + bool manage_write_handle; + bool auto_clear; + bool is_initialized; + public: + InterProcessEvent() : is_initialized(false) { /* ... */ } + InterProcessEvent(bool autoclear); + ~InterProcessEvent(); + + Result Initialize(bool autoclear = true); + void Initialize(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear = true); + Handle DetachReadableHandle(); + Handle DetachWritableHandle(); + Handle GetReadableHandle() const; + Handle GetWritableHandle() const; + void Finalize(); + + void Signal(); + void Reset(); + void Wait(); + bool TryWait(); + bool TimedWait(u64 ns); + }; + +} diff --git a/source/os/impl/os_waitable_holder_base.hpp b/source/os/impl/os_waitable_holder_base.hpp new file mode 100644 index 00000000..dbe229bd --- /dev/null +++ b/source/os/impl/os_waitable_holder_base.hpp @@ -0,0 +1,77 @@ +/* + * 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 sts::os::impl { + + class WaitableObjectList; + class WaitableManagerImpl; + + class WaitableHolderBase { + public: + util::IntrusiveListNode manager_node; + util::IntrusiveListNode object_list_node; + private: + WaitableManagerImpl *manager = nullptr; + public: + /* Gets whether the held waitable is currently signaled. */ + virtual TriBool IsSignaled() const = 0; + /* Adds to manager's object list, returns is signaled. */ + virtual TriBool LinkToObjectList() = 0; + /* Removes from the manager's object list. */ + virtual void UnlinkFromObjectList() = 0; + /* Gets handle to output, returns INVALID_HANDLE on failure. */ + virtual Handle GetHandle() const = 0; + /* Gets the amount of time remaining until this wakes up. */ + virtual u64 GetWakeupTime() const { + return U64_MAX; + } + + /* Interface with manager. */ + void SetManager(WaitableManagerImpl *m) { + this->manager = m; + } + + WaitableManagerImpl *GetManager() const { + return this->manager; + } + + bool IsLinkedToManager() const { + return this->manager != nullptr; + } + }; + + class WaitableHolderOfUserObject : public WaitableHolderBase { + public: + /* All user objects have no handle to wait on. */ + virtual Handle GetHandle() const override { + return INVALID_HANDLE; + } + }; + + class WaitableHolderOfKernelObject : public WaitableHolderBase { + public: + /* All kernel objects have native handles, and thus don't have object list semantics. */ + virtual TriBool LinkToObjectList() override { + return TriBool::Undefined; + } + virtual void UnlinkFromObjectList() override { + /* ... */ + } + }; + +} diff --git a/source/os/impl/os_waitable_holder_impl.hpp b/source/os/impl/os_waitable_holder_impl.hpp new file mode 100644 index 00000000..59fe202b --- /dev/null +++ b/source/os/impl/os_waitable_holder_impl.hpp @@ -0,0 +1,53 @@ +/* + * 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_waitable_holder_of_handle.hpp" +#include "os_waitable_holder_of_event.hpp" +#include "os_waitable_holder_of_inter_process_event.hpp" +#include "os_waitable_holder_of_interrupt_event.hpp" +#include "os_waitable_holder_of_thread.hpp" +#include "os_waitable_holder_of_message_queue.hpp" + +namespace sts::os::impl { + + struct WaitableHolderImpl { + union { + TYPED_STORAGE(WaitableHolderOfHandle) holder_of_handle_storage; + TYPED_STORAGE(WaitableHolderOfEvent) holder_of_event_storage; + TYPED_STORAGE(WaitableHolderOfInterProcessEvent) holder_of_inter_process_event_storage; + TYPED_STORAGE(WaitableHolderOfInterruptEvent) holder_of_interrupt_event_storage; + TYPED_STORAGE(WaitableHolderOfThread) holder_of_thread_storage; + TYPED_STORAGE(WaitableHolderOfMessageQueueForNotFull) holder_of_mq_for_not_full_storage; + TYPED_STORAGE(WaitableHolderOfMessageQueueForNotEmpty) holder_of_mq_for_not_empty_storage; + }; + }; + + #define CHECK_HOLDER(T) \ + static_assert(std::is_base_of<::sts::os::impl::WaitableHolderBase, T>::value && std::is_trivially_destructible::value, #T) + + CHECK_HOLDER(WaitableHolderOfHandle); + CHECK_HOLDER(WaitableHolderOfEvent); + CHECK_HOLDER(WaitableHolderOfInterProcessEvent); + CHECK_HOLDER(WaitableHolderOfInterruptEvent); + CHECK_HOLDER(WaitableHolderOfThread); + CHECK_HOLDER(WaitableHolderOfMessageQueueForNotFull); + CHECK_HOLDER(WaitableHolderOfMessageQueueForNotEmpty); + + #undef CHECK_HOLDER + + static_assert(std::is_trivial::value && std::is_trivially_destructible::value, "WaitableHolderImpl"); + static_assert(sizeof(WaitableHolderImpl) == WaitableHolder::ImplStorageSize, "WaitableHolderImpl size"); +} diff --git a/source/os/impl/os_waitable_holder_of_event.hpp b/source/os/impl/os_waitable_holder_of_event.hpp new file mode 100644 index 00000000..fc440fac --- /dev/null +++ b/source/os/impl/os_waitable_holder_of_event.hpp @@ -0,0 +1,52 @@ +/* + * 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_waitable_holder_base.hpp" +#include "os_waitable_object_list.hpp" + +namespace sts::os::impl { + + class WaitableHolderOfEvent : public WaitableHolderOfUserObject { + private: + Event *event; + private: + TriBool IsSignaledImpl() const { + return this->event->signaled ? TriBool::True : TriBool::False; + } + public: + explicit WaitableHolderOfEvent(Event *e) : event(e) { /* ... */ } + + /* IsSignaled, Link, Unlink implemented. */ + virtual TriBool IsSignaled() const override { + std::scoped_lock lk(this->event->lock); + return this->IsSignaledImpl(); + } + + virtual TriBool LinkToObjectList() override { + std::scoped_lock lk(this->event->lock); + + GetReference(this->event->waitable_object_list_storage).LinkWaitableHolder(*this); + return this->IsSignaledImpl(); + } + + virtual void UnlinkFromObjectList() override { + std::scoped_lock lk(this->event->lock); + + GetReference(this->event->waitable_object_list_storage).UnlinkWaitableHolder(*this); + } + }; + +} diff --git a/source/os/impl/os_waitable_holder_of_handle.hpp b/source/os/impl/os_waitable_holder_of_handle.hpp new file mode 100644 index 00000000..f11acf70 --- /dev/null +++ b/source/os/impl/os_waitable_holder_of_handle.hpp @@ -0,0 +1,37 @@ +/* + * 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_waitable_holder_base.hpp" + +namespace sts::os::impl { + + class WaitableHolderOfHandle : public WaitableHolderOfKernelObject { + private: + Handle handle; + public: + explicit WaitableHolderOfHandle(Handle h) : handle(h) { /* ... */ } + + /* IsSignaled, GetHandle both implemented. */ + virtual TriBool IsSignaled() const override { + return TriBool::Undefined; + } + + virtual Handle GetHandle() const override { + return this->handle; + } + }; + +} diff --git a/source/os/impl/os_waitable_holder_of_inter_process_event.hpp b/source/os/impl/os_waitable_holder_of_inter_process_event.hpp new file mode 100644 index 00000000..8439a7b6 --- /dev/null +++ b/source/os/impl/os_waitable_holder_of_inter_process_event.hpp @@ -0,0 +1,39 @@ +/* + * 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_waitable_holder_base.hpp" +#include "os_inter_process_event.hpp" + +namespace sts::os::impl { + + class WaitableHolderOfInterProcessEvent : public WaitableHolderOfKernelObject { + private: + InterProcessEvent *event; + public: + explicit WaitableHolderOfInterProcessEvent(InterProcessEvent *e) : event(e) { /* ... */ } + + /* IsSignaled, GetHandle both implemented. */ + virtual TriBool IsSignaled() const override { + return TriBool::Undefined; + } + + virtual Handle GetHandle() const override { + STS_ASSERT(this->event->is_initialized); + return this->event->GetReadableHandle(); + } + }; + +} diff --git a/source/os/impl/os_waitable_holder_of_interrupt_event.hpp b/source/os/impl/os_waitable_holder_of_interrupt_event.hpp new file mode 100644 index 00000000..f54952c3 --- /dev/null +++ b/source/os/impl/os_waitable_holder_of_interrupt_event.hpp @@ -0,0 +1,38 @@ +/* + * 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_waitable_holder_base.hpp" + +namespace sts::os::impl { + + class WaitableHolderOfInterruptEvent : public WaitableHolderOfKernelObject { + private: + InterruptEvent *event; + public: + explicit WaitableHolderOfInterruptEvent(InterruptEvent *e) : event(e) { /* ... */ } + + /* IsSignaled, GetHandle both implemented. */ + virtual TriBool IsSignaled() const override { + return TriBool::Undefined; + } + + virtual Handle GetHandle() const override { + STS_ASSERT(this->event->is_initialized); + return this->event->handle.Get(); + } + }; + +} diff --git a/source/os/impl/os_waitable_holder_of_message_queue.hpp b/source/os/impl/os_waitable_holder_of_message_queue.hpp new file mode 100644 index 00000000..d85c6123 --- /dev/null +++ b/source/os/impl/os_waitable_holder_of_message_queue.hpp @@ -0,0 +1,63 @@ +/* + * 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_waitable_holder_base.hpp" +#include "os_waitable_object_list.hpp" + +namespace sts::os::impl { + + template + class WaitableHolderOfMessageQueue : public WaitableHolderOfUserObject { + static_assert(WaitKind == MessageQueueWaitKind::ForNotEmpty || WaitKind == MessageQueueWaitKind::ForNotFull, "MessageQueueHolder WaitKind!"); + private: + MessageQueue *message_queue; + private: + TriBool IsSignaledImpl() const { + if constexpr (WaitKind == MessageQueueWaitKind::ForNotEmpty) { + /* ForNotEmpty. */ + return this->message_queue->IsEmpty() ? TriBool::False : TriBool::True; + } else /* if constexpr (WaitKind == MessageQueueWaitKind::ForNotFull) */ { + /* ForNotFull */ + return this->message_queue->IsFull() ? TriBool::False : TriBool::True; + } + } + public: + explicit WaitableHolderOfMessageQueue(MessageQueue *mq) : message_queue(mq) { /* ... */ } + + /* IsSignaled, Link, Unlink implemented. */ + virtual TriBool IsSignaled() const override { + std::scoped_lock lk(this->message_queue->queue_lock); + return this->IsSignaledImpl(); + } + + virtual TriBool LinkToObjectList() override { + std::scoped_lock lk(this->message_queue->queue_lock); + + GetReference(this->message_queue->waitable_object_list_storage).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); + } + }; + + using WaitableHolderOfMessageQueueForNotEmpty = WaitableHolderOfMessageQueue; + using WaitableHolderOfMessageQueueForNotFull = WaitableHolderOfMessageQueue; + +} diff --git a/source/os/impl/os_waitable_holder_of_thread.hpp b/source/os/impl/os_waitable_holder_of_thread.hpp new file mode 100644 index 00000000..844eaf8b --- /dev/null +++ b/source/os/impl/os_waitable_holder_of_thread.hpp @@ -0,0 +1,39 @@ +/* + * 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_waitable_holder_base.hpp" + +namespace sts::os::impl { + + /* Nintendo implements this as a user wait object, operating on Thread state. */ + /* Libnx doesn't have an equivalent, so we'll use the thread's handle for kernel semantics. */ + class WaitableHolderOfThread : public WaitableHolderOfKernelObject { + private: + Thread *thread; + public: + explicit WaitableHolderOfThread(Thread *t) : thread(t) { /* ... */ } + + /* IsSignaled, GetHandle both implemented. */ + virtual TriBool IsSignaled() const override { + return TriBool::Undefined; + } + + virtual Handle GetHandle() const override { + return this->thread->GetHandle(); + } + }; + +} diff --git a/source/os/impl/os_waitable_manager_impl.cpp b/source/os/impl/os_waitable_manager_impl.cpp new file mode 100644 index 00000000..85b89e2c --- /dev/null +++ b/source/os/impl/os_waitable_manager_impl.cpp @@ -0,0 +1,177 @@ +/* + * 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 "os_waitable_manager_impl.hpp" +#include "os_waitable_object_list.hpp" + +namespace sts::os::impl{ + + WaitableHolderBase *WaitableManagerImpl::WaitAnyImpl(bool infinite, u64 timeout) { + /* Set processing thread handle while in scope. */ + this->waiting_thread_handle = threadGetCurHandle(); + ON_SCOPE_EXIT { this->waiting_thread_handle = INVALID_HANDLE; }; + + /* Prepare for processing. */ + this->signaled_holder = nullptr; + WaitableHolderBase *result = this->LinkHoldersToObjectList(); + + /* Check if we've been signaled. */ + { + std::scoped_lock lk(this->lock); + if (this->signaled_holder != nullptr) { + result = this->signaled_holder; + } + } + + /* Process object array. */ + if (result == nullptr) { + result = this->WaitAnyHandleImpl(infinite, timeout); + } + + /* Unlink holders from the current object list. */ + this->UnlinkHoldersFromObjectList(); + + return result; + } + + WaitableHolderBase *WaitableManagerImpl::WaitAnyHandleImpl(bool infinite, u64 timeout) { + Handle object_handles[MaximumHandleCount]; + WaitableHolderBase *objects[MaximumHandleCount]; + + const size_t count = this->BuildHandleArray(object_handles, objects); + const u64 end_time = infinite ? U64_MAX : armTicksToNs(armGetSystemTick()); + + while (true) { + this->current_time = armTicksToNs(armGetSystemTick()); + + u64 min_timeout = 0; + WaitableHolderBase *min_timeout_object = this->RecalculateNextTimeout(&min_timeout, end_time); + + s32 index; + if (count == 0 && min_timeout == 0) { + index = WaitTimedOut; + } else { + index = this->WaitSynchronization(object_handles, count, min_timeout); + STS_ASSERT(index != WaitInvalid); + } + + switch (index) { + case WaitTimedOut: + if (min_timeout_object) { + this->current_time = armTicksToNs(armGetSystemTick()); + if (min_timeout_object->IsSignaled() == TriBool::True) { + std::scoped_lock lk(this->lock); + this->signaled_holder = min_timeout_object; + return this->signaled_holder; + } + continue; + } + return nullptr; + case WaitCancelled: + if (this->signaled_holder) { + return this->signaled_holder; + } + continue; + default: /* 0 - 0x3F, valid. */ + { + std::scoped_lock lk(this->lock); + this->signaled_holder = objects[index]; + return this->signaled_holder; + } + } + } + } + + s32 WaitableManagerImpl::WaitSynchronization(Handle *handles, size_t count, u64 timeout) { + s32 index = WaitInvalid; + + R_TRY_CATCH(svcWaitSynchronization(&index, handles, count, timeout)) { + R_CATCH(ResultKernelTimedOut) { return WaitTimedOut; } + R_CATCH(ResultKernelCancelled) { return WaitCancelled; } + /* All other results are critical errors. */ + /* 7601: Thread termination requested. */ + /* E401: Handle is dead. */ + /* E601: Handle list address invalid. */ + /* EE01: Too many handles. */ + } R_END_TRY_CATCH_WITH_ASSERT; + + return index; + } + + size_t WaitableManagerImpl::BuildHandleArray(Handle *out_handles, WaitableHolderBase **out_objects) { + size_t count = 0; + + for (WaitableHolderBase &holder_base : this->waitable_list) { + if (Handle handle = holder_base.GetHandle(); handle != INVALID_HANDLE) { + STS_ASSERT(count < MaximumHandleCount); + + out_handles[count] = handle; + out_objects[count] = &holder_base; + count++; + } + } + + return count; + } + + WaitableHolderBase *WaitableManagerImpl::LinkHoldersToObjectList() { + WaitableHolderBase *signaled_holder = nullptr; + + for (WaitableHolderBase &holder_base : this->waitable_list) { + TriBool is_signaled = holder_base.LinkToObjectList(); + + if (signaled_holder == nullptr && is_signaled == TriBool::True) { + signaled_holder = &holder_base; + } + } + + return signaled_holder; + } + + void WaitableManagerImpl::UnlinkHoldersFromObjectList() { + for (WaitableHolderBase &holder_base : this->waitable_list) { + holder_base.UnlinkFromObjectList(); + } + } + + WaitableHolderBase *WaitableManagerImpl::RecalculateNextTimeout(u64 *out_min_timeout, u64 end_time) { + WaitableHolderBase *min_timeout_holder = nullptr; + u64 min_time = end_time; + + for (WaitableHolderBase &holder_base : this->waitable_list) { + if (const u64 cur_time = holder_base.GetWakeupTime(); cur_time < min_time) { + min_timeout_holder = &holder_base; + min_time = cur_time; + } + } + + if (min_time < this->current_time) { + *out_min_timeout = 0; + } else { + *out_min_timeout = min_time - this->current_time; + } + return min_timeout_holder; + } + + void WaitableManagerImpl::SignalAndWakeupThread(WaitableHolderBase *holder_base) { + std::scoped_lock lk(this->lock); + + if (this->signaled_holder == nullptr) { + this->signaled_holder = holder_base; + R_ASSERT(svcCancelSynchronization(this->waiting_thread_handle)); + } + } + +} diff --git a/source/os/impl/os_waitable_manager_impl.hpp b/source/os/impl/os_waitable_manager_impl.hpp new file mode 100644 index 00000000..bea1a0f2 --- /dev/null +++ b/source/os/impl/os_waitable_manager_impl.hpp @@ -0,0 +1,94 @@ +/* + * 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_waitable_holder_base.hpp" + +namespace sts::os::impl { + + class WaitableManagerImpl { + public: + static constexpr size_t MaximumHandleCount = 0x40; + static constexpr s32 WaitInvalid = -3; + static constexpr s32 WaitCancelled = -2; + static constexpr s32 WaitTimedOut = -1; + using ListType = util::IntrusiveListMemberTraits<&WaitableHolderBase::manager_node>::ListType; + private: + ListType waitable_list; + WaitableHolderBase *signaled_holder; + u64 current_time; + Mutex lock; + Handle waiting_thread_handle; + private: + WaitableHolderBase *WaitAnyImpl(bool infinite, u64 timeout); + WaitableHolderBase *WaitAnyHandleImpl(bool infinite, u64 timeout); + s32 WaitSynchronization(Handle *handles, size_t count, u64 timeout); + size_t BuildHandleArray(Handle *out_handles, WaitableHolderBase **out_objects); + + WaitableHolderBase *LinkHoldersToObjectList(); + void UnlinkHoldersFromObjectList(); + + WaitableHolderBase *RecalculateNextTimeout(u64 *out_min_timeout, u64 end_time); + public: + /* Wait. */ + WaitableHolderBase *WaitAny() { + return this->WaitAnyImpl(true, U64_MAX); + } + + WaitableHolderBase *TryWaitAny() { + return this->WaitAnyImpl(false, 0); + } + + WaitableHolderBase *TimedWaitAny(u64 timeout) { + return this->WaitAnyImpl(false, timeout); + } + + /* List management. */ + bool IsEmpty() const { + return this->waitable_list.empty(); + } + + void LinkWaitableHolder(WaitableHolderBase &holder_base) { + this->waitable_list.push_back(holder_base); + } + + void UnlinkWaitableHolder(WaitableHolderBase &holder_base) { + this->waitable_list.erase(this->waitable_list.iterator_to(holder_base)); + } + + void UnlinkAll() { + while (!this->IsEmpty()) { + this->waitable_list.front().SetManager(nullptr); + this->waitable_list.pop_front(); + } + } + + void MoveAllFrom(WaitableManagerImpl &other) { + /* Set manager for all of the other's waitables. */ + for (auto &w : other.waitable_list) { + w.SetManager(this); + } + this->waitable_list.splice(this->waitable_list.end(), other.waitable_list); + } + + /* Other. */ + u64 GetCurrentTime() const { + return this->current_time; + } + + void SignalAndWakeupThread(WaitableHolderBase *holder_base); + }; + +} diff --git a/source/os/impl/os_waitable_object_list.hpp b/source/os/impl/os_waitable_object_list.hpp new file mode 100644 index 00000000..7129b661 --- /dev/null +++ b/source/os/impl/os_waitable_object_list.hpp @@ -0,0 +1,53 @@ +/* + * 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_waitable_holder_base.hpp" +#include "os_waitable_manager_impl.hpp" + +namespace sts::os::impl { + + class WaitableObjectList { + public: + using ListType = util::IntrusiveListMemberTraits<&WaitableHolderBase::object_list_node>::ListType; + private: + ListType object_list; + public: + void SignalAllThreads() { + for (WaitableHolderBase &holder_base : this->object_list) { + holder_base.GetManager()->SignalAndWakeupThread(&holder_base); + } + } + + void WakeupAllThreads() { + for (WaitableHolderBase &holder_base : this->object_list) { + holder_base.GetManager()->SignalAndWakeupThread(nullptr); + } + } + + bool IsEmpty() const { + return this->object_list.empty(); + } + + void LinkWaitableHolder(WaitableHolderBase &holder_base) { + this->object_list.push_back(holder_base); + } + + void UnlinkWaitableHolder(WaitableHolderBase &holder_base) { + this->object_list.erase(this->object_list.iterator_to(holder_base)); + } + }; + +} diff --git a/source/os/os_event.cpp b/source/os/os_event.cpp new file mode 100644 index 00000000..51b5ebe1 --- /dev/null +++ b/source/os/os_event.cpp @@ -0,0 +1,106 @@ +/* + * 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 +#include "impl/os_waitable_object_list.hpp" + +namespace sts::os { + + Event::Event(bool a, bool s) : auto_clear(a), signaled(s) { + new (GetPointer(this->waitable_object_list_storage)) impl::WaitableObjectList(); + } + + Event::~Event() { + GetReference(this->waitable_object_list_storage).~WaitableObjectList(); + } + + void Event::Signal() { + std::scoped_lock lk(this->lock); + + /* If we're already signaled, nothing more to do. */ + if (this->signaled) { + return; + } + + this->signaled = true; + + /* Signal! */ + if (this->auto_clear) { + /* If we're auto clear, signal one thread, which will clear. */ + this->cv.Signal(); + } else { + /* If we're manual clear, increment counter and wake all. */ + this->counter++; + this->cv.Broadcast(); + } + + /* Wake up whatever manager, if any. */ + GetReference(this->waitable_object_list_storage).SignalAllThreads(); + } + + void Event::Reset() { + std::scoped_lock lk(this->lock); + this->signaled = false; + } + + void Event::Wait() { + std::scoped_lock lk(this->lock); + + u64 cur_counter = this->counter; + while (!this->signaled) { + if (this->counter != cur_counter) { + break; + } + this->cv.Wait(&this->lock); + } + + if (this->auto_clear) { + this->signaled = false; + } + } + + bool Event::TryWait() { + std::scoped_lock lk(this->lock); + + const bool success = this->signaled; + if (this->auto_clear) { + this->signaled = false; + } + + return success; + } + + bool Event::TimedWait(u64 ns) { + TimeoutHelper timeout_helper(ns); + std::scoped_lock lk(this->lock); + + u64 cur_counter = this->counter; + while (!this->signaled) { + if (this->counter != cur_counter) { + break; + } + if (R_FAILED(this->cv.TimedWait(&this->lock, timeout_helper.NsUntilTimeout()))) { + return false; + } + } + + if (this->auto_clear) { + this->signaled = false; + } + + return true; + } + +} diff --git a/source/os/os_interrupt_event.cpp b/source/os/os_interrupt_event.cpp new file mode 100644 index 00000000..96201ff9 --- /dev/null +++ b/source/os/os_interrupt_event.cpp @@ -0,0 +1,112 @@ +/* + * 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 +#include "impl/os_waitable_object_list.hpp" + +namespace sts::os { + + Result InterruptEvent::Initialize(u32 interrupt_id, bool autoclear) { + STS_ASSERT(!this->is_initialized); + this->auto_clear = autoclear; + + const auto type = this->auto_clear ? svc::InterruptType_Edge : svc::InterruptType_Level; + R_TRY(svcCreateInterruptEvent(this->handle.GetPointer(), interrupt_id, type)); + + this->is_initialized = true; + return ResultSuccess; + } + + void InterruptEvent::Finalize() { + STS_ASSERT(this->is_initialized); + R_ASSERT(svcCloseHandle(this->handle.Move())); + this->auto_clear = true; + this->is_initialized = false; + } + + InterruptEvent::InterruptEvent(u32 interrupt_id, bool autoclear) { + this->is_initialized = false; + R_ASSERT(this->Initialize(interrupt_id, autoclear)); + } + + void InterruptEvent::Reset() { + R_ASSERT(svcClearEvent(this->handle.Get())); + } + + void InterruptEvent::Wait() { + STS_ASSERT(this->is_initialized); + + while (true) { + /* Continuously wait, until success. */ + R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), U64_MAX)) { + R_CATCH(ResultKernelCancelled) { continue; } + } R_END_TRY_CATCH_WITH_ASSERT; + + /* Clear, if we must. */ + if (this->auto_clear) { + R_TRY_CATCH(svcResetSignal(this->handle.Get())) { + /* Some other thread might have caught this before we did. */ + R_CATCH(ResultKernelInvalidState) { continue; } + } R_END_TRY_CATCH_WITH_ASSERT; + } + return; + } + } + + bool InterruptEvent::TryWait() { + STS_ASSERT(this->is_initialized); + + if (this->auto_clear) { + /* Auto-clear. Just try to reset. */ + return R_SUCCEEDED(svcResetSignal(this->handle.Get())); + } else { + /* Not auto-clear. */ + while (true) { + /* Continuously wait, until success or timeout. */ + R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), 0)) { + R_CATCH(ResultKernelTimedOut) { return false; } + R_CATCH(ResultKernelCancelled) { continue; } + } R_END_TRY_CATCH_WITH_ASSERT; + + /* We succeeded, so we're signaled. */ + return true; + } + } + } + + bool InterruptEvent::TimedWait(u64 ns) { + STS_ASSERT(this->is_initialized); + + TimeoutHelper timeout_helper(ns); + while (true) { + /* Continuously wait, until success or timeout. */ + R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), timeout_helper.NsUntilTimeout())) { + R_CATCH(ResultKernelTimedOut) { return false; } + R_CATCH(ResultKernelCancelled) { continue; } + } R_END_TRY_CATCH_WITH_ASSERT; + + /* Clear, if we must. */ + if (this->auto_clear) { + R_TRY_CATCH(svcResetSignal(this->handle.Get())) { + /* Some other thread might have caught this before we did. */ + R_CATCH(ResultKernelInvalidState) { continue; } + } R_END_TRY_CATCH_WITH_ASSERT; + } + + return true; + } + } + +} diff --git a/source/os/os_message_queue.cpp b/source/os/os_message_queue.cpp index 2008ed7b..26b1ae6d 100644 --- a/source/os/os_message_queue.cpp +++ b/source/os/os_message_queue.cpp @@ -14,14 +14,21 @@ * along with this program. If not, see . */ #include +#include "impl/os_waitable_object_list.hpp" namespace sts::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() { + GetReference(this->waitable_object_list_storage).~WaitableObjectList(); + } + void MessageQueue::SendInternal(uintptr_t data) { /* Ensure we don't corrupt the queue, but this should never happen. */ - if (this->count >= this->capacity) { - std::abort(); - } + STS_ASSERT(this->count < this->capacity); /* Write data to tail of queue. */ this->buffer[(this->count++ + this->offset) % this->capacity] = data; @@ -29,9 +36,7 @@ namespace sts::os { void MessageQueue::SendNextInternal(uintptr_t data) { /* Ensure we don't corrupt the queue, but this should never happen. */ - if (this->count >= this->capacity) { - std::abort(); - } + STS_ASSERT(this->count < this->capacity); /* Write data to head of queue. */ this->offset = (this->offset + this->capacity - 1) % this->capacity; @@ -41,9 +46,7 @@ namespace sts::os { uintptr_t MessageQueue::ReceiveInternal() { /* Ensure we don't corrupt the queue, but this should never happen. */ - if (this->count == 0) { - std::abort(); - } + STS_ASSERT(this->count > 0); uintptr_t data = this->buffer[this->offset]; this->offset = (this->offset + 1) % this->capacity; @@ -53,9 +56,7 @@ namespace sts::os { uintptr_t MessageQueue::PeekInternal() { /* Ensure we don't corrupt the queue, but this should never happen. */ - if (this->count == 0) { - std::abort(); - } + STS_ASSERT(this->count > 0); return this->buffer[this->offset]; } diff --git a/source/os/os_system_event.cpp b/source/os/os_system_event.cpp new file mode 100644 index 00000000..1e64fa03 --- /dev/null +++ b/source/os/os_system_event.cpp @@ -0,0 +1,189 @@ +/* + * 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 +#include "impl/os_waitable_holder_impl.hpp" + +namespace sts::os { + + SystemEvent::SystemEvent(bool inter_process, bool autoclear) : state(SystemEventState::Uninitialized) { + if (inter_process) { + R_ASSERT(this->InitializeAsInterProcessEvent(autoclear)); + } else { + R_ASSERT(this->InitializeAsEvent(autoclear)); + } + } + + SystemEvent::SystemEvent(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) : state(SystemEventState::Uninitialized) { + this->AttachHandles(read_handle, manage_read_handle, write_handle, manage_write_handle, autoclear); + } + + SystemEvent::~SystemEvent() { + this->Finalize(); + } + + Event &SystemEvent::GetEvent() { + STS_ASSERT(this->state == SystemEventState::Event); + return GetReference(this->storage_for_event); + } + + const Event &SystemEvent::GetEvent() const { + STS_ASSERT(this->state == SystemEventState::Event); + return GetReference(this->storage_for_event); + } + + impl::InterProcessEvent &SystemEvent::GetInterProcessEvent() { + STS_ASSERT(this->state == SystemEventState::InterProcessEvent); + return GetReference(this->storage_for_inter_process_event); + } + + const impl::InterProcessEvent &SystemEvent::GetInterProcessEvent() const { + STS_ASSERT(this->state == SystemEventState::InterProcessEvent); + return GetReference(this->storage_for_inter_process_event); + } + + Result SystemEvent::InitializeAsEvent(bool autoclear) { + STS_ASSERT(this->state == SystemEventState::Uninitialized); + new (GetPointer(this->storage_for_event)) Event(autoclear); + this->state = SystemEventState::Event; + return ResultSuccess; + } + + Result SystemEvent::InitializeAsInterProcessEvent(bool autoclear) { + STS_ASSERT(this->state == SystemEventState::Uninitialized); + new (GetPointer(this->storage_for_inter_process_event)) impl::InterProcessEvent(); + this->state = SystemEventState::InterProcessEvent; + R_TRY_CLEANUP(this->GetInterProcessEvent().Initialize(autoclear), { + this->Finalize(); + }); + return ResultSuccess; + } + + void SystemEvent::AttachHandles(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) { + STS_ASSERT(this->state == SystemEventState::Uninitialized); + new (GetPointer(this->storage_for_inter_process_event)) impl::InterProcessEvent(); + this->state = SystemEventState::InterProcessEvent; + this->GetInterProcessEvent().Initialize(read_handle, manage_read_handle, write_handle, manage_write_handle, autoclear); + } + + void SystemEvent::AttachReadableHandle(Handle read_handle, bool manage_read_handle, bool autoclear) { + this->AttachHandles(read_handle, manage_read_handle, INVALID_HANDLE, false, autoclear); + } + + void SystemEvent::AttachWritableHandle(Handle write_handle, bool manage_write_handle, bool autoclear) { + this->AttachHandles(INVALID_HANDLE, false, write_handle, manage_write_handle, autoclear); + } + + Handle SystemEvent::DetachReadableHandle() { + STS_ASSERT(this->state == SystemEventState::InterProcessEvent); + return this->GetInterProcessEvent().DetachReadableHandle(); + } + + Handle SystemEvent::DetachWritableHandle() { + STS_ASSERT(this->state == SystemEventState::InterProcessEvent); + return this->GetInterProcessEvent().DetachWritableHandle(); + } + + Handle SystemEvent::GetReadableHandle() const { + STS_ASSERT(this->state == SystemEventState::InterProcessEvent); + return this->GetInterProcessEvent().GetReadableHandle(); + } + + Handle SystemEvent::GetWritableHandle() const { + STS_ASSERT(this->state == SystemEventState::InterProcessEvent); + return this->GetInterProcessEvent().GetWritableHandle(); + } + + void SystemEvent::Finalize() { + switch (this->state) { + case SystemEventState::Uninitialized: + break; + case SystemEventState::Event: + this->GetEvent().~Event(); + break; + case SystemEventState::InterProcessEvent: + this->GetInterProcessEvent().~InterProcessEvent(); + break; + default: + std::abort(); + } + this->state = SystemEventState::Uninitialized; + } + + void SystemEvent::Signal() { + switch (this->state) { + case SystemEventState::Event: + this->GetEvent().Signal(); + break; + case SystemEventState::InterProcessEvent: + this->GetInterProcessEvent().Signal(); + break; + case SystemEventState::Uninitialized: + default: + std::abort(); + } + } + + void SystemEvent::Reset() { + switch (this->state) { + case SystemEventState::Event: + this->GetEvent().Reset(); + break; + case SystemEventState::InterProcessEvent: + this->GetInterProcessEvent().Reset(); + break; + case SystemEventState::Uninitialized: + default: + std::abort(); + } + } + void SystemEvent::Wait() { + switch (this->state) { + case SystemEventState::Event: + this->GetEvent().Wait(); + break; + case SystemEventState::InterProcessEvent: + this->GetInterProcessEvent().Wait(); + break; + case SystemEventState::Uninitialized: + default: + std::abort(); + } + } + + bool SystemEvent::TryWait() { + switch (this->state) { + case SystemEventState::Event: + return this->GetEvent().TryWait(); + case SystemEventState::InterProcessEvent: + return this->GetInterProcessEvent().TryWait(); + case SystemEventState::Uninitialized: + default: + std::abort(); + } + } + + bool SystemEvent::TimedWait(u64 ns) { + switch (this->state) { + case SystemEventState::Event: + return this->GetEvent().TimedWait(ns); + case SystemEventState::InterProcessEvent: + return this->GetInterProcessEvent().TimedWait(ns); + case SystemEventState::Uninitialized: + default: + std::abort(); + } + } +} diff --git a/source/os/os_waitable_holder.cpp b/source/os/os_waitable_holder.cpp new file mode 100644 index 00000000..c44b65f5 --- /dev/null +++ b/source/os/os_waitable_holder.cpp @@ -0,0 +1,111 @@ +/* + * 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 +#include "impl/os_waitable_holder_impl.hpp" +#include "impl/os_waitable_manager_impl.hpp" + +namespace sts::os { + + WaitableHolder::WaitableHolder(Handle handle) { + /* Don't allow invalid handles. */ + STS_ASSERT(handle != INVALID_HANDLE); + + /* Initialize appropriate holder. */ + new (GetPointer(this->impl_storage)) impl::WaitableHolderOfHandle(handle); + + /* Set user-data. */ + this->user_data = 0; + } + + WaitableHolder::WaitableHolder(Event *event) { + /* Initialize appropriate holder. */ + new (GetPointer(this->impl_storage)) impl::WaitableHolderOfEvent(event); + + /* Set user-data. */ + this->user_data = 0; + } + + WaitableHolder::WaitableHolder(SystemEvent *event) { + /* Initialize appropriate holder. */ + switch (event->GetState()) { + case SystemEventState::Event: + new (GetPointer(this->impl_storage)) impl::WaitableHolderOfEvent(&event->GetEvent()); + break; + case SystemEventState::InterProcessEvent: + new (GetPointer(this->impl_storage)) impl::WaitableHolderOfInterProcessEvent(&event->GetInterProcessEvent()); + break; + case SystemEventState::Uninitialized: + default: + std::abort(); + } + + /* Set user-data. */ + this->user_data = 0; + } + + WaitableHolder::WaitableHolder(InterruptEvent *event) { + /* Initialize appropriate holder. */ + new (GetPointer(this->impl_storage)) impl::WaitableHolderOfInterruptEvent(event); + + /* Set user-data. */ + this->user_data = 0; + } + + WaitableHolder::WaitableHolder(Thread *thread) { + /* Initialize appropriate holder. */ + new (GetPointer(this->impl_storage)) impl::WaitableHolderOfThread(thread); + + /* Set user-data. */ + this->user_data = 0; + } + + WaitableHolder::WaitableHolder(MessageQueue *message_queue, MessageQueueWaitKind wait_kind) { + /* Initialize appropriate holder. */ + switch (wait_kind) { + case MessageQueueWaitKind::ForNotFull: + new (GetPointer(this->impl_storage)) impl::WaitableHolderOfMessageQueueForNotFull(message_queue); + break; + case MessageQueueWaitKind::ForNotEmpty: + new (GetPointer(this->impl_storage)) impl::WaitableHolderOfMessageQueueForNotEmpty(message_queue); + break; + default: + std::abort(); + } + + /* Set user-data. */ + this->user_data = 0; + } + + WaitableHolder::~WaitableHolder() { + auto holder_base = reinterpret_cast(GetPointer(this->impl_storage)); + + /* Don't allow destruction of a linked waitable holder. */ + STS_ASSERT(!holder_base->IsLinkedToManager()); + + holder_base->~WaitableHolderBase(); + } + + void WaitableHolder::UnlinkFromWaitableManager() { + auto holder_base = reinterpret_cast(GetPointer(this->impl_storage)); + + /* Don't allow unlinking of an unlinked holder. */ + STS_ASSERT(holder_base->IsLinkedToManager()); + + holder_base->GetManager()->UnlinkWaitableHolder(*holder_base); + holder_base->SetManager(nullptr); + } + +} diff --git a/source/os/os_waitable_manager.cpp b/source/os/os_waitable_manager.cpp new file mode 100644 index 00000000..bfec7eda --- /dev/null +++ b/source/os/os_waitable_manager.cpp @@ -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 . + */ +#include +#include "impl/os_waitable_holder_impl.hpp" +#include "impl/os_waitable_manager_impl.hpp" + +namespace sts::os { + + WaitableManager::WaitableManager() { + /* Initialize storage. */ + new (GetPointer(this->impl_storage)) impl::WaitableManagerImpl(); + } + + WaitableManager::~WaitableManager() { + auto &impl = GetReference(this->impl_storage); + + /* Don't allow destruction of a non-empty waitable holder. */ + STS_ASSERT(impl.IsEmpty()); + + impl.~WaitableManagerImpl(); + } + + + /* Wait. */ + WaitableHolder *WaitableManager::WaitAny() { + auto &impl = GetReference(this->impl_storage); + + /* Don't allow waiting on empty list. */ + STS_ASSERT(!impl.IsEmpty()); + + return reinterpret_cast(impl.WaitAny()); + } + + WaitableHolder *WaitableManager::TryWaitAny() { + auto &impl = GetReference(this->impl_storage); + + /* Don't allow waiting on empty list. */ + STS_ASSERT(!impl.IsEmpty()); + + return reinterpret_cast(impl.TryWaitAny()); + } + + WaitableHolder *WaitableManager::TimedWaitAny(u64 timeout) { + auto &impl = GetReference(this->impl_storage); + + /* Don't allow waiting on empty list. */ + STS_ASSERT(!impl.IsEmpty()); + + return reinterpret_cast(impl.TimedWaitAny(timeout)); + } + + /* Link. */ + void WaitableManager::LinkWaitableHolder(WaitableHolder *holder) { + auto &impl = GetReference(this->impl_storage); + auto holder_base = reinterpret_cast(GetPointer(holder->impl_storage)); + + /* Don't allow double-linking a holder. */ + STS_ASSERT(!holder_base->IsLinkedToManager()); + + impl.LinkWaitableHolder(*holder_base); + holder_base->SetManager(&impl); + } + + void WaitableManager::UnlinkAll() { + auto &impl = GetReference(this->impl_storage); + impl.UnlinkAll(); + } + + void WaitableManager::MoveAllFrom(WaitableManager *other) { + auto &dst_impl = GetReference(this->impl_storage); + auto &src_impl = GetReference(other->impl_storage); + + dst_impl.MoveAllFrom(src_impl); + } + +} diff --git a/source/patcher/patcher_api.cpp b/source/patcher/patcher_api.cpp index 8834a93a..63257d7a 100644 --- a/source/patcher/patcher_api.cpp +++ b/source/patcher/patcher_api.cpp @@ -101,15 +101,11 @@ namespace sts::patcher { void ApplyIpsPatch(u8 *mapped_module, size_t mapped_size, size_t protected_size, size_t offset, bool is_ips32, FILE *f_ips) { /* Validate offset/protected size. */ - if (offset > protected_size) { - std::abort(); - } + STS_ASSERT(offset <= protected_size); u8 buffer[sizeof(Ips32TailMagic)]; while (true) { - if (fread(buffer, is_ips32 ? sizeof(Ips32TailMagic) : sizeof(IpsTailMagic), 1, f_ips) != 1) { - std::abort(); - } + STS_ASSERT(fread(buffer, is_ips32 ? sizeof(Ips32TailMagic) : sizeof(IpsTailMagic), 1, f_ips) == 1); if (IsIpsTail(is_ips32, buffer)) { break; @@ -119,24 +115,18 @@ namespace sts::patcher { u32 patch_offset = GetIpsPatchOffset(is_ips32, buffer); /* Size of patch. */ - if (fread(buffer, 2, 1, f_ips) != 1) { - std::abort(); - } + STS_ASSERT(fread(buffer, 2, 1, f_ips) == 1); u32 patch_size = GetIpsPatchSize(is_ips32, buffer); /* Check for RLE encoding. */ if (patch_size == 0) { /* Size of RLE. */ - if (fread(buffer, 2, 1, f_ips) != 1) { - std::abort(); - } + STS_ASSERT(fread(buffer, 2, 1, f_ips) == 1); u32 rle_size = (buffer[0] << 8) | (buffer[1]); /* Value for RLE. */ - if (fread(buffer, 1, 1, f_ips) != 1) { - std::abort(); - } + STS_ASSERT(fread(buffer, 1, 1, f_ips) == 1); /* Ensure we don't write to protected region. */ if (patch_offset < protected_size) { @@ -179,9 +169,7 @@ namespace sts::patcher { if (patch_offset + read_size > mapped_size) { read_size = mapped_size - patch_offset; } - if (fread(mapped_module + patch_offset, read_size, 1, f_ips) != 1) { - std::abort(); - } + STS_ASSERT(fread(mapped_module + patch_offset, read_size, 1, f_ips) == 1); if (patch_size > read_size) { fseek(f_ips, patch_size - read_size, SEEK_CUR); } diff --git a/source/updater/updater_api.cpp b/source/updater/updater_api.cpp index 2e6afd25..d078b965 100644 --- a/source/updater/updater_api.cpp +++ b/source/updater/updater_api.cpp @@ -142,9 +142,7 @@ namespace sts::updater { Result GetBootImagePackageDataId(u64 *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size) { /* Ensure we can read content metas. */ constexpr size_t MaxContentMetas = 0x40; - if (work_buffer_size < sizeof(NcmMetaRecord) * MaxContentMetas) { - std::abort(); - } + STS_ASSERT(work_buffer_size >= sizeof(NcmMetaRecord) * MaxContentMetas); /* Open NAND System meta database, list contents. */ NcmContentMetaDatabase meta_db; @@ -161,9 +159,7 @@ namespace sts::updater { return ResultUpdaterBootImagePackageNotFound; } - if (total_entries != written_entries) { - std::abort(); - } + STS_ASSERT(total_entries == written_entries); /* Output is sorted, return the lowest valid exfat entry. */ if (total_entries > 1) { @@ -203,7 +199,7 @@ namespace sts::updater { return ResultUpdaterBootImagePackageNotFound; } } R_END_TRY_CATCH; - ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } }; + ON_SCOPE_EXIT { R_ASSERT(romfsUnmount(GetBootImagePackageMountPath())); }; /* Read and validate hashes of boot images. */ { @@ -258,7 +254,7 @@ namespace sts::updater { return ResultUpdaterBootImagePackageNotFound; } } R_END_TRY_CATCH; - ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } }; + ON_SCOPE_EXIT { R_ASSERT(romfsUnmount(GetBootImagePackageMountPath())); }; /* Read and validate hashes of boot images. */ { @@ -329,7 +325,7 @@ namespace sts::updater { return ResultUpdaterBootImagePackageNotFound; } } R_END_TRY_CATCH; - ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } }; + ON_SCOPE_EXIT { R_ASSERT(romfsUnmount(GetBootImagePackageMountPath())); }; { Boot0Accessor boot0_accessor; @@ -386,7 +382,7 @@ namespace sts::updater { return ResultUpdaterBootImagePackageNotFound; } } R_END_TRY_CATCH; - ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } }; + ON_SCOPE_EXIT { R_ASSERT(romfsUnmount(GetBootImagePackageMountPath())); }; { Boot0Accessor boot0_accessor; @@ -533,9 +529,7 @@ namespace sts::updater { /* Get a session to ncm. */ DoWithSmSession([&]() { - if (R_FAILED(ncmInitialize())) { - std::abort(); - } + R_ASSERT(ncmInitialize()); }); ON_SCOPE_EXIT { ncmExit(); }; diff --git a/source/updater/updater_bis_management.cpp b/source/updater/updater_bis_management.cpp index 27912c0e..364365fa 100644 --- a/source/updater/updater_bis_management.cpp +++ b/source/updater/updater_bis_management.cpp @@ -35,23 +35,18 @@ namespace sts::updater { } Result BisAccessor::Read(void *dst, size_t size, u64 offset) { - if (offset % SectorAlignment) { - std::abort(); - } + STS_ASSERT((offset % SectorAlignment) == 0); return fsStorageRead(&this->storage, offset, dst, size); } Result BisAccessor::Write(u64 offset, const void *src, size_t size) { - if (offset % SectorAlignment) { - std::abort(); - } + STS_ASSERT((offset % SectorAlignment) == 0); return fsStorageWrite(&this->storage, offset, src, size); } Result BisAccessor::Write(u64 offset, size_t size, const char *bip_path, void *work_buffer, size_t work_buffer_size) { - if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) { - std::abort(); - } + STS_ASSERT((offset % SectorAlignment) == 0); + STS_ASSERT((work_buffer_size % SectorAlignment) == 0); FILE *bip_fp = fopen(bip_path, "rb"); if (bip_fp == NULL) { @@ -68,9 +63,7 @@ namespace sts::updater { return fsdevGetLastResult(); } } - if (written + read_size > size) { - std::abort(); - } + STS_ASSERT(written + read_size <= size); size_t aligned_size = ((read_size + SectorAlignment - 1) / SectorAlignment) * SectorAlignment; R_TRY(this->Write(offset + written, work_buffer, aligned_size)); @@ -84,9 +77,8 @@ namespace sts::updater { } Result BisAccessor::Clear(u64 offset, u64 size, void *work_buffer, size_t work_buffer_size) { - if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) { - std::abort(); - } + STS_ASSERT((offset % SectorAlignment) == 0); + STS_ASSERT((work_buffer_size % SectorAlignment) == 0); std::memset(work_buffer, 0, work_buffer_size); @@ -100,9 +92,8 @@ namespace sts::updater { } Result BisAccessor::GetHash(void *dst, u64 offset, u64 size, u64 hash_size, void *work_buffer, size_t work_buffer_size) { - if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) { - std::abort(); - } + STS_ASSERT((offset % SectorAlignment) == 0); + STS_ASSERT((work_buffer_size % SectorAlignment) == 0); Sha256Context sha_ctx; sha256ContextCreate(&sha_ctx); @@ -122,18 +113,12 @@ namespace sts::updater { size_t Boot0Accessor::GetBootloaderVersion(void *bct) { u32 version = *reinterpret_cast(reinterpret_cast(bct) + BctVersionOffset); - if (version > BctVersionMax) { - std::abort(); - } - + STS_ASSERT(version <= BctVersionMax); return static_cast(version); } size_t Boot0Accessor::GetEksIndex(size_t bootloader_version) { - if (bootloader_version > BctVersionMax) { - std::abort(); - } - + STS_ASSERT(bootloader_version <= BctVersionMax); return (bootloader_version > 0) ? bootloader_version - 1 : 0; } diff --git a/source/updater/updater_bis_management.hpp b/source/updater/updater_bis_management.hpp index 913e09f7..83078e21 100644 --- a/source/updater/updater_bis_management.hpp +++ b/source/updater/updater_bis_management.hpp @@ -140,9 +140,7 @@ namespace sts::updater { public: Result Read(size_t *out_size, void *dst, size_t size, EnumType which) { const auto entry = FindEntry(which); - if (size < entry->size) { - std::abort(); - } + STS_ASSERT(size >= entry->size); R_TRY(BisAccessor::Read(dst, entry->size, entry->offset)); @@ -152,10 +150,8 @@ namespace sts::updater { Result Write(const void *src, size_t size, EnumType which) { const auto entry = FindEntry(which); - if (size > entry->size || size % BisAccessor::SectorAlignment != 0) { - std::abort(); - } - + STS_ASSERT(size <= entry->size); + STS_ASSERT((size % BisAccessor::SectorAlignment) == 0); return BisAccessor::Write(entry->offset, src, size); } diff --git a/source/updater/updater_bis_save.cpp b/source/updater/updater_bis_save.cpp index a0615876..07a5f568 100644 --- a/source/updater/updater_bis_save.cpp +++ b/source/updater/updater_bis_save.cpp @@ -33,9 +33,9 @@ namespace sts::updater { } Result BisSave::Initialize(void *work_buffer, size_t work_buffer_size) { - if (work_buffer_size < SaveSize || reinterpret_cast(work_buffer) & 0xFFF || work_buffer_size & 0x1FF) { - std::abort(); - } + STS_ASSERT(work_buffer_size >= SaveSize); + STS_ASSERT(util::IsAligned(reinterpret_cast(work_buffer), 0x1000)); + STS_ASSERT(util::IsAligned(work_buffer_size, 0x200)); R_TRY(this->accessor.Initialize()); this->save_buffer = work_buffer; diff --git a/source/updater/updater_paths.cpp b/source/updater/updater_paths.cpp index 794297b5..228d89c4 100644 --- a/source/updater/updater_paths.cpp +++ b/source/updater/updater_paths.cpp @@ -34,9 +34,7 @@ namespace sts::updater { constexpr const char *Package2PathA = "bip:/a/package2"; const char *ChooseCandidatePath(const char * const *candidates, size_t num_candidates) { - if (num_candidates == 0) { - std::abort(); - } + STS_ASSERT(num_candidates > 0); for (size_t i = 0; i < num_candidates; i++) { struct stat buf; diff --git a/source/util/util_compression.cpp b/source/util/util_compression.cpp index 5cc8b836..ba33163b 100644 --- a/source/util/util_compression.cpp +++ b/source/util/util_compression.cpp @@ -25,9 +25,8 @@ namespace sts::util { /* Compression utilities. */ int CompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size) { /* Size checks. */ - if (dst_size > std::numeric_limits::max() || src_size > std::numeric_limits::max()) { - std::abort(); - } + STS_ASSERT(dst_size <= std::numeric_limits::max()); + STS_ASSERT(src_size <= std::numeric_limits::max()); /* This is just a thin wrapper around LZ4. */ return LZ4_compress_default(reinterpret_cast(src), reinterpret_cast(dst), static_cast(src_size), static_cast(dst_size)); @@ -36,9 +35,8 @@ namespace sts::util { /* Decompression utilities. */ int DecompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size) { /* Size checks. */ - if (dst_size > std::numeric_limits::max() || src_size > std::numeric_limits::max()) { - std::abort(); - } + STS_ASSERT(dst_size <= std::numeric_limits::max()); + STS_ASSERT(src_size <= std::numeric_limits::max()); /* This is just a thin wrapper around LZ4. */ return LZ4_decompress_safe(reinterpret_cast(src), reinterpret_cast(dst), static_cast(src_size), static_cast(dst_size)); diff --git a/source/util/util_ini.cpp b/source/util/util_ini.cpp index a552b594..0bf4214c 100644 --- a/source/util/util_ini.cpp +++ b/source/util/util_ini.cpp @@ -50,9 +50,7 @@ namespace sts::util::ini { size_t try_read = std::min(size_t(num - 1), ctx->num_left); size_t actually_read; R_ASSERT(fsFileRead(ctx->f, ctx->offset, str, try_read, FS_READOPTION_NONE, &actually_read)); - if (actually_read != try_read) { - std::abort(); - } + STS_ASSERT(actually_read == try_read); /* Only "read" up to the first \n. */ size_t offset = actually_read;