diff --git a/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp b/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp index 592834889..f2a9dc917 100644 --- a/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp @@ -324,28 +324,20 @@ namespace ams::fatal { bool is_creport; CpuContext cpu_ctx; bool generate_error_report; - Event erpt_event; - Event battery_event; + os::Event *erpt_event; + os::Event *battery_event; size_t stack_dump_size; u64 stack_dump_base; u8 stack_dump[0x100]; u64 tls_address; u8 tls_dump[0x100]; - void ClearState() { - this->result = ResultSuccess(); - this->program_id = ncm::ProgramId::Invalid; - std::memset(this->proc_name, 0, sizeof(this->proc_name)); - this->is_creport = false; - std::memset(&this->cpu_ctx, 0, sizeof(this->cpu_ctx)); - this->generate_error_report = false; - std::memset(&this->erpt_event, 0, sizeof(this->erpt_event)); - std::memset(&this->battery_event, 0, sizeof(this->battery_event)); - this->stack_dump_size = 0; - this->stack_dump_base = 0; - std::memset(this->stack_dump, 0, sizeof(this->stack_dump)); - this->tls_address = 0; - std::memset(this->tls_dump, 0, sizeof(this->tls_dump)); + ThrowContext(os::Event *erpt, os::Event *bat) + : result(ResultSuccess()), program_id(), proc_name(), is_creport(), cpu_ctx(), generate_error_report(), + erpt_event(erpt), battery_event(bat), + stack_dump_size(), stack_dump_base(), stack_dump(), tls_address(), tls_dump() + { + /* ... */ } }; diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_file_storage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_file_storage.hpp index 2ef9cdf11..a6b3855a6 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_file_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_file_storage.hpp @@ -63,7 +63,7 @@ namespace ams::fs { s64 size; os::Mutex mutex; public: - constexpr explicit FileHandleStorage(FileHandle handle, bool close_file) : handle(handle), close_file(close_file), size(InvalidSize), mutex() { /* ... */ } + constexpr explicit FileHandleStorage(FileHandle handle, bool close_file) : handle(handle), close_file(close_file), size(InvalidSize), mutex(false) { /* ... */ } constexpr explicit FileHandleStorage(FileHandle handle) : FileHandleStorage(handle, false) { /* ... */ } virtual ~FileHandleStorage() override { diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp index ac3d5ae97..ce204dfbe 100644 --- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp +++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp @@ -70,7 +70,7 @@ namespace ams::kvdb { Path GetPath(const void *key, size_t key_size); Result GetKey(size_t *out_size, void *out_key, size_t max_out_size, const FileName &file_name); public: - FileKeyValueStore() { /* ... */ } + FileKeyValueStore() : lock(false) { /* ... */ } /* Basic accessors. */ Result Initialize(const char *dir); diff --git a/libraries/libstratosphere/include/stratosphere/lmem/impl/lmem_impl_common.hpp b/libraries/libstratosphere/include/stratosphere/lmem/impl/lmem_impl_common.hpp index 11158801f..4cc2240a0 100644 --- a/libraries/libstratosphere/include/stratosphere/lmem/impl/lmem_impl_common.hpp +++ b/libraries/libstratosphere/include/stratosphere/lmem/impl/lmem_impl_common.hpp @@ -85,7 +85,7 @@ namespace ams::lmem::impl { void *heap_start; void *heap_end; - os::Mutex mutex; + os::MutexType mutex; u8 option; ImplementationHeapHead impl_head; }; diff --git a/libraries/libstratosphere/include/stratosphere/lr/lr_location_resolver_manager_impl.hpp b/libraries/libstratosphere/include/stratosphere/lr/lr_location_resolver_manager_impl.hpp index daa198e06..c9da61cb1 100644 --- a/libraries/libstratosphere/include/stratosphere/lr/lr_location_resolver_manager_impl.hpp +++ b/libraries/libstratosphere/include/stratosphere/lr/lr_location_resolver_manager_impl.hpp @@ -28,7 +28,7 @@ namespace ams::lr { std::shared_ptr registered_location_resolver = nullptr; std::shared_ptr add_on_content_location_resolver = nullptr; - os::Mutex mutex; + os::Mutex mutex{false}; public: /* Actual commands. */ virtual Result OpenLocationResolver(sf::Out> out, ncm::StorageId storage_id) override; diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp index a6b75b883..94cdd4317 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp @@ -83,7 +83,7 @@ namespace ams::ncm { ContentMetaDatabaseRoot() { /* ... */ } }; private: - os::RecursiveMutex mutex; + os::Mutex mutex; bool initialized; ContentStorageRoot content_storage_roots[MaxContentStorageRoots]; ContentMetaDatabaseRoot content_meta_database_roots[MaxContentMetaDatabaseRoots]; @@ -91,7 +91,7 @@ namespace ams::ncm { u32 num_content_meta_entries; RightsIdCache rights_id_cache; public: - ContentManagerImpl() : initialized(false) { /* ... */ }; + ContentManagerImpl() : mutex(true), initialized(false) { /* ... */ }; ~ContentManagerImpl(); public: Result Initialize(const ContentManagerConfig &config); diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp index 7f93a7dad..4959f42fe 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp @@ -102,7 +102,7 @@ namespace ams::ncm { return result; } public: - InstallTaskBase() : data(), progress(), cancel_requested() { /* ... */ } + InstallTaskBase() : data(), progress(), progress_mutex(false), cancel_mutex(false), cancel_requested(), throughput_mutex(false) { /* ... */ } virtual ~InstallTaskBase() { /* ... */ }; public: virtual void Cancel(); diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id_cache.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id_cache.hpp index f29d01a8a..93dadc0f3 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id_cache.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id_cache.hpp @@ -37,7 +37,7 @@ namespace ams::ncm { u64 counter; os::Mutex mutex; public: - RightsIdCache() { + RightsIdCache() : mutex(false) { this->Invalidate(); } diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp index 061da55e1..57578afda 100644 --- a/libraries/libstratosphere/include/stratosphere/os.hpp +++ b/libraries/libstratosphere/include/stratosphere/os.hpp @@ -26,15 +26,13 @@ #include #include #include -#include +#include #include #include -#include #include #include #include #include #include #include -#include -#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable.hpp b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable.hpp new file mode 100644 index 000000000..33a34a706 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#if defined(ATMOSPHERE_OS_HORIZON) + #include +#else + #error "Unknown OS for ams::os::impl::InternalConditionVariableImpl" +#endif + +namespace ams::os::impl { + + class InternalConditionVariable { + private: + InternalConditionVariableImpl impl; + public: + constexpr InternalConditionVariable() : impl() { /* ... */ } + + constexpr void Initialize() { + this->impl.Initialize(); + } + + void Signal() { + this->impl.Signal(); + } + + void Broadcast() { + this->impl.Broadcast(); + } + + void Wait(InternalCriticalSection *cs) { + this->impl.Wait(cs); + } + + ConditionVariableStatus TimedWait(InternalCriticalSection *cs, const TimeoutHelper &timeout_helper) { + return this->impl.TimedWait(cs, timeout_helper); + } + }; + + using InternalConditionVariableStorage = TYPED_STORAGE(InternalConditionVariable); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable_impl.os.horizon.hpp b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable_impl.os.horizon.hpp new file mode 100644 index 000000000..1b3031b04 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable_impl.os.horizon.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include + +namespace ams::os::impl { + + class TimeoutHelper; + + class InternalConditionVariableImpl { + private: + u32 value; + public: + constexpr InternalConditionVariableImpl() : value(0) { /* ... */ } + + constexpr void Initialize() { + this->value = 0; + } + + void Signal(); + void Broadcast(); + + void Wait(InternalCriticalSection *cs); + ConditionVariableStatus TimedWait(InternalCriticalSection *cs, const TimeoutHelper &timeout_helper); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section.hpp b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section.hpp new file mode 100644 index 000000000..d1b0871f6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +#if defined(ATMOSPHERE_OS_HORIZON) + #include +#else + #error "Unknown OS for ams::os::impl::InternalCriticalSectionImpl" +#endif + +namespace ams::os::impl { + + class InternalCriticalSection { + private: + InternalCriticalSectionImpl impl; + public: + constexpr InternalCriticalSection() : impl() { /* ... */ } + + constexpr void Initialize() { this->impl.Initialize(); } + constexpr void Finalize() { this->impl.Finalize(); } + + void Enter() { return this->impl.Enter(); } + bool TryEnter() { return this->impl.TryEnter(); } + void Leave() { return this->impl.Leave(); } + + bool IsLockedByCurrentThread() const { return this->impl.IsLockedByCurrentThread(); } + + ALWAYS_INLINE void Lock() { return this->Enter(); } + ALWAYS_INLINE bool TryLock() { return this->TryEnter(); } + ALWAYS_INLINE void Unlock() { return this->Leave(); } + + ALWAYS_INLINE void lock() { return this->Lock(); } + ALWAYS_INLINE bool try_lock() { return this->TryLock(); } + ALWAYS_INLINE void unlock() { return this->Unlock(); } + + InternalCriticalSectionImpl *Get() { + return std::addressof(this->impl); + } + + const InternalCriticalSectionImpl *Get() const { + return std::addressof(this->impl); + } + }; + + using InternalCriticalSectionStorage = TYPED_STORAGE(InternalCriticalSection); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section_impl.os.horizon.hpp b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section_impl.os.horizon.hpp new file mode 100644 index 000000000..1b8dabfd2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section_impl.os.horizon.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::os::impl { + + class ReadWriteLockImpl; + class InternalConditionVariableImpl; + + class InternalCriticalSectionImpl { + private: + friend class ReadWriteLockImpl; + friend class InternalConditionVariableImpl; + private: + u32 thread_handle; + public: + constexpr InternalCriticalSectionImpl() : thread_handle(svc::InvalidHandle) { /* ... */ } + + constexpr void Initialize() { this->thread_handle = svc::InvalidHandle; } + constexpr void Finalize() { /* ... */} + + void Enter(); + bool TryEnter(); + void Leave(); + + bool IsLockedByCurrentThread() const; + + ALWAYS_INLINE void Lock() { return this->Enter(); } + ALWAYS_INLINE bool TryLock() { return this->TryEnter(); } + ALWAYS_INLINE void Unlock() { return this->Leave(); } + + ALWAYS_INLINE void lock() { return this->Lock(); } + ALWAYS_INLINE bool try_lock() { return this->TryLock(); } + ALWAYS_INLINE void unlock() { return this->Unlock(); } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_condition_variable.hpp b/libraries/libstratosphere/include/stratosphere/os/os_condition_variable.hpp new file mode 100644 index 000000000..d44383849 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_condition_variable.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include +#include + +namespace ams::os { + + class ConditionVariable { + NON_COPYABLE(ConditionVariable); + NON_MOVEABLE(ConditionVariable); + private: + ConditionVariableType cv; + public: + constexpr ConditionVariable() : cv{::ams::os::ConditionVariableType::State_Initialized, {{0}}} { /* ... */ } + + ~ConditionVariable() { FinalizeConditionVariable(std::addressof(this->cv)); } + + void Signal() { + SignalConditionVariable(std::addressof(this->cv)); + } + + void Broadcast() { + BroadcastConditionVariable(std::addressof(this->cv)); + } + + void Wait(ams::os::MutexType &mutex) { + WaitConditionVariable(std::addressof(this->cv), std::addressof(mutex)); + } + + ConditionVariableStatus TimedWait(ams::os::MutexType &mutex, TimeSpan timeout) { + return TimedWaitConditionVariable(std::addressof(this->cv), std::addressof(mutex), timeout); + } + + operator ConditionVariableType &() { + return this->cv; + } + + operator const ConditionVariableType &() const { + return this->cv; + } + + ConditionVariableType *GetBase() { + return std::addressof(this->cv); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_condition_variable_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_condition_variable_api.hpp new file mode 100644 index 000000000..83f1dd2d7 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_condition_variable_api.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::os { + + struct MutexType; + struct ConditionVariableType; + + void InitializeConditionVariable(ConditionVariableType *cv); + void FinalizeConditionVariable(ConditionVariableType *cv); + + void SignalConditionVariable(ConditionVariableType *cv); + void BroadcastConditionVariable(ConditionVariableType *cv); + + void WaitConditionVariable(ConditionVariableType *cv, MutexType *m); + ConditionVariableStatus TimedWaitConditionVariable(ConditionVariableType *cv, MutexType *m, TimeSpan timeout); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_condition_variable_common.hpp b/libraries/libstratosphere/include/stratosphere/os/os_condition_variable_common.hpp new file mode 100644 index 000000000..65fc8467a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_condition_variable_common.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::os { + + enum class ConditionVariableStatus { + TimedOut = 0, + Success = 1, + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/os/os_condition_variable_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_condition_variable_types.hpp new file mode 100644 index 000000000..7eeba689e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_condition_variable_types.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::os { + + struct ConditionVariableType { + enum State { + State_NotInitialized = 0, + State_Initialized = 1, + }; + + u8 state; + union { + s32 _arr[sizeof(impl::InternalConditionVariableStorage) / sizeof(s32)]; + impl::InternalConditionVariableStorage _storage; + }; + }; + static_assert(std::is_trivial::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp b/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp deleted file mode 100644 index d2b05bd96..000000000 --- a/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include "os_mutex.hpp" - -namespace ams::os { - - enum class ConditionVariableStatus { - TimedOut = 0, - Success = 1, - }; - - class ConditionVariable { - NON_COPYABLE(ConditionVariable); - NON_MOVEABLE(ConditionVariable); - private: - CondVar cv; - public: - constexpr ConditionVariable() : cv() { /* ... */ } - - ConditionVariableStatus TimedWait(::Mutex *m, u64 timeout) { - if (timeout > 0) { - /* Abort on any error other than timed out/success. */ - R_TRY_CATCH(condvarWaitTimeout(&this->cv, m, timeout)) { - R_CATCH(svc::ResultTimedOut) { return ConditionVariableStatus::TimedOut; } - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - - return ConditionVariableStatus::Success; - } - return ConditionVariableStatus::TimedOut; - } - - void Wait(::Mutex *m) { - R_ABORT_UNLESS(condvarWait(&this->cv, m)); - } - - ConditionVariableStatus TimedWait(os::Mutex *m, u64 timeout) { - return this->TimedWait(m->GetMutex(), timeout); - } - - void Wait(os::Mutex *m) { - return this->Wait(m->GetMutex()); - } - - void Signal() { - condvarWakeOne(&this->cv); - } - - void Broadcast() { - condvarWakeAll(&this->cv); - } - }; - -} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/os/os_event.hpp b/libraries/libstratosphere/include/stratosphere/os/os_event.hpp index 5c8254465..4bad3879c 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_event.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_event.hpp @@ -15,39 +15,58 @@ */ #pragma once -#include "os_mutex.hpp" -#include "os_condvar.hpp" -#include "os_timeout_helper.hpp" +#include +#include +#include +#include namespace ams::os { - namespace impl { - - class WaitableObjectList; - class WaitableHolderOfEvent; - - } - class Event { - friend class impl::WaitableHolderOfEvent; NON_COPYABLE(Event); NON_MOVEABLE(Event); private: - util::TypedStorage waitable_object_list_storage; - Mutex lock; - ConditionVariable cv; - u64 counter = 0; - bool auto_clear; - bool signaled; + EventType event; public: - Event(bool a = true, bool s = false); - ~Event(); + explicit Event(EventClearMode clear_mode) { + InitializeEvent(std::addressof(this->event), false, clear_mode); + } - void Signal(); - void Reset(); - void Wait(); - bool TryWait(); - bool TimedWait(u64 ns); + ~Event() { + FinalizeEvent(std::addressof(this->event)); + } + + void Wait() { + return WaitEvent(std::addressof(this->event)); + } + + bool TryWait() { + return TryWaitEvent(std::addressof(this->event)); + } + + bool TimedWait(TimeSpan timeout) { + return TimedWaitEvent(std::addressof(this->event), timeout); + } + + void Signal() { + return SignalEvent(std::addressof(this->event)); + } + + void Clear() { + return ClearEvent(std::addressof(this->event)); + } + + operator EventType &() { + return this->event; + } + + operator const EventType &() const { + return this->event; + } + + EventType *GetBase() { + return std::addressof(this->event); + } }; } diff --git a/libraries/libstratosphere/include/stratosphere/os/os_event_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_event_api.hpp new file mode 100644 index 000000000..0f7b75016 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_event_api.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::os { + + struct EventType; + struct WaitableHolderType; + + void InitializeEvent(EventType *event, bool signaled, EventClearMode clear_mode); + void FinalizeEvent(EventType *event); + + void SignalEvent(EventType *event); + void WaitEvent(EventType *event); + bool TryWaitEvent(EventType *event); + bool TimedWaitEvent(EventType *event, TimeSpan timeout); + void ClearEvent(EventType *event); + + void InitializeWaitableHolder(WaitableHolderType *waitable_holder, EventType *event); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_event_common.hpp b/libraries/libstratosphere/include/stratosphere/os/os_event_common.hpp new file mode 100644 index 000000000..625c435c5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_event_common.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::os { + + enum EventClearMode { + EventClearMode_ManualClear = 0, + EventClearMode_AutoClear = 1, + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/os/os_event_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_event_types.hpp new file mode 100644 index 000000000..7b61a5cea --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_event_types.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include + +namespace ams::os { + + namespace impl { + + class WaitableObjectList; + + } + + struct EventType { + enum State { + State_NotInitialized = 0, + State_Initialized = 1, + }; + + util::TypedStorage waitable_object_list_storage; + bool signaled; + bool initially_signaled; + u8 clear_mode; + u8 state; + u32 broadcast_counter_low; + u32 broadcast_counter_high; + + impl::InternalCriticalSectionStorage cs_event; + impl::InternalConditionVariableStorage cv_signaled; + }; + static_assert(std::is_trivial::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_interrupt_event.hpp b/libraries/libstratosphere/include/stratosphere/os/os_interrupt_event.hpp index 94a970f6f..8e1b6c0da 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_interrupt_event.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_interrupt_event.hpp @@ -15,35 +15,56 @@ */ #pragma once -#include "os_managed_handle.hpp" +#include +#include +#include +#include +#include namespace ams::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; + InterruptEventType event; public: - InterruptEvent() : auto_clear(true), is_initialized(false) { } - InterruptEvent(u32 interrupt_id, bool autoclear = true); + explicit InterruptEvent(InterruptName name, EventClearMode clear_mode) { + InitializeInterruptEvent(std::addressof(this->event), name, clear_mode); + } - Result Initialize(u32 interrupt_id, bool autoclear = true); - void Finalize(); + ~InterruptEvent() { + FinalizeInterruptEvent(std::addressof(this->event)); + } - void Reset(); - void Wait(); - bool TryWait(); - bool TimedWait(u64 ns); + void Wait() { + return WaitInterruptEvent(std::addressof(this->event)); + } + + bool TryWait() { + return TryWaitInterruptEvent(std::addressof(this->event)); + } + + bool TimedWait(TimeSpan timeout) { + return TimedWaitInterruptEvent(std::addressof(this->event), timeout); + } + + void Clear() { + return ClearInterruptEvent(std::addressof(this->event)); + } + + operator InterruptEventType &() { + return this->event; + } + + operator const InterruptEventType &() const { + return this->event; + } + + InterruptEventType *GetBase() { + return std::addressof(this->event); + } }; + } diff --git a/libraries/libstratosphere/include/stratosphere/os/os_interrupt_event_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_interrupt_event_api.hpp new file mode 100644 index 000000000..b2dcf794a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_interrupt_event_api.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::os { + + struct InterruptEventType; + struct WaitableHolderType; + + void InitializeInterruptEvent(InterruptEventType *event, InterruptName name, EventClearMode clear_mode); + void FinalizeInterruptEvent(InterruptEventType *event); + + void WaitInterruptEvent(InterruptEventType *event); + bool TryWaitInterruptEvent(InterruptEventType *event); + bool TimedWaitInterruptEvent(InterruptEventType *event, TimeSpan timeout); + void ClearInterruptEvent(InterruptEventType *event); + + void InitializeWaitableHolder(WaitableHolderType *waitable_holder, InterruptEventType *event); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_interrupt_event_common.hpp b/libraries/libstratosphere/include/stratosphere/os/os_interrupt_event_common.hpp new file mode 100644 index 000000000..99e710976 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_interrupt_event_common.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::os { + + using InterruptName = s32; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/os/os_interrupt_event_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_interrupt_event_types.hpp new file mode 100644 index 000000000..685832096 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_interrupt_event_types.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include + +namespace ams::os { + + namespace impl { + + class WaitableObjectList; + class InterruptEventImpl; + + } + + struct InterruptEventType { + enum State { + State_NotInitialized = 0, + State_Initialized = 1, + }; + + util::TypedStorage waitable_object_list_storage; + + u8 clear_mode; + u8 state; + + util::TypedStorage impl; + }; + static_assert(std::is_trivial::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_message_queue.hpp b/libraries/libstratosphere/include/stratosphere/os/os_message_queue.hpp index 06ea9be7d..d7f6205e6 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_message_queue.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_message_queue.hpp @@ -15,75 +15,87 @@ */ #pragma once -#include "os_mutex.hpp" -#include "os_condvar.hpp" +#include +#include +#include namespace ams::os { - namespace impl { - - class WaitableObjectList; - - template - class WaitableHolderOfMessageQueue; - - } - class MessageQueue { - template - friend class impl::WaitableHolderOfMessageQueue; NON_COPYABLE(MessageQueue); NON_MOVEABLE(MessageQueue); private: - util::TypedStorage waitlist_not_empty; - util::TypedStorage waitlist_not_full; - Mutex queue_lock; - ConditionVariable cv_not_full; - ConditionVariable cv_not_empty; - std::unique_ptr buffer; - size_t capacity; - - size_t count; - size_t offset; - private: - constexpr inline bool IsFull() const { - return this->count >= this->capacity; - } - - constexpr inline bool IsEmpty() const { - return this->count == 0; - } - - void SendInternal(uintptr_t data); - void SendNextInternal(uintptr_t data); - uintptr_t ReceiveInternal(); - uintptr_t PeekInternal(); + MessageQueueType mq; public: - MessageQueue(std::unique_ptr buf, size_t c); - ~MessageQueue(); + explicit MessageQueue(uintptr_t *buf, size_t count) { + InitializeMessageQueue(std::addressof(this->mq), buf, count); + } - /* For convenience. */ - MessageQueue(size_t c) : MessageQueue(std::make_unique(c), c) { /* ... */ } + ~MessageQueue() { FinalizeMessageQueue(std::addressof(this->mq)); } /* Sending (FIFO functionality) */ - void Send(uintptr_t data); - bool TrySend(uintptr_t data); - bool TimedSend(uintptr_t data, u64 timeout); + void Send(uintptr_t data) { + return SendMessageQueue(std::addressof(this->mq), data); + } + + bool TrySend(uintptr_t data) { + return TrySendMessageQueue(std::addressof(this->mq), data); + } + + bool TimedSend(uintptr_t data, TimeSpan timeout) { + return TimedSendMessageQueue(std::addressof(this->mq), data, timeout); + } /* Sending (LIFO functionality) */ - void SendNext(uintptr_t data); - bool TrySendNext(uintptr_t data); - bool TimedSendNext(uintptr_t data, u64 timeout); + void SendNext(uintptr_t data) { + return SendNextMessageQueue(std::addressof(this->mq), data); + } + + bool TrySendNext(uintptr_t data) { + return TrySendNextMessageQueue(std::addressof(this->mq), data); + } + + bool TimedSendNext(uintptr_t data, TimeSpan timeout) { + return TimedSendNextMessageQueue(std::addressof(this->mq), data, timeout); + } /* Receive functionality */ - void Receive(uintptr_t *out); - bool TryReceive(uintptr_t *out); - bool TimedReceive(uintptr_t *out, u64 timeout); + void Receive(uintptr_t *out) { + return ReceiveMessageQueue(out, std::addressof(this->mq)); + } + + bool TryReceive(uintptr_t *out) { + return TryReceiveMessageQueue(out, std::addressof(this->mq)); + } + + bool TimedReceive(uintptr_t *out, TimeSpan timeout) { + return TimedReceiveMessageQueue(out, std::addressof(this->mq), timeout); + } /* Peek functionality */ - void Peek(uintptr_t *out); - bool TryPeek(uintptr_t *out); - bool TimedPeek(uintptr_t *out, u64 timeout); + void Peek(uintptr_t *out) const { + return PeekMessageQueue(out, std::addressof(this->mq)); + } + + bool TryPeek(uintptr_t *out) const { + return TryPeekMessageQueue(out, std::addressof(this->mq)); + } + + bool TimedPeek(uintptr_t *out, TimeSpan timeout) const { + return TimedPeekMessageQueue(out, std::addressof(this->mq), timeout); + } + + operator MessageQueueType &() { + return this->mq; + } + + operator const MessageQueueType &() const { + return this->mq; + } + + MessageQueueType *GetBase() { + return std::addressof(this->mq); + } }; } diff --git a/libraries/libstratosphere/include/stratosphere/os/os_message_queue_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_message_queue_api.hpp new file mode 100644 index 000000000..51fc0a5c2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_message_queue_api.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::os { + + struct MessageQueueType; + struct WaitableHolderType; + + void InitializeMessageQueue(MessageQueueType *mq, uintptr_t *buffer, size_t count); + void FinalizeMessageQueue(MessageQueueType *mq); + + /* Sending (FIFO functionality) */ + void SendMessageQueue(MessageQueueType *mq, uintptr_t data); + bool TrySendMessageQueue(MessageQueueType *mq, uintptr_t data); + bool TimedSendMessageQueue(MessageQueueType *mq, uintptr_t data, TimeSpan timeout); + + /* Sending (LIFO functionality) */ + void SendNextMessageQueue(MessageQueueType *mq, uintptr_t data); + bool TrySendNextMessageQueue(MessageQueueType *mq, uintptr_t data); + bool TimedSendNextMessageQueue(MessageQueueType *mq, uintptr_t data, TimeSpan timeout); + + /* Receive functionality */ + void ReceiveMessageQueue(uintptr_t *out, MessageQueueType *mq); + bool TryReceiveMessageQueue(uintptr_t *out, MessageQueueType *mq); + bool TimedReceiveMessageQueue(uintptr_t *out, MessageQueueType *mq, TimeSpan timeout); + + /* Peek functionality */ + void PeekMessageQueue(uintptr_t *out, const MessageQueueType *mq); + bool TryPeekMessageQueue(uintptr_t *out, const MessageQueueType *mq); + bool TimedPeekMessageQueue(uintptr_t *out, const MessageQueueType *mq, TimeSpan timeout); + + void InitializeWaitableHolder(WaitableHolderType *waitable_holder, MessageQueueType *event, MessageQueueWaitType wait_type); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_message_queue_common.hpp b/libraries/libstratosphere/include/stratosphere/os/os_message_queue_common.hpp new file mode 100644 index 000000000..4d135c0a2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_message_queue_common.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::os { + + enum class MessageQueueWaitType { + ForNotFull = 1, + ForNotEmpty = 2, + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/os/os_message_queue_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_message_queue_types.hpp new file mode 100644 index 000000000..7c7789c64 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_message_queue_types.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include + +namespace ams::os { + + namespace impl { + + class WaitableObjectList; + + } + + struct MessageQueueType { + enum State { + State_NotInitialized = 0, + State_Initialized = 1, + }; + + util::TypedStorage waitlist_not_full; + util::TypedStorage waitlist_not_empty; + uintptr_t *buffer; + s32 capacity; + s32 count; + s32 offset; + u8 state; + + mutable impl::InternalCriticalSectionStorage cs_queue; + mutable impl::InternalConditionVariableStorage cv_not_full; + mutable impl::InternalConditionVariableStorage cv_not_empty; + }; + static_assert(std::is_trivial::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_mutex.hpp b/libraries/libstratosphere/include/stratosphere/os/os_mutex.hpp index 5fdec6d43..a5c4265a1 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_mutex.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_mutex.hpp @@ -15,82 +15,60 @@ */ #pragma once -#include "os_common_types.hpp" +#include +#include +#include namespace ams::os { - class ConditionVariable; - class Mutex { NON_COPYABLE(Mutex); NON_MOVEABLE(Mutex); - friend class ams::os::ConditionVariable; private: - ::Mutex m; - private: - constexpr ::Mutex *GetMutex() { - return &this->m; - } + MutexType mutex; public: - constexpr Mutex() : m() { /* ... */ } + constexpr explicit Mutex(bool recursive) : mutex{::ams::os::MutexType::State_Initialized, recursive, 0, 0, nullptr, {{0}}} { /* ... */ } + + ~Mutex() { FinalizeMutex(std::addressof(this->mutex)); } void lock() { - mutexLock(GetMutex()); + return LockMutex(std::addressof(this->mutex)); } void unlock() { - mutexUnlock(GetMutex()); + return UnlockMutex(std::addressof(this->mutex)); } bool try_lock() { - return mutexTryLock(GetMutex()); + return TryLockMutex(std::addressof(this->mutex)); } - void Lock() { - lock(); + bool IsLockedByCurrentThread() const { + return IsMutexLockedByCurrentThread(std::addressof(this->mutex)); } - void Unlock() { - unlock(); + ALWAYS_INLINE void Lock() { + return this->lock(); } - bool TryLock() { - return try_lock(); - } - }; - - class RecursiveMutex { - private: - ::RMutex m; - private: - constexpr ::RMutex *GetMutex() { - return &this->m; - } - public: - constexpr RecursiveMutex() : m() { /* ... */ } - - void lock() { - rmutexLock(GetMutex()); + ALWAYS_INLINE void Unlock() { + return this->unlock(); } - void unlock() { - rmutexUnlock(GetMutex()); + ALWAYS_INLINE bool TryLock() { + return this->try_lock(); } - bool try_lock() { - return rmutexTryLock(GetMutex()); + operator MutexType &() { + return this->mutex; } - void Lock() { - lock(); + operator const MutexType &() const { + return this->mutex; } - void Unlock() { - unlock(); - } - - bool TryLock() { - return try_lock(); + MutexType *GetBase() { + return std::addressof(this->mutex); } }; diff --git a/libraries/libstratosphere/include/stratosphere/os/os_mutex_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_mutex_api.hpp new file mode 100644 index 000000000..84ce0dd98 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_mutex_api.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::os { + + struct MutexType; + + void InitializeMutex(MutexType *mutex, bool recursive, int lock_level); + void FinalizeMutex(MutexType *mutex); + + void LockMutex(MutexType *mutex); + bool TryLockMutex(MutexType *mutex); + void UnlockMutex(MutexType *mutex); + + bool IsMutexLockedByCurrentThread(const MutexType *mutex); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_mutex_common.hpp b/libraries/libstratosphere/include/stratosphere/os/os_mutex_common.hpp new file mode 100644 index 000000000..b6ef8962a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_mutex_common.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::os { + + constexpr inline s32 MutexLockLevelMin = 1; + constexpr inline s32 MutexLockLevelMax = BITSIZEOF(s32) - 1; + constexpr inline s32 MutexLockLevelInitial = 0; + + constexpr inline s32 MutexRecursiveLockCountMax = (1 << BITSIZEOF(u16)) - 1; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/os/os_mutex_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_mutex_types.hpp new file mode 100644 index 000000000..5af5549b3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_mutex_types.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::os { + + struct ThreadType; + + struct MutexType { + enum State { + State_NotInitialized = 0, + State_Initialized = 1, + }; + + u8 state; + bool is_recursive; + s32 lock_level; + s32 nest_count; + ThreadType *owner_thread; + union { + s32 _arr[sizeof(impl::InternalCriticalSectionStorage) / sizeof(s32)]; + impl::InternalCriticalSectionStorage _storage; + }; + }; + static_assert(std::is_trivial::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_semaphore.hpp b/libraries/libstratosphere/include/stratosphere/os/os_semaphore.hpp index eb43aada2..a00d620c2 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_semaphore.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_semaphore.hpp @@ -13,43 +13,58 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #pragma once -#include "os_mutex.hpp" -#include "os_condvar.hpp" +#include +#include namespace ams::os { - namespace impl { - - class WaitableObjectList; - class WaitableHolderOfSemaphore; - - } - class Semaphore { - friend class impl::WaitableHolderOfSemaphore; NON_COPYABLE(Semaphore); NON_MOVEABLE(Semaphore); private: - util::TypedStorage waitlist; - os::Mutex mutex; - os::ConditionVariable condvar; - int count; - int max_count; + SemaphoreType sema; public: - explicit Semaphore(int c, int mc); - ~Semaphore(); + explicit Semaphore(s32 count, s32 max_count) { + InitializeSemaphore(std::addressof(this->sema), count, max_count); + } - void Acquire(); - bool TryAcquire(); - bool TimedAcquire(u64 timeout); + ~Semaphore() { FinalizeSemaphore(std::addressof(this->sema)); } - void Release(); - void Release(int count); + void Acquire() { + return os::AcquireSemaphore(std::addressof(this->sema)); + } - constexpr inline int GetCurrentCount() const { - return this->count; + bool TryAcquire() { + return os::TryAcquireSemaphore(std::addressof(this->sema)); + } + + bool TimedAcquire(TimeSpan timeout) { + return os::TimedAcquireSemaphore(std::addressof(this->sema), timeout); + } + + void Release() { + return os::ReleaseSemaphore(std::addressof(this->sema)); + } + + void Release(s32 count) { + return os::ReleaseSemaphore(std::addressof(this->sema), count); + } + + s32 GetCurrentCount() const { + return os::GetCurrentSemaphoreCount(std::addressof(this->sema)); + } + + operator SemaphoreType &() { + return this->sema; + } + + operator const SemaphoreType &() const { + return this->sema; + } + + SemaphoreType *GetBase() { + return std::addressof(this->sema); } }; diff --git a/libraries/libstratosphere/include/stratosphere/os/os_semaphore_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_semaphore_api.hpp new file mode 100644 index 000000000..bd761911a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_semaphore_api.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::os { + + struct SemaphoreType; + struct WaitableHolderType; + + void InitializeSemaphore(SemaphoreType *sema, s32 count, s32 max_count); + void FinalizeSemaphore(SemaphoreType *sema); + + void AcquireSemaphore(SemaphoreType *sema); + bool TryAcquireSemaphore(SemaphoreType *sema); + bool TimedAcquireSemaphore(SemaphoreType *sema, TimeSpan timeout); + + void ReleaseSemaphore(SemaphoreType *sema); + void ReleaseSemaphore(SemaphoreType *sema, s32 count); + + s32 GetCurrentSemaphoreCount(const SemaphoreType *sema); + + void InitializeWaitableHolder(WaitableHolderType *waitable_holder, SemaphoreType *sema); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_semaphore_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_semaphore_types.hpp new file mode 100644 index 000000000..37d4b4d21 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_semaphore_types.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include + +namespace ams::os { + + namespace impl { + + class WaitableObjectList; + + } + + struct SemaphoreType { + enum State { + State_NotInitialized = 0, + State_Initialized = 1, + }; + + util::TypedStorage waitlist; + u8 state; + int count; + int max_count; + + impl::InternalCriticalSectionStorage cs_sema; + impl::InternalConditionVariableStorage cv_not_zero; + }; + static_assert(std::is_trivial::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_system_event.hpp b/libraries/libstratosphere/include/stratosphere/os/os_system_event.hpp index 7ceca197c..e46d9ca85 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_system_event.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_system_event.hpp @@ -15,66 +15,99 @@ */ #pragma once -#include "os_event.hpp" +#include +#include +#include namespace ams::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; + SystemEventType system_event; 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; + SystemEvent() { + this->system_event.state = SystemEventType::State_NotInitialized; } - void Signal(); - void Reset(); - void Wait(); - bool TryWait(); - bool TimedWait(u64 ns); + explicit SystemEvent(EventClearMode clear_mode, bool inter_process) { + R_ABORT_UNLESS(CreateSystemEvent(std::addressof(this->system_event), clear_mode, inter_process)); + } + + explicit SystemEvent(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, EventClearMode clear_mode) { + AttachSystemEvent(std::addressof(this->system_event), read_handle, manage_read_handle, write_handle, manage_write_handle, clear_mode); + } + + ~SystemEvent() { + if (this->system_event.state == SystemEventType::State_NotInitialized) { + return; + } + DestroySystemEvent(std::addressof(this->system_event)); + } + + void Attach(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, EventClearMode clear_mode) { + AMS_ABORT_UNLESS(this->system_event.state == SystemEventType::State_NotInitialized); + return AttachSystemEvent(std::addressof(this->system_event), read_handle, manage_read_handle, write_handle, manage_write_handle, clear_mode); + } + + void AttachReadableHandle(Handle read_handle, bool manage_read_handle, EventClearMode clear_mode) { + AMS_ABORT_UNLESS(this->system_event.state == SystemEventType::State_NotInitialized); + return AttachReadableHandleToSystemEvent(std::addressof(this->system_event), read_handle, manage_read_handle, clear_mode); + } + + void AttachWritableHandle(Handle write_handle, bool manage_write_handle, EventClearMode clear_mode) { + AMS_ABORT_UNLESS(this->system_event.state == SystemEventType::State_NotInitialized); + return AttachWritableHandleToSystemEvent(std::addressof(this->system_event), write_handle, manage_write_handle, clear_mode); + } + + Handle DetachReadableHandle() { + return DetachReadableHandleOfSystemEvent(std::addressof(this->system_event)); + } + + Handle DetachWritableHandle() { + return DetachWritableHandleOfSystemEvent(std::addressof(this->system_event)); + } + + void Wait() { + return WaitSystemEvent(std::addressof(this->system_event)); + } + + bool TryWait() { + return TryWaitSystemEvent(std::addressof(this->system_event)); + } + + bool TimedWait(TimeSpan timeout) { + return TimedWaitSystemEvent(std::addressof(this->system_event), timeout); + } + + void Signal() { + return SignalSystemEvent(std::addressof(this->system_event)); + } + + void Clear() { + return ClearSystemEvent(std::addressof(this->system_event)); + } + + Handle GetReadableHandle() const { + return GetReadableHandleOfSystemEvent(std::addressof(this->system_event)); + } + + Handle GetWritableHandle() const { + return GetWritableHandleOfSystemEvent(std::addressof(this->system_event)); + } + + operator SystemEventType &() { + return this->system_event; + } + + operator const SystemEventType &() const { + return this->system_event; + } + + SystemEventType *GetBase() { + return std::addressof(this->system_event); + } }; } diff --git a/libraries/libstratosphere/include/stratosphere/os/os_system_event_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_system_event_api.hpp new file mode 100644 index 000000000..d31c0e044 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_system_event_api.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::os { + + struct SystemEventType; + struct WaitableHolderType; + + Result CreateSystemEvent(SystemEventType *event, EventClearMode clear_mode, bool inter_process); + void DestroySystemEvent(SystemEventType *event); + + void AttachSystemEvent(SystemEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode); + void AttachReadableHandleToSystemEvent(SystemEventType *event, Handle read_handle, bool manage_read_handle, EventClearMode clear_mode); + void AttachWritableHandleToSystemEvent(SystemEventType *event, Handle write_handle, bool manage_write_handle, EventClearMode clear_mode); + + Handle DetachReadableHandleOfSystemEvent(SystemEventType *event); + Handle DetachWritableHandleOfSystemEvent(SystemEventType *event); + + Handle GetReadableHandleOfSystemEvent(const SystemEventType *event); + Handle GetWritableHandleOfSystemEvent(const SystemEventType *event); + + void SignalSystemEvent(SystemEventType *event); + void WaitSystemEvent(SystemEventType *event); + bool TryWaitSystemEvent(SystemEventType *event); + bool TimedWaitSystemEvent(SystemEventType *event, TimeSpan timeout); + void ClearSystemEvent(SystemEventType *event); + + void InitializeWaitableHolder(WaitableHolderType *waitable_holder, SystemEventType *event); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_system_event_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_system_event_types.hpp new file mode 100644 index 000000000..cff31bc0e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_system_event_types.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::os { + + namespace impl { + + struct InterProcessEventType { + enum State { + State_NotInitialized = 0, + State_Initialized = 1, + }; + + util::TypedStorage waitable_object_list_storage; + + bool auto_clear; + u8 state; + bool is_readable_handle_managed; + bool is_writable_handle_managed; + Handle readable_handle; + Handle writable_handle; + }; + static_assert(std::is_trivial::value); + + } + + struct SystemEventType { + enum State { + State_NotInitialized = 0, + State_InitializedAsEvent = 1, + State_InitializedAsInterProcessEvent = 2, + }; + + union { + EventType event; + impl::InterProcessEventType inter_process_event; + }; + + u8 state; + }; + static_assert(std::is_trivial::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_thread.hpp b/libraries/libstratosphere/include/stratosphere/os/os_thread.hpp index 2e24d57be..909a98517 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_thread.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_thread.hpp @@ -13,107 +13,9 @@ * 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_memory_common.hpp" +#include +#include +#include +#include -namespace ams::os { - - class Thread { - NON_COPYABLE(Thread); - NON_MOVEABLE(Thread); - private: - ::Thread thr; - public: - constexpr Thread() : thr{} { /* ... */ } - - Result Initialize(ThreadFunc entry, void *arg, void *stack_mem, size_t stack_sz, int prio, int cpuid = -2) { - return threadCreate(&this->thr, entry, arg, stack_mem, stack_sz, prio, cpuid); - } - - Result Initialize(ThreadFunc entry, void *arg, size_t stack_sz, int prio, int cpuid = -2) { - return threadCreate(&this->thr, entry, arg, nullptr, stack_sz, prio, cpuid); - } - - Handle GetHandle() const { - return this->thr.handle; - } - - Result Start() { - return threadStart(&this->thr); - } - - Result Wait() { - return threadWaitForExit(&this->thr); - } - - Result Join() { - R_TRY(threadWaitForExit(&this->thr)); - R_TRY(threadClose(&this->thr)); - return ResultSuccess(); - } - - Result CancelSynchronization() { - return svcCancelSynchronization(this->thr.handle); - } - }; - - template - class StaticThread { - NON_COPYABLE(StaticThread); - NON_MOVEABLE(StaticThread); - static_assert(util::IsAligned(StackSize, os::MemoryPageSize), "StaticThread must have aligned resource size"); - private: - alignas(os::MemoryPageSize) u8 stack_mem[StackSize]; - ::Thread thr; - public: - constexpr StaticThread() : stack_mem{}, thr{} { /* ... */ } - - constexpr StaticThread(ThreadFunc entry, void *arg, int prio, int cpuid = -2) : StaticThread() { - R_ABORT_UNLESS(this->Initialize(entry, arg, prio, cpuid)); - } - - Result Initialize(ThreadFunc entry, void *arg, int prio, int cpuid = -2) { - return threadCreate(&this->thr, entry, arg, this->stack_mem, StackSize, prio, cpuid); - } - - Handle GetHandle() const { - return this->thr.handle; - } - - Result Start() { - return threadStart(&this->thr); - } - - Result Wait() { - return threadWaitForExit(&this->thr); - } - - Result Join() { - R_TRY(threadWaitForExit(&this->thr)); - R_TRY(threadClose(&this->thr)); - return ResultSuccess(); - } - - Result CancelSynchronization() { - return svcCancelSynchronization(this->thr.handle); - } - }; - - ALWAYS_INLINE s32 GetCurrentThreadPriority() { - s32 prio; - R_ABORT_UNLESS(svcGetThreadPriority(&prio, CUR_THREAD_HANDLE)); - return prio; - } - - /* TODO: ThreadManager? */ - ALWAYS_INLINE s32 GetCurrentProcessorNumber() { - return svcGetCurrentProcessorNumber(); - } - - ALWAYS_INLINE s32 GetCurrentCoreNumber() { - return GetCurrentProcessorNumber(); - } - -} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_thread_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_thread_api.hpp new file mode 100644 index 000000000..3d7c42166 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_thread_api.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::os { + + struct ThreadType; + struct WaitableHolderType; + + Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority, s32 ideal_core); + Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority); + void DestroyThread(ThreadType *thread); + void StartThread(ThreadType *thread); + + ThreadType *GetCurrentThread(); + + void WaitThread(ThreadType *thread); + bool TryWaitThread(ThreadType *thread); + + void YieldThread(); + void SleepThread(TimeSpan time); + + s32 SuspendThread(ThreadType *thread); + s32 ResumeThread(ThreadType *thread); + s32 GetThreadSuspendCount(const ThreadType *thread); + + void CancelThreadSynchronization(ThreadType *Thread); + + /* TODO: void GetThreadContext(ThreadContextInfo *out_context, const ThreadType *thread); */ + + s32 ChangeThreadPriority(ThreadType *thread, s32 priority); + s32 GetThreadPriority(const ThreadType *thread); + s32 GetThreadCurrentPriority(const ThreadType *thread); + + void SetThreadName(ThreadType *thread, const char *name); + void SetThreadNamePointer(ThreadType *thread, const char *name); + const char *GetThreadNamePointer(const ThreadType *thread); + + s32 GetCurrentProcessorNumber(); + s32 GetCurrentCoreNumber(); + + void SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask); + void GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread); + + u64 GetThreadAvailableCoreMask(); + + ThreadId GetThreadId(const ThreadType *thread); + + void InitializeWaitableHolder(WaitableHolderType *holder, ThreadType *thread); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_thread_common.hpp b/libraries/libstratosphere/include/stratosphere/os/os_thread_common.hpp new file mode 100644 index 000000000..8e634dc9e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_thread_common.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::os { + + constexpr inline s32 ThreadSuspendCountMax = 127; + + constexpr inline s32 ThreadNameLengthMax = 0x20; + + constexpr inline s32 ThreadPriorityRangeSize = 32; + constexpr inline s32 HighestThreadPriority = 0; + constexpr inline s32 DefaultThreadPriority = ThreadPriorityRangeSize / 2; + constexpr inline s32 LowestThreadPriority = ThreadPriorityRangeSize - 1; + + constexpr inline s32 LowestSystemThreadPriority = 35; + constexpr inline s32 HighestSystemThreadPriority = -12; + + constexpr inline size_t StackGuardAlignment = 4_KB; + constexpr inline size_t ThreadStackAlignment = 4_KB; + + using ThreadFunction = void (*)(void *); + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/os/os_thread_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_thread_types.hpp new file mode 100644 index 000000000..c687add16 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_thread_types.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace ams::os { + + namespace impl { + + class WaitableObjectList; + + } + + using ThreadId = u64; + + /* TODO */ + using ThreadImpl = ::Thread; + + struct ThreadType { + enum State { + State_NotInitialized = 0, + State_Initialized = 1, + State_DestroyedBeforeStarted = 2, + State_Started = 3, + State_Terminated = 4, + }; + + TYPED_STORAGE(util::IntrusiveListNode) all_threads_node; + util::TypedStorage waitlist; + uintptr_t reserved[4]; + u8 state; + u8 suspend_count; + s32 base_priority; + char name_buffer[ThreadNameLengthMax]; + const char *name_pointer; + ThreadId thread_id; + void *stack; + size_t stack_size; + ThreadFunction function; + void *argument; + mutable impl::InternalCriticalSectionStorage cs_thread; + mutable impl::InternalConditionVariableStorage cv_thread; + + ThreadImpl *thread_impl; + ThreadImpl thread_impl_storage; + }; + static_assert(std::is_trivial::value); + + constexpr inline s32 IdealCoreDontCare = -1; + constexpr inline s32 IdealCoreUseDefault = -2; + constexpr inline s32 IdealCoreNoUpdate = -3; + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_timeout_helper.hpp b/libraries/libstratosphere/include/stratosphere/os/os_timeout_helper.hpp deleted file mode 100644 index b69d475e1..000000000 --- a/libraries/libstratosphere/include/stratosphere/os/os_timeout_helper.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include "os_common_types.hpp" - -namespace ams::os { - - class TimeoutHelper { - private: - u64 end_tick; - public: - TimeoutHelper(u64 ns) { - /* Special case zero-time timeouts. */ - if (ns == 0) { - end_tick = 0; - return; - } - - u64 cur_tick = armGetSystemTick(); - this->end_tick = cur_tick + NsToTick(ns) + 1; - } - - static constexpr inline u64 NsToTick(u64 ns) { - return (ns * 12) / 625; - } - - static constexpr inline u64 TickToNs(u64 tick) { - return (tick * 625) / 12; - } - - inline bool TimedOut() const { - if (this->end_tick == 0) { - return true; - } - - return armGetSystemTick() >= this->end_tick; - } - - inline u64 NsUntilTimeout() const { - u64 diff = TickToNs(this->end_tick - armGetSystemTick()); - - if (this->TimedOut()) { - return 0; - } - - return diff; - } - }; - -} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_waitable.hpp b/libraries/libstratosphere/include/stratosphere/os/os_waitable.hpp new file mode 100644 index 000000000..13fa5c9f7 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_waitable.hpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/os/os_waitable_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_waitable_api.hpp new file mode 100644 index 000000000..2ede0d930 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_waitable_api.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::os { + + struct WaitableHolderType; + struct WaitableManagerType; + + void InitializeWaitableManager(WaitableManagerType *manager); + void FinalizeWaitableManager(WaitableManagerType *manager); + + WaitableHolderType *WaitAny(WaitableManagerType *manager); + WaitableHolderType *TryWaitAny(WaitableManagerType *manager); + WaitableHolderType *TimedWaitAny(WaitableManagerType *manager, TimeSpan timeout); + + void FinalizeWaitableHolder(WaitableHolderType *holder); + + void LinkWaitableHolder(WaitableManagerType *manager, WaitableHolderType *holder); + void UnlinkWaitableHolder(WaitableHolderType *holder); + void UnlinkAllWaitableHolder(WaitableManagerType *manager); + + void MoveAllWaitableHolder(WaitableManagerType *dst, WaitableManagerType *src); + + void SetWaitableHolderUserData(WaitableHolderType *holder, uintptr_t user_data); + uintptr_t GetWaitableHolderUserData(const WaitableHolderType *holder); + + void InitializeWaitableHolder(WaitableHolderType *holder, Handle handle); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_waitable_holder.hpp b/libraries/libstratosphere/include/stratosphere/os/os_waitable_holder.hpp deleted file mode 100644 index 959a8486f..000000000 --- a/libraries/libstratosphere/include/stratosphere/os/os_waitable_holder.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include "os_common_types.hpp" - -namespace ams::os { - - class WaitableManager; - - class Event; - class SystemEvent; - class InterruptEvent; - class Thread; - class MessageQueue; - class Semaphore; - - 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(Semaphore *semaphore); - 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/libraries/libstratosphere/include/stratosphere/os/os_waitable_manager.hpp b/libraries/libstratosphere/include/stratosphere/os/os_waitable_manager.hpp deleted file mode 100644 index 8da05fdc4..000000000 --- a/libraries/libstratosphere/include/stratosphere/os/os_waitable_manager.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include "os_mutex.hpp" - -namespace ams::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/libraries/libstratosphere/include/stratosphere/os/os_waitable_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_waitable_types.hpp new file mode 100644 index 000000000..d4aa47fd3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_waitable_types.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::os { + + namespace impl { + + class WaitableManagerImpl; + struct WaitableHolderImpl; + + } + + struct WaitableManagerType { + enum State { + State_NotInitialized, + State_Initialized, + }; + + u8 state; + bool is_waiting; + util::TypedStorage impl_storage; + }; + static_assert(std::is_trivial::value); + + struct WaitableHolderType { + util::TypedStorage impl_storage; + uintptr_t user_data; + }; + static_assert(std::is_trivial::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp b/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp new file mode 100644 index 000000000..7119d4ec7 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::os { + +} diff --git a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp index 4878a350a..673df1e08 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp @@ -109,7 +109,7 @@ namespace ams::sf::cmif { virtual void *AllocateDomain() = 0; virtual void FreeDomain(void *) = 0; protected: - ServerDomainManager(DomainEntryStorage *entry_storage, size_t entry_count) : entry_manager(entry_storage, entry_count) { /* ... */ } + ServerDomainManager(DomainEntryStorage *entry_storage, size_t entry_count) : entry_owner_lock(false), entry_manager(entry_storage, entry_count) { /* ... */ } inline DomainServiceObject *AllocateDomainServiceObject() { void *storage = this->AllocateDomain(); diff --git a/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_api.hpp b/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_api.hpp index 7a80c9f50..0b7c501e7 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_api.hpp @@ -28,6 +28,9 @@ namespace ams::sf::hipc { NeedsRetry, }; + void AttachWaitableHolderForAccept(os::WaitableHolderType *holder, Handle port); + void AttachWaitableHolderForReply(os::WaitableHolderType *holder, Handle request); + Result Receive(ReceiveResult *out_recv_result, Handle session_handle, const cmif::PointerAndSize &message_buffer); Result Receive(bool *out_closed, Handle session_handle, const cmif::PointerAndSize &message_buffer); Result Reply(Handle session_handle, const cmif::PointerAndSize &message_buffer); diff --git a/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp b/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp index 11af973c6..fabff0f93 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp @@ -47,7 +47,7 @@ namespace ams::sf::hipc { using ServerDomainSessionManager::DomainEntryStorage; using ServerDomainSessionManager::DomainStorage; private: - class ServerBase : public os::WaitableHolder { + class ServerBase : public os::WaitableHolderType { friend class ServerManagerBase; template friend class ServerManager; @@ -60,9 +60,9 @@ namespace ams::sf::hipc { bool service_managed; public: ServerBase(Handle ph, sm::ServiceName sn, bool m, cmif::ServiceObjectHolder &&sh) : - os::WaitableHolder(ph), static_object(std::move(sh)), port_handle(ph), service_name(sn), service_managed(m) + static_object(std::move(sh)), port_handle(ph), service_name(sn), service_managed(m) { - /* ... */ + hipc::AttachWaitableHolderForAccept(this, ph); } virtual ~ServerBase() = 0; @@ -87,7 +87,7 @@ namespace ams::sf::hipc { } else { R_ABORT_UNLESS(sm::UnregisterService(this->service_name)); } - R_ABORT_UNLESS(svcCloseHandle(this->port_handle)); + R_ABORT_UNLESS(svc::CloseHandle(this->port_handle)); } } @@ -118,30 +118,30 @@ namespace ams::sf::hipc { }; private: /* Management of waitables. */ - os::WaitableManager waitable_manager; + os::WaitableManagerType waitable_manager; os::Event request_stop_event; - os::WaitableHolder request_stop_event_holder; + os::WaitableHolderType request_stop_event_holder; os::Event notify_event; - os::WaitableHolder notify_event_holder; + os::WaitableHolderType notify_event_holder; os::Mutex waitable_selection_mutex; os::Mutex waitlist_mutex; - os::WaitableManager waitlist; + os::WaitableManagerType waitlist; os::Mutex deferred_session_mutex; using DeferredSessionList = typename util::IntrusiveListMemberTraits<&ServerSession::deferred_list_node>::ListType; DeferredSessionList deferred_session_list; private: virtual void RegisterSessionToWaitList(ServerSession *session) override final; - void RegisterToWaitList(os::WaitableHolder *holder); + void RegisterToWaitList(os::WaitableHolderType *holder); void ProcessWaitList(); bool WaitAndProcessImpl(); - Result ProcessForServer(os::WaitableHolder *holder); - Result ProcessForMitmServer(os::WaitableHolder *holder); - Result ProcessForSession(os::WaitableHolder *holder); + Result ProcessForServer(os::WaitableHolderType *holder); + Result ProcessForMitmServer(os::WaitableHolderType *holder); + Result ProcessForSession(os::WaitableHolderType *holder); void ProcessDeferredSessions(); @@ -154,13 +154,13 @@ namespace ams::sf::hipc { if constexpr (!ServiceObjectTraits::IsMitmServiceObject) { /* Non-mitm server. */ - server->SetUserData(static_cast(UserDataTag::Server)); + os::SetWaitableHolderUserData(server, static_cast(UserDataTag::Server)); } else { /* Mitm server. */ - server->SetUserData(static_cast(UserDataTag::MitmServer)); + os::SetWaitableHolderUserData(server, static_cast(UserDataTag::MitmServer)); } - this->waitable_manager.LinkWaitableHolder(server); + os::LinkWaitableHolder(std::addressof(this->waitable_manager), server); } template @@ -175,12 +175,16 @@ namespace ams::sf::hipc { public: ServerManagerBase(DomainEntryStorage *entry_storage, size_t entry_count) : ServerDomainSessionManager(entry_storage, entry_count), - request_stop_event(false), request_stop_event_holder(&request_stop_event), - notify_event(false), notify_event_holder(¬ify_event) + request_stop_event(os::EventClearMode_ManualClear), notify_event(os::EventClearMode_ManualClear), + waitable_selection_mutex(false), waitlist_mutex(false), deferred_session_mutex(false) { /* Link waitables. */ - this->waitable_manager.LinkWaitableHolder(&this->request_stop_event_holder); - this->waitable_manager.LinkWaitableHolder(&this->notify_event_holder); + os::InitializeWaitableManager(std::addressof(this->waitable_manager)); + os::InitializeWaitableHolder(std::addressof(this->request_stop_event_holder), this->request_stop_event.GetBase()); + os::LinkWaitableHolder(std::addressof(this->waitable_manager), std::addressof(this->request_stop_event_holder)); + os::InitializeWaitableHolder(std::addressof(this->notify_event_holder), this->notify_event.GetBase()); + os::LinkWaitableHolder(std::addressof(this->waitable_manager), std::addressof(this->notify_event_holder)); + os::InitializeWaitableManager(std::addressof(this->waitlist)); } template> @@ -224,13 +228,13 @@ namespace ams::sf::hipc { } /* Processing. */ - os::WaitableHolder *WaitSignaled(); + os::WaitableHolderType *WaitSignaled(); void ResumeProcessing(); void RequestStopProcessing(); - void AddUserWaitableHolder(os::WaitableHolder *waitable); + void AddUserWaitableHolder(os::WaitableHolderType *waitable); - Result Process(os::WaitableHolder *waitable); + Result Process(os::WaitableHolderType *waitable); void WaitAndProcess(); void LoopProcess(); }; @@ -356,7 +360,7 @@ namespace ams::sf::hipc { return this->GetObjectBySessionIndex(session, this->saved_messages_start, hipc::TlsMessageBufferSize); } public: - ServerManager() : ServerManagerBase(this->domain_entry_storages, ManagerOptions::MaxDomainObjects) { + ServerManager() : ServerManagerBase(this->domain_entry_storages, ManagerOptions::MaxDomainObjects), resource_mutex(false) { /* Clear storages. */ #define SF_SM_MEMCLEAR(obj) if constexpr (sizeof(obj) > 0) { std::memset(obj, 0, sizeof(obj)); } SF_SM_MEMCLEAR(this->server_storages); diff --git a/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp b/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp index 23278f8fa..117af73cb 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp @@ -38,7 +38,7 @@ namespace ams::sf::hipc { } - class ServerSession : public os::WaitableHolder { + class ServerSession : public os::WaitableHolderType { friend class ServerSessionManager; friend class ServerManagerBase; friend class impl::HipcManager; @@ -54,14 +54,16 @@ namespace ams::sf::hipc { bool is_closed; bool has_received; public: - ServerSession(Handle h, cmif::ServiceObjectHolder &&obj) : WaitableHolder(h), srv_obj_holder(std::move(obj)), session_handle(h) { + ServerSession(Handle h, cmif::ServiceObjectHolder &&obj) : srv_obj_holder(std::move(obj)), session_handle(h) { + hipc::AttachWaitableHolderForReply(this, h); this->is_closed = false; this->has_received = false; this->forward_service = nullptr; AMS_ABORT_UNLESS(!this->IsMitmSession()); } - ServerSession(Handle h, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) : WaitableHolder(h), srv_obj_holder(std::move(obj)), session_handle(h) { + ServerSession(Handle h, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) : srv_obj_holder(std::move(obj)), session_handle(h) { + hipc::AttachWaitableHolderForReply(this, h); this->is_closed = false; this->has_received = false; this->forward_service = std::move(fsrv); diff --git a/libraries/libstratosphere/source/ams/ams_emummc_api.cpp b/libraries/libstratosphere/source/ams/ams_emummc_api.cpp index df18d5b3e..f00f86535 100644 --- a/libraries/libstratosphere/source/ams/ams_emummc_api.cpp +++ b/libraries/libstratosphere/source/ams/ams_emummc_api.cpp @@ -58,7 +58,7 @@ namespace ams::emummc { }; /* Globals. */ - os::Mutex g_lock; + os::Mutex g_lock(false); ExosphereConfig g_exo_config; bool g_is_emummc; bool g_has_cached; diff --git a/libraries/libstratosphere/source/cfg/cfg_privileged_process.cpp b/libraries/libstratosphere/source/cfg/cfg_privileged_process.cpp index 87d9b378e..b941208c5 100644 --- a/libraries/libstratosphere/source/cfg/cfg_privileged_process.cpp +++ b/libraries/libstratosphere/source/cfg/cfg_privileged_process.cpp @@ -25,7 +25,7 @@ namespace ams::cfg { constexpr os::ProcessId InitialProcessIdMaxDeprecated = {0x50}; /* Privileged process globals. */ - os::Mutex g_lock; + os::Mutex g_lock(false); bool g_got_privileged_process_status = false; os::ProcessId g_min_initial_process_id = os::InvalidProcessId, g_max_initial_process_id = os::InvalidProcessId; os::ProcessId g_cur_process_id = os::InvalidProcessId; diff --git a/libraries/libstratosphere/source/cfg/cfg_sd_card.cpp b/libraries/libstratosphere/source/cfg/cfg_sd_card.cpp index 3c644a351..a79a23fa7 100644 --- a/libraries/libstratosphere/source/cfg/cfg_sd_card.cpp +++ b/libraries/libstratosphere/source/cfg/cfg_sd_card.cpp @@ -30,7 +30,7 @@ namespace ams::cfg { constexpr size_t NumRequiredServicesForSdCardAccess = util::size(RequiredServicesForSdCardAccess); /* SD card globals. */ - os::Mutex g_sd_card_lock; + os::Mutex g_sd_card_lock(false); bool g_sd_card_initialized = false; FsFileSystem g_sd_card_filesystem = {}; diff --git a/libraries/libstratosphere/source/fs/fs_memory_management.cpp b/libraries/libstratosphere/source/fs/fs_memory_management.cpp index ccf3f4396..da4ba2701 100644 --- a/libraries/libstratosphere/source/fs/fs_memory_management.cpp +++ b/libraries/libstratosphere/source/fs/fs_memory_management.cpp @@ -30,7 +30,7 @@ namespace ams::fs { std::free(ptr); } - os::Mutex g_lock; + os::Mutex g_lock(false); AllocateFunction g_allocate_func = DefaultAllocate; DeallocateFunction g_deallocate_func = DefaultDeallocate; diff --git a/libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.cpp b/libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.cpp index c8bf11997..10b2e3ec5 100644 --- a/libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.cpp +++ b/libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.cpp @@ -60,7 +60,7 @@ namespace ams::fs::impl { } FileSystemAccessor::FileSystemAccessor(const char *n, std::unique_ptr &&fs, std::unique_ptr &&generator) - : impl(std::move(fs)), mount_name_generator(std::move(generator)), + : impl(std::move(fs)), open_list_lock(false), mount_name_generator(std::move(generator)), access_log_enabled(false), data_cache_attachable(false), path_cache_attachable(false), path_cache_attached(false), multi_commit_supported(false) { R_ABORT_UNLESS(ValidateMountName(n)); diff --git a/libraries/libstratosphere/source/fs/fsa/fs_mount_table.hpp b/libraries/libstratosphere/source/fs/fsa/fs_mount_table.hpp index 23145d840..e0034588d 100644 --- a/libraries/libstratosphere/source/fs/fsa/fs_mount_table.hpp +++ b/libraries/libstratosphere/source/fs/fsa/fs_mount_table.hpp @@ -28,7 +28,7 @@ namespace ams::fs::impl { FileSystemList fs_list; os::Mutex mutex; public: - constexpr MountTable() : fs_list(), mutex() { /* ... */ } + constexpr MountTable() : fs_list(), mutex(false) { /* ... */ } private: bool CanAcceptMountName(const char *name); public: diff --git a/libraries/libstratosphere/source/fssystem/fssystem_aes_xts_storage.cpp b/libraries/libstratosphere/source/fssystem/fssystem_aes_xts_storage.cpp index db19eceb9..959725bdd 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_aes_xts_storage.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_aes_xts_storage.cpp @@ -17,7 +17,7 @@ namespace ams::fssystem { - AesXtsStorage::AesXtsStorage(IStorage *base, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size) : base_storage(base), block_size(block_size), mutex() { + AesXtsStorage::AesXtsStorage(IStorage *base, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size) : base_storage(base), block_size(block_size), mutex(false) { AMS_ASSERT(base != nullptr); AMS_ASSERT(key1 != nullptr); AMS_ASSERT(key2 != nullptr); diff --git a/libraries/libstratosphere/source/fssystem/fssystem_directory_savedata_filesystem.cpp b/libraries/libstratosphere/source/fssystem/fssystem_directory_savedata_filesystem.cpp index f867917cc..723cfd1c6 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_directory_savedata_filesystem.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_directory_savedata_filesystem.cpp @@ -74,13 +74,13 @@ namespace ams::fssystem { } DirectorySaveDataFileSystem::DirectorySaveDataFileSystem(std::shared_ptr fs) - : PathResolutionFileSystem(fs), open_writable_files(0) + : PathResolutionFileSystem(fs), accessor_mutex(false), open_writable_files(0) { /* ... */ } DirectorySaveDataFileSystem::DirectorySaveDataFileSystem(std::unique_ptr fs) - : PathResolutionFileSystem(std::move(fs)), open_writable_files(0) + : PathResolutionFileSystem(std::move(fs)), accessor_mutex(false), open_writable_files(0) { /* ... */ } diff --git a/libraries/libstratosphere/source/fssystem/fssystem_pooled_buffer.cpp b/libraries/libstratosphere/source/fssystem/fssystem_pooled_buffer.cpp index 7b224aa00..315d24e56 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_pooled_buffer.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_pooled_buffer.cpp @@ -27,7 +27,7 @@ namespace ams::fssystem { uintptr_t address; size_t size; public: - constexpr AdditionalDeviceAddressEntry() : mutex(), is_registered(), address(), size() { /* ... */ } + constexpr AdditionalDeviceAddressEntry() : mutex(false), is_registered(), address(), size() { /* ... */ } void Register(uintptr_t addr, size_t sz) { std::scoped_lock lk(this->mutex); @@ -78,7 +78,7 @@ namespace ams::fssystem { constexpr size_t HeapAllocatableSizeMaxForLarge = HeapBlockSize * (static_cast(1) << HeapOrderMaxForLarge); /* TODO: SdkMutex */ - os::Mutex g_heap_mutex; + os::Mutex g_heap_mutex(false); FileSystemBuddyHeap g_heap; std::atomic g_retry_count; diff --git a/libraries/libstratosphere/source/hid/hid_api.cpp b/libraries/libstratosphere/source/hid/hid_api.cpp index e5d3bfc7d..da4bc2591 100644 --- a/libraries/libstratosphere/source/hid/hid_api.cpp +++ b/libraries/libstratosphere/source/hid/hid_api.cpp @@ -20,7 +20,7 @@ namespace ams::hid { namespace { /* Global lock. */ - os::Mutex g_hid_lock; + os::Mutex g_hid_lock(false); bool g_initialized_hid = false; /* Helper. */ diff --git a/libraries/libstratosphere/source/hos/hos_version_api.cpp b/libraries/libstratosphere/source/hos/hos_version_api.cpp index c1bfc3141..113ae1caa 100644 --- a/libraries/libstratosphere/source/hos/hos_version_api.cpp +++ b/libraries/libstratosphere/source/hos/hos_version_api.cpp @@ -22,7 +22,7 @@ namespace ams::hos { hos::Version g_hos_version; bool g_has_cached; - os::Mutex g_mutex; + os::Mutex g_mutex(false); void CacheValues() { if (__atomic_load_n(&g_has_cached, __ATOMIC_SEQ_CST)) { diff --git a/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.cpp b/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.cpp index 3b58dd4ae..7afc2d78c 100644 --- a/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.cpp +++ b/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.cpp @@ -33,12 +33,6 @@ namespace ams::lmem::impl { new (&out->list_node) util::IntrusiveListNode; new (&out->child_list) decltype(out->child_list); - /* Only initialize mutex if option requires it. */ - if (option & CreateOption_ThreadSafe) { - static_assert(std::is_trivially_destructible::value); - new (&out->mutex) os::Mutex; - } - /* Set fields. */ out->magic = magic; out->heap_start = start; diff --git a/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.hpp b/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.hpp index 0e254f4fa..99018b642 100644 --- a/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.hpp +++ b/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.hpp @@ -30,13 +30,13 @@ namespace ams::lmem::impl { public: explicit ScopedHeapLock(HeapHandle h) : handle(h) { if (this->handle->option & CreateOption_ThreadSafe) { - this->handle->mutex.Lock(); + os::LockMutex(std::addressof(this->handle->mutex)); } } ~ScopedHeapLock() { if (this->handle->option & CreateOption_ThreadSafe) { - this->handle->mutex.Unlock(); + os::UnlockMutex(std::addressof(this->handle->mutex)); } } }; diff --git a/libraries/libstratosphere/source/lmem/lmem_exp_heap.cpp b/libraries/libstratosphere/source/lmem/lmem_exp_heap.cpp index 5d8e8352f..606040438 100644 --- a/libraries/libstratosphere/source/lmem/lmem_exp_heap.cpp +++ b/libraries/libstratosphere/source/lmem/lmem_exp_heap.cpp @@ -19,10 +19,17 @@ namespace ams::lmem { HeapHandle CreateExpHeap(void *address, size_t size, u32 option) { - return impl::CreateExpHeap(address, size, option); + HeapHandle handle = impl::CreateExpHeap(address, size, option); + if (option & CreateOption_ThreadSafe) { + os::InitializeMutex(std::addressof(handle->mutex), false, 0); + } + return handle; } void DestroyExpHeap(HeapHandle handle) { + if (handle->option & CreateOption_ThreadSafe) { + os::FinalizeMutex(std::addressof(handle->mutex)); + } impl::DestroyExpHeap(handle); } diff --git a/libraries/libstratosphere/source/lmem/lmem_unit_heap.cpp b/libraries/libstratosphere/source/lmem/lmem_unit_heap.cpp index 3b613bb71..edc7c8cd6 100644 --- a/libraries/libstratosphere/source/lmem/lmem_unit_heap.cpp +++ b/libraries/libstratosphere/source/lmem/lmem_unit_heap.cpp @@ -19,18 +19,33 @@ namespace ams::lmem { HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option) { - return impl::CreateUnitHeap(address, size, unit_size, DefaultAlignment, static_cast(option), InfoPlacement_Head, nullptr); + HeapHandle handle = impl::CreateUnitHeap(address, size, unit_size, DefaultAlignment, static_cast(option), InfoPlacement_Head, nullptr); + if (option & CreateOption_ThreadSafe) { + os::InitializeMutex(std::addressof(handle->mutex), false, 0); + } + return handle; } HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, InfoPlacement info_placement) { - return impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast(option), info_placement, nullptr); + HeapHandle handle = impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast(option), info_placement, nullptr); + if (option & CreateOption_ThreadSafe) { + os::InitializeMutex(std::addressof(handle->mutex), false, 0); + } + return handle; } HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, HeapCommonHead *heap_head) { - return impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast(option), InfoPlacement_Head, heap_head); + HeapHandle handle = impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast(option), InfoPlacement_Head, heap_head); + if (option & CreateOption_ThreadSafe) { + os::InitializeMutex(std::addressof(handle->mutex), false, 0); + } + return handle; } void DestroyUnitHeap(HeapHandle handle) { + if (handle->option & CreateOption_ThreadSafe) { + os::FinalizeMutex(std::addressof(handle->mutex)); + } impl::DestroyUnitHeap(handle); } diff --git a/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_central.hpp b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_central.hpp index c67bf839a..327e029b0 100644 --- a/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_central.hpp +++ b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_central.hpp @@ -201,14 +201,14 @@ namespace ams::mem::impl::heap { s32 static_thread_quota; s32 dynamic_thread_quota; bool use_virtual_memory; - os::RecursiveMutex lock; + os::Mutex lock; ListHeader spanpage_list; ListHeader full_spanpage_list; ListHeader freelists[FreeListCount]; FreeListAvailableWord freelists_bitmap[NumFreeListBitmaps]; ListHeader smallmem_lists[TlsHeapStatic::NumClassInfo]; public: - TlsHeapCentral() { + TlsHeapCentral() : lock(true) { this->span_table.total_pages = 0; } diff --git a/libraries/libstratosphere/source/mem/impl/mem_impl_platform.os.horizon.cpp b/libraries/libstratosphere/source/mem/impl/mem_impl_platform.os.horizon.cpp index d1f7e6634..11370ec54 100644 --- a/libraries/libstratosphere/source/mem/impl/mem_impl_platform.os.horizon.cpp +++ b/libraries/libstratosphere/source/mem/impl/mem_impl_platform.os.horizon.cpp @@ -20,7 +20,7 @@ namespace ams::mem::impl { namespace { - os::Mutex g_virt_mem_enabled_lock; + os::Mutex g_virt_mem_enabled_lock(false); bool g_virt_mem_enabled_detected; bool g_virt_mem_enabled; diff --git a/libraries/libstratosphere/source/ncm/ncm_install_task_base.cpp b/libraries/libstratosphere/source/ncm/ncm_install_task_base.cpp index 5032cf027..506e89fac 100644 --- a/libraries/libstratosphere/source/ncm/ncm_install_task_base.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_install_task_base.cpp @@ -626,7 +626,6 @@ namespace ams::ncm { } Result InstallTaskBase::PreparePlaceHolder() { - static os::Mutex placeholder_mutex; size_t total_size = 0; /* Count the number of content meta entries. */ @@ -635,7 +634,9 @@ namespace ams::ncm { for (s32 i = 0; i < count; i++) { R_UNLESS(!this->IsCancelRequested(), ncm::ResultCreatePlaceHolderCancelled()); - std::scoped_lock lk(placeholder_mutex); + + static os::Mutex s_placeholder_mutex(false); + std::scoped_lock lk(s_placeholder_mutex); InstallContentMeta content_meta; R_TRY(this->data->Get(std::addressof(content_meta), i)); diff --git a/libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.hpp b/libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.hpp index 487e81f44..1d8084451 100644 --- a/libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.hpp +++ b/libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.hpp @@ -43,7 +43,7 @@ namespace ams::ncm { CacheEntry *FindInCache(PlaceHolderId placeholder_id); CacheEntry *GetFreeEntry();; public: - PlaceHolderAccessor() : cur_counter(0), delay_flush(false) { + PlaceHolderAccessor() : cur_counter(0), cache_mutex(false), delay_flush(false) { for (size_t i = 0; i < MaxCacheEntries; i++) { caches[i].id = InvalidPlaceHolderId; } diff --git a/libraries/libstratosphere/source/os/impl/os_inter_process_event.cpp b/libraries/libstratosphere/source/os/impl/os_inter_process_event.cpp index 4db3e0b57..8ae899184 100644 --- a/libraries/libstratosphere/source/os/impl/os_inter_process_event.cpp +++ b/libraries/libstratosphere/source/os/impl/os_inter_process_event.cpp @@ -13,167 +13,139 @@ * 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" +#include "os_inter_process_event_impl.hpp" +#include "os_waitable_object_list.hpp" namespace ams::os::impl { namespace { - Result CreateEventHandles(Handle *out_readable, Handle *out_writable) { - /* Create the event handles. */ - R_TRY_CATCH(svcCreateEvent(out_writable, out_readable)) { - R_CONVERT(svc::ResultOutOfResource, ResultOutOfResource()); - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + inline void SetupInterProcessEventType(InterProcessEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode) { + /* Set handles. */ + event->readable_handle = read_handle; + event->is_readable_handle_managed = read_handle_managed; + event->writable_handle = write_handle; + event->is_writable_handle_managed = write_handle_managed; - return ResultSuccess(); + /* Set auto clear. */ + event->auto_clear = (clear_mode == EventClearMode_AutoClear); + + /* Create the waitlist node. */ + new (GetPointer(event->waitable_object_list_storage)) impl::WaitableObjectList; + + /* Set state. */ + event->state = InterProcessEventType::State_Initialized; } } - InterProcessEvent::InterProcessEvent(bool autoclear) : is_initialized(false) { - R_ABORT_UNLESS(this->Initialize(autoclear)); - } - - InterProcessEvent::~InterProcessEvent() { - this->Finalize(); - } - - Result InterProcessEvent::Initialize(bool autoclear) { - AMS_ABORT_UNLESS(!this->is_initialized); + Result CreateInterProcessEvent(InterProcessEventType *event, EventClearMode clear_mode) { Handle rh, wh; - R_TRY(CreateEventHandles(&rh, &wh)); - this->Initialize(rh, true, wh, true, autoclear); + R_TRY(impl::InterProcessEventImpl::Create(std::addressof(wh), std::addressof(rh))); + + SetupInterProcessEventType(event, rh, true, wh, true, clear_mode); return ResultSuccess(); } - void InterProcessEvent::Initialize(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) { - AMS_ABORT_UNLESS(!this->is_initialized); - AMS_ABORT_UNLESS(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; + void DestroyInterProcessEvent(InterProcessEventType *event) { + AMS_ASSERT(event->state == InterProcessEventType::State_Initialized); + + /* Clear the state. */ + event->state = InterProcessEventType::State_NotInitialized; + + /* Close handles if required. */ + if (event->is_readable_handle_managed) { + if (event->readable_handle != svc::InvalidHandle) { + impl::InterProcessEventImpl::Close(event->readable_handle); + } + event->is_readable_handle_managed = false; + } + + if (event->is_writable_handle_managed) { + if (event->writable_handle != svc::InvalidHandle) { + impl::InterProcessEventImpl::Close(event->writable_handle); + } + event->is_writable_handle_managed = false; + } + + /* Destroy the waitlist. */ + GetReference(event->waitable_object_list_storage).~WaitableObjectList(); } - Handle InterProcessEvent::DetachReadableHandle() { - AMS_ABORT_UNLESS(this->is_initialized); - const Handle handle = this->read_handle; - AMS_ABORT_UNLESS(handle != INVALID_HANDLE); - this->read_handle = INVALID_HANDLE; - this->manage_read_handle = false; + void AttachInterProcessEvent(InterProcessEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode) { + AMS_ASSERT(read_handle != svc::InvalidHandle || write_handle != svc::InvalidHandle); + + return SetupInterProcessEventType(event, read_handle, read_handle_managed, write_handle, write_handle_managed, clear_mode); + } + + Handle DetachReadableHandleOfInterProcessEvent(InterProcessEventType *event) { + AMS_ASSERT(event->state == InterProcessEventType::State_Initialized); + + const Handle handle = event->readable_handle; + + event->readable_handle = svc::InvalidHandle; + event->is_readable_handle_managed = false; + + return handle; + } + Handle DetachWritableHandleOfInterProcessEvent(InterProcessEventType *event) { + AMS_ASSERT(event->state == InterProcessEventType::State_Initialized); + + const Handle handle = event->writable_handle; + + event->writable_handle = svc::InvalidHandle; + event->is_writable_handle_managed = false; + return handle; } - Handle InterProcessEvent::DetachWritableHandle() { - AMS_ABORT_UNLESS(this->is_initialized); - const Handle handle = this->write_handle; - AMS_ABORT_UNLESS(handle != INVALID_HANDLE); - this->write_handle = INVALID_HANDLE; - this->manage_write_handle = false; - return handle; + void WaitInterProcessEvent(InterProcessEventType *event) { + AMS_ASSERT(event->state == InterProcessEventType::State_Initialized); + + return impl::InterProcessEventImpl::Wait(event->readable_handle, event->auto_clear); } - Handle InterProcessEvent::GetReadableHandle() const { - AMS_ABORT_UNLESS(this->is_initialized); - return this->read_handle; + bool TryWaitInterProcessEvent(InterProcessEventType *event) { + AMS_ASSERT(event->state == InterProcessEventType::State_Initialized); + + return impl::InterProcessEventImpl::TryWait(event->readable_handle, event->auto_clear); } - Handle InterProcessEvent::GetWritableHandle() const { - AMS_ABORT_UNLESS(this->is_initialized); - return this->write_handle; + bool TimedWaitInterProcessEvent(InterProcessEventType *event, TimeSpan timeout) { + AMS_ASSERT(event->state == InterProcessEventType::State_Initialized); + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); + + return impl::InterProcessEventImpl::TimedWait(event->readable_handle, event->auto_clear, timeout); } - void InterProcessEvent::Finalize() { - if (this->is_initialized) { - if (this->manage_read_handle && this->read_handle != INVALID_HANDLE) { - R_ABORT_UNLESS(svcCloseHandle(this->read_handle)); - } - if (this->manage_write_handle && this->write_handle != INVALID_HANDLE) { - R_ABORT_UNLESS(svcCloseHandle(this->write_handle)); - } + void SignalInterProcessEvent(InterProcessEventType *event) { + AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized); + + return impl::InterProcessEventImpl::Signal(event->writable_handle); + } + + void ClearInterProcessEvent(InterProcessEventType *event) { + AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized); + + auto handle = event->readable_handle; + if (handle == svc::InvalidHandle) { + handle = event->writable_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; + return impl::InterProcessEventImpl::Clear(handle); } - void InterProcessEvent::Signal() { - R_ABORT_UNLESS(svcSignalEvent(this->GetWritableHandle())); + Handle GetReadableHandleOfInterProcessEvent(const InterProcessEventType *event) { + AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized); + + return event->readable_handle; } - void InterProcessEvent::Reset() { - Handle handle = this->GetReadableHandle(); - if (handle == INVALID_HANDLE) { - handle = this->GetWritableHandle(); - } - R_ABORT_UNLESS(svcClearEvent(handle)); + Handle GetWritableHandleOfInterProcessEvent(const InterProcessEventType *event) { + AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized); + + return event->writable_handle; } - void InterProcessEvent::Wait() { - const Handle handle = this->GetReadableHandle(); - - while (true) { - /* Continuously wait, until success. */ - R_TRY_CATCH(svcWaitSynchronizationSingle(handle, std::numeric_limits::max())) { - R_CATCH(svc::ResultCancelled) { continue; } - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - - /* 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(svc::ResultInvalidState) { continue; } - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - } - 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(svc::ResultTimedOut) { return false; } - R_CATCH(svc::ResultCancelled) { continue; } - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - - /* 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(svc::ResultTimedOut) { return false; } - R_CATCH(svc::ResultCancelled) { continue; } - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - - /* 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(svc::ResultInvalidState) { continue; } - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - } - - return true; - } - } } diff --git a/libraries/libstratosphere/source/os/impl/os_inter_process_event.hpp b/libraries/libstratosphere/source/os/impl/os_inter_process_event.hpp index 92f7c38ad..0b5ee902e 100644 --- a/libraries/libstratosphere/source/os/impl/os_inter_process_event.hpp +++ b/libraries/libstratosphere/source/os/impl/os_inter_process_event.hpp @@ -18,37 +18,24 @@ namespace ams::os::impl { - class WaitableHolderOfInterProcessEvent; + Result CreateInterProcessEvent(InterProcessEventType *event, EventClearMode clear_mode); + void DestroyInterProcessEvent(InterProcessEventType *event); - 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(); + void AttachInterProcessEvent(InterProcessEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode); - 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(); + Handle DetachReadableHandleOfInterProcessEvent(InterProcessEventType *event); + Handle DetachWritableHandleOfInterProcessEvent(InterProcessEventType *event); - void Signal(); - void Reset(); - void Wait(); - bool TryWait(); - bool TimedWait(u64 ns); - }; + void WaitInterProcessEvent(InterProcessEventType *event); + bool TryWaitInterProcessEvent(InterProcessEventType *event); + bool TimedWaitInterProcessEvent(InterProcessEventType *event, TimeSpan timeout); + + void SignalInterProcessEvent(InterProcessEventType *event); + void ClearInterProcessEvent(InterProcessEventType *event); + + Handle GetReadableHandleOfInterProcessEvent(const InterProcessEventType *event); + Handle GetWritableHandleOfInterProcessEvent(const InterProcessEventType *event); + + void InitializeWaitableHolder(WaitableHolderType *waitable_holder, InterProcessEventType *event); } diff --git a/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.hpp b/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.hpp new file mode 100644 index 000000000..0a98a7a4f --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#if defined(ATMOSPHERE_OS_HORIZON) + #include "os_inter_process_event_impl.os.horizon.hpp" +#else + #error "Unknown OS for ams::os::InterProcessEventImpl" +#endif \ No newline at end of file diff --git a/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.os.horizon.cpp new file mode 100644 index 000000000..db40e5f05 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.os.horizon.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_inter_process_event.hpp" +#include "os_inter_process_event_impl.os.horizon.hpp" +#include "os_timeout_helper.hpp" + +namespace ams::os::impl { + + Result InterProcessEventImpl::Create(Handle *out_write, Handle *out_read) { + /* Create the event handles. */ + svc::Handle wh, rh; + R_TRY_CATCH(svc::CreateEvent(std::addressof(wh), std::addressof(rh))) { + R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource()) + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + *out_write = wh; + *out_read = rh; + return ResultSuccess(); + } + + void InterProcessEventImpl::Close(Handle handle) { + if (handle != svc::InvalidHandle) { + R_ABORT_UNLESS(svc::CloseHandle(svc::Handle(handle))); + } + } + + void InterProcessEventImpl::Signal(Handle handle) { + R_ABORT_UNLESS(svc::SignalEvent(svc::Handle(handle))); + } + + void InterProcessEventImpl::Clear(Handle handle) { + R_ABORT_UNLESS(svc::ClearEvent(svc::Handle(handle))); + } + + void InterProcessEventImpl::Wait(Handle handle, bool auto_clear) { + while (true) { + /* Continuously wait, until success. */ + s32 index; + Result res = svc::WaitSynchronization(std::addressof(index), reinterpret_cast(std::addressof(handle)), 1, svc::WaitInfinite); + if (R_SUCCEEDED(res)) { + /* Clear, if we must. */ + if (auto_clear) { + R_TRY_CATCH(svc::ResetSignal(svc::Handle(handle))) { + /* Some other thread might have caught this before we did. */ + R_CATCH(svc::ResultInvalidState) { continue; } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + } + + return; + } + + AMS_ASSERT(svc::ResultCancelled::Includes(res)); + } + } + + bool InterProcessEventImpl::TryWait(Handle handle, bool auto_clear) { + /* If we're auto clear, just try to reset. */ + if (auto_clear) { + return R_SUCCEEDED(svc::ResetSignal(svc::Handle(handle))); + } + + /* Not auto-clear. */ + while (true) { + /* Continuously wait, until success or timeout. */ + s32 index; + Result res = svc::WaitSynchronization(std::addressof(index), reinterpret_cast(std::addressof(handle)), 1, 0); + + /* If we succeeded, we're signaled. */ + if (R_SUCCEEDED(res)) { + return true; + } + + /* If we timed out, we're not signaled. */ + if (svc::ResultTimedOut::Includes(res)) { + return false; + } + + AMS_ASSERT(svc::ResultCancelled::Includes(res)); + } + } + + bool InterProcessEventImpl::TimedWait(Handle handle, bool auto_clear, TimeSpan timeout) { + TimeoutHelper timeout_helper(timeout); + + while (true) { + /* Continuously wait, until success. */ + s32 index; + Result res = svc::WaitSynchronization(std::addressof(index), reinterpret_cast(std::addressof(handle)), 1, timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds()); + if (R_SUCCEEDED(res)) { + /* Clear, if we must. */ + if (auto_clear) { + R_TRY_CATCH(svc::ResetSignal(svc::Handle(handle))) { + /* Some other thread might have caught this before we did. */ + R_CATCH(svc::ResultInvalidState) { continue; } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + } + + return true; + } + + if (svc::ResultTimedOut::Includes(res)) { + return false; + } + + AMS_ASSERT(svc::ResultCancelled::Includes(res)); + } + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.os.horizon.hpp b/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.os.horizon.hpp new file mode 100644 index 000000000..1149e03dc --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.os.horizon.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::os::impl { + + class InterProcessEventImpl { + public: + static Result Create(Handle *out_write, Handle *out_read); + static void Close(Handle handle); + static void Signal(Handle handle); + static void Clear(Handle handle); + static void Wait(Handle handle, bool auto_clear); + static bool TryWait(Handle handle, bool auto_clear); + static bool TimedWait(Handle handle, bool auto_clear, TimeSpan timeout); + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/os/impl/os_internal_condition_variable_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_internal_condition_variable_impl.os.horizon.cpp new file mode 100644 index 000000000..c26f57566 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_internal_condition_variable_impl.os.horizon.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_timeout_helper.hpp" +#include "os_thread_manager.hpp" + +namespace ams::os::impl { + + void InternalConditionVariableImpl::Signal() { + ams::svc::SignalProcessWideKey(reinterpret_cast(std::addressof(this->value)), 1); + } + + void InternalConditionVariableImpl::Broadcast() { + ams::svc::SignalProcessWideKey(reinterpret_cast(std::addressof(this->value)), -1); + } + + void InternalConditionVariableImpl::Wait(InternalCriticalSection *cs) { + const auto cur_handle = GetCurrentThreadHandle(); + AMS_ASSERT((cs->Get()->thread_handle & ~ams::svc::HandleWaitMask) == cur_handle); + + R_ABORT_UNLESS(ams::svc::WaitProcessWideKeyAtomic(reinterpret_cast(std::addressof(cs->Get()->thread_handle)), reinterpret_cast(std::addressof(this->value)), cur_handle, -1)); + } + + ConditionVariableStatus InternalConditionVariableImpl::TimedWait(InternalCriticalSection *cs, const TimeoutHelper &timeout_helper) { + const auto cur_handle = GetCurrentThreadHandle(); + AMS_ASSERT((cs->Get()->thread_handle & ~ams::svc::HandleWaitMask) == cur_handle); + + const TimeSpan left = timeout_helper.GetTimeLeftOnTarget(); + if (left > 0) { + R_TRY_CATCH(ams::svc::WaitProcessWideKeyAtomic(reinterpret_cast(std::addressof(cs->Get()->thread_handle)), reinterpret_cast(std::addressof(this->value)), cur_handle, left.GetNanoSeconds())) { + R_CATCH(svc::ResultTimedOut) { + cs->Enter(); + return ConditionVariableStatus::TimedOut; + } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + return ConditionVariableStatus::Success; + } else { + return ConditionVariableStatus::TimedOut; + } + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_internal_critical_section_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_internal_critical_section_impl.os.horizon.cpp new file mode 100644 index 000000000..effa13f5f --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_internal_critical_section_impl.os.horizon.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_timeout_helper.hpp" +#include "os_thread_manager.hpp" + +namespace ams::os::impl { + + namespace { + + ALWAYS_INLINE void DataMemoryBarrierForCriticalSection() { + #if defined(ATMOSPHERE_ARCH_ARM64) + /* ... */ + #else + #error "Unknown architecture for os::impl::InternalCriticalSectionImpl DataMemoryBarrier" + #endif + } + + ALWAYS_INLINE u32 LoadExclusive(u32 *ptr) { + u32 value; + + #if defined(ATMOSPHERE_ARCH_ARM64) + __asm__ __volatile__("ldaxr %w[value], [%[ptr]]" : [value]"=&r"(value) : [ptr]"r"(ptr) : "memory"); + #else + #error "Unknown architecture for os::impl::InternalCriticalSectionImpl LoadExclusive" + #endif + + return value; + } + + ALWAYS_INLINE int StoreExclusive(u32 *ptr, u32 value) { + int result; + + #if defined(ATMOSPHERE_ARCH_ARM64) + __asm__ __volatile__("stlxr %w[result], %w[value], [%[ptr]]" : [result]"=&r"(result) : [value]"r"(value), [ptr]"r"(ptr) : "memory"); + #else + #error "Unknown architecture for os::impl::InternalCriticalSectionImpl StoreExclusive" + #endif + + + return result; + } + + ALWAYS_INLINE void ClearExclusive() { + #if defined(ATMOSPHERE_ARCH_ARM64) + __asm__ __volatile__("clrex" ::: "memory"); + #else + #error "Unknown architecture for os::impl::InternalCriticalSectionImpl ClearExclusive" + #endif + } + + } + + void InternalCriticalSectionImpl::Enter() { + AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0); + + const auto cur_handle = GetCurrentThreadHandle(); + AMS_ASSERT((this->thread_handle & ~ams::svc::HandleWaitMask) != cur_handle); + + u32 value = LoadExclusive(std::addressof(this->thread_handle)); + while (true) { + if (AMS_LIKELY(value == svc::InvalidHandle)) { + if (AMS_UNLIKELY(StoreExclusive(std::addressof(this->thread_handle), cur_handle) != 0)) { + value = LoadExclusive(std::addressof(this->thread_handle)); + continue; + } + break; + } + + if (AMS_LIKELY((value & ams::svc::HandleWaitMask) == 0)) { + if (AMS_UNLIKELY(StoreExclusive(std::addressof(this->thread_handle), value | ams::svc::HandleWaitMask) != 0)) { + value = LoadExclusive(std::addressof(this->thread_handle)); + continue; + } + } + + R_ABORT_UNLESS(ams::svc::ArbitrateLock(value & ~ams::svc::HandleWaitMask, reinterpret_cast(std::addressof(this->thread_handle)), cur_handle)); + + value = LoadExclusive(std::addressof(this->thread_handle)); + if (AMS_LIKELY((value & ~ams::svc::HandleWaitMask) == cur_handle)) { + ClearExclusive(); + break; + } + } + + DataMemoryBarrierForCriticalSection(); + } + + bool InternalCriticalSectionImpl::TryEnter() { + AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0); + + const auto cur_handle = GetCurrentThreadHandle(); + + while (true) { + u32 value = LoadExclusive(std::addressof(this->thread_handle)); + if (AMS_UNLIKELY(value != svc::InvalidHandle)) { + break; + } + + DataMemoryBarrierForCriticalSection(); + + if (AMS_LIKELY(StoreExclusive(std::addressof(this->thread_handle), cur_handle) == 0)) { + return true; + } + } + + ClearExclusive(); + DataMemoryBarrierForCriticalSection(); + + return false; + } + + void InternalCriticalSectionImpl::Leave() { + AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0); + + const auto cur_handle = GetCurrentThreadHandle(); + u32 value = LoadExclusive(std::addressof(this->thread_handle)); + + while (true) { + if (AMS_UNLIKELY(value != cur_handle)) { + ClearExclusive(); + break; + } + + DataMemoryBarrierForCriticalSection(); + + if (AMS_LIKELY(StoreExclusive(std::addressof(this->thread_handle), 0) == 0)) { + break; + } + + value = LoadExclusive(std::addressof(this->thread_handle)); + } + + DataMemoryBarrierForCriticalSection(); + + AMS_ASSERT((value | ams::svc::HandleWaitMask) == (cur_handle | ams::svc::HandleWaitMask)); + if (value & ams::svc::HandleWaitMask) { + R_ABORT_UNLESS(ams::svc::ArbitrateUnlock(reinterpret_cast(std::addressof(this->thread_handle)))); + } + } + + bool InternalCriticalSectionImpl::IsLockedByCurrentThread() const { + const auto cur_handle = GetCurrentThreadHandle(); + return (this->thread_handle & ~ams::svc::HandleWaitMask) == cur_handle; + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_interrupt_event_impl.hpp b/libraries/libstratosphere/source/os/impl/os_interrupt_event_impl.hpp new file mode 100644 index 000000000..842affffd --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_interrupt_event_impl.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#if defined(ATMOSPHERE_OS_HORIZON) + #include "os_interrupt_event_target_impl.os.horizon.hpp" +#else + #error "Unknown OS for ams::os::InterruptEventImpl" +#endif + +namespace ams::os::impl { + + class InterruptEventImpl { + private: + InterruptEventTargetImpl impl; + public: + explicit InterruptEventImpl(InterruptName name, EventClearMode clear_mode) : impl(name, clear_mode) { /* ... */ } + + void Clear() { + return this->impl.Clear(); + } + + void Wait() { + return this->impl.Wait(); + } + + bool TryWait() { + return this->impl.TryWait(); + } + + bool TimedWait(TimeSpan timeout) { + return this->impl.TimedWait(timeout); + } + + TriBool IsSignaled() { + return this->impl.IsSignaled(); + } + + Handle GetHandle() const { + return this->impl.GetHandle(); + } + }; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_interrupt_event_target_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_interrupt_event_target_impl.os.horizon.cpp new file mode 100644 index 000000000..f68187fbf --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_interrupt_event_target_impl.os.horizon.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_interrupt_event_target_impl.os.horizon.hpp" +#include "os_timeout_helper.hpp" + +namespace ams::os::impl { + + InterruptEventHorizonImpl::InterruptEventHorizonImpl(InterruptName name, EventClearMode clear_mode) { + this->manual_clear = (clear_mode == EventClearMode_ManualClear); + + auto interrupt_type = this->manual_clear ? svc::InterruptType_Level : svc::InterruptType_Edge; + svc::Handle handle; + R_ABORT_UNLESS(svc::CreateInterruptEvent(std::addressof(handle), static_cast(name), interrupt_type)); + + this->handle = handle; + } + + InterruptEventHorizonImpl::~InterruptEventHorizonImpl() { + R_ABORT_UNLESS(svc::CloseHandle(this->handle)); + } + + void InterruptEventHorizonImpl::Clear() { + R_ABORT_UNLESS(svc::ClearEvent(this->handle)); + } + + void InterruptEventHorizonImpl::Wait() { + while (true) { + /* Continuously wait, until success. */ + s32 index; + Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(this->handle), 1, svc::WaitInfinite); + if (R_SUCCEEDED(res)) { + /* Clear, if we must. */ + if (!this->manual_clear) { + R_TRY_CATCH(svc::ResetSignal(this->handle)) { + /* Some other thread might have caught this before we did. */ + R_CATCH(svc::ResultInvalidState) { continue; } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + } + + return; + } + + AMS_ASSERT(svc::ResultCancelled::Includes(res)); + } + } + + bool InterruptEventHorizonImpl::TryWait() { + /* If we're auto clear, just try to reset. */ + if (!this->manual_clear) { + return R_SUCCEEDED(svc::ResetSignal(this->handle)); + } + + /* Not auto-clear. */ + while (true) { + /* Continuously wait, until success or timeout. */ + s32 index; + Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(this->handle), 1, 0); + + /* If we succeeded, we're signaled. */ + if (R_SUCCEEDED(res)) { + return true; + } + + /* If we timed out, we're not signaled. */ + if (svc::ResultTimedOut::Includes(res)) { + return false; + } + + AMS_ASSERT(svc::ResultCancelled::Includes(res)); + } + } + + bool InterruptEventHorizonImpl::TimedWait(TimeSpan timeout) { + TimeoutHelper timeout_helper(timeout); + + while (true) { + /* Continuously wait, until success. */ + s32 index; + Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(this->handle), 1, timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds()); + if (R_SUCCEEDED(res)) { + /* Clear, if we must. */ + if (!this->manual_clear) { + R_TRY_CATCH(svc::ResetSignal(this->handle)) { + /* Some other thread might have caught this before we did. */ + R_CATCH(svc::ResultInvalidState) { continue; } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + } + + return true; + } + + if (svc::ResultTimedOut::Includes(res)) { + return false; + } + + AMS_ASSERT(svc::ResultCancelled::Includes(res)); + } + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_interrupt_event_target_impl.os.horizon.hpp b/libraries/libstratosphere/source/os/impl/os_interrupt_event_target_impl.os.horizon.hpp new file mode 100644 index 000000000..c70f35224 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_interrupt_event_target_impl.os.horizon.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::os::impl { + + class InterruptEventHorizonImpl { + private: + svc::Handle handle; + bool manual_clear; + public: + explicit InterruptEventHorizonImpl(InterruptName name, EventClearMode mode); + ~InterruptEventHorizonImpl(); + + void Clear(); + void Wait(); + bool TryWait(); + bool TimedWait(TimeSpan timeout); + + TriBool IsSignaled() { + return TriBool::Undefined; + } + + Handle GetHandle() const { + return this->handle; + } + }; + + using InterruptEventTargetImpl = InterruptEventHorizonImpl; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_mutex_impl.hpp b/libraries/libstratosphere/source/os/impl/os_mutex_impl.hpp new file mode 100644 index 000000000..d905e72ee --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_mutex_impl.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::os::impl { + + void PushAndCheckLockLevel(MutexType *mutex); + void PopAndCheckLockLevel(MutexType *mutex); + +} diff --git a/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp b/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp index 61525e908..11e5209b6 100644 --- a/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp +++ b/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp @@ -16,6 +16,7 @@ #pragma once #include #include "os_rng_manager_impl.hpp" +#include "os_thread_manager_types.hpp" #include "os_tick_manager_impl.hpp" namespace ams::os::impl { @@ -24,12 +25,15 @@ namespace ams::os::impl { private: RngManager rng_manager{}; /* TODO */ + ThreadManager thread_manager{}; + /* TODO */ TickManager tick_manager{}; /* TODO */ public: - constexpr OsResourceManager() = default; + OsResourceManager() = default; constexpr ALWAYS_INLINE RngManager &GetRngManager() { return this->rng_manager; } + constexpr ALWAYS_INLINE ThreadManager &GetThreadManager() { return this->thread_manager; } constexpr ALWAYS_INLINE TickManager &GetTickManager() { return this->tick_manager; } }; diff --git a/libraries/libstratosphere/source/os/impl/os_rng_manager.hpp b/libraries/libstratosphere/source/os/impl/os_rng_manager.hpp index 7abc003f8..387dcbe41 100644 --- a/libraries/libstratosphere/source/os/impl/os_rng_manager.hpp +++ b/libraries/libstratosphere/source/os/impl/os_rng_manager.hpp @@ -15,6 +15,7 @@ */ #pragma once #include +#include "os_resource_manager.hpp" namespace ams::os::impl { diff --git a/libraries/libstratosphere/source/os/impl/os_rng_manager_impl.hpp b/libraries/libstratosphere/source/os/impl/os_rng_manager_impl.hpp index 25406633b..b782edde3 100644 --- a/libraries/libstratosphere/source/os/impl/os_rng_manager_impl.hpp +++ b/libraries/libstratosphere/source/os/impl/os_rng_manager_impl.hpp @@ -26,7 +26,7 @@ namespace ams::os::impl { private: void Initialize(); public: - constexpr RngManager() : mt(), lock(), initialized() { /* ... */ } + constexpr RngManager() : mt(), lock(false), initialized() { /* ... */ } public: u64 GenerateRandomU64(); }; diff --git a/libraries/libstratosphere/source/os/impl/os_thread_manager.cpp b/libraries/libstratosphere/source/os/impl/os_thread_manager.cpp new file mode 100644 index 000000000..8fee951ae --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_thread_manager.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_thread_manager.hpp" +#include "os_waitable_manager_impl.hpp" +#include "os_waitable_holder_base.hpp" +#include "os_waitable_holder_impl.hpp" +#include "os_waitable_object_list.hpp" + +namespace ams::os::impl { + + void SetupThreadObjectUnsafe(ThreadType *thread, ThreadImpl *thread_impl, ThreadFunction function, void *arg, void *stack, size_t stack_size, s32 priority) { + /* Setup objects. */ + new (GetPointer(thread->cs_thread)) impl::InternalCriticalSection; + new (GetPointer(thread->cv_thread)) impl::InternalConditionVariable; + + new (GetPointer(thread->all_threads_node)) util::IntrusiveListNode; + new (GetPointer(thread->waitlist)) WaitableObjectList; + + /* Set member variables. */ + thread->thread_impl = (thread_impl != nullptr) ? thread_impl : std::addressof(thread->thread_impl_storage); + thread->function = function; + thread->argument = arg; + thread->stack = stack; + thread->stack_size = stack_size; + thread->base_priority = priority; + thread->suspend_count = 0; + thread->name_buffer[0] = '\x00'; + thread->name_pointer = thread->name_buffer; + + /* Mark initialized. */ + thread->state = ThreadType::State_Initialized; + } + + void ThreadManager::InvokeThread(ThreadType *thread) { + auto &manager = GetThreadManager(); + + manager.SetCurrentThread(thread); + manager.NotifyThreadNameChanged(thread); + + { + std::unique_lock lk(GetReference(thread->cs_thread)); + while (thread->state == ThreadType::State_Initialized) { + GetReference(thread->cv_thread).Wait(GetPointer(thread->cs_thread)); + } + + if (thread->state == ThreadType::State_Started) { + lk.unlock(); + + thread->function(thread->argument); + } + } + + manager.CleanupThread(); + } + + ThreadManager::ThreadManager() : impl(std::addressof(main_thread)), total_thread_stack_size(0), num_created_threads(0) { + this->main_thread.state = ThreadType::State_Started; + + this->SetCurrentThread(std::addressof(this->main_thread)); + + this->PlaceThreadObjectUnderThreadManagerSafe(std::addressof(this->main_thread)); + } + + void ThreadManager::CleanupThread() { + ThreadType *thread = this->GetCurrentThread(); + + { + std::scoped_lock lk(GetReference(thread->cs_thread)); + + thread->state = ThreadType::State_Terminated; + + GetReference(thread->cv_thread).Broadcast(); + GetReference(thread->waitlist).SignalAllThreads(); + } + } + + Result ThreadManager::CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority, s32 ideal_core) { + SetupThreadObjectUnsafe(thread, nullptr, function, argument, stack, stack_size, priority); + + auto guard = SCOPE_GUARD { thread->state = ThreadType::State_NotInitialized; }; + R_TRY(this->impl.CreateThread(thread, ideal_core)); + guard.Cancel(); + + this->PlaceThreadObjectUnderThreadManagerSafe(thread); + + return ResultSuccess(); + } + + Result ThreadManager::CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority) { + return this->CreateThread(thread, function, argument, stack, stack_size, priority, this->impl.GetDefaultCoreNumber()); + } + + void ThreadManager::DestroyThread(ThreadType *thread) { + { + std::scoped_lock lk(GetReference(thread->cs_thread)); + + if (thread->state == ThreadType::State_Initialized) { + thread->state = ThreadType::State_DestroyedBeforeStarted; + this->impl.StartThread(thread); + GetReference(thread->cv_thread).Signal(); + } + } + + this->impl.WaitForThreadExit(thread); + + AMS_ASSERT(thread->state == ThreadType::State_Initialized); + + { + std::scoped_lock lk(GetReference(thread->cs_thread)); + + /* NOTE: Here Nintendo would cleanup the alias stack. */ + + this->impl.DestroyThreadUnsafe(thread); + + thread->state = ThreadType::State_NotInitialized; + + GetReference(thread->waitlist).~WaitableObjectList(); + + thread->name_buffer[0] = '\x00'; + + { + std::scoped_lock tlk(this->cs); + this->EraseFromAllThreadsListUnsafe(thread); + } + } + } + + void ThreadManager::StartThread(ThreadType *thread) { + std::scoped_lock lk(GetReference(thread->cs_thread)); + + AMS_ASSERT(thread->state == ThreadType::State_Initialized); + + this->impl.StartThread(thread); + thread->state = ThreadType::State_Started; + + GetReference(thread->cv_thread).Signal(); + } + + void ThreadManager::WaitThread(ThreadType *thread) { + this->impl.WaitForThreadExit(thread); + + { + std::scoped_lock lk(GetReference(thread->cs_thread)); + + /* Note: Here Nintendo would cleanup the alias stack. */ + } + } + + bool ThreadManager::TryWaitThread(ThreadType *thread) { + const bool result = this->impl.TryWaitForThreadExit(thread); + + if (result) { + std::scoped_lock lk(GetReference(thread->cs_thread)); + + /* Note: Here Nintendo would cleanup the alias stack. */ + } + + return result; + } + + s32 ThreadManager::SuspendThread(ThreadType *thread) { + std::scoped_lock lk(GetReference(thread->cs_thread)); + + auto prev_suspend_count = thread->suspend_count; + AMS_ASSERT(prev_suspend_count < ThreadSuspendCountMax); + thread->suspend_count = prev_suspend_count + 1; + + if (prev_suspend_count == 0) { + this->impl.SuspendThreadUnsafe(thread); + } + return prev_suspend_count; + } + + s32 ThreadManager::ResumeThread(ThreadType *thread) { + std::scoped_lock lk(GetReference(thread->cs_thread)); + + auto prev_suspend_count = thread->suspend_count; + if (prev_suspend_count > 0) { + thread->suspend_count = prev_suspend_count - 1; + if (prev_suspend_count == 1) { + this->impl.ResumeThreadUnsafe(thread); + } + } + return prev_suspend_count; + } + + void ThreadManager::CancelThreadSynchronization(ThreadType *thread) { + std::scoped_lock lk(GetReference(thread->cs_thread)); + + this->impl.CancelThreadSynchronizationUnsafe(thread); + } + + /* TODO void ThreadManager::GetThreadContext(ThreadContextInfo *out_context, const ThreadType *thread); */ + + void ThreadManager::SetInitialThreadNameUnsafe(ThreadType *thread) { + if (thread == std::addressof(this->main_thread)) { + constexpr const char MainThreadName[] = "MainThread"; + static_assert(sizeof(thread->name_buffer) >= sizeof(MainThreadName)); + static_assert(MainThreadName[sizeof(MainThreadName) - 1] == '\x00'); + std::memcpy(thread->name_buffer, MainThreadName, sizeof(MainThreadName)); + } else { + constexpr const char ThreadNamePrefix[] = "Thread_0x"; + constexpr size_t ThreadNamePrefixSize = sizeof(ThreadNamePrefix) - 1; + const u64 func = reinterpret_cast(thread->function); + static_assert(ThreadNamePrefixSize + sizeof(func) * 2 + 1 <= sizeof(thread->name_buffer)); + std::snprintf(thread->name_buffer, sizeof(thread->name_buffer), "%s%016lX", ThreadNamePrefix, func); + } + + thread->name_pointer = thread->name_buffer; + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_thread_manager.hpp b/libraries/libstratosphere/source/os/impl/os_thread_manager.hpp new file mode 100644 index 000000000..ae6c28e72 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_thread_manager.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "os_thread_manager_types.hpp" +#include "os_resource_manager.hpp" + +namespace ams::os::impl { + + constexpr inline s32 CoreAffinityMaskBitWidth = BITSIZEOF(u64); + + ALWAYS_INLINE ThreadManager &GetThreadManager() { + return GetResourceManager().GetThreadManager(); + } + + ALWAYS_INLINE ThreadType *GetCurrentThread() { + return GetThreadManager().GetCurrentThread(); + } + + ALWAYS_INLINE Handle GetCurrentThreadHandle() { + /* return GetCurrentThread()->thread_impl->handle; */ + return ::threadGetCurHandle(); + } + + void SetupThreadObjectUnsafe(ThreadType *thread, ThreadImpl *thread_impl, ThreadFunction function, void *arg, void *stack, size_t stack_size, s32 priority); + +} diff --git a/libraries/libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.cpp new file mode 100644 index 000000000..3f3c95534 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_thread_manager_impl.os.horizon.hpp" +#include "os_thread_manager.hpp" + +namespace ams::os::impl { + + thread_local ThreadType *g_current_thread_pointer; + + namespace { + + s32 ConvertToHorizonPriority(s32 user_priority) { + const s32 horizon_priority = user_priority + UserThreadPriorityOffset; + AMS_ASSERT(HighestTargetThreadPriority <= horizon_priority && horizon_priority <= LowestTargetThreadPriority); + return horizon_priority; + } + + s32 ConvertToUserPriority(s32 horizon_priority) { + AMS_ASSERT(HighestTargetThreadPriority <= horizon_priority && horizon_priority <= LowestTargetThreadPriority); + return horizon_priority - UserThreadPriorityOffset; + } + + void InvokeThread(uintptr_t _thread) { + ThreadType *thread = reinterpret_cast(_thread); + + /* Set the thread's id. */ + u64 thread_id; + R_ABORT_UNLESS(svc::GetThreadId(std::addressof(thread_id), svc::Handle(thread->thread_impl->handle))); + thread->thread_id = thread_id; + + /* Invoke the thread. */ + ThreadManager::InvokeThread(thread); + } + + } + + ThreadManagerHorizonImpl::ThreadManagerHorizonImpl(ThreadType *main_thread) { + /* Get the thread impl object from libnx. */ + ThreadImpl *thread_impl = ::threadGetSelf(); + + /* Get the thread priority. */ + s32 horizon_priority; + R_ABORT_UNLESS(svc::GetThreadPriority(std::addressof(horizon_priority), thread_impl->handle)); + + SetupThreadObjectUnsafe(main_thread, thread_impl, nullptr, nullptr, thread_impl->stack_mirror, thread_impl->stack_sz, ConvertToUserPriority(horizon_priority)); + + /* Set the thread id. */ + u64 thread_id; + R_ABORT_UNLESS(svc::GetThreadId(std::addressof(thread_id), svc::Handle(thread_impl->handle))); + main_thread->thread_id = thread_id; + + /* NOTE: Here Nintendo would set the thread pointer in TLS. */ + } + + Result ThreadManagerHorizonImpl::CreateThread(ThreadType *thread, s32 ideal_core) { + /* Note: Here Nintendo would set the stack args to point to ThreadManager::InvokeThread. */ + + s32 count = 0; + while (true) { + R_TRY_CATCH(::threadCreate(thread->thread_impl, reinterpret_cast<::ThreadFunc>(&InvokeThread), thread, thread->stack, thread->stack_size, ConvertToHorizonPriority(thread->base_priority), ideal_core)) { + R_CATCH(svc::ResultOutOfResource) { + if ((++count) < 10) { + os::SleepThread(TimeSpan::FromMilliSeconds(10)); + continue; + } + return os::ResultOutOfResource(); + } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + return ResultSuccess(); + } + } + + void ThreadManagerHorizonImpl::DestroyThreadUnsafe(ThreadType *thread) { + R_ABORT_UNLESS(::threadClose(thread->thread_impl)); + } + + void ThreadManagerHorizonImpl::StartThread(const ThreadType *thread) { + R_ABORT_UNLESS(::threadStart(thread->thread_impl)); + } + + void ThreadManagerHorizonImpl::WaitForThreadExit(ThreadType *thread) { + const svc::Handle handle(thread->thread_impl->handle); + + while (true) { + s32 index; + R_TRY_CATCH(svc::WaitSynchronization(std::addressof(index), std::addressof(handle), 1, svc::WaitInfinite)) { + R_CATCH(svc::ResultCancelled) { continue; } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + return; + } + } + + bool ThreadManagerHorizonImpl::TryWaitForThreadExit(ThreadType *thread) { + const svc::Handle handle(thread->thread_impl->handle); + + while (true) { + /* Continuously wait, until success or timeout. */ + s32 index; + Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(handle), 1, 0); + + /* If we succeeded, we're signaled. */ + if (R_SUCCEEDED(res)) { + return true; + } + + /* If we timed out, we're not signaled. */ + if (svc::ResultTimedOut::Includes(res)) { + return false; + } + + AMS_ABORT_UNLESS(svc::ResultCancelled::Includes(res)); + } + } + + void ThreadManagerHorizonImpl::YieldThread() { + if (hos::GetVersion() >= hos::Version_400) { + svc::SleepThread(svc::YieldType_WithCoreMigration); + } else { + svc::SleepThread(svc::YieldType_WithoutCoreMigration); + } + } + + bool ThreadManagerHorizonImpl::ChangePriority(ThreadType *thread, s32 priority) { + const svc::Handle handle(thread->thread_impl->handle); + + auto res = svc::SetThreadPriority(handle, ConvertToHorizonPriority(priority)); + if (svc::ResultInvalidPriority::Includes(res)) { + AMS_ABORT("Invalid thread priority"); + } + + return R_SUCCEEDED(res); + } + + s32 ThreadManagerHorizonImpl::GetCurrentPriority(const ThreadType *thread) const { + const svc::Handle handle(thread->thread_impl->handle); + s32 priority; + + R_ABORT_UNLESS(svc::GetThreadPriority(std::addressof(priority), handle)); + + return ConvertToUserPriority(priority); + } + + ThreadId ThreadManagerHorizonImpl::GetThreadId(const ThreadType *thread) const { + return thread->thread_id; + } + + void ThreadManagerHorizonImpl::SuspendThreadUnsafe(ThreadType *thread) { + const svc::Handle handle(thread->thread_impl->handle); + + R_ABORT_UNLESS(svc::SetThreadActivity(handle, svc::ThreadActivity_Paused)); + } + + void ThreadManagerHorizonImpl::ResumeThreadUnsafe(ThreadType *thread) { + const svc::Handle handle(thread->thread_impl->handle); + + R_ABORT_UNLESS(svc::SetThreadActivity(handle, svc::ThreadActivity_Runnable)); + } + + void ThreadManagerHorizonImpl::CancelThreadSynchronizationUnsafe(ThreadType *thread) { + const svc::Handle handle(thread->thread_impl->handle); + + R_ABORT_UNLESS(svc::CancelSynchronization(handle)); + } + + /* TODO: void GetThreadContextUnsafe(ThreadContextInfo *out_context, const ThreadType *thread); */ + + s32 ThreadManagerHorizonImpl::GetCurrentCoreNumber() const { + return svc::GetCurrentProcessorNumber(); + } + + void ThreadManagerHorizonImpl::SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) const { + const svc::Handle handle(thread->thread_impl->handle); + R_ABORT_UNLESS(svc::SetThreadCoreMask(handle, ideal_core, affinity_mask)); + } + + void ThreadManagerHorizonImpl::GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const { + s32 ideal_core; + u64 affinity_mask; + + const svc::Handle handle(thread->thread_impl->handle); + R_ABORT_UNLESS(svc::GetThreadCoreMask(std::addressof(ideal_core), std::addressof(affinity_mask), handle)); + + if (out_ideal_core) { + *out_ideal_core = ideal_core; + } + if (out_affinity_mask) { + *out_affinity_mask = affinity_mask; + } + } + + u64 ThreadManagerHorizonImpl::GetThreadAvailableCoreMask() const { + u64 core_mask; + R_ABORT_UNLESS(svc::GetInfo(std::addressof(core_mask), svc::InfoType_CoreMask, svc::PseudoHandle::CurrentProcess, 0)); + return core_mask; + } + + +} diff --git a/libraries/libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.hpp b/libraries/libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.hpp new file mode 100644 index 000000000..b0e4f8538 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::os::impl { + + constexpr inline s32 TargetThreadPriorityRangeSize = svc::LowestThreadPriority - svc::HighestThreadPriority + 1; + constexpr inline s32 ReservedThreadPriorityRangeSize = svc::SystemThreadPriorityHighest; + constexpr inline s32 SystemThreadPriorityRangeSize = TargetThreadPriorityRangeSize - ReservedThreadPriorityRangeSize; + constexpr inline s32 UserThreadPriorityOffset = 28; + + constexpr inline s32 HighestTargetThreadPriority = 0; + constexpr inline s32 LowestTargetThreadPriority = TargetThreadPriorityRangeSize - 1; + + extern thread_local ThreadType *g_current_thread_pointer; + + class ThreadManagerHorizonImpl { + NON_COPYABLE(ThreadManagerHorizonImpl); + NON_MOVEABLE(ThreadManagerHorizonImpl); + public: + explicit ThreadManagerHorizonImpl(ThreadType *main_thread); + + Result CreateThread(ThreadType *thread, s32 ideal_core); + void DestroyThreadUnsafe(ThreadType *thread); + void StartThread(const ThreadType *thread); + void WaitForThreadExit(ThreadType *thread); + bool TryWaitForThreadExit(ThreadType *thread); + void YieldThread(); + bool ChangePriority(ThreadType *thread, s32 priority); + s32 GetCurrentPriority(const ThreadType *thread) const; + ThreadId GetThreadId(const ThreadType *thread) const; + + void SuspendThreadUnsafe(ThreadType *thread); + void ResumeThreadUnsafe(ThreadType *thread); + + void CancelThreadSynchronizationUnsafe(ThreadType *thread); + + /* TODO: void GetThreadContextUnsafe(ThreadContextInfo *out_context, const ThreadType *thread); */ + + void NotifyThreadNameChangedImpl(const ThreadType *thread) const { /* ... */ } + + void SetCurrentThread(ThreadType *thread) const { + g_current_thread_pointer = thread; + } + + ThreadType *GetCurrentThread() const { + return g_current_thread_pointer; + } + + s32 GetCurrentCoreNumber() const; + s32 GetDefaultCoreNumber() const { return svc::IdealCoreUseProcessValue; } + + void SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) const; + void GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const; + u64 GetThreadAvailableCoreMask() const; + + NORETURN void ExitProcessImpl() { + svc::ExitProcess(); + AMS_ABORT("Process was exited"); + } + }; + + using ThreadManagerImpl = ThreadManagerHorizonImpl; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_thread_manager_types.hpp b/libraries/libstratosphere/source/os/impl/os_thread_manager_types.hpp new file mode 100644 index 000000000..3209d1d76 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_thread_manager_types.hpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#ifdef ATMOSPHERE_OS_HORIZON + #include "os_thread_manager_impl.os.horizon.hpp" +#else + #error "Unknown OS for ThreadManagerImpl" +#endif + +namespace ams::os::impl { + + class ThreadManager { + NON_COPYABLE(ThreadManager); + NON_MOVEABLE(ThreadManager); + private: + class ThreadListTraits { + public: + using ListType = util::IntrusiveList; + private: + friend class util::IntrusiveList; + + static constexpr util::IntrusiveListNode &GetNode(ThreadType &parent) { + return GetReference(parent.all_threads_node); + } + + static constexpr util::IntrusiveListNode const &GetNode(ThreadType const &parent) { + return GetReference(parent.all_threads_node); + } + + static ThreadType &GetParent(util::IntrusiveListNode &node) { + return *reinterpret_cast(reinterpret_cast(std::addressof(node)) - OFFSETOF(ThreadType, all_threads_node)); + } + + static ThreadType const &GetParent(util::IntrusiveListNode const &node) { + return *reinterpret_cast(reinterpret_cast(std::addressof(node)) - OFFSETOF(ThreadType, all_threads_node)); + } + }; + + using AllThreadsList = ThreadListTraits::ListType; + private: + ThreadManagerImpl impl; + ThreadType main_thread; + InternalCriticalSection cs; + AllThreadsList all_threads_list; + size_t total_thread_stack_size; + s32 num_created_threads; + public: + ThreadManager(); + + void CleanupThread(); + s32 GetThreadCountForDebug() const { return this->num_created_threads; } + + Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority, s32 ideal_core); + Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority); + + void DestroyThread(ThreadType *thread); + void StartThread(ThreadType *thread); + void WaitThread(ThreadType *thread); + bool TryWaitThread(ThreadType *thread); + + void YieldThread() { return this->impl.YieldThread(); } + + bool ChangePriority(ThreadType *thread, s32 priority) { return this->impl.ChangePriority(thread, priority); } + s32 GetCurrentPriority(const ThreadType *thread) const { return this->impl.GetCurrentPriority(thread); } + ThreadType *GetCurrentThread() const { return this->impl.GetCurrentThread(); } + + s32 SuspendThread(ThreadType *thread); + s32 ResumeThread(ThreadType *thread); + + void CancelThreadSynchronization(ThreadType *thread); + + /* TODO void GetThreadContext(ThreadContextInfo *out_context, const ThreadType *thread); */ + + void SetInitialThreadNameUnsafe(ThreadType *thread); + + void NotifyThreadNameChanged(const ThreadType *thread) const { return this->impl.NotifyThreadNameChangedImpl(thread); } + void SetCurrentThread(ThreadType *thread) const { return this->impl.SetCurrentThread(thread); } + s32 GetCurrentCoreNumber() const { return this->impl.GetCurrentCoreNumber(); } + void SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) const { return this->impl.SetThreadCoreMask(thread, ideal_core, affinity_mask); } + void GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const { return this->impl.GetThreadCoreMask(out_ideal_core, out_affinity_mask, thread); } + u64 GetThreadAvailableCoreMask() const { return this->impl.GetThreadAvailableCoreMask(); } + + void PushBackToAllThreadsListUnsafe(ThreadType *thread) { + this->all_threads_list.push_back(*thread); + ++this->num_created_threads; + this->total_thread_stack_size += thread->stack_size; + } + + void EraseFromAllThreadsListUnsafe(ThreadType *thread) { + this->all_threads_list.erase(this->all_threads_list.iterator_to(*thread)); + --this->num_created_threads; + this->total_thread_stack_size -= thread->stack_size; + } + + void PushBackToAllThreadsListSafe(ThreadType *thread) { + std::scoped_lock lk(this->cs); + this->PushBackToAllThreadsListUnsafe(thread); + } + + void EraseFromAllThreadsListSafe(ThreadType *thread) { + std::scoped_lock lk(this->cs); + this->EraseFromAllThreadsListUnsafe(thread); + } + + void PlaceThreadObjectUnderThreadManagerSafe(ThreadType *thread) { + SetInitialThreadNameUnsafe(thread); + { + std::scoped_lock lk(this->cs); + this->PushBackToAllThreadsListUnsafe(thread); + } + } + + ThreadType *AllocateThreadType() const { + return reinterpret_cast(std::malloc(sizeof(ThreadType))); + } + + void FreeThreadType(ThreadType *thread) const { + std::free(thread); + } + + const ThreadType *GetMainThread() const { + return std::addressof(this->main_thread); + } + + size_t GetTotalThreadStackSize() const { + return this->total_thread_stack_size; + } + + ThreadId GetThreadId(const ThreadType *thread) { + return this->impl.GetThreadId(thread); + } + public: + static void InvokeThread(ThreadType *thread); + }; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_timeout_helper.cpp b/libraries/libstratosphere/source/os/impl/os_timeout_helper.cpp new file mode 100644 index 000000000..41468ad98 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_timeout_helper.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_timeout_helper.hpp" + +namespace ams::os::impl { + + TargetTimeSpan TimeoutHelper::GetTimeLeftOnTarget() const { + /* If the absolute tick is zero, we're expired. */ + if (this->absolute_end_tick.GetInt64Value() == 0) { + return 0; + } + + /* Check if we've expired. */ + const Tick cur_tick = impl::GetTickManager().GetTick(); + if (cur_tick >= this->absolute_end_tick) { + return 0; + } + + /* Return the converted difference as a timespan. */ + return TimeoutHelperImpl::ConvertToImplTime(this->absolute_end_tick - cur_tick); + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_timeout_helper.hpp b/libraries/libstratosphere/source/os/impl/os_timeout_helper.hpp new file mode 100644 index 000000000..7d128c8bc --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_timeout_helper.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "os_tick_manager.hpp" + +#if defined(ATMOSPHERE_OS_HORIZON) + #include "os_timeout_helper_impl.os.horizon.hpp" +#else + #error "Unknown OS for ams::os::TimeoutHelper" +#endif + +namespace ams::os::impl { + + class TimeoutHelper { + private: + Tick absolute_end_tick; + public: + explicit TimeoutHelper(TimeSpan timeout) { + if (timeout == 0) { + /* If timeout is zero, don't do relative tick calculations. */ + this->absolute_end_tick = Tick(0); + } else { + const auto &tick_manager = impl::GetTickManager(); + + const u64 cur_tick = tick_manager.GetTick().GetInt64Value(); + const u64 timeout_tick = tick_manager.ConvertToTick(timeout).GetInt64Value(); + const u64 end_tick = cur_tick + timeout_tick + 1; + + this->absolute_end_tick = Tick(std::min(std::numeric_limits::max(), end_tick)); + } + } + + static void Sleep(TimeSpan tm) { + TimeoutHelperImpl::Sleep(tm); + } + + bool TimedOut() const { + if (this->absolute_end_tick.GetInt64Value() == 0) { + return true; + } + + const Tick cur_tick = impl::GetTickManager().GetTick(); + + return cur_tick >= this->absolute_end_tick; + } + + TargetTimeSpan GetTimeLeftOnTarget() const; + }; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_timeout_helper_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_timeout_helper_impl.os.horizon.cpp new file mode 100644 index 000000000..47ea439e6 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_timeout_helper_impl.os.horizon.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_timeout_helper_impl.os.horizon.hpp" +#include "os_thread_manager.hpp" + +namespace ams::os::impl { + + void TimeoutHelperImpl::Sleep(TimeSpan tm) { + if (tm == TimeSpan(0)) { + GetThreadManager().YieldThread(); + } else { + ams::svc::SleepThread(tm.GetNanoSeconds()); + } + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_timeout_helper_impl.os.horizon.hpp b/libraries/libstratosphere/source/os/impl/os_timeout_helper_impl.os.horizon.hpp new file mode 100644 index 000000000..42ec0dd46 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_timeout_helper_impl.os.horizon.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "os_tick_manager.hpp" + +namespace ams::os::impl { + + using TargetTimeSpan = ::ams::TimeSpan; + + class TimeoutHelperImpl { + public: + static TargetTimeSpan ConvertToImplTime(Tick tick) { + return impl::GetTickManager().ConvertToTimeSpan(tick); + } + + static void Sleep(TimeSpan tm); + }; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_holder_base.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_holder_base.hpp index d64fc1b12..c6c098f96 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_holder_base.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_holder_base.hpp @@ -37,8 +37,8 @@ namespace ams::os::impl { /* 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 std::numeric_limits::max(); + virtual TimeSpan GetAbsoluteWakeupTime() const { + return TimeSpan::FromNanoSeconds(std::numeric_limits::max()); } /* Interface with manager. */ @@ -58,18 +58,18 @@ namespace ams::os::impl { class WaitableHolderOfUserObject : public WaitableHolderBase { public: /* All user objects have no handle to wait on. */ - virtual Handle GetHandle() const override { - return INVALID_HANDLE; + virtual Handle GetHandle() const override final { + return svc::InvalidHandle; } }; class WaitableHolderOfKernelObject : public WaitableHolderBase { public: /* All kernel objects have native handles, and thus don't have object list semantics. */ - virtual TriBool LinkToObjectList() override { + virtual TriBool LinkToObjectList() override final { return TriBool::Undefined; } - virtual void UnlinkFromObjectList() override { + virtual void UnlinkFromObjectList() override final { /* ... */ } }; diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp index 162acb136..25b3c02a7 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp @@ -51,6 +51,6 @@ namespace ams::os::impl { #undef CHECK_HOLDER - static_assert(std::is_trivial::value && std::is_trivially_destructible::value, "WaitableHolderImpl"); - static_assert(sizeof(WaitableHolderImpl) == WaitableHolder::ImplStorageSize, "WaitableHolderImpl size"); + static_assert(std::is_trivial::value && std::is_trivially_destructible::value); + static_assert(sizeof(WaitableHolderImpl) == sizeof(os::WaitableHolderType::impl_storage)); } diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_event.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_event.hpp index 2e356e30f..8b203c4aa 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_event.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_event.hpp @@ -21,29 +21,29 @@ namespace ams::os::impl { class WaitableHolderOfEvent : public WaitableHolderOfUserObject { private: - Event *event; + EventType *event; private: TriBool IsSignaledImpl() const { return this->event->signaled ? TriBool::True : TriBool::False; } public: - explicit WaitableHolderOfEvent(Event *e) : event(e) { /* ... */ } + explicit WaitableHolderOfEvent(EventType *e) : event(e) { /* ... */ } /* IsSignaled, Link, Unlink implemented. */ virtual TriBool IsSignaled() const override { - std::scoped_lock lk(this->event->lock); + std::scoped_lock lk(GetReference(this->event->cs_event)); return this->IsSignaledImpl(); } virtual TriBool LinkToObjectList() override { - std::scoped_lock lk(this->event->lock); + std::scoped_lock lk(GetReference(this->event->cs_event)); GetReference(this->event->waitable_object_list_storage).LinkWaitableHolder(*this); return this->IsSignaledImpl(); } virtual void UnlinkFromObjectList() override { - std::scoped_lock lk(this->event->lock); + std::scoped_lock lk(GetReference(this->event->cs_event)); GetReference(this->event->waitable_object_list_storage).UnlinkWaitableHolder(*this); } diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_inter_process_event.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_inter_process_event.hpp index 99adfdde1..3012da3eb 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_inter_process_event.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_inter_process_event.hpp @@ -21,9 +21,9 @@ namespace ams::os::impl { class WaitableHolderOfInterProcessEvent : public WaitableHolderOfKernelObject { private: - InterProcessEvent *event; + InterProcessEventType *event; public: - explicit WaitableHolderOfInterProcessEvent(InterProcessEvent *e) : event(e) { /* ... */ } + explicit WaitableHolderOfInterProcessEvent(InterProcessEventType *e) : event(e) { /* ... */ } /* IsSignaled, GetHandle both implemented. */ virtual TriBool IsSignaled() const override { @@ -31,8 +31,7 @@ namespace ams::os::impl { } virtual Handle GetHandle() const override { - AMS_ABORT_UNLESS(this->event->is_initialized); - return this->event->GetReadableHandle(); + return this->event->readable_handle; } }; diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_interrupt_event.cpp b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_interrupt_event.cpp new file mode 100644 index 000000000..e553f131c --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_interrupt_event.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_waitable_holder_of_interrupt_event.hpp" +#include "os_interrupt_event_impl.hpp" + +namespace ams::os::impl { + + Handle WaitableHolderOfInterruptEvent::GetHandle() const { + return GetReference(event->impl).GetHandle(); + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_interrupt_event.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_interrupt_event.hpp index 66ae893af..10dcea933 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_interrupt_event.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_interrupt_event.hpp @@ -20,19 +20,16 @@ namespace ams::os::impl { class WaitableHolderOfInterruptEvent : public WaitableHolderOfKernelObject { private: - InterruptEvent *event; + InterruptEventType *event; public: - explicit WaitableHolderOfInterruptEvent(InterruptEvent *e) : event(e) { /* ... */ } + explicit WaitableHolderOfInterruptEvent(InterruptEventType *e) : event(e) { /* ... */ } /* IsSignaled, GetHandle both implemented. */ virtual TriBool IsSignaled() const override { return TriBool::Undefined; } - virtual Handle GetHandle() const override { - AMS_ABORT_UNLESS(this->event->is_initialized); - return this->event->handle.Get(); - } + virtual Handle GetHandle() const override; }; } diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_message_queue.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_message_queue.hpp index 0beb92938..bc5468399 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_message_queue.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_message_queue.hpp @@ -19,57 +19,57 @@ namespace ams::os::impl { - template + template class WaitableHolderOfMessageQueue : public WaitableHolderOfUserObject { - static_assert(WaitKind == MessageQueueWaitKind::ForNotEmpty || WaitKind == MessageQueueWaitKind::ForNotFull, "MessageQueueHolder WaitKind!"); + static_assert(WaitType == MessageQueueWaitType::ForNotEmpty || WaitType == MessageQueueWaitType::ForNotFull); private: - MessageQueue *message_queue; + MessageQueueType *mq; private: constexpr inline TriBool IsSignaledImpl() const { - if constexpr (WaitKind == MessageQueueWaitKind::ForNotEmpty) { + if constexpr (WaitType == MessageQueueWaitType::ForNotEmpty) { /* ForNotEmpty. */ - return this->message_queue->IsEmpty() ? TriBool::False : TriBool::True; - } else if constexpr (WaitKind == MessageQueueWaitKind::ForNotFull) { + return this->mq->count > 0 ? TriBool::True : TriBool::False; + } else if constexpr (WaitType == MessageQueueWaitType::ForNotFull) { /* ForNotFull */ - return this->message_queue->IsFull() ? TriBool::False : TriBool::True; + return this->mq->count < this->mq->capacity ? TriBool::True : TriBool::False; } else { - static_assert(WaitKind != WaitKind); + static_assert(WaitType != WaitType); } } constexpr inline WaitableObjectList &GetObjectList() const { - if constexpr (WaitKind == MessageQueueWaitKind::ForNotEmpty) { - return GetReference(this->message_queue->waitlist_not_empty); - } else if constexpr (WaitKind == MessageQueueWaitKind::ForNotFull) { - return GetReference(this->message_queue->waitlist_not_full); + if constexpr (WaitType == MessageQueueWaitType::ForNotEmpty) { + return GetReference(this->mq->waitlist_not_empty); + } else if constexpr (WaitType == MessageQueueWaitType::ForNotFull) { + return GetReference(this->mq->waitlist_not_full); } else { - static_assert(WaitKind != WaitKind); + static_assert(WaitType != WaitType); } } public: - explicit WaitableHolderOfMessageQueue(MessageQueue *mq) : message_queue(mq) { /* ... */ } + explicit WaitableHolderOfMessageQueue(MessageQueueType *mq) : mq(mq) { /* ... */ } /* IsSignaled, Link, Unlink implemented. */ virtual TriBool IsSignaled() const override { - std::scoped_lock lk(this->message_queue->queue_lock); + std::scoped_lock lk(GetReference(this->mq->cs_queue)); return this->IsSignaledImpl(); } virtual TriBool LinkToObjectList() override { - std::scoped_lock lk(this->message_queue->queue_lock); + std::scoped_lock lk(GetReference(this->mq->cs_queue)); this->GetObjectList().LinkWaitableHolder(*this); return this->IsSignaledImpl(); } virtual void UnlinkFromObjectList() override { - std::scoped_lock lk(this->message_queue->queue_lock); + std::scoped_lock lk(GetReference(this->mq->cs_queue)); this->GetObjectList().UnlinkWaitableHolder(*this); } }; - using WaitableHolderOfMessageQueueForNotEmpty = WaitableHolderOfMessageQueue; - using WaitableHolderOfMessageQueueForNotFull = WaitableHolderOfMessageQueue; + using WaitableHolderOfMessageQueueForNotEmpty = WaitableHolderOfMessageQueue; + using WaitableHolderOfMessageQueueForNotFull = WaitableHolderOfMessageQueue; } diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_semaphore.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_semaphore.hpp index 846b328ee..bc42b4213 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_semaphore.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_semaphore.hpp @@ -21,29 +21,29 @@ namespace ams::os::impl { class WaitableHolderOfSemaphore : public WaitableHolderOfUserObject { private: - Semaphore *semaphore; + SemaphoreType *semaphore; private: TriBool IsSignaledImpl() const { return this->semaphore->count > 0 ? TriBool::True : TriBool::False; } public: - explicit WaitableHolderOfSemaphore(Semaphore *s) : semaphore(s) { /* ... */ } + explicit WaitableHolderOfSemaphore(SemaphoreType *s) : semaphore(s) { /* ... */ } /* IsSignaled, Link, Unlink implemented. */ virtual TriBool IsSignaled() const override { - std::scoped_lock lk(this->semaphore->mutex); + std::scoped_lock lk(GetReference(this->semaphore->cs_sema)); return this->IsSignaledImpl(); } virtual TriBool LinkToObjectList() override { - std::scoped_lock lk(this->semaphore->mutex); + std::scoped_lock lk(GetReference(this->semaphore->cs_sema)); GetReference(this->semaphore->waitlist).LinkWaitableHolder(*this); return this->IsSignaledImpl(); } virtual void UnlinkFromObjectList() override { - std::scoped_lock lk(this->semaphore->mutex); + std::scoped_lock lk(GetReference(this->semaphore->cs_sema)); GetReference(this->semaphore->waitlist).UnlinkWaitableHolder(*this); } diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_thread.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_thread.hpp index 6212a0b79..19a10a89b 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_thread.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_thread.hpp @@ -18,21 +18,33 @@ namespace ams::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 { + class WaitableHolderOfThread : public WaitableHolderOfUserObject { private: - Thread *thread; + ThreadType *thread; + private: + TriBool IsSignaledImpl() const { + return this->thread->state == ThreadType::State_Terminated ? TriBool::True : TriBool::False; + } public: - explicit WaitableHolderOfThread(Thread *t) : thread(t) { /* ... */ } + explicit WaitableHolderOfThread(ThreadType *t) : thread(t) { /* ... */ } - /* IsSignaled, GetHandle both implemented. */ + /* IsSignaled, Link, Unlink implemented. */ virtual TriBool IsSignaled() const override { - return TriBool::Undefined; + std::scoped_lock lk(GetReference(this->thread->cs_thread)); + return this->IsSignaledImpl(); } - virtual Handle GetHandle() const override { - return this->thread->GetHandle(); + virtual TriBool LinkToObjectList() override { + std::scoped_lock lk(GetReference(this->thread->cs_thread)); + + GetReference(this->thread->waitlist).LinkWaitableHolder(*this); + return this->IsSignaledImpl(); + } + + virtual void UnlinkFromObjectList() override { + std::scoped_lock lk(GetReference(this->thread->cs_thread)); + + GetReference(this->thread->waitlist).UnlinkWaitableHolder(*this); } }; diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp index 73691df56..4da762eb6 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp @@ -15,21 +15,19 @@ */ #include "os_waitable_manager_impl.hpp" #include "os_waitable_object_list.hpp" +#include "os_tick_manager.hpp" -namespace ams::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; }; +namespace ams::os::impl { + WaitableHolderBase *WaitableManagerImpl::WaitAnyImpl(bool infinite, TimeSpan timeout) { /* Prepare for processing. */ this->signaled_holder = nullptr; + this->target_impl.SetCurrentThreadHandleForCancelWait(); WaitableHolderBase *result = this->LinkHoldersToObjectList(); /* Check if we've been signaled. */ { - std::scoped_lock lk(this->lock); + std::scoped_lock lk(this->cs_wait); if (this->signaled_holder != nullptr) { result = this->signaled_holder; } @@ -43,36 +41,42 @@ namespace ams::os::impl{ /* Unlink holders from the current object list. */ this->UnlinkHoldersFromObjectList(); + this->target_impl.ClearCurrentThreadHandleForCancelWait(); + return result; } - WaitableHolderBase *WaitableManagerImpl::WaitAnyHandleImpl(bool infinite, u64 timeout) { + WaitableHolderBase *WaitableManagerImpl::WaitAnyHandleImpl(bool infinite, TimeSpan timeout) { Handle object_handles[MaximumHandleCount]; WaitableHolderBase *objects[MaximumHandleCount]; - const size_t count = this->BuildHandleArray(object_handles, objects); - const u64 end_time = infinite ? std::numeric_limits::max() : armTicksToNs(armGetSystemTick()); + const s32 count = this->BuildHandleArray(object_handles, objects, MaximumHandleCount); + const TimeSpan end_time = infinite ? TimeSpan::FromNanoSeconds(std::numeric_limits::max()) : GetCurrentTick().ToTimeSpan() + timeout; while (true) { - this->current_time = armTicksToNs(armGetSystemTick()); + this->current_time = GetCurrentTick().ToTimeSpan(); - u64 min_timeout = 0; + TimeSpan min_timeout = 0; WaitableHolderBase *min_timeout_object = this->RecalculateNextTimeout(&min_timeout, end_time); s32 index; - if (count == 0 && min_timeout == 0) { - index = WaitTimedOut; + if (infinite && min_timeout_object == nullptr) { + index = this->target_impl.WaitAny(object_handles, MaximumHandleCount, count); } else { - index = this->WaitSynchronization(object_handles, count, min_timeout); - AMS_ABORT_UNLESS(index != WaitInvalid); + if (count == 0 && min_timeout == 0) { + index = WaitTimedOut; + } else { + index = this->target_impl.TimedWaitAny(object_handles, MaximumHandleCount, count, min_timeout); + AMS_ABORT_UNLESS(index != WaitInvalid); + } } switch (index) { case WaitTimedOut: if (min_timeout_object) { - this->current_time = armTicksToNs(armGetSystemTick()); + this->current_time = GetCurrentTick().ToTimeSpan(); if (min_timeout_object->IsSignaled() == TriBool::True) { - std::scoped_lock lk(this->lock); + std::scoped_lock lk(this->cs_wait); this->signaled_holder = min_timeout_object; return this->signaled_holder; } @@ -86,7 +90,7 @@ namespace ams::os::impl{ continue; default: /* 0 - 0x3F, valid. */ { - std::scoped_lock lk(this->lock); + std::scoped_lock lk(this->cs_wait); this->signaled_holder = objects[index]; return this->signaled_holder; } @@ -94,28 +98,12 @@ namespace ams::os::impl{ } } - s32 WaitableManagerImpl::WaitSynchronization(Handle *handles, size_t count, u64 timeout) { - s32 index = WaitInvalid; - - R_TRY_CATCH(svcWaitSynchronization(&index, handles, count, timeout)) { - R_CATCH(svc::ResultTimedOut) { return WaitTimedOut; } - R_CATCH(svc::ResultCancelled) { return WaitCancelled; } - /* All other results are critical errors. */ - /* svc::ResultThreadTerminating */ - /* svc::ResultInvalidHandle. */ - /* svc::ResultInvalidPointer */ - /* svc::ResultOutOfRange */ - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - - return index; - } - - size_t WaitableManagerImpl::BuildHandleArray(Handle *out_handles, WaitableHolderBase **out_objects) { - size_t count = 0; + s32 WaitableManagerImpl::BuildHandleArray(Handle out_handles[], WaitableHolderBase *out_objects[], s32 num) { + s32 count = 0; for (WaitableHolderBase &holder_base : this->waitable_list) { - if (Handle handle = holder_base.GetHandle(); handle != INVALID_HANDLE) { - AMS_ABORT_UNLESS(count < MaximumHandleCount); + if (Handle handle = holder_base.GetHandle(); handle != svc::InvalidHandle) { + AMS_ASSERT(count < num); out_handles[count] = handle; out_objects[count] = &holder_base; @@ -146,12 +134,12 @@ namespace ams::os::impl{ } } - WaitableHolderBase *WaitableManagerImpl::RecalculateNextTimeout(u64 *out_min_timeout, u64 end_time) { + WaitableHolderBase *WaitableManagerImpl::RecalculateNextTimeout(TimeSpan *out_min_timeout, TimeSpan end_time) { WaitableHolderBase *min_timeout_holder = nullptr; - u64 min_time = end_time; + TimeSpan min_time = end_time; for (WaitableHolderBase &holder_base : this->waitable_list) { - if (const u64 cur_time = holder_base.GetWakeupTime(); cur_time < min_time) { + if (const TimeSpan cur_time = holder_base.GetAbsoluteWakeupTime(); cur_time < min_time) { min_timeout_holder = &holder_base; min_time = cur_time; } @@ -166,11 +154,11 @@ namespace ams::os::impl{ } void WaitableManagerImpl::SignalAndWakeupThread(WaitableHolderBase *holder_base) { - std::scoped_lock lk(this->lock); + std::scoped_lock lk(this->cs_wait); if (this->signaled_holder == nullptr) { this->signaled_holder = holder_base; - R_ABORT_UNLESS(svcCancelSynchronization(this->waiting_thread_handle)); + this->target_impl.CancelWait(); } } diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp index 0c068dd4c..a014c4ff1 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp @@ -16,11 +16,17 @@ #pragma once #include "os_waitable_holder_base.hpp" +#if defined(ATMOSPHERE_OS_HORIZON) + #include "os_waitable_manager_target_impl.os.horizon.hpp" +#else + #error "Unknown OS for ams::os::WaitableManagerTargetImpl" +#endif + namespace ams::os::impl { class WaitableManagerImpl { public: - static constexpr size_t MaximumHandleCount = 0x40; + static constexpr size_t MaximumHandleCount = WaitableManagerTargetImpl::MaximumHandleCount; static constexpr s32 WaitInvalid = -3; static constexpr s32 WaitCancelled = -2; static constexpr s32 WaitTimedOut = -1; @@ -28,31 +34,30 @@ namespace ams::os::impl { private: ListType waitable_list; WaitableHolderBase *signaled_holder; - u64 current_time; - Mutex lock; - Handle waiting_thread_handle; + TimeSpan current_time; + InternalCriticalSection cs_wait; + WaitableManagerTargetImpl target_impl; 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 *WaitAnyImpl(bool infinite, TimeSpan timeout); + WaitableHolderBase *WaitAnyHandleImpl(bool infinite, TimeSpan timeout); + s32 BuildHandleArray(Handle out_handles[], WaitableHolderBase *out_objects[], s32 num); WaitableHolderBase *LinkHoldersToObjectList(); void UnlinkHoldersFromObjectList(); - WaitableHolderBase *RecalculateNextTimeout(u64 *out_min_timeout, u64 end_time); + WaitableHolderBase *RecalculateNextTimeout(TimeSpan *out_min_timeout, TimeSpan end_time); public: /* Wait. */ WaitableHolderBase *WaitAny() { - return this->WaitAnyImpl(true, std::numeric_limits::max()); + return this->WaitAnyImpl(true, TimeSpan::FromNanoSeconds(std::numeric_limits::max())); } WaitableHolderBase *TryWaitAny() { - return this->WaitAnyImpl(false, 0); + return this->WaitAnyImpl(false, TimeSpan(0)); } - WaitableHolderBase *TimedWaitAny(u64 timeout) { - return this->WaitAnyImpl(false, timeout); + WaitableHolderBase *TimedWaitAny(TimeSpan ts) { + return this->WaitAnyImpl(false, ts); } /* List management. */ @@ -84,7 +89,7 @@ namespace ams::os::impl { } /* Other. */ - u64 GetCurrentTime() const { + TimeSpan GetCurrentTime() const { return this->current_time; } diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.cpp new file mode 100644 index 000000000..2f4b95131 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_waitable_holder_base.hpp" +#include "os_waitable_manager_impl.hpp" + +namespace ams::os::impl { + + s32 WaitableManagerHorizonImpl::WaitSynchronizationN(s32 num, Handle arr[], s32 array_size, s64 ns) { + AMS_ASSERT(!(num == 0 && ns == 0)); + s32 index = WaitableManagerImpl::WaitInvalid; + + R_TRY_CATCH(svc::WaitSynchronization(std::addressof(index), static_cast(arr), num, ns)) { + R_CATCH(svc::ResultTimedOut) { return WaitableManagerImpl::WaitTimedOut; } + R_CATCH(svc::ResultCancelled) { return WaitableManagerImpl::WaitCancelled; } + /* All other results are critical errors. */ + /* svc::ResultThreadTerminating */ + /* svc::ResultInvalidHandle. */ + /* svc::ResultInvalidPointer */ + /* svc::ResultOutOfRange */ + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + return index; + } + + void WaitableManagerHorizonImpl::CancelWait() { + R_ABORT_UNLESS(svc::CancelSynchronization(this->handle)); + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.hpp new file mode 100644 index 000000000..03918c95f --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "os_thread_manager.hpp" + +namespace ams::os::impl { + + class WaitableManagerHorizonImpl { + public: + static constexpr size_t MaximumHandleCount = svc::MaxWaitSynchronizationHandleCount; + private: + Handle handle; + private: + s32 WaitSynchronizationN(s32 num, Handle arr[], s32 array_size, s64 ns); + public: + void CancelWait(); + + s32 WaitAny(Handle arr[], s32 array_size, s32 num) { + return this->WaitSynchronizationN(num, arr, array_size, svc::WaitInfinite); + } + + s32 TryWaitAny(Handle arr[], s32 array_size, s32 num) { + return this->WaitSynchronizationN(num, arr, array_size, 0); + } + + s32 TimedWaitAny(Handle arr[], s32 array_size, s32 num, TimeSpan ts) { + s64 timeout = ts.GetNanoSeconds(); + if (timeout < 0) { + timeout = 0; + } + return this->WaitSynchronizationN(num, arr, array_size, timeout); + } + + void SetCurrentThreadHandleForCancelWait() { + this->handle = GetCurrentThreadHandle(); + } + + void ClearCurrentThreadHandleForCancelWait() { + this->handle = svc::InvalidHandle; + } + }; + + using WaitableManagerTargetImpl = WaitableManagerHorizonImpl; + +} diff --git a/libraries/libstratosphere/source/os/os_condition_variable.cpp b/libraries/libstratosphere/source/os/os_condition_variable.cpp new file mode 100644 index 000000000..4feec65c7 --- /dev/null +++ b/libraries/libstratosphere/source/os/os_condition_variable.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/os_thread_manager.hpp" +#include "impl/os_timeout_helper.hpp" +#include "impl/os_mutex_impl.hpp" + +namespace ams::os { + + void InitializeConditionVariable(ConditionVariableType *cv) { + /* Construct object. */ + new (GetPointer(cv->_storage)) impl::InternalConditionVariable; + + /* Mark initialized. */ + cv->state = ConditionVariableType::State_Initialized; + } + + void FinalizeConditionVariable(ConditionVariableType *cv) { + AMS_ASSERT(cv->state == ConditionVariableType::State_Initialized); + + /* Mark not initialized. */ + cv->state = ConditionVariableType::State_NotInitialized; + + /* Destroy objects. */ + GetReference(cv->_storage).~InternalConditionVariable(); + } + + void SignalConditionVariable(ConditionVariableType *cv) { + AMS_ASSERT(cv->state == ConditionVariableType::State_Initialized); + + GetReference(cv->_storage).Signal(); + } + + void BroadcastConditionVariable(ConditionVariableType *cv) { + AMS_ASSERT(cv->state == ConditionVariableType::State_Initialized); + + GetReference(cv->_storage).Broadcast(); + } + + void WaitConditionVariable(ConditionVariableType *cv, MutexType *m) { + AMS_ASSERT(cv->state == ConditionVariableType::State_Initialized); + + AMS_ASSERT(m->state == MutexType::State_Initialized); + AMS_ASSERT(m->owner_thread == impl::GetCurrentThread()); + AMS_ASSERT(m->nest_count == 1); + + impl::PopAndCheckLockLevel(m); + + if ((--m->nest_count) == 0) { + m->owner_thread = nullptr; + } + + GetReference(cv->_storage).Wait(GetPointer(m->_storage)); + + impl::PushAndCheckLockLevel(m); + + ++m->nest_count; + m->owner_thread = impl::GetCurrentThread(); + } + + ConditionVariableStatus TimedWaitConditionVariable(ConditionVariableType *cv, MutexType *m, TimeSpan timeout) { + AMS_ASSERT(cv->state == ConditionVariableType::State_Initialized); + + AMS_ASSERT(m->state == MutexType::State_Initialized); + AMS_ASSERT(m->owner_thread == impl::GetCurrentThread()); + AMS_ASSERT(m->nest_count == 1); + + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); + + impl::PopAndCheckLockLevel(m); + + if ((--m->nest_count) == 0) { + m->owner_thread = nullptr; + } + + ConditionVariableStatus status; + if (timeout == TimeSpan(0)) { + GetReference(m->_storage).Leave(); + GetReference(m->_storage).Enter(); + status = ConditionVariableStatus::TimedOut; + } else { + impl::TimeoutHelper timeout_helper(timeout); + status = GetReference(cv->_storage).TimedWait(GetPointer(m->_storage), timeout_helper); + } + + impl::PushAndCheckLockLevel(m); + + ++m->nest_count; + m->owner_thread = impl::GetCurrentThread(); + + return status; + } + +} diff --git a/libraries/libstratosphere/source/os/os_event.cpp b/libraries/libstratosphere/source/os/os_event.cpp index bc1965d47..372e6fd64 100644 --- a/libraries/libstratosphere/source/os/os_event.cpp +++ b/libraries/libstratosphere/source/os/os_event.cpp @@ -13,93 +13,158 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include "impl/os_timeout_helper.hpp" #include "impl/os_waitable_object_list.hpp" +#include "impl/os_waitable_holder_impl.hpp" namespace ams::os { - Event::Event(bool a, bool s) : auto_clear(a), signaled(s) { - new (GetPointer(this->waitable_object_list_storage)) impl::WaitableObjectList(); + namespace { + + ALWAYS_INLINE u64 GetBroadcastCounterUnsafe(EventType *event) { + const u64 upper = event->broadcast_counter_high; + return (upper << BITSIZEOF(event->broadcast_counter_low)) | event->broadcast_counter_low; + } + + ALWAYS_INLINE void IncrementBroadcastCounterUnsafe(EventType *event) { + if ((++event->broadcast_counter_low) == 0) { + ++event->broadcast_counter_high; + } + } + } - Event::~Event() { - GetReference(this->waitable_object_list_storage).~WaitableObjectList(); + void InitializeEvent(EventType *event, bool signaled, EventClearMode clear_mode) { + /* Initialize internal variables. */ + new (GetPointer(event->cs_event)) impl::InternalCriticalSection; + new (GetPointer(event->cv_signaled)) impl::InternalConditionVariable; + + /* Initialize the waitable object list. */ + new (GetPointer(event->waitable_object_list_storage)) impl::WaitableObjectList(); + + /* Initialize member variables. */ + event->signaled = signaled; + event->initially_signaled = signaled; + event->clear_mode = static_cast(clear_mode); + event->broadcast_counter_low = 0; + event->broadcast_counter_high = 0; + + /* Mark initialized. */ + event->state = EventType::State_Initialized; } - void Event::Signal() { - std::scoped_lock lk(this->lock); + void FinalizeEvent(EventType *event) { + AMS_ASSERT(event->state == EventType::State_Initialized); + + /* Mark uninitialized. */ + event->state = EventType::State_NotInitialized; + + /* Destroy objects. */ + GetReference(event->waitable_object_list_storage).~WaitableObjectList(); + GetReference(event->cv_signaled).~InternalConditionVariable(); + GetReference(event->cs_event).~InternalCriticalSection(); + } + + void SignalEvent(EventType *event) { + AMS_ASSERT(event->state == EventType::State_Initialized); + + std::scoped_lock lk(GetReference(event->cs_event)); /* If we're already signaled, nothing more to do. */ - if (this->signaled) { + if (event->signaled) { return; } - this->signaled = true; + event->signaled = true; /* Signal! */ - if (this->auto_clear) { - /* If we're auto clear, signal one thread, which will clear. */ - this->cv.Signal(); - } else { + if (event->clear_mode == EventClearMode_ManualClear) { /* If we're manual clear, increment counter and wake all. */ - this->counter++; - this->cv.Broadcast(); + IncrementBroadcastCounterUnsafe(event); + GetReference(event->cv_signaled).Broadcast(); + } else { + /* If we're auto clear, signal one thread, which will clear. */ + GetReference(event->cv_signaled).Signal(); } /* Wake up whatever manager, if any. */ - GetReference(this->waitable_object_list_storage).SignalAllThreads(); + GetReference(event->waitable_object_list_storage).SignalAllThreads(); } - void Event::Reset() { - std::scoped_lock lk(this->lock); - this->signaled = false; - } + void WaitEvent(EventType *event) { + AMS_ASSERT(event->state == EventType::State_Initialized); - void Event::Wait() { - std::scoped_lock lk(this->lock); + std::scoped_lock lk(GetReference(event->cs_event)); - u64 cur_counter = this->counter; - while (!this->signaled) { - if (this->counter != cur_counter) { + const auto cur_counter = GetBroadcastCounterUnsafe(event); + while (!event->signaled) { + if (cur_counter != GetBroadcastCounterUnsafe(event)) { break; } - this->cv.Wait(&this->lock); + GetReference(event->cv_signaled).Wait(GetPointer(event->cs_event)); } - if (this->auto_clear) { - this->signaled = false; + if (event->clear_mode == EventClearMode_AutoClear) { + event->signaled = false; } } - bool Event::TryWait() { - std::scoped_lock lk(this->lock); + bool TryWaitEvent(EventType *event) { + AMS_ASSERT(event->state == EventType::State_Initialized); - const bool success = this->signaled; - if (this->auto_clear) { - this->signaled = false; + std::scoped_lock lk(GetReference(event->cs_event)); + + const bool signaled = event->signaled; + if (event->clear_mode == EventClearMode_AutoClear) { + event->signaled = false; } - return success; + return signaled; } - bool Event::TimedWait(u64 ns) { - TimeoutHelper timeout_helper(ns); - std::scoped_lock lk(this->lock); + bool TimedWaitEvent(EventType *event, TimeSpan timeout) { + AMS_ASSERT(event->state == EventType::State_Initialized); + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); - u64 cur_counter = this->counter; - while (!this->signaled) { - if (this->counter != cur_counter) { - break; - } - if (this->cv.TimedWait(&this->lock, timeout_helper.NsUntilTimeout()) == ConditionVariableStatus::TimedOut) { - return false; - } - } + { + impl::TimeoutHelper timeout_helper(timeout); + std::scoped_lock lk(GetReference(event->cs_event)); - if (this->auto_clear) { - this->signaled = false; + const auto cur_counter = GetBroadcastCounterUnsafe(event); + while (!event->signaled) { + if (cur_counter != GetBroadcastCounterUnsafe(event)) { + break; + } + + auto wait_res = GetReference(event->cv_signaled).TimedWait(GetPointer(event->cs_event), timeout_helper); + if (wait_res == ConditionVariableStatus::TimedOut) { + return false; + } + } + + if (event->clear_mode == EventClearMode_AutoClear) { + event->signaled = false; + } } return true; } + void ClearEvent(EventType *event) { + AMS_ASSERT(event->state == EventType::State_Initialized); + + std::scoped_lock lk(GetReference(event->cs_event)); + + /* Clear the signaled state. */ + event->signaled = false; + } + + void InitializeWaitableHolder(WaitableHolderType *waitable_holder, EventType *event) { + AMS_ASSERT(event->state == EventType::State_Initialized); + + new (GetPointer(waitable_holder->impl_storage)) impl::WaitableHolderOfEvent(event); + + waitable_holder->user_data = 0; + } + } diff --git a/libraries/libstratosphere/source/os/os_interrupt_event.cpp b/libraries/libstratosphere/source/os/os_interrupt_event.cpp index 87e4f77f0..e98231930 100644 --- a/libraries/libstratosphere/source/os/os_interrupt_event.cpp +++ b/libraries/libstratosphere/source/os/os_interrupt_event.cpp @@ -13,99 +13,51 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include "impl/os_interrupt_event_impl.hpp" #include "impl/os_waitable_object_list.hpp" namespace ams::os { - Result InterruptEvent::Initialize(u32 interrupt_id, bool autoclear) { - AMS_ABORT_UNLESS(!this->is_initialized); - this->auto_clear = autoclear; + void InitializeInterruptEvent(InterruptEventType *event, InterruptName name, EventClearMode clear_mode) { + /* Initialize member variables. */ + event->clear_mode = static_cast(clear_mode); - const auto type = this->auto_clear ? svc::InterruptType_Edge : svc::InterruptType_Level; - R_TRY(svcCreateInterruptEvent(this->handle.GetPointer(), interrupt_id, type)); + /* Initialize implementation. */ + new (GetPointer(event->impl)) impl::InterruptEventImpl(name, clear_mode); - this->is_initialized = true; - return ResultSuccess(); + /* Mark initialized. */ + event->state = InterruptEventType::State_Initialized; } - void InterruptEvent::Finalize() { - AMS_ABORT_UNLESS(this->is_initialized); - R_ABORT_UNLESS(svcCloseHandle(this->handle.Move())); - this->auto_clear = true; - this->is_initialized = false; + void FinalizeInterruptEvent(InterruptEventType *event) { + AMS_ASSERT(event->state == InterruptEventType::State_Initialized); + + /* Mark uninitialized. */ + event->state = InterruptEventType::State_NotInitialized; + + /* Destroy objects. */ + GetReference(event->impl).~InterruptEventImpl(); } - InterruptEvent::InterruptEvent(u32 interrupt_id, bool autoclear) { - this->is_initialized = false; - R_ABORT_UNLESS(this->Initialize(interrupt_id, autoclear)); + void WaitInterruptEvent(InterruptEventType *event) { + AMS_ASSERT(event->state == InterruptEventType::State_Initialized); + return GetReference(event->impl).Wait(); } - void InterruptEvent::Reset() { - R_ABORT_UNLESS(svcClearEvent(this->handle.Get())); + bool TryWaitInterruptEvent(InterruptEventType *event) { + AMS_ASSERT(event->state == InterruptEventType::State_Initialized); + return GetReference(event->impl).TryWait(); } - void InterruptEvent::Wait() { - AMS_ABORT_UNLESS(this->is_initialized); - - while (true) { - /* Continuously wait, until success. */ - R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), std::numeric_limits::max())) { - R_CATCH(svc::ResultCancelled) { continue; } - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - - /* 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(svc::ResultInvalidState) { continue; } - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - } - return; - } + bool TimedWaitInterruptEvent(InterruptEventType *event, TimeSpan timeout) { + AMS_ASSERT(event->state == InterruptEventType::State_Initialized); + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); + return GetReference(event->impl).TimedWait(timeout); } - bool InterruptEvent::TryWait() { - AMS_ABORT_UNLESS(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(svc::ResultTimedOut) { return false; } - R_CATCH(svc::ResultCancelled) { continue; } - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - - /* We succeeded, so we're signaled. */ - return true; - } - } - } - - bool InterruptEvent::TimedWait(u64 ns) { - AMS_ABORT_UNLESS(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(svc::ResultTimedOut) { return false; } - R_CATCH(svc::ResultCancelled) { continue; } - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - - /* 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(svc::ResultInvalidState) { continue; } - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - } - - return true; - } + void ClearInterruptEvent(InterruptEventType *event) { + AMS_ASSERT(event->state == InterruptEventType::State_Initialized); + return GetReference(event->impl).Clear(); } } diff --git a/libraries/libstratosphere/source/os/os_message_queue.cpp b/libraries/libstratosphere/source/os/os_message_queue.cpp index 9421bccac..795ecf4a9 100644 --- a/libraries/libstratosphere/source/os/os_message_queue.cpp +++ b/libraries/libstratosphere/source/os/os_message_queue.cpp @@ -14,233 +14,387 @@ * along with this program. If not, see . */ #include "impl/os_waitable_object_list.hpp" +#include "impl/os_timeout_helper.hpp" namespace ams::os { - MessageQueue::MessageQueue(std::unique_ptr buf, size_t c): buffer(std::move(buf)), capacity(c), count(0), offset(0) { - new (GetPointer(this->waitlist_not_empty)) impl::WaitableObjectList(); - new (GetPointer(this->waitlist_not_full)) impl::WaitableObjectList(); - } + namespace { - MessageQueue::~MessageQueue() { - GetReference(this->waitlist_not_empty).~WaitableObjectList(); - GetReference(this->waitlist_not_full).~WaitableObjectList(); - } - - void MessageQueue::SendInternal(uintptr_t data) { - /* Ensure we don't corrupt the queue, but this should never happen. */ - AMS_ABORT_UNLESS(this->count < this->capacity); - - /* Write data to tail of queue. */ - this->buffer[(this->count++ + this->offset) % this->capacity] = data; - } - - void MessageQueue::SendNextInternal(uintptr_t data) { - /* Ensure we don't corrupt the queue, but this should never happen. */ - AMS_ABORT_UNLESS(this->count < this->capacity); - - /* Write data to head of queue. */ - this->offset = (this->offset + this->capacity - 1) % this->capacity; - this->buffer[this->offset] = data; - this->count++; - } - - uintptr_t MessageQueue::ReceiveInternal() { - /* Ensure we don't corrupt the queue, but this should never happen. */ - AMS_ABORT_UNLESS(this->count > 0); - - uintptr_t data = this->buffer[this->offset]; - this->offset = (this->offset + 1) % this->capacity; - this->count--; - return data; - } - - inline uintptr_t MessageQueue::PeekInternal() { - /* Ensure we don't corrupt the queue, but this should never happen. */ - AMS_ABORT_UNLESS(this->count > 0); - - return this->buffer[this->offset]; - } - - void MessageQueue::Send(uintptr_t data) { - /* Acquire mutex, wait sendable. */ - std::scoped_lock lock(this->queue_lock); - - while (this->IsFull()) { - this->cv_not_full.Wait(&this->queue_lock); + ALWAYS_INLINE bool IsMessageQueueFull(const MessageQueueType *mq) { + return mq->count >= mq->capacity; } - /* Send, signal. */ - this->SendInternal(data); - this->cv_not_empty.Broadcast(); - GetReference(this->waitlist_not_empty).SignalAllThreads(); - } - - bool MessageQueue::TrySend(uintptr_t data) { - std::scoped_lock lock(this->queue_lock); - if (this->IsFull()) { - return false; + ALWAYS_INLINE bool IsMessageQueueEmpty(const MessageQueueType *mq) { + return mq->count == 0; + } + + void SendUnsafe(MessageQueueType *mq, uintptr_t data) { + /* Ensure our limits are correct. */ + auto count = mq->count; + auto capacity = mq->capacity; + AMS_ASSERT(count < capacity); + + /* Determine where we're writing. */ + auto ind = mq->offset + count; + if (ind >= capacity) { + ind -= capacity; + } + AMS_ASSERT(0 <= ind && ind < capacity); + + /* Write the data. */ + mq->buffer[ind] = data; + ++count; + + /* Update tracking. */ + mq->count = count; + } + + void SendNextUnsafe(MessageQueueType *mq, uintptr_t data) { + /* Ensure our limits are correct. */ + auto count = mq->count; + auto capacity = mq->capacity; + AMS_ASSERT(count < capacity); + + /* Determine where we're writing. */ + auto offset = mq->offset - 1; + if (offset < 0) { + offset += capacity; + } + AMS_ASSERT(0 <= offset && offset < capacity); + + /* Write the data. */ + mq->buffer[offset] = data; + ++count; + + /* Update tracking. */ + mq->offset = offset; + mq->count = count; + } + + uintptr_t ReceiveUnsafe(MessageQueueType *mq) { + /* Ensure our limits are correct. */ + auto count = mq->count; + auto offset = mq->offset; + auto capacity = mq->capacity; + AMS_ASSERT(count > 0); + AMS_ASSERT(offset >= 0 && offset < capacity); + + /* Get the data. */ + auto data = mq->buffer[offset]; + + /* Calculate new tracking variables. */ + if ((++offset) >= capacity) { + offset -= capacity; + } + --count; + + /* Update tracking. */ + mq->offset = offset; + mq->count = count; + + return data; + } + + uintptr_t PeekUnsafe(const MessageQueueType *mq) { + /* Ensure our limits are correct. */ + auto count = mq->count; + auto offset = mq->offset; + AMS_ASSERT(count > 0); + + return mq->buffer[offset]; } - /* Send, signal. */ - this->SendInternal(data); - this->cv_not_empty.Broadcast(); - GetReference(this->waitlist_not_empty).SignalAllThreads(); - return true; } - bool MessageQueue::TimedSend(uintptr_t data, u64 timeout) { - std::scoped_lock lock(this->queue_lock); - TimeoutHelper timeout_helper(timeout); + void InitializeMessageQueue(MessageQueueType *mq, uintptr_t *buffer, size_t count) { + AMS_ASSERT(buffer != nullptr); + AMS_ASSERT(count >= 1); - while (this->IsFull()) { - if (timeout_helper.TimedOut()) { + /* Setup objects. */ + new (GetPointer(mq->cs_queue)) impl::InternalCriticalSection; + new (GetPointer(mq->cv_not_full)) impl::InternalConditionVariable; + new (GetPointer(mq->cv_not_empty)) impl::InternalConditionVariable; + + /* Setup wait lists. */ + new (GetPointer(mq->waitlist_not_empty)) impl::WaitableObjectList; + new (GetPointer(mq->waitlist_not_full)) impl::WaitableObjectList; + + /* Set member variables. */ + mq->buffer = buffer; + mq->capacity = static_cast(count); + mq->count = 0; + mq->offset = 0; + + /* Mark initialized. */ + mq->state = MessageQueueType::State_Initialized; + } + + void FinalizeMessageQueue(MessageQueueType *mq) { + AMS_ASSERT(mq->state = MessageQueueType::State_Initialized); + + AMS_ASSERT(GetReference(mq->waitlist_not_empty).IsEmpty()); + AMS_ASSERT(GetReference(mq->waitlist_not_full).IsEmpty()); + + /* Mark uninitialized. */ + mq->state = MessageQueueType::State_NotInitialized; + + /* Destroy wait lists. */ + GetReference(mq->waitlist_not_empty).~WaitableObjectList(); + GetReference(mq->waitlist_not_full).~WaitableObjectList(); + + /* Destroy objects. */ + GetReference(mq->cv_not_empty).~InternalConditionVariable(); + GetReference(mq->cv_not_full).~InternalConditionVariable(); + GetReference(mq->cs_queue).~InternalCriticalSection(); + } + + /* Sending (FIFO functionality) */ + void SendMessageQueue(MessageQueueType *mq, uintptr_t data) { + AMS_ASSERT(mq->state == MessageQueueType::State_Initialized); + + { + /* Acquire mutex, wait sendable. */ + std::scoped_lock lk(GetReference(mq->cs_queue)); + + while (IsMessageQueueFull(mq)) { + GetReference(mq->cv_not_full).Wait(GetPointer(mq->cs_queue)); + } + + /* Send, signal. */ + SendUnsafe(mq, data); + GetReference(mq->cv_not_empty).Broadcast(); + GetReference(mq->waitlist_not_empty).SignalAllThreads(); + } + } + + bool TrySendMessageQueue(MessageQueueType *mq, uintptr_t data) { + AMS_ASSERT(mq->state == MessageQueueType::State_Initialized); + + { + /* Acquire mutex, check sendable. */ + std::scoped_lock lk(GetReference(mq->cs_queue)); + + if (IsMessageQueueFull(mq)) { return false; } - this->cv_not_full.TimedWait(&this->queue_lock, timeout_helper.NsUntilTimeout()); + /* Send, signal. */ + SendUnsafe(mq, data); + GetReference(mq->cv_not_empty).Broadcast(); + GetReference(mq->waitlist_not_empty).SignalAllThreads(); } - /* Send, signal. */ - this->SendInternal(data); - this->cv_not_empty.Broadcast(); - GetReference(this->waitlist_not_empty).SignalAllThreads(); return true; } - void MessageQueue::SendNext(uintptr_t data) { - /* Acquire mutex, wait sendable. */ - std::scoped_lock lock(this->queue_lock); + bool TimedSendMessageQueue(MessageQueueType *mq, uintptr_t data, TimeSpan timeout) { + AMS_ASSERT(mq->state == MessageQueueType::State_Initialized); + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); - while (this->IsFull()) { - this->cv_not_full.Wait(&this->queue_lock); + { + /* Acquire mutex, wait sendable. */ + impl::TimeoutHelper timeout_helper(timeout); + std::scoped_lock lk(GetReference(mq->cs_queue)); + + while (IsMessageQueueFull(mq)) { + if (timeout_helper.TimedOut()) { + return false; + } + GetReference(mq->cv_not_full).TimedWait(GetPointer(mq->cs_queue), timeout_helper); + } + + /* Send, signal. */ + SendUnsafe(mq, data); + GetReference(mq->cv_not_empty).Broadcast(); + GetReference(mq->waitlist_not_empty).SignalAllThreads(); } - /* Send, signal. */ - this->SendNextInternal(data); - this->cv_not_empty.Broadcast(); - GetReference(this->waitlist_not_empty).SignalAllThreads(); - } - - bool MessageQueue::TrySendNext(uintptr_t data) { - std::scoped_lock lock(this->queue_lock); - if (this->IsFull()) { - return false; - } - - /* Send, signal. */ - this->SendNextInternal(data); - this->cv_not_empty.Broadcast(); - GetReference(this->waitlist_not_empty).SignalAllThreads(); return true; } - bool MessageQueue::TimedSendNext(uintptr_t data, u64 timeout) { - std::scoped_lock lock(this->queue_lock); - TimeoutHelper timeout_helper(timeout); + /* Sending (LIFO functionality) */ + void SendNextMessageQueue(MessageQueueType *mq, uintptr_t data) { + AMS_ASSERT(mq->state == MessageQueueType::State_Initialized); - while (this->IsFull()) { - if (timeout_helper.TimedOut()) { + { + /* Acquire mutex, wait sendable. */ + std::scoped_lock lk(GetReference(mq->cs_queue)); + + while (IsMessageQueueFull(mq)) { + GetReference(mq->cv_not_full).Wait(GetPointer(mq->cs_queue)); + } + + /* Send, signal. */ + SendNextUnsafe(mq, data); + GetReference(mq->cv_not_empty).Broadcast(); + GetReference(mq->waitlist_not_empty).SignalAllThreads(); + } + } + + bool TrySendNextMessageQueue(MessageQueueType *mq, uintptr_t data) { + AMS_ASSERT(mq->state == MessageQueueType::State_Initialized); + + { + /* Acquire mutex, check sendable. */ + std::scoped_lock lk(GetReference(mq->cs_queue)); + + if (IsMessageQueueFull(mq)) { return false; } - this->cv_not_full.TimedWait(&this->queue_lock, timeout_helper.NsUntilTimeout()); + /* Send, signal. */ + SendNextUnsafe(mq, data); + GetReference(mq->cv_not_empty).Broadcast(); + GetReference(mq->waitlist_not_empty).SignalAllThreads(); } - /* Send, signal. */ - this->SendNextInternal(data); - this->cv_not_empty.Broadcast(); - GetReference(this->waitlist_not_empty).SignalAllThreads(); return true; } - void MessageQueue::Receive(uintptr_t *out) { - /* Acquire mutex, wait receivable. */ - std::scoped_lock lock(this->queue_lock); + bool TimedSendNextMessageQueue(MessageQueueType *mq, uintptr_t data, TimeSpan timeout) { + AMS_ASSERT(mq->state == MessageQueueType::State_Initialized); + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); - while (this->IsEmpty()) { - this->cv_not_empty.Wait(&this->queue_lock); + { + /* Acquire mutex, wait sendable. */ + impl::TimeoutHelper timeout_helper(timeout); + std::scoped_lock lk(GetReference(mq->cs_queue)); + + while (IsMessageQueueFull(mq)) { + if (timeout_helper.TimedOut()) { + return false; + } + GetReference(mq->cv_not_full).TimedWait(GetPointer(mq->cs_queue), timeout_helper); + } + + /* Send, signal. */ + SendNextUnsafe(mq, data); + GetReference(mq->cv_not_empty).Broadcast(); + GetReference(mq->waitlist_not_empty).SignalAllThreads(); } - /* Receive, signal. */ - *out = this->ReceiveInternal(); - this->cv_not_full.Broadcast(); - GetReference(this->waitlist_not_full).SignalAllThreads(); - } - - bool MessageQueue::TryReceive(uintptr_t *out) { - /* Acquire mutex, wait receivable. */ - std::scoped_lock lock(this->queue_lock); - - if (this->IsEmpty()) { - return false; - } - - /* Receive, signal. */ - *out = this->ReceiveInternal(); - this->cv_not_full.Broadcast(); - GetReference(this->waitlist_not_full).SignalAllThreads(); return true; } - bool MessageQueue::TimedReceive(uintptr_t *out, u64 timeout) { - std::scoped_lock lock(this->queue_lock); - TimeoutHelper timeout_helper(timeout); + /* Receive functionality */ + void ReceiveMessageQueue(uintptr_t *out, MessageQueueType *mq) { + AMS_ASSERT(mq->state == MessageQueueType::State_Initialized); - while (this->IsEmpty()) { - if (timeout_helper.TimedOut()) { + { + /* Acquire mutex, wait receivable. */ + std::scoped_lock lk(GetReference(mq->cs_queue)); + + while (IsMessageQueueEmpty(mq)) { + GetReference(mq->cv_not_empty).Wait(GetPointer(mq->cs_queue)); + } + + /* Receive, signal. */ + *out = ReceiveUnsafe(mq); + GetReference(mq->cv_not_full).Broadcast(); + GetReference(mq->waitlist_not_full).SignalAllThreads(); + } + } + + bool TryReceiveMessageQueue(uintptr_t *out, MessageQueueType *mq) { + AMS_ASSERT(mq->state == MessageQueueType::State_Initialized); + + { + /* Acquire mutex, check receivable. */ + std::scoped_lock lk(GetReference(mq->cs_queue)); + + if (IsMessageQueueEmpty(mq)) { return false; } - this->cv_not_empty.TimedWait(&this->queue_lock, timeout_helper.NsUntilTimeout()); + /* Receive, signal. */ + *out = ReceiveUnsafe(mq); + GetReference(mq->cv_not_full).Broadcast(); + GetReference(mq->waitlist_not_full).SignalAllThreads(); } - /* Receive, signal. */ - *out = this->ReceiveInternal(); - this->cv_not_full.Broadcast(); - GetReference(this->waitlist_not_full).SignalAllThreads(); return true; } - void MessageQueue::Peek(uintptr_t *out) { - /* Acquire mutex, wait receivable. */ - std::scoped_lock lock(this->queue_lock); + bool TimedReceiveMessageQueue(uintptr_t *out, MessageQueueType *mq, TimeSpan timeout) { + AMS_ASSERT(mq->state == MessageQueueType::State_Initialized); + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); - while (this->IsEmpty()) { - this->cv_not_empty.Wait(&this->queue_lock); + { + /* Acquire mutex, wait receivable. */ + impl::TimeoutHelper timeout_helper(timeout); + std::scoped_lock lk(GetReference(mq->cs_queue)); + + while (IsMessageQueueEmpty(mq)) { + if (timeout_helper.TimedOut()) { + return false; + } + GetReference(mq->cv_not_empty).TimedWait(GetPointer(mq->cs_queue), timeout_helper); + } + + /* Receive, signal. */ + *out = ReceiveUnsafe(mq); + GetReference(mq->cv_not_full).Broadcast(); + GetReference(mq->waitlist_not_full).SignalAllThreads(); } - /* Peek. */ - *out = this->PeekInternal(); - } - - bool MessageQueue::TryPeek(uintptr_t *out) { - /* Acquire mutex, wait receivable. */ - std::scoped_lock lock(this->queue_lock); - - if (this->IsEmpty()) { - return false; - } - - /* Peek. */ - *out = this->PeekInternal(); return true; } - bool MessageQueue::TimedPeek(uintptr_t *out, u64 timeout) { - std::scoped_lock lock(this->queue_lock); - TimeoutHelper timeout_helper(timeout); + /* Peek functionality */ + void PeekMessageQueue(uintptr_t *out, const MessageQueueType *mq) { + AMS_ASSERT(mq->state == MessageQueueType::State_Initialized); - while (this->IsEmpty()) { - if (timeout_helper.TimedOut()) { + { + /* Acquire mutex, wait receivable. */ + std::scoped_lock lk(GetReference(mq->cs_queue)); + + while (IsMessageQueueEmpty(mq)) { + GetReference(mq->cv_not_empty).Wait(GetPointer(mq->cs_queue)); + } + + /* Peek. */ + *out = PeekUnsafe(mq); + } + } + + bool TryPeekMessageQueue(uintptr_t *out, const MessageQueueType *mq) { + AMS_ASSERT(mq->state == MessageQueueType::State_Initialized); + + { + /* Acquire mutex, check receivable. */ + std::scoped_lock lk(GetReference(mq->cs_queue)); + + if (IsMessageQueueEmpty(mq)) { return false; } - this->cv_not_empty.TimedWait(&this->queue_lock, timeout_helper.NsUntilTimeout()); + /* Peek. */ + *out = PeekUnsafe(mq); + } + + return true; + } + + bool TimedPeekMessageQueue(uintptr_t *out, const MessageQueueType *mq, TimeSpan timeout) { + AMS_ASSERT(mq->state == MessageQueueType::State_Initialized); + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); + + { + /* Acquire mutex, wait receivable. */ + impl::TimeoutHelper timeout_helper(timeout); + std::scoped_lock lk(GetReference(mq->cs_queue)); + + while (IsMessageQueueEmpty(mq)) { + if (timeout_helper.TimedOut()) { + return false; + } + GetReference(mq->cv_not_empty).TimedWait(GetPointer(mq->cs_queue), timeout_helper); + } + + /* Peek. */ + *out = PeekUnsafe(mq); } - /* Peek. */ - *out = this->PeekInternal(); return true; } diff --git a/libraries/libstratosphere/source/os/os_mutex.cpp b/libraries/libstratosphere/source/os/os_mutex.cpp new file mode 100644 index 000000000..5b33bda12 --- /dev/null +++ b/libraries/libstratosphere/source/os/os_mutex.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/os_thread_manager.hpp" +#include "impl/os_mutex_impl.hpp" + +namespace ams::os { + + namespace impl { + + #ifdef ATMOSPHERE_BUILD_FOR_AUDITING + + void PushAndCheckLockLevel(MutexType *mutex) { + /* If auditing isn't specified, don't bother. */ + if (mutex->lock_level == 0) { + return; + } + + /* TODO: Implement mutex level auditing. */ + } + + void PopAndCheckLockLevel(MutexType *mutex) { + /* If auditing isn't specified, don't bother. */ + if (mutex->lock_level == 0) { + return; + } + + /* TODO: Implement mutex level auditing. */ + } + + #else + + void PushAndCheckLockLevel(MutexType *mutex) { + /* ... */ + } + + void PopAndCheckLockLevel(MutexType *mutex) { + /* ... */ + } + + #endif + + } + + namespace { + + ALWAYS_INLINE void AfterLockMutex(MutexType *mutex, ThreadType *cur_thread) { + AMS_ASSERT(mutex->nest_count < MutexRecursiveLockCountMax); + + impl::PushAndCheckLockLevel(mutex); + + ++mutex->nest_count; + mutex->owner_thread = cur_thread; + } + + } + + void InitializeMutex(MutexType *mutex, bool recursive, int lock_level) { + AMS_ASSERT((lock_level == 0) || (MutexLockLevelMin <= lock_level && lock_level <= MutexLockLevelMax)); + + /* Create object. */ + new (GetPointer(mutex->_storage)) impl::InternalCriticalSection; + + /* Set member variables. */ + mutex->is_recursive = recursive; + mutex->lock_level = lock_level; + mutex->nest_count = 0; + mutex->owner_thread = nullptr; + + /* Mark initialized. */ + mutex->state = MutexType::State_Initialized; + } + + void FinalizeMutex(MutexType *mutex) { + AMS_ASSERT(mutex->state == MutexType::State_Initialized); + + /* Mark not intialized. */ + mutex->state = MutexType::State_NotInitialized; + + /* Destroy object. */ + GetReference(mutex->_storage).~InternalCriticalSection(); + } + + void LockMutex(MutexType *mutex) { + AMS_ASSERT(mutex->state == MutexType::State_Initialized); + + ThreadType *current = impl::GetCurrentThread(); + + if (!mutex->is_recursive) { + AMS_ASSERT(mutex->owner_thread != current); + GetReference(mutex->_storage).Enter(); + } else { + if (mutex->owner_thread == current) { + AMS_ASSERT(mutex->nest_count >= 1); + } else { + GetReference(mutex->_storage).Enter(); + } + } + + AfterLockMutex(mutex, current); + } + + bool TryLockMutex(MutexType *mutex) { + AMS_ASSERT(mutex->state == MutexType::State_Initialized); + + ThreadType *current = impl::GetCurrentThread(); + + if (!mutex->is_recursive) { + AMS_ASSERT(mutex->owner_thread != current); + if (!GetReference(mutex->_storage).TryEnter()) { + return false; + } + } else { + if (mutex->owner_thread == current) { + AMS_ASSERT(mutex->nest_count >= 1); + } else { + if (!GetReference(mutex->_storage).TryEnter()) { + return false; + } + } + } + + AfterLockMutex(mutex, current); + + return true; + } + + void UnlockMutex(MutexType *mutex) { + AMS_ASSERT(mutex->state == MutexType::State_Initialized); + AMS_ASSERT(mutex->nest_count > 0); + AMS_ASSERT(mutex->owner_thread == impl::GetCurrentThread()); + + impl::PopAndCheckLockLevel(mutex); + + if ((--mutex->nest_count) == 0) { + mutex->owner_thread = nullptr; + GetReference(mutex->_storage).Leave(); + } + } + + bool IsMutexLockedByCurrentThread(const MutexType *mutex) { + AMS_ASSERT(mutex->state == MutexType::State_Initialized); + + return mutex->owner_thread == impl::GetCurrentThread(); + } + +} diff --git a/libraries/libstratosphere/source/os/os_random.cpp b/libraries/libstratosphere/source/os/os_random.cpp index cd3962d20..b0bc60c54 100644 --- a/libraries/libstratosphere/source/os/os_random.cpp +++ b/libraries/libstratosphere/source/os/os_random.cpp @@ -21,7 +21,7 @@ namespace ams::os { namespace { util::TinyMT g_random; - os::Mutex g_random_mutex; + os::Mutex g_random_mutex(false); bool g_initialized_random; template diff --git a/libraries/libstratosphere/source/os/os_semaphore.cpp b/libraries/libstratosphere/source/os/os_semaphore.cpp index 64415467d..e55691095 100644 --- a/libraries/libstratosphere/source/os/os_semaphore.cpp +++ b/libraries/libstratosphere/source/os/os_semaphore.cpp @@ -14,72 +14,132 @@ * along with this program. If not, see . */ #include "impl/os_waitable_object_list.hpp" +#include "impl/os_timeout_helper.hpp" namespace ams::os { - Semaphore::Semaphore(int c, int mc) : count(c), max_count(mc) { - new (GetPointer(this->waitlist)) impl::WaitableObjectList(); + void InitializeSemaphore(SemaphoreType *sema, s32 count, s32 max_count) { + AMS_ASSERT(max_count >= 1); + AMS_ASSERT(count >= 0); + + /* Setup objects. */ + new (GetPointer(sema->cs_sema)) impl::InternalCriticalSection; + new (GetPointer(sema->cv_not_zero)) impl::InternalConditionVariable; + + /* Setup wait lists. */ + new (GetPointer(sema->waitlist)) impl::WaitableObjectList; + + /* Set member variables. */ + sema->count = count; + sema->max_count = max_count; + + /* Mark initialized. */ + sema->state = SemaphoreType::State_Initialized; } - Semaphore::~Semaphore() { - GetReference(this->waitlist).~WaitableObjectList(); + void FinalizeSemaphore(SemaphoreType *sema) { + AMS_ASSERT(sema->state = SemaphoreType::State_Initialized); + + AMS_ASSERT(GetReference(sema->waitlist).IsEmpty()); + + /* Mark uninitialized. */ + sema->state = SemaphoreType::State_NotInitialized; + + /* Destroy wait lists. */ + GetReference(sema->waitlist).~WaitableObjectList(); + + /* Destroy objects. */ + GetReference(sema->cv_not_zero).~InternalConditionVariable(); + GetReference(sema->cs_sema).~InternalCriticalSection(); } - void Semaphore::Acquire() { - std::scoped_lock lk(this->mutex); + void AcquireSemaphore(SemaphoreType *sema) { + AMS_ASSERT(sema->state == SemaphoreType::State_Initialized); - while (this->count == 0) { - this->condvar.Wait(&this->mutex); + { + std::scoped_lock lk(GetReference(sema->cs_sema)); + + while (sema->count == 0) { + GetReference(sema->cv_not_zero).Wait(GetPointer(sema->cs_sema)); + } + + --sema->count; } - - this->count--; } - bool Semaphore::TryAcquire() { - std::scoped_lock lk(this->mutex); + bool TryAcquireSemaphore(SemaphoreType *sema) { + AMS_ASSERT(sema->state == SemaphoreType::State_Initialized); - if (this->count == 0) { - return false; - } + { + std::scoped_lock lk(GetReference(sema->cs_sema)); - this->count--; - return true; - } - - bool Semaphore::TimedAcquire(u64 timeout) { - std::scoped_lock lk(this->mutex); - TimeoutHelper timeout_helper(timeout); - - while (this->count == 0) { - if (timeout_helper.TimedOut()) { + if (sema->count == 0) { return false; } - this->condvar.TimedWait(&this->mutex, timeout_helper.NsUntilTimeout()); + --sema->count; } - this->count--; return true; } - void Semaphore::Release() { - std::scoped_lock lk(this->mutex); + bool TimedAcquireSemaphore(SemaphoreType *sema, TimeSpan timeout) { + AMS_ASSERT(sema->state == SemaphoreType::State_Initialized); + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); - AMS_ABORT_UNLESS(this->count + 1 <= this->max_count); - this->count++; + { + impl::TimeoutHelper timeout_helper(timeout); + std::scoped_lock lk(GetReference(sema->cs_sema)); - this->condvar.Signal(); - GetReference(this->waitlist).SignalAllThreads(); + while (sema->count == 0) { + if (timeout_helper.TimedOut()) { + return false; + } + GetReference(sema->cv_not_zero).TimedWait(GetPointer(sema->cs_sema), timeout_helper); + } + + --sema->count; + } + + return true; } - void Semaphore::Release(int count) { - std::scoped_lock lk(this->mutex); + void ReleaseSemaphore(SemaphoreType *sema) { + AMS_ASSERT(sema->state == SemaphoreType::State_Initialized); - AMS_ABORT_UNLESS(this->count + count <= this->max_count); - this->count += count; + { + std::scoped_lock lk(GetReference(sema->cs_sema)); - this->condvar.Broadcast(); - GetReference(this->waitlist).SignalAllThreads(); + AMS_ASSERT(sema->count + 1 <= sema->max_count); + + ++sema->count; + + GetReference(sema->cv_not_zero).Signal(); + GetReference(sema->waitlist).SignalAllThreads(); + } } + void ReleaseSemaphore(SemaphoreType *sema, s32 count) { + AMS_ASSERT(sema->state == SemaphoreType::State_Initialized); + + { + std::scoped_lock lk(GetReference(sema->cs_sema)); + + AMS_ASSERT(sema->count + count <= sema->max_count); + + sema->count += count; + + GetReference(sema->cv_not_zero).Signal(); + GetReference(sema->waitlist).SignalAllThreads(); + } + } + + s32 GetCurrentSemaphoreCount(const SemaphoreType *sema) { + AMS_ASSERT(sema->state == SemaphoreType::State_Initialized); + + return sema->count; + } + + // void InitializeWaitableHolder(WaitableHolderType *waitable_holder, SemaphoreType *sema); + } diff --git a/libraries/libstratosphere/source/os/os_system_event.cpp b/libraries/libstratosphere/source/os/os_system_event.cpp index 403001960..829b98d7c 100644 --- a/libraries/libstratosphere/source/os/os_system_event.cpp +++ b/libraries/libstratosphere/source/os/os_system_event.cpp @@ -15,174 +15,120 @@ */ #include #include "impl/os_waitable_holder_impl.hpp" +#include "impl/os_inter_process_event.hpp" +#include "impl/os_timeout_helper.hpp" namespace ams::os { - SystemEvent::SystemEvent(bool inter_process, bool autoclear) : state(SystemEventState::Uninitialized) { + Result CreateSystemEvent(SystemEventType *event, EventClearMode clear_mode, bool inter_process) { if (inter_process) { - R_ABORT_UNLESS(this->InitializeAsInterProcessEvent(autoclear)); + R_TRY(impl::CreateInterProcessEvent(std::addressof(event->inter_process_event), clear_mode)); + event->state = SystemEventType::State_InitializedAsInterProcessEvent; } else { - R_ABORT_UNLESS(this->InitializeAsEvent(autoclear)); + InitializeEvent(std::addressof(event->event), false, clear_mode); + event->state = SystemEventType::State_InitializedAsEvent; } - } - - 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() { - AMS_ABORT_UNLESS(this->state == SystemEventState::Event); - return GetReference(this->storage_for_event); - } - - const Event &SystemEvent::GetEvent() const { - AMS_ABORT_UNLESS(this->state == SystemEventState::Event); - return GetReference(this->storage_for_event); - } - - impl::InterProcessEvent &SystemEvent::GetInterProcessEvent() { - AMS_ABORT_UNLESS(this->state == SystemEventState::InterProcessEvent); - return GetReference(this->storage_for_inter_process_event); - } - - const impl::InterProcessEvent &SystemEvent::GetInterProcessEvent() const { - AMS_ABORT_UNLESS(this->state == SystemEventState::InterProcessEvent); - return GetReference(this->storage_for_inter_process_event); - } - - Result SystemEvent::InitializeAsEvent(bool autoclear) { - AMS_ABORT_UNLESS(this->state == SystemEventState::Uninitialized); - new (GetPointer(this->storage_for_event)) Event(autoclear); - this->state = SystemEventState::Event; return ResultSuccess(); } - Result SystemEvent::InitializeAsInterProcessEvent(bool autoclear) { - AMS_ABORT_UNLESS(this->state == SystemEventState::Uninitialized); - new (GetPointer(this->storage_for_inter_process_event)) impl::InterProcessEvent(); - this->state = SystemEventState::InterProcessEvent; + void DestroySystemEvent(SystemEventType *event) { + auto state = event->state; + event->state = SystemEventType::State_NotInitialized; - /* Ensure we end up in a correct state if initialization fails. */ - { - auto guard = SCOPE_GUARD { this->Finalize(); }; - R_TRY(this->GetInterProcessEvent().Initialize(autoclear)); - guard.Cancel(); - } - - return ResultSuccess(); - } - - void SystemEvent::AttachHandles(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) { - AMS_ABORT_UNLESS(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() { - AMS_ABORT_UNLESS(this->state == SystemEventState::InterProcessEvent); - return this->GetInterProcessEvent().DetachReadableHandle(); - } - - Handle SystemEvent::DetachWritableHandle() { - AMS_ABORT_UNLESS(this->state == SystemEventState::InterProcessEvent); - return this->GetInterProcessEvent().DetachWritableHandle(); - } - - Handle SystemEvent::GetReadableHandle() const { - AMS_ABORT_UNLESS(this->state == SystemEventState::InterProcessEvent); - return this->GetInterProcessEvent().GetReadableHandle(); - } - - Handle SystemEvent::GetWritableHandle() const { - AMS_ABORT_UNLESS(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; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - 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: + switch (state) { + case SystemEventType::State_InitializedAsInterProcessEvent: impl::DestroyInterProcessEvent(std::addressof(event->inter_process_event)); break; + case SystemEventType::State_InitializedAsEvent: FinalizeEvent(std::addressof(event->event)); break; AMS_UNREACHABLE_DEFAULT_CASE(); } } - void SystemEvent::Reset() { - switch (this->state) { - case SystemEventState::Event: - this->GetEvent().Reset(); - break; - case SystemEventState::InterProcessEvent: - this->GetInterProcessEvent().Reset(); - break; - case SystemEventState::Uninitialized: - AMS_UNREACHABLE_DEFAULT_CASE(); - } + void AttachSystemEvent(SystemEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode) { + AMS_ASSERT(read_handle != svc::InvalidHandle || write_handle != svc::InvalidHandle); + impl::AttachInterProcessEvent(std::addressof(event->inter_process_event), read_handle, read_handle_managed, write_handle, write_handle_managed, clear_mode); + event->state = SystemEventType::State_InitializedAsInterProcessEvent; } - void SystemEvent::Wait() { - switch (this->state) { - case SystemEventState::Event: - this->GetEvent().Wait(); - break; - case SystemEventState::InterProcessEvent: - this->GetInterProcessEvent().Wait(); - break; - case SystemEventState::Uninitialized: + + void AttachReadableHandleToSystemEvent(SystemEventType *event, Handle read_handle, bool manage_read_handle, EventClearMode clear_mode) { + return AttachSystemEvent(event, read_handle, manage_read_handle, svc::InvalidHandle, false, clear_mode); + } + + void AttachWritableHandleToSystemEvent(SystemEventType *event, Handle write_handle, bool manage_write_handle, EventClearMode clear_mode) { + return AttachSystemEvent(event, svc::InvalidHandle, false, write_handle, manage_write_handle, clear_mode); + } + + Handle DetachReadableHandleOfSystemEvent(SystemEventType *event) { + AMS_ASSERT(event->state == SystemEventType::State_InitializedAsInterProcessEvent); + return impl::DetachReadableHandleOfInterProcessEvent(std::addressof(event->inter_process_event)); + } + + Handle DetachWritableHandleOfSystemEvent(SystemEventType *event) { + AMS_ASSERT(event->state == SystemEventType::State_InitializedAsInterProcessEvent); + return impl::DetachWritableHandleOfInterProcessEvent(std::addressof(event->inter_process_event)); + } + + Handle GetReadableHandleOfSystemEvent(const SystemEventType *event) { + AMS_ASSERT(event->state == SystemEventType::State_InitializedAsInterProcessEvent); + return impl::GetReadableHandleOfInterProcessEvent(std::addressof(event->inter_process_event)); + } + + Handle GetWritableHandleOfSystemEvent(const SystemEventType *event) { + AMS_ASSERT(event->state == SystemEventType::State_InitializedAsInterProcessEvent); + return impl::GetWritableHandleOfInterProcessEvent(std::addressof(event->inter_process_event)); + + } + + void SignalSystemEvent(SystemEventType *event) { + switch (event->state) { + case SystemEventType::State_InitializedAsInterProcessEvent: return impl::SignalInterProcessEvent(std::addressof(event->inter_process_event)); + case SystemEventType::State_InitializedAsEvent: return SignalEvent(std::addressof(event->event)); AMS_UNREACHABLE_DEFAULT_CASE(); } } - bool SystemEvent::TryWait() { - switch (this->state) { - case SystemEventState::Event: - return this->GetEvent().TryWait(); - case SystemEventState::InterProcessEvent: - return this->GetInterProcessEvent().TryWait(); - case SystemEventState::Uninitialized: + void WaitSystemEvent(SystemEventType *event) { + switch (event->state) { + case SystemEventType::State_InitializedAsInterProcessEvent: return impl::WaitInterProcessEvent(std::addressof(event->inter_process_event)); + case SystemEventType::State_InitializedAsEvent: return WaitEvent(std::addressof(event->event)); AMS_UNREACHABLE_DEFAULT_CASE(); } } - 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: + bool TryWaitSystemEvent(SystemEventType *event) { + switch (event->state) { + case SystemEventType::State_InitializedAsInterProcessEvent: return impl::TryWaitInterProcessEvent(std::addressof(event->inter_process_event)); + case SystemEventType::State_InitializedAsEvent: return TryWaitEvent(std::addressof(event->event)); AMS_UNREACHABLE_DEFAULT_CASE(); } } + + bool TimedWaitSystemEvent(SystemEventType *event, TimeSpan timeout) { + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); + + switch (event->state) { + case SystemEventType::State_InitializedAsInterProcessEvent: return impl::TimedWaitInterProcessEvent(std::addressof(event->inter_process_event), timeout); + case SystemEventType::State_InitializedAsEvent: return TimedWaitEvent(std::addressof(event->event), timeout); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void ClearSystemEvent(SystemEventType *event) { + switch (event->state) { + case SystemEventType::State_InitializedAsInterProcessEvent: return impl::ClearInterProcessEvent(std::addressof(event->inter_process_event)); + case SystemEventType::State_InitializedAsEvent: return ClearEvent(std::addressof(event->event)); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void InitializeWaitableHolder(WaitableHolderType *waitable_holder, SystemEventType *event) { + switch (event->state) { + case SystemEventType::State_InitializedAsInterProcessEvent: + new (GetPointer(waitable_holder->impl_storage)) impl::WaitableHolderOfInterProcessEvent(std::addressof(event->inter_process_event)); + break; + case SystemEventType::State_InitializedAsEvent: + new (GetPointer(waitable_holder->impl_storage)) impl::WaitableHolderOfEvent(std::addressof(event->event)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + } diff --git a/libraries/libstratosphere/source/os/os_thread.cpp b/libraries/libstratosphere/source/os/os_thread.cpp new file mode 100644 index 000000000..1dcbb2de3 --- /dev/null +++ b/libraries/libstratosphere/source/os/os_thread.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/os_thread_manager.hpp" +#include "impl/os_timeout_helper.hpp" +#include "impl/os_waitable_holder_impl.hpp" + +namespace ams::os { + + namespace { + + size_t CheckThreadNameLength(const char *name) { + const char *cur = name; + for (size_t len = 0; len < ThreadNameLengthMax; ++len) { + if (*(cur++) == 0) { + return len; + } + } + + AMS_ABORT("ThreadNameLength too large"); + } + + void ValidateThreadArguments(ThreadType *thread, void *stack, size_t stack_size, s32 priority) { + AMS_ASSERT(stack != nullptr); + AMS_ASSERT(util::IsAligned(reinterpret_cast(stack), ThreadStackAlignment)); + AMS_ASSERT(stack_size > 0); + AMS_ASSERT(util::IsAligned(stack_size, ThreadStackAlignment)); + } + + } + + Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority, s32 ideal_core) { + ValidateThreadArguments(thread, stack, stack_size, priority); + AMS_ASSERT(GetThreadAvailableCoreMask() & (1ul << ideal_core)); + return impl::GetThreadManager().CreateThread(thread, function, argument, stack, stack_size, priority, ideal_core); + } + + Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority) { + ValidateThreadArguments(thread, stack, stack_size, priority); + return impl::GetThreadManager().CreateThread(thread, function, argument, stack, stack_size, priority); + } + + void DestroyThread(ThreadType *thread) { + auto &manager = impl::GetThreadManager(); + + AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated); + AMS_ASSERT(thread != manager.GetMainThread()); + + manager.DestroyThread(thread); + } + + void StartThread(ThreadType *thread) { + AMS_ASSERT(thread->state == ThreadType::State_Initialized); + impl::GetThreadManager().StartThread(thread); + } + + ThreadType *GetCurrentThread() { + return impl::GetCurrentThread(); + } + + void WaitThread(ThreadType *thread) { + AMS_ASSERT(thread != impl::GetCurrentThread()); + AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated); + + return impl::GetThreadManager().WaitThread(thread); + } + + bool TryWaitThread(ThreadType *thread) { + AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated); + return impl::GetThreadManager().TryWaitThread(thread); + } + + void YieldThread() { + return impl::GetThreadManager().YieldThread(); + } + + void SleepThread(TimeSpan time) { + impl::TimeoutHelper::Sleep(time); + } + + s32 SuspendThread(ThreadType *thread) { + AMS_ASSERT(thread->state == ThreadType::State_Started); + AMS_ASSERT(thread != impl::GetCurrentThread()); + + return impl::GetThreadManager().SuspendThread(thread); + } + + s32 ResumeThread(ThreadType *thread) { + AMS_ASSERT(thread->state == ThreadType::State_Started); + + return impl::GetThreadManager().ResumeThread(thread); + } + + s32 GetThreadSuspendCount(const ThreadType *thread) { + return thread->suspend_count; + } + + void CancelThreadSynchronization(ThreadType *thread) { + AMS_ASSERT(thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated); + + return impl::GetThreadManager().CancelThreadSynchronization(thread); + } + + /* TODO: void GetThreadContext(ThreadContextInfo *out_context, const ThreadType *thread); */ + + s32 ChangeThreadPriority(ThreadType *thread, s32 priority) { + AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_DestroyedBeforeStarted || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated); + { + std::scoped_lock lk(GetReference(thread->cs_thread)); + + const s32 prev_prio = thread->base_priority; + + bool success = impl::GetThreadManager().ChangePriority(thread, priority); + AMS_ASSERT(success); + + thread->base_priority = priority; + + return prev_prio; + } + } + + s32 GetThreadPriority(const ThreadType *thread) { + AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_DestroyedBeforeStarted || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated); + return thread->base_priority; + } + + s32 GetThreadCurrentPriority(const ThreadType *thread) { + AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_DestroyedBeforeStarted || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated); + return impl::GetThreadManager().GetCurrentPriority(thread); + } + + void SetThreadName(ThreadType *thread, const char *name) { + AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_DestroyedBeforeStarted || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated); + + if (name == nullptr) { + impl::GetThreadManager().SetInitialThreadNameUnsafe(thread); + return; + } + + const size_t name_size = CheckThreadNameLength(name) + 1; + std::memcpy(thread->name_buffer, name, name_size); + SetThreadNamePointer(thread, thread->name_buffer); + } + + void SetThreadNamePointer(ThreadType *thread, const char *name) { + AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_DestroyedBeforeStarted || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated); + + if (name == nullptr) { + impl::GetThreadManager().SetInitialThreadNameUnsafe(thread); + return; + } + + thread->name_pointer = name; + impl::GetThreadManager().NotifyThreadNameChanged(thread); + } + + const char *GetThreadNamePointer(const ThreadType *thread) { + AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_DestroyedBeforeStarted || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated); + + return thread->name_pointer; + } + + s32 GetCurrentCoreNumber() { + return impl::GetThreadManager().GetCurrentCoreNumber(); + } + + s32 GetCurrentProcessorNumber() { + return GetCurrentCoreNumber(); + } + + void SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) { + AMS_ASSERT(ideal_core == IdealCoreDontCare || ideal_core == IdealCoreUseDefault || ideal_core == IdealCoreNoUpdate || (0 <= ideal_core && ideal_core < impl::CoreAffinityMaskBitWidth)); + if (ideal_core != IdealCoreUseDefault) { + AMS_ASSERT(affinity_mask != 0); + AMS_ASSERT((affinity_mask & ~GetThreadAvailableCoreMask()) == 0); + } + if (ideal_core >= 0) { + AMS_ASSERT((affinity_mask & (1ul << ideal_core)) != 0); + } + + return impl::GetThreadManager().SetThreadCoreMask(thread, ideal_core, affinity_mask); + } + + void GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) { + return impl::GetThreadManager().GetThreadCoreMask(out_ideal_core, out_affinity_mask, thread); + } + + u64 GetThreadAvailableCoreMask() { + return impl::GetThreadManager().GetThreadAvailableCoreMask(); + } + + ThreadId GetThreadId(const ThreadType *thread) { + return impl::GetThreadManager().GetThreadId(thread); + } + +} diff --git a/libraries/libstratosphere/source/os/os_waitable.cpp b/libraries/libstratosphere/source/os/os_waitable.cpp new file mode 100644 index 000000000..20870881f --- /dev/null +++ b/libraries/libstratosphere/source/os/os_waitable.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/os_waitable_manager_impl.hpp" +#include "impl/os_waitable_holder_base.hpp" +#include "impl/os_waitable_holder_impl.hpp" + +namespace ams::os { + + namespace { + + ALWAYS_INLINE impl::WaitableManagerImpl &GetWaitableManagerImpl(WaitableManagerType *manager) { + return GetReference(manager->impl_storage); + } + + ALWAYS_INLINE WaitableHolderType *ReinterpretAsWaitableHolder(impl::WaitableHolderBase *base) { + return reinterpret_cast(base); + } + + } + + void InitializeWaitableManager(WaitableManagerType *manager) { + /* Initialize storage. */ + new (std::addressof(GetWaitableManagerImpl(manager))) impl::WaitableManagerImpl; + + /* Mark initialized. */ + manager->state = WaitableManagerType::State_Initialized; + } + + void FinalizeWaitableManager(WaitableManagerType *manager) { + auto &impl = GetWaitableManagerImpl(manager); + + AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized); + AMS_ASSERT(impl.IsEmpty()); + + /* Mark not initialized. */ + manager->state = WaitableManagerType::State_NotInitialized; + + /* Destroy. */ + impl.~WaitableManagerImpl(); + } + + WaitableHolderType *WaitAny(WaitableManagerType *manager) { + auto &impl = GetWaitableManagerImpl(manager); + + AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized); + AMS_ASSERT(!impl.IsEmpty()); + + auto *holder = ReinterpretAsWaitableHolder(impl.WaitAny()); + AMS_ASSERT(holder != nullptr); + return holder; + } + + WaitableHolderType *TryWaitAny(WaitableManagerType *manager) { + auto &impl = GetWaitableManagerImpl(manager); + + AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized); + AMS_ASSERT(!impl.IsEmpty()); + + auto *holder = ReinterpretAsWaitableHolder(impl.TryWaitAny()); + return holder; + } + + WaitableHolderType *TimedWaitAny(WaitableManagerType *manager, TimeSpan timeout) { + auto &impl = GetWaitableManagerImpl(manager); + + AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized); + AMS_ASSERT(!impl.IsEmpty()); + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); + + auto *holder = ReinterpretAsWaitableHolder(impl.TimedWaitAny(timeout)); + return holder; + } + + void FinalizeWaitableHolder(WaitableHolderType *holder) { + auto *holder_base = reinterpret_cast(GetPointer(holder->impl_storage)); + + AMS_ASSERT(!holder_base->IsLinkedToManager()); + + holder_base->~WaitableHolderBase(); + } + + void LinkWaitableHolder(WaitableManagerType *manager, WaitableHolderType *holder) { + auto &impl = GetWaitableManagerImpl(manager); + auto *holder_base = reinterpret_cast(GetPointer(holder->impl_storage)); + + AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized); + AMS_ASSERT(!holder_base->IsLinkedToManager()); + + impl.LinkWaitableHolder(*holder_base); + holder_base->SetManager(&impl); + } + + void UnlinkWaitableHolder(WaitableHolderType *holder) { + auto *holder_base = reinterpret_cast(GetPointer(holder->impl_storage)); + + /* Don't allow unlinking of an unlinked holder. */ + AMS_ABORT_UNLESS(holder_base->IsLinkedToManager()); + + holder_base->GetManager()->UnlinkWaitableHolder(*holder_base); + holder_base->SetManager(nullptr); + } + + void UnlinkAllWaitableHolder(WaitableManagerType *manager) { + auto &impl = GetWaitableManagerImpl(manager); + + AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized); + + return impl.UnlinkAll(); + } + + void MoveAllWaitableHolder(WaitableManagerType *_dst, WaitableManagerType *_src) { + auto &dst = GetWaitableManagerImpl(_dst); + auto &src = GetWaitableManagerImpl(_src); + + AMS_ASSERT(_dst->state == WaitableManagerType::State_Initialized); + AMS_ASSERT(_src->state == WaitableManagerType::State_Initialized); + + return dst.MoveAllFrom(src); + } + + void SetWaitableHolderUserData(WaitableHolderType *holder, uintptr_t user_data) { + holder->user_data = user_data; + } + + uintptr_t GetWaitableHolderUserData(const WaitableHolderType *holder) { + return holder->user_data; + } + + void InitializeWaitableHolder(WaitableHolderType *holder, Handle handle) { + AMS_ASSERT(handle != svc::InvalidHandle); + + new (GetPointer(holder->impl_storage)) impl::WaitableHolderOfHandle(handle); + + holder->user_data = 0; + } + +} diff --git a/libraries/libstratosphere/source/os/os_waitable_holder.cpp b/libraries/libstratosphere/source/os/os_waitable_holder.cpp deleted file mode 100644 index c986b2e89..000000000 --- a/libraries/libstratosphere/source/os/os_waitable_holder.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include "impl/os_waitable_holder_impl.hpp" -#include "impl/os_waitable_manager_impl.hpp" - -namespace ams::os { - - WaitableHolder::WaitableHolder(Handle handle) { - /* Don't allow invalid handles. */ - AMS_ABORT_UNLESS(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: - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* 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(Semaphore *semaphore) { - /* Initialize appropriate holder. */ - new (GetPointer(this->impl_storage)) impl::WaitableHolderOfSemaphore(semaphore); - - /* 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; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* 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. */ - AMS_ABORT_UNLESS(!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. */ - AMS_ABORT_UNLESS(holder_base->IsLinkedToManager()); - - holder_base->GetManager()->UnlinkWaitableHolder(*holder_base); - holder_base->SetManager(nullptr); - } - -} diff --git a/libraries/libstratosphere/source/os/os_waitable_manager.cpp b/libraries/libstratosphere/source/os/os_waitable_manager.cpp deleted file mode 100644 index 56f67fb84..000000000 --- a/libraries/libstratosphere/source/os/os_waitable_manager.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include "impl/os_waitable_holder_impl.hpp" -#include "impl/os_waitable_manager_impl.hpp" - -namespace ams::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. */ - AMS_ABORT_UNLESS(impl.IsEmpty()); - - impl.~WaitableManagerImpl(); - } - - - /* Wait. */ - WaitableHolder *WaitableManager::WaitAny() { - auto &impl = GetReference(this->impl_storage); - - /* Don't allow waiting on empty list. */ - AMS_ABORT_UNLESS(!impl.IsEmpty()); - - return reinterpret_cast(impl.WaitAny()); - } - - WaitableHolder *WaitableManager::TryWaitAny() { - auto &impl = GetReference(this->impl_storage); - - /* Don't allow waiting on empty list. */ - AMS_ABORT_UNLESS(!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. */ - AMS_ABORT_UNLESS(!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. */ - AMS_ABORT_UNLESS(!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/libraries/libstratosphere/source/patcher/patcher_api.cpp b/libraries/libstratosphere/source/patcher/patcher_api.cpp index 74c7637fb..c7ec0ab9b 100644 --- a/libraries/libstratosphere/source/patcher/patcher_api.cpp +++ b/libraries/libstratosphere/source/patcher/patcher_api.cpp @@ -31,7 +31,7 @@ namespace ams::patcher { constexpr size_t ModuleIpsPatchLength = 2 * sizeof(ro::ModuleId) + IpsFileExtensionLength; /* Global data. */ - os::Mutex apply_patch_lock; + os::Mutex apply_patch_lock(false); u8 g_patch_read_buffer[os::MemoryPageSize]; /* Helpers. */ diff --git a/libraries/libstratosphere/source/pm/pm_info_api.cpp b/libraries/libstratosphere/source/pm/pm_info_api.cpp index 94f3da321..3c550d0f7 100644 --- a/libraries/libstratosphere/source/pm/pm_info_api.cpp +++ b/libraries/libstratosphere/source/pm/pm_info_api.cpp @@ -21,7 +21,7 @@ namespace ams::pm::info { namespace { /* Global lock. */ - os::Mutex g_info_lock; + os::Mutex g_info_lock(false); /* TODO: Less memory-intensive storage? */ std::set g_cached_launched_programs; diff --git a/libraries/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp b/libraries/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp index 7b9d1dcfc..db84b5abe 100644 --- a/libraries/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp +++ b/libraries/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp @@ -106,7 +106,7 @@ namespace ams::sf::cmif { return entry->object.Clone(); } - ServerDomainManager::EntryManager::EntryManager(DomainEntryStorage *entry_storage, size_t entry_count) { + ServerDomainManager::EntryManager::EntryManager(DomainEntryStorage *entry_storage, size_t entry_count) : lock(false) { this->entries = reinterpret_cast(entry_storage); this->num_entries = entry_count; for (size_t i = 0; i < this->num_entries; i++) { diff --git a/libraries/libstratosphere/source/sf/hipc/sf_hipc_api.cpp b/libraries/libstratosphere/source/sf/hipc/sf_hipc_api.cpp index 3f6658a6c..113c47fe0 100644 --- a/libraries/libstratosphere/source/sf/hipc/sf_hipc_api.cpp +++ b/libraries/libstratosphere/source/sf/hipc/sf_hipc_api.cpp @@ -19,7 +19,7 @@ namespace ams::sf::hipc { namespace { - NX_INLINE Result ReceiveImpl(Handle session_handle, void *message_buf, size_t message_buf_size) { + ALWAYS_INLINE Result ReceiveImpl(Handle session_handle, void *message_buf, size_t message_buf_size) { s32 unused_index; if (message_buf == armGetTls()) { /* Consider: AMS_ABORT_UNLESS(message_buf_size == TlsMessageBufferSize); */ @@ -29,7 +29,7 @@ namespace ams::sf::hipc { } } - NX_INLINE Result ReplyImpl(Handle session_handle, void *message_buf, size_t message_buf_size) { + ALWAYS_INLINE Result ReplyImpl(Handle session_handle, void *message_buf, size_t message_buf_size) { s32 unused_index; if (message_buf == armGetTls()) { /* Consider: AMS_ABORT_UNLESS(message_buf_size == TlsMessageBufferSize); */ @@ -41,6 +41,14 @@ namespace ams::sf::hipc { } + void AttachWaitableHolderForAccept(os::WaitableHolderType *holder, Handle port) { + return os::InitializeWaitableHolder(holder, port); + } + + void AttachWaitableHolderForReply(os::WaitableHolderType *holder, Handle request) { + return os::InitializeWaitableHolder(holder, request); + } + Result Receive(ReceiveResult *out_recv_result, Handle session_handle, const cmif::PointerAndSize &message_buffer) { R_TRY_CATCH(ReceiveImpl(session_handle, message_buffer.GetPointer(), message_buffer.GetSize())) { R_CATCH(svc::ResultSessionClosed) { diff --git a/libraries/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp b/libraries/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp index 8d5ae412c..85023c022 100644 --- a/libraries/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp +++ b/libraries/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp @@ -40,7 +40,7 @@ namespace ams::sf::hipc::impl { }; /* Globals. */ - os::Mutex g_query_server_lock; + os::Mutex g_query_server_lock(false); bool g_constructed_server = false; bool g_registered_any = false; @@ -49,8 +49,9 @@ namespace ams::sf::hipc::impl { } constexpr size_t QueryServerProcessThreadStackSize = 0x4000; - constexpr int QueryServerProcessThreadPriority = 27; - os::StaticThread g_query_server_process_thread; + constexpr s32 QueryServerProcessThreadPriority = -1; + alignas(os::ThreadStackAlignment) u8 g_server_process_thread_stack[QueryServerProcessThreadStackSize]; + os::ThreadType g_query_server_process_thread; constexpr size_t MaxServers = 0; TYPED_STORAGE(sf::hipc::ServerManager) g_query_server_storage; @@ -61,16 +62,16 @@ namespace ams::sf::hipc::impl { std::scoped_lock lk(g_query_server_lock); - if (!g_constructed_server) { + if (AMS_UNLIKELY(!g_constructed_server)) { new (GetPointer(g_query_server_storage)) sf::hipc::ServerManager(); g_constructed_server = true; } R_ABORT_UNLESS(GetPointer(g_query_server_storage)->RegisterSession(query_handle, cmif::ServiceObjectHolder(std::make_shared(query_func)))); - if (!g_registered_any) { - R_ABORT_UNLESS(g_query_server_process_thread.Initialize(&QueryServerProcessThreadMain, GetPointer(g_query_server_storage), QueryServerProcessThreadPriority)); - R_ABORT_UNLESS(g_query_server_process_thread.Start()); + if (AMS_UNLIKELY(!g_registered_any)) { + R_ABORT_UNLESS(os::CreateThread(std::addressof(g_query_server_process_thread), &QueryServerProcessThreadMain, GetPointer(g_query_server_storage), g_server_process_thread_stack, sizeof(g_server_process_thread_stack), QueryServerProcessThreadPriority)); + os::StartThread(std::addressof(g_query_server_process_thread)); g_registered_any = true; } } diff --git a/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp b/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp index a3de70bad..c763dbae2 100644 --- a/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp +++ b/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp @@ -34,56 +34,56 @@ namespace ams::sf::hipc { session->has_received = false; /* Set user data tag. */ - session->SetUserData(static_cast(UserDataTag::Session)); + os::SetWaitableHolderUserData(session, static_cast(UserDataTag::Session)); this->RegisterToWaitList(session); } - void ServerManagerBase::RegisterToWaitList(os::WaitableHolder *holder) { + void ServerManagerBase::RegisterToWaitList(os::WaitableHolderType *holder) { std::scoped_lock lk(this->waitlist_mutex); - this->waitlist.LinkWaitableHolder(holder); + os::LinkWaitableHolder(std::addressof(this->waitlist), holder); this->notify_event.Signal(); } void ServerManagerBase::ProcessWaitList() { std::scoped_lock lk(this->waitlist_mutex); - this->waitable_manager.MoveAllFrom(&this->waitlist); + os::MoveAllWaitableHolder(std::addressof(this->waitable_manager), std::addressof(this->waitlist)); } - os::WaitableHolder *ServerManagerBase::WaitSignaled() { + os::WaitableHolderType *ServerManagerBase::WaitSignaled() { std::scoped_lock lk(this->waitable_selection_mutex); while (true) { this->ProcessWaitList(); - auto selected = this->waitable_manager.WaitAny(); + auto selected = os::WaitAny(std::addressof(this->waitable_manager)); if (selected == &this->request_stop_event_holder) { return nullptr; } else if (selected == &this->notify_event_holder) { - this->notify_event.Reset(); + this->notify_event.Clear(); } else { - selected->UnlinkFromWaitableManager(); + os::UnlinkWaitableHolder(selected); return selected; } } } void ServerManagerBase::ResumeProcessing() { - this->request_stop_event.Reset(); + this->request_stop_event.Clear(); } void ServerManagerBase::RequestStopProcessing() { this->request_stop_event.Signal(); } - void ServerManagerBase::AddUserWaitableHolder(os::WaitableHolder *waitable) { - const auto user_data_tag = static_cast(waitable->GetUserData()); + void ServerManagerBase::AddUserWaitableHolder(os::WaitableHolderType *waitable) { + const auto user_data_tag = static_cast(os::GetWaitableHolderUserData(waitable)); AMS_ABORT_UNLESS(user_data_tag != UserDataTag::Server); AMS_ABORT_UNLESS(user_data_tag != UserDataTag::MitmServer); AMS_ABORT_UNLESS(user_data_tag != UserDataTag::Session); this->RegisterToWaitList(waitable); } - Result ServerManagerBase::ProcessForServer(os::WaitableHolder *holder) { - AMS_ABORT_UNLESS(static_cast(holder->GetUserData()) == UserDataTag::Server); + Result ServerManagerBase::ProcessForServer(os::WaitableHolderType *holder) { + AMS_ABORT_UNLESS(static_cast(os::GetWaitableHolderUserData(holder)) == UserDataTag::Server); ServerBase *server = static_cast(holder); ON_SCOPE_EXIT { this->RegisterToWaitList(server); }; @@ -100,8 +100,8 @@ namespace ams::sf::hipc { return this->AcceptSession(server->port_handle, std::move(obj)); } - Result ServerManagerBase::ProcessForMitmServer(os::WaitableHolder *holder) { - AMS_ABORT_UNLESS(static_cast(holder->GetUserData()) == UserDataTag::MitmServer); + Result ServerManagerBase::ProcessForMitmServer(os::WaitableHolderType *holder) { + AMS_ABORT_UNLESS(static_cast(os::GetWaitableHolderUserData(holder)) == UserDataTag::MitmServer); ServerBase *server = static_cast(holder); ON_SCOPE_EXIT { this->RegisterToWaitList(server); }; @@ -118,8 +118,8 @@ namespace ams::sf::hipc { return this->AcceptMitmSession(server->port_handle, std::move(obj), std::move(fsrv)); } - Result ServerManagerBase::ProcessForSession(os::WaitableHolder *holder) { - AMS_ABORT_UNLESS(static_cast(holder->GetUserData()) == UserDataTag::Session); + Result ServerManagerBase::ProcessForSession(os::WaitableHolderType *holder) { + AMS_ABORT_UNLESS(static_cast(os::GetWaitableHolderUserData(holder)) == UserDataTag::Session); ServerSession *session = static_cast(holder); @@ -176,8 +176,8 @@ namespace ams::sf::hipc { } } - Result ServerManagerBase::Process(os::WaitableHolder *holder) { - switch (static_cast(holder->GetUserData())) { + Result ServerManagerBase::Process(os::WaitableHolderType *holder) { + switch (static_cast(os::GetWaitableHolderUserData(holder))) { case UserDataTag::Server: return this->ProcessForServer(holder); break; diff --git a/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp b/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp index 43ebb0c88..edec2ba3f 100644 --- a/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp +++ b/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp @@ -77,6 +77,7 @@ namespace ams::sf::hipc { void ServerSessionManager::CloseSessionImpl(ServerSession *session) { const Handle session_handle = session->session_handle; + os::FinalizeWaitableHolder(session); this->DestroySession(session); R_ABORT_UNLESS(svcCloseHandle(session_handle)); } diff --git a/libraries/libstratosphere/source/sm/sm_utils.cpp b/libraries/libstratosphere/source/sm/sm_utils.cpp index 3fe0475ae..62d79f3c1 100644 --- a/libraries/libstratosphere/source/sm/sm_utils.cpp +++ b/libraries/libstratosphere/source/sm/sm_utils.cpp @@ -20,22 +20,22 @@ namespace ams::sm::impl { namespace { /* Globals. */ - os::RecursiveMutex g_user_session_mutex; - os::RecursiveMutex g_mitm_ack_session_mutex; - os::RecursiveMutex g_per_thread_session_mutex; + os::Mutex g_user_session_mutex(true); + os::Mutex g_mitm_ack_session_mutex(true); + os::Mutex g_per_thread_session_mutex(true); } /* Utilities. */ - os::RecursiveMutex &GetUserSessionMutex() { + os::Mutex &GetUserSessionMutex() { return g_user_session_mutex; } - os::RecursiveMutex &GetMitmAcknowledgementSessionMutex() { + os::Mutex &GetMitmAcknowledgementSessionMutex() { return g_mitm_ack_session_mutex; } - os::RecursiveMutex &GetPerThreadSessionMutex() { + os::Mutex &GetPerThreadSessionMutex() { return g_per_thread_session_mutex; } diff --git a/libraries/libstratosphere/source/sm/sm_utils.hpp b/libraries/libstratosphere/source/sm/sm_utils.hpp index 1db2bb6b4..d3b4b153e 100644 --- a/libraries/libstratosphere/source/sm/sm_utils.hpp +++ b/libraries/libstratosphere/source/sm/sm_utils.hpp @@ -21,13 +21,13 @@ namespace ams::sm::impl { /* Utilities. */ - os::RecursiveMutex &GetUserSessionMutex(); - os::RecursiveMutex &GetMitmAcknowledgementSessionMutex(); - os::RecursiveMutex &GetPerThreadSessionMutex(); + os::Mutex &GetUserSessionMutex(); + os::Mutex &GetMitmAcknowledgementSessionMutex(); + os::Mutex &GetPerThreadSessionMutex(); template Result DoWithUserSession(F f) { - std::scoped_lock lk(GetUserSessionMutex()); + std::scoped_lock lk(GetUserSessionMutex()); { R_ABORT_UNLESS(smInitialize()); ON_SCOPE_EXIT { smExit(); }; @@ -38,7 +38,7 @@ namespace ams::sm::impl { template Result DoWithMitmAcknowledgementSession(F f) { - std::scoped_lock lk(GetMitmAcknowledgementSessionMutex()); + std::scoped_lock lk(GetMitmAcknowledgementSessionMutex()); { R_ABORT_UNLESS(smAtmosphereMitmInitialize()); ON_SCOPE_EXIT { smAtmosphereMitmExit(); }; @@ -51,7 +51,7 @@ namespace ams::sm::impl { Result DoWithPerThreadSession(F f) { Service srv; { - std::scoped_lock lk(GetPerThreadSessionMutex()); + std::scoped_lock lk(GetPerThreadSessionMutex()); R_ABORT_UNLESS(smAtmosphereOpenSession(&srv)); } { diff --git a/libraries/libvapours/include/vapours/svc/svc_common.hpp b/libraries/libvapours/include/vapours/svc/svc_common.hpp index 3e3850a0b..9e4fc430d 100644 --- a/libraries/libvapours/include/vapours/svc/svc_common.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_common.hpp @@ -28,8 +28,14 @@ namespace ams::svc { using Handle = u32; #endif + enum { + HandleWaitMask = (1u << 30), + }; + constexpr inline size_t MaxWaitSynchronizationHandleCount = 0x40; + constexpr inline s64 WaitInfinite = -1; + enum PseudoHandle : Handle { CurrentThread = 0xFFFF8000, CurrentProcess = 0xFFFF8001, diff --git a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp index 1fe271945..9ecb75018 100644 --- a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp @@ -279,8 +279,14 @@ namespace ams::svc { ThreadActivity_Paused = 1, }; - constexpr s32 LowestThreadPriority = 63; - constexpr s32 HighestThreadPriority = 0; + constexpr inline s32 IdealCoreDontCare = -1; + constexpr inline s32 IdealCoreUseProcessValue = -2; + constexpr inline s32 IdealCoreNoUpdate = -3; + + constexpr inline s32 LowestThreadPriority = 63; + constexpr inline s32 HighestThreadPriority = 0; + + constexpr inline s32 SystemThreadPriorityHighest = 16; /* Process types. */ enum ProcessInfoType : u32 { diff --git a/libraries/libvapours/include/vapours/timespan.hpp b/libraries/libvapours/include/vapours/timespan.hpp index 51d755c32..201b0c211 100644 --- a/libraries/libvapours/include/vapours/timespan.hpp +++ b/libraries/libvapours/include/vapours/timespan.hpp @@ -21,25 +21,18 @@ namespace ams { - class TimeSpan { - private: + struct TimeSpanType { + public: s64 ns; - private: - constexpr explicit ALWAYS_INLINE TimeSpan(s64 v) : ns(v) { /* ... */ } public: - constexpr explicit ALWAYS_INLINE TimeSpan() : TimeSpan(0) { /* ... */ } + static constexpr ALWAYS_INLINE TimeSpanType FromNanoSeconds(s64 ns) { return {ns}; } + static constexpr ALWAYS_INLINE TimeSpanType FromMicroSeconds(s64 ms) { return FromNanoSeconds(ms * INT64_C(1000)); } + static constexpr ALWAYS_INLINE TimeSpanType FromMilliSeconds(s64 ms) { return FromMicroSeconds(ms * INT64_C(1000)); } + static constexpr ALWAYS_INLINE TimeSpanType FromSeconds(s64 s) { return FromMilliSeconds(s * INT64_C(1000)); } + static constexpr ALWAYS_INLINE TimeSpanType FromMinutes(s64 m) { return FromSeconds(m * INT64_C(60)); } + static constexpr ALWAYS_INLINE TimeSpanType FromHours(s64 h) { return FromMinutes(h * INT64_C(60)); } + static constexpr ALWAYS_INLINE TimeSpanType FromDays(s64 d) { return FromMinutes(d * INT64_C(24)); } - static constexpr ALWAYS_INLINE TimeSpan FromNanoSeconds(s64 ns) { return TimeSpan(ns); } - static constexpr ALWAYS_INLINE TimeSpan FromMicroSeconds(s64 ms) { return FromNanoSeconds(ms * INT64_C(1000)); } - static constexpr ALWAYS_INLINE TimeSpan FromMilliSeconds(s64 ms) { return FromMicroSeconds(ms * INT64_C(1000)); } - static constexpr ALWAYS_INLINE TimeSpan FromSeconds(s64 s) { return FromMilliSeconds(s * INT64_C(1000)); } - static constexpr ALWAYS_INLINE TimeSpan FromMinutes(s64 m) { return FromSeconds(m * INT64_C(60)); } - static constexpr ALWAYS_INLINE TimeSpan FromHours(s64 h) { return FromMinutes(h * INT64_C(60)); } - static constexpr ALWAYS_INLINE TimeSpan FromDays(s64 d) { return FromMinutes(d * INT64_C(24)); } - - template - constexpr explicit ALWAYS_INLINE TimeSpan(const std::chrono::duration& c) : TimeSpan(static_cast(c).count()) { /* ... */ } - public: constexpr ALWAYS_INLINE s64 GetNanoSeconds() const { return this->ns; } constexpr ALWAYS_INLINE s64 GetMicroSeconds() const { return this->GetNanoSeconds() / (INT64_C(1000)); } constexpr ALWAYS_INLINE s64 GetMilliSeconds() const { return this->GetNanoSeconds() / (INT64_C(1000) * INT64_C(1000)); } @@ -48,17 +41,60 @@ namespace ams { constexpr ALWAYS_INLINE s64 GetHours() const { return this->GetNanoSeconds() / (INT64_C(1000) * INT64_C(1000) * INT64_C(1000) * INT64_C( 60) * INT64_C( 60)); } constexpr ALWAYS_INLINE s64 GetDays() const { return this->GetNanoSeconds() / (INT64_C(1000) * INT64_C(1000) * INT64_C(1000) * INT64_C( 60) * INT64_C( 60) * INT64_C( 24)); } - constexpr ALWAYS_INLINE bool operator==(const TimeSpan &rhs) const { return this->ns == rhs.ns; } - constexpr ALWAYS_INLINE bool operator!=(const TimeSpan &rhs) const { return this->ns != rhs.ns; } - constexpr ALWAYS_INLINE bool operator<=(const TimeSpan &rhs) const { return this->ns <= rhs.ns; } - constexpr ALWAYS_INLINE bool operator>=(const TimeSpan &rhs) const { return this->ns >= rhs.ns; } - constexpr ALWAYS_INLINE bool operator< (const TimeSpan &rhs) const { return this->ns < rhs.ns; } - constexpr ALWAYS_INLINE bool operator> (const TimeSpan &rhs) const { return this->ns > rhs.ns; } + constexpr ALWAYS_INLINE friend bool operator==(const TimeSpanType &lhs, const TimeSpanType &rhs) { return lhs.ns == rhs.ns; } + constexpr ALWAYS_INLINE friend bool operator!=(const TimeSpanType &lhs, const TimeSpanType &rhs) { return lhs.ns != rhs.ns; } + constexpr ALWAYS_INLINE friend bool operator<=(const TimeSpanType &lhs, const TimeSpanType &rhs) { return lhs.ns <= rhs.ns; } + constexpr ALWAYS_INLINE friend bool operator>=(const TimeSpanType &lhs, const TimeSpanType &rhs) { return lhs.ns >= rhs.ns; } + constexpr ALWAYS_INLINE friend bool operator< (const TimeSpanType &lhs, const TimeSpanType &rhs) { return lhs.ns < rhs.ns; } + constexpr ALWAYS_INLINE friend bool operator> (const TimeSpanType &lhs, const TimeSpanType &rhs) { return lhs.ns > rhs.ns; } - constexpr ALWAYS_INLINE TimeSpan &operator+=(TimeSpan rhs) { this->ns += rhs.ns; return *this; } - constexpr ALWAYS_INLINE TimeSpan &operator-=(TimeSpan rhs) { this->ns -= rhs.ns; return *this; } - constexpr ALWAYS_INLINE TimeSpan operator+(TimeSpan rhs) const { TimeSpan r(*this); return r += rhs; } - constexpr ALWAYS_INLINE TimeSpan operator-(TimeSpan rhs) const { TimeSpan r(*this); return r -= rhs; } + constexpr ALWAYS_INLINE TimeSpanType &operator+=(const TimeSpanType &rhs) { this->ns += rhs.ns; return *this; } + constexpr ALWAYS_INLINE TimeSpanType &operator-=(const TimeSpanType &rhs) { this->ns -= rhs.ns; return *this; } + + constexpr ALWAYS_INLINE friend TimeSpanType operator+(const TimeSpanType &lhs, const TimeSpanType &rhs) { TimeSpanType r(lhs); return r += rhs; } + constexpr ALWAYS_INLINE friend TimeSpanType operator-(const TimeSpanType &lhs, const TimeSpanType &rhs) { TimeSpanType r(lhs); return r -= rhs; } + }; + + class TimeSpan { + private: + using ZeroTag = const class ZeroTagImpl{} *; + private: + TimeSpanType ts; + public: + constexpr ALWAYS_INLINE TimeSpan(ZeroTag z = nullptr) : ts(TimeSpanType::FromNanoSeconds(0)) { /* ... */ } + constexpr ALWAYS_INLINE TimeSpan(const TimeSpanType &t) : ts(t) { /* ... */ } + + template + constexpr ALWAYS_INLINE TimeSpan(const std::chrono::duration& c) : ts(TimeSpanType::FromNanoSeconds(static_cast(c).count())) { /* ... */ } + public: + static constexpr ALWAYS_INLINE TimeSpan FromNanoSeconds(s64 ns) { return TimeSpanType::FromNanoSeconds(ns); } + static constexpr ALWAYS_INLINE TimeSpan FromMicroSeconds(s64 ms) { return TimeSpanType::FromMicroSeconds(ms); } + static constexpr ALWAYS_INLINE TimeSpan FromMilliSeconds(s64 ms) { return TimeSpanType::FromMilliSeconds(ms); } + static constexpr ALWAYS_INLINE TimeSpan FromSeconds(s64 s) { return TimeSpanType::FromSeconds(s); } + static constexpr ALWAYS_INLINE TimeSpan FromMinutes(s64 m) { return TimeSpanType::FromMinutes(m); } + static constexpr ALWAYS_INLINE TimeSpan FromHours(s64 h) { return TimeSpanType::FromHours(h); } + static constexpr ALWAYS_INLINE TimeSpan FromDays(s64 d) { return TimeSpanType::FromDays(d); } + + constexpr ALWAYS_INLINE s64 GetNanoSeconds() const { return this->ts.GetNanoSeconds(); } + constexpr ALWAYS_INLINE s64 GetMicroSeconds() const { return this->ts.GetMicroSeconds(); } + constexpr ALWAYS_INLINE s64 GetMilliSeconds() const { return this->ts.GetMilliSeconds(); } + constexpr ALWAYS_INLINE s64 GetSeconds() const { return this->ts.GetSeconds(); } + constexpr ALWAYS_INLINE s64 GetMinutes() const { return this->ts.GetMinutes(); } + constexpr ALWAYS_INLINE s64 GetHours() const { return this->ts.GetHours(); } + constexpr ALWAYS_INLINE s64 GetDays() const { return this->ts.GetDays(); } + + constexpr ALWAYS_INLINE friend bool operator==(const TimeSpan &lhs, const TimeSpan &rhs) { return lhs.ts == rhs.ts; } + constexpr ALWAYS_INLINE friend bool operator!=(const TimeSpan &lhs, const TimeSpan &rhs) { return lhs.ts != rhs.ts; } + constexpr ALWAYS_INLINE friend bool operator<=(const TimeSpan &lhs, const TimeSpan &rhs) { return lhs.ts <= rhs.ts; } + constexpr ALWAYS_INLINE friend bool operator>=(const TimeSpan &lhs, const TimeSpan &rhs) { return lhs.ts >= rhs.ts; } + constexpr ALWAYS_INLINE friend bool operator< (const TimeSpan &lhs, const TimeSpan &rhs) { return lhs.ts < rhs.ts; } + constexpr ALWAYS_INLINE friend bool operator> (const TimeSpan &lhs, const TimeSpan &rhs) { return lhs.ts > rhs.ts; } + + constexpr ALWAYS_INLINE TimeSpan &operator+=(const TimeSpan &rhs) { this->ts += rhs.ts; return *this; } + constexpr ALWAYS_INLINE TimeSpan &operator-=(const TimeSpan &rhs) { this->ts -= rhs.ts; return *this; } + + constexpr ALWAYS_INLINE friend TimeSpan operator+(const TimeSpan &lhs, const TimeSpan &rhs) { TimeSpan r(lhs); return r += rhs; } + constexpr ALWAYS_INLINE friend TimeSpan operator-(const TimeSpan &lhs, const TimeSpan &rhs) { TimeSpan r(lhs); return r -= rhs; } }; } \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/amsmitm_debug.cpp b/stratosphere/ams_mitm/source/amsmitm_debug.cpp index 28798e0a5..fd9a311c4 100644 --- a/stratosphere/ams_mitm/source/amsmitm_debug.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_debug.cpp @@ -20,7 +20,7 @@ namespace ams::mitm { namespace { - os::Mutex g_throw_lock; + os::Mutex g_throw_lock(false); bool g_threw; Result g_throw_result; @@ -28,12 +28,14 @@ namespace ams::mitm { void DebugThrowThreadFunc(void *arg); constexpr size_t DebugThrowThreadStackSize = 0x4000; - constexpr int DebugThrowThreadPriority = 49; - os::StaticThread g_debug_throw_thread(&DebugThrowThreadFunc, nullptr, DebugThrowThreadPriority); + constexpr int DebugThrowThreadPriority = 21; + os::ThreadType g_debug_throw_thread; + + alignas(os::ThreadStackAlignment) u8 g_debug_throw_thread_stack[DebugThrowThreadStackSize]; void DebugThrowThreadFunc(void *arg) { /* TODO: Better heuristic for fatal startup than sleep. */ - svcSleepThread(10'000'000'000ul); + os::SleepThread(TimeSpan::FromSeconds(10)); fatalThrow(g_throw_result.GetValue()); } @@ -48,7 +50,8 @@ namespace ams::mitm { g_throw_result = res; g_threw = true; - R_ABORT_UNLESS(g_debug_throw_thread.Start()); + R_ABORT_UNLESS(os::CreateThread(std::addressof(g_debug_throw_thread), DebugThrowThreadFunc, nullptr, g_debug_throw_thread_stack, sizeof(g_debug_throw_thread_stack), DebugThrowThreadPriority)); + os::StartThread(std::addressof(g_debug_throw_thread)); } } diff --git a/stratosphere/ams_mitm/source/amsmitm_initialization.cpp b/stratosphere/ams_mitm/source/amsmitm_initialization.cpp index 90b073871..1c1ca65be 100644 --- a/stratosphere/ams_mitm/source/amsmitm_initialization.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_initialization.cpp @@ -50,11 +50,13 @@ namespace ams::mitm { void InitializeThreadFunc(void *arg); constexpr size_t InitializeThreadStackSize = 0x4000; - constexpr int InitializeThreadPriority = 0x15; - os::StaticThread g_initialize_thread(&InitializeThreadFunc, nullptr, InitializeThreadPriority); + constexpr int InitializeThreadPriority = -7; /* Globals. */ - os::Event g_init_event(false); + os::Event g_init_event(os::EventClearMode_ManualClear); + + os::ThreadType g_initialize_thread; + alignas(os::ThreadStackAlignment) u8 g_initialize_thread_stack[InitializeThreadStackSize]; /* Console-unique data backup and protection. */ constexpr size_t CalibrationBinarySize = 0x8000; @@ -221,7 +223,8 @@ namespace ams::mitm { } void StartInitialize() { - R_ABORT_UNLESS(g_initialize_thread.Start()); + R_ABORT_UNLESS(os::CreateThread(std::addressof(g_initialize_thread), InitializeThreadFunc, nullptr, g_initialize_thread_stack, sizeof(g_initialize_thread_stack), InitializeThreadPriority)); + os::StartThread(std::addressof(g_initialize_thread)); } bool IsInitialized() { diff --git a/stratosphere/ams_mitm/source/amsmitm_module.hpp b/stratosphere/ams_mitm/source/amsmitm_module.hpp index 5662ff981..75333f47b 100644 --- a/stratosphere/ams_mitm/source/amsmitm_module.hpp +++ b/stratosphere/ams_mitm/source/amsmitm_module.hpp @@ -22,12 +22,12 @@ namespace ams::mitm { class ModuleBase {}; #define DEFINE_MITM_MODULE_CLASS(ss, prio) class MitmModule : public ::ams::mitm::ModuleBase { \ - public: \ - static constexpr size_t ThreadPriority = prio; \ - static constexpr size_t StackSize = ss; \ - alignas(os::MemoryPageSize) static inline u8 Stack[StackSize]; \ - public: \ - static void ThreadFunction(void *); \ + public: \ + static constexpr s32 ThreadPriority = prio; \ + static constexpr size_t StackSize = ss; \ + alignas(os::ThreadStackAlignment) static inline u8 Stack[StackSize]; \ + public: \ + static void ThreadFunction(void *); \ } template @@ -37,7 +37,7 @@ namespace ams::mitm { static constexpr void *Stack = &M::Stack[0]; static constexpr size_t StackSize = M::StackSize; - static constexpr size_t ThreadPriority = M::ThreadPriority; + static constexpr s32 ThreadPriority = M::ThreadPriority; static constexpr ::ThreadFunc ThreadFunction = &M::ThreadFunction; }; diff --git a/stratosphere/ams_mitm/source/amsmitm_module_management.cpp b/stratosphere/ams_mitm/source/amsmitm_module_management.cpp index 317b5103f..f27a72e7b 100644 --- a/stratosphere/ams_mitm/source/amsmitm_module_management.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_module_management.cpp @@ -40,7 +40,7 @@ namespace ams::mitm { struct ModuleDefinition { ThreadFunc main; void *stack_mem; - u32 priority; + s32 priority; u32 stack_size; }; @@ -56,7 +56,7 @@ namespace ams::mitm { }; } - ams::os::Thread g_module_threads[ModuleId_Count]; + ams::os::ThreadType g_module_threads[ModuleId_Count]; constexpr ModuleDefinition g_module_definitions[ModuleId_Count] = { GetModuleDefinition(), @@ -72,19 +72,19 @@ namespace ams::mitm { /* Create thread for each module. */ for (u32 i = 0; i < static_cast(ModuleId_Count); i++) { const ModuleDefinition &cur_module = g_module_definitions[i]; - R_ABORT_UNLESS(g_module_threads[i].Initialize(cur_module.main, nullptr, cur_module.stack_mem, cur_module.stack_size, cur_module.priority)); + R_ABORT_UNLESS(os::CreateThread(g_module_threads + i, cur_module.main, nullptr, cur_module.stack_mem, cur_module.stack_size, cur_module.priority)); } /* Start thread for each module. */ for (u32 i = 0; i < static_cast(ModuleId_Count); i++) { - R_ABORT_UNLESS(g_module_threads[i].Start()); + os::StartThread(g_module_threads + i); } } void WaitAllModules() { /* Wait on thread for each module. */ for (u32 i = 0; i < static_cast(ModuleId_Count); i++) { - g_module_threads[i].Join(); + os::WaitThread(g_module_threads + i); } } diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.hpp b/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.hpp index c81f6ad00..09e06742b 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.hpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.hpp @@ -19,6 +19,6 @@ namespace ams::mitm::bpc { - DEFINE_MITM_MODULE_CLASS(0x8000, 32); + DEFINE_MITM_MODULE_CLASS(0x8000, 4); } diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp index 20ee2f2c7..8c13c3f70 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp @@ -30,8 +30,8 @@ namespace ams::mitm::fs { constexpr const char AtmosphereHblWebContentDir[] = "/atmosphere/hbl_html/"; constexpr const char ProgramWebContentDir[] = "/manual_html/"; - os::Mutex g_data_storage_lock; - os::Mutex g_storage_cache_lock; + os::Mutex g_data_storage_lock(false); + os::Mutex g_storage_cache_lock(false); std::unordered_map> g_storage_cache; std::shared_ptr GetStorageCacheEntry(ncm::ProgramId program_id) { diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp index 311a3d4e2..5b16af1d3 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp @@ -21,7 +21,7 @@ namespace ams::mitm::fs { namespace { - os::Mutex g_boot0_access_mutex; + os::Mutex g_boot0_access_mutex(false); u8 g_boot0_bct_buffer[Boot0Storage::BctEndOffset]; } diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp index 8594045e9..ef4c874b2 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp @@ -22,10 +22,11 @@ namespace ams::mitm::fs { namespace { - os::Mutex g_mq_lock; + os::Mutex g_mq_lock(false); bool g_started_req_thread; - os::MessageQueue g_req_mq(1); - os::MessageQueue g_ack_mq(1); + uintptr_t g_mq_storage[2]; + os::MessageQueue g_req_mq(g_mq_storage + 0, 1); + os::MessageQueue g_ack_mq(g_mq_storage + 1, 1); void RomfsInitializerThreadFunction(void *arg) { while (true) { @@ -38,14 +39,16 @@ namespace ams::mitm::fs { } constexpr size_t RomfsInitializerThreadStackSize = 0x8000; - constexpr int RomfsInitializerThreadPriority = 44; - os::StaticThread g_romfs_initializer_thread(&RomfsInitializerThreadFunction, nullptr, RomfsInitializerThreadPriority); + constexpr int RomfsInitializerThreadPriority = 16; + os::ThreadType g_romfs_initializer_thread; + alignas(os::ThreadStackAlignment) u8 g_romfs_initializer_thread_stack[RomfsInitializerThreadStackSize]; void RequestInitializeStorage(uintptr_t storage_uptr) { std::scoped_lock lk(g_mq_lock); - if (!g_started_req_thread) { - R_ABORT_UNLESS(g_romfs_initializer_thread.Start()); + if (AMS_UNLIKELY(!g_started_req_thread)) { + R_ABORT_UNLESS(os::CreateThread(std::addressof(g_romfs_initializer_thread), RomfsInitializerThreadFunction, nullptr, g_romfs_initializer_thread_stack, sizeof(g_romfs_initializer_thread_stack), RomfsInitializerThreadPriority)); + os::StartThread(std::addressof(g_romfs_initializer_thread)); g_started_req_thread = true; } @@ -59,7 +62,7 @@ namespace ams::mitm::fs { using namespace ams::fs; - LayeredRomfsStorage::LayeredRomfsStorage(std::unique_ptr s_r, std::unique_ptr f_r, ncm::ProgramId pr_id) : storage_romfs(std::move(s_r)), file_romfs(std::move(f_r)), initialize_event(false, false), program_id(std::move(pr_id)), is_initialized(false), started_initialize(false) { + LayeredRomfsStorage::LayeredRomfsStorage(std::unique_ptr s_r, std::unique_ptr f_r, ncm::ProgramId pr_id) : storage_romfs(std::move(s_r)), file_romfs(std::move(f_r)), initialize_event(os::EventClearMode_ManualClear), program_id(std::move(pr_id)), is_initialized(false), started_initialize(false) { /* ... */ } diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_module.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_module.cpp index 9602ae3e5..3d27f0d84 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_module.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_module.cpp @@ -39,7 +39,7 @@ namespace ams::mitm::fs { constexpr size_t ThreadStackSize = mitm::ModuleTraits::StackSize; alignas(os::MemoryPageSize) u8 g_extra_thread_stacks[NumExtraThreads][ThreadStackSize]; - os::Thread g_extra_threads[NumExtraThreads]; + os::ThreadType g_extra_threads[NumExtraThreads]; void LoopServerThread(void *arg) { /* Loop forever, servicing our services. */ @@ -49,16 +49,16 @@ namespace ams::mitm::fs { void ProcessForServerOnAllThreads() { /* Initialize threads. */ if constexpr (NumExtraThreads > 0) { - const s32 priority = os::GetCurrentThreadPriority(); + const s32 priority = os::GetThreadCurrentPriority(os::GetCurrentThread()); for (size_t i = 0; i < NumExtraThreads; i++) { - R_ABORT_UNLESS(g_extra_threads[i].Initialize(LoopServerThread, nullptr, g_extra_thread_stacks[i], ThreadStackSize, priority)); + R_ABORT_UNLESS(os::CreateThread(g_extra_threads + i, LoopServerThread, nullptr, g_extra_thread_stacks[i], ThreadStackSize, priority)); } } /* Start extra threads. */ if constexpr (NumExtraThreads > 0) { for (size_t i = 0; i < NumExtraThreads; i++) { - R_ABORT_UNLESS(g_extra_threads[i].Start()); + os::StartThread(g_extra_threads + i); } } @@ -68,7 +68,7 @@ namespace ams::mitm::fs { /* Wait for extra threads to finish. */ if constexpr (NumExtraThreads > 0) { for (size_t i = 0; i < NumExtraThreads; i++) { - R_ABORT_UNLESS(g_extra_threads[i].Join()); + os::WaitThread(g_extra_threads + i); } } } diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_module.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_module.hpp index 79e40242c..fe1e03688 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_module.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_module.hpp @@ -19,6 +19,6 @@ namespace ams::mitm::fs { - DEFINE_MITM_MODULE_CLASS(0x8000, 43); + DEFINE_MITM_MODULE_CLASS(0x8000, 15); } diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp index 5d46b0f11..cebd406bd 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp @@ -257,7 +257,7 @@ namespace ams::mitm::fs { } } - os::Mutex g_fs_romfs_path_lock; + os::Mutex g_fs_romfs_path_lock(false); char g_fs_romfs_path_buffer[fs::EntryNameLengthMax + 1]; NOINLINE void OpenFileSystemRomfsDirectory(FsDir *out, ncm::ProgramId program_id, BuildDirectoryContext *parent, fs::OpenDirectoryMode mode, FsFileSystem *fs) { diff --git a/stratosphere/ams_mitm/source/hid_mitm/hidmitm_module.hpp b/stratosphere/ams_mitm/source/hid_mitm/hidmitm_module.hpp index f48ce9c35..b507755fe 100644 --- a/stratosphere/ams_mitm/source/hid_mitm/hidmitm_module.hpp +++ b/stratosphere/ams_mitm/source/hid_mitm/hidmitm_module.hpp @@ -19,6 +19,6 @@ namespace ams::mitm::hid { - DEFINE_MITM_MODULE_CLASS(0x8000, 47); + DEFINE_MITM_MODULE_CLASS(0x8000, 19); } diff --git a/stratosphere/ams_mitm/source/ns_mitm/nsmitm_module.hpp b/stratosphere/ams_mitm/source/ns_mitm/nsmitm_module.hpp index 04db6df79..095bdd905 100644 --- a/stratosphere/ams_mitm/source/ns_mitm/nsmitm_module.hpp +++ b/stratosphere/ams_mitm/source/ns_mitm/nsmitm_module.hpp @@ -19,6 +19,6 @@ namespace ams::mitm::ns { - DEFINE_MITM_MODULE_CLASS(0x4000, 48); + DEFINE_MITM_MODULE_CLASS(0x4000, 20); } diff --git a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp index 778f3748d..7c5f3f1d4 100644 --- a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp @@ -25,7 +25,7 @@ namespace ams::mitm::settings { GetRegionCode = 4, }; private: - os::Mutex lock; + os::Mutex lock{false}; cfg::OverrideLocale locale; bool got_locale; public: diff --git a/stratosphere/ams_mitm/source/set_mitm/setmitm_module.hpp b/stratosphere/ams_mitm/source/set_mitm/setmitm_module.hpp index 4cf8c2eff..c76f37f23 100644 --- a/stratosphere/ams_mitm/source/set_mitm/setmitm_module.hpp +++ b/stratosphere/ams_mitm/source/set_mitm/setmitm_module.hpp @@ -19,6 +19,6 @@ namespace ams::mitm::settings { - DEFINE_MITM_MODULE_CLASS(0x8000, 43); + DEFINE_MITM_MODULE_CLASS(0x8000, 20); } diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp index dc2cf1b35..b10131814 100644 --- a/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp @@ -22,7 +22,7 @@ namespace ams::mitm::settings { namespace { - os::Mutex g_firmware_version_lock; + os::Mutex g_firmware_version_lock(false); bool g_cached_firmware_version; settings::FirmwareVersion g_firmware_version; settings::FirmwareVersion g_ams_firmware_version; @@ -30,7 +30,7 @@ namespace ams::mitm::settings { void CacheFirmwareVersion() { std::scoped_lock lk(g_firmware_version_lock); - if (g_cached_firmware_version) { + if (AMS_LIKELY(g_cached_firmware_version)) { return; } diff --git a/stratosphere/boot/source/boot_display.cpp b/stratosphere/boot/source/boot_display.cpp index aa5828e8f..1ade06ae7 100644 --- a/stratosphere/boot/source/boot_display.cpp +++ b/stratosphere/boot/source/boot_display.cpp @@ -56,6 +56,10 @@ namespace ams::boot { constexpr size_t ApbMiscSize = os::MemoryPageSize; constexpr size_t MipiCalSize = os::MemoryPageSize; + constexpr s32 DsiWaitForCommandMilliSecondsMax = 250; + constexpr s32 DsiWaitForCommandCompletionMilliSeconds = 5; + constexpr s32 DsiWaitForHostControlMilliSecondsMax = 150; + /* Types. */ /* Globals. */ @@ -151,10 +155,10 @@ namespace ams::boot { } void WaitDsiTrigger() { - os::TimeoutHelper timeout_helper(250'000'000ul); + os::Tick timeout = os::GetSystemTick() + os::ConvertToTick(TimeSpan::FromMilliSeconds(DsiWaitForCommandMilliSecondsMax)); while (true) { - if (timeout_helper.TimedOut()) { + if (os::GetSystemTick() >= timeout) { break; } if (reg::Read(g_dsi_regs + sizeof(u32) * DSI_TRIGGER) == 0) { @@ -162,14 +166,14 @@ namespace ams::boot { } } - svcSleepThread(5'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(DsiWaitForCommandCompletionMilliSeconds)); } void WaitDsiHostControl() { - os::TimeoutHelper timeout_helper(150'000'000ul); + os::Tick timeout = os::GetSystemTick() + os::ConvertToTick(TimeSpan::FromMilliSeconds(DsiWaitForHostControlMilliSecondsMax)); while (true) { - if (timeout_helper.TimedOut()) { + if (os::GetSystemTick() >= timeout) { break; } if ((reg::Read(g_dsi_regs + sizeof(u32) * DSI_HOST_CONTROL) & DSI_HOST_CONTROL_IMM_BTA) == 0) { diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp index 78bd68e31..f1bcf3dd7 100644 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp @@ -52,7 +52,7 @@ namespace ams::i2c::driver::impl { } /* Close interrupt event. */ - this->interrupt_event.Finalize(); + os::FinalizeInterruptEvent(std::addressof(this->interrupt_event)); /* Close PCV. */ pcv::Finalize(); @@ -152,10 +152,10 @@ namespace ams::i2c::driver::impl { break; } - this->interrupt_event.Reset(); - if (!this->interrupt_event.TimedWait(InterruptTimeout)) { + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), InterruptTimeout)) { this->HandleTransactionResult(i2c::ResultBusBusy()); - this->interrupt_event.Reset(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); return i2c::ResultTimedOut(); } @@ -175,10 +175,10 @@ namespace ams::i2c::driver::impl { break; } - this->interrupt_event.Reset(); - if (!this->interrupt_event.TimedWait(InterruptTimeout)) { + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), InterruptTimeout)) { this->HandleTransactionResult(i2c::ResultBusBusy()); - this->interrupt_event.Reset(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); return i2c::ResultTimedOut(); } } @@ -200,11 +200,11 @@ namespace ams::i2c::driver::impl { /* Receive bytes. */ while (remaining > 0) { - this->interrupt_event.Reset(); - if (!this->interrupt_event.TimedWait(InterruptTimeout)) { + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), InterruptTimeout)) { this->HandleTransactionResult(i2c::ResultBusBusy()); this->ClearInterruptMask(); - this->interrupt_event.Reset(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); return i2c::ResultTimedOut(); } @@ -241,7 +241,8 @@ namespace ams::i2c::driver::impl { }; const auto index = ConvertToIndex(bus); AMS_ABORT_UNLESS(index < util::size(s_interrupts)); - R_ABORT_UNLESS(this->interrupt_event.Initialize(s_interrupts[index], false)); + os::InitializeInterruptEvent(std::addressof(this->interrupt_event), s_interrupts[index], os::EventClearMode_ManualClear); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); } void BusAccessor::SetClock(SpeedMode speed_mode) { @@ -423,7 +424,7 @@ namespace ams::i2c::driver::impl { this->HandleTransactionResult(transaction_result); this->ClearInterruptMask(); - this->interrupt_event.Reset(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); return transaction_result; } diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.hpp index eb2320abc..8fa682627 100644 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.hpp +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.hpp @@ -25,9 +25,9 @@ namespace ams::i2c::driver::impl { Send = 0, Receive = 1, }; - static constexpr u64 InterruptTimeout = 100'000'000ul; + static constexpr TimeSpan InterruptTimeout = TimeSpan::FromMilliSeconds(100); private: - os::InterruptEvent interrupt_event; + os::InterruptEventType interrupt_event; os::Mutex open_mutex; os::Mutex register_mutex; Registers *i2c_registers = nullptr; @@ -38,7 +38,7 @@ namespace ams::i2c::driver::impl { PcvModule pcv_module = PcvModule_I2C1; bool suspended = false; public: - BusAccessor() { /* ... */ } + BusAccessor() : open_mutex(false), register_mutex(false) { /* ... */ } private: inline void ClearInterruptMask() const { reg::Write(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0); diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.hpp index 6863fcb42..27465a2fd 100644 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.hpp +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.hpp @@ -34,10 +34,18 @@ namespace ams::i2c::driver::impl { bool power_bus_suspended = false; Session sessions[MaxDriverSessions]; BusAccessor bus_accessors[ConvertToIndex(Bus::Count)]; - os::Mutex transaction_mutexes[ConvertToIndex(Bus::Count)]; + TYPED_STORAGE(os::Mutex) transaction_mutexes[ConvertToIndex(Bus::Count)]; public: - ResourceManager() { - /* ... */ + ResourceManager() : initialize_mutex(false), session_open_mutex(false) { + for (size_t i = 0; i < util::size(this->transaction_mutexes); i++) { + new (GetPointer(this->transaction_mutexes[i])) os::Mutex(false); + } + } + + ~ResourceManager() { + for (size_t i = 0; i < util::size(this->transaction_mutexes); i++) { + GetReference(this->transaction_mutexes[i]).~Mutex(); + } } private: size_t GetFreeSessionId() const; @@ -57,7 +65,7 @@ namespace ams::i2c::driver::impl { } os::Mutex& GetTransactionMutex(Bus bus) { - return this->transaction_mutexes[ConvertToIndex(bus)]; + return GetReference(this->transaction_mutexes[ConvertToIndex(bus)]); } void Initialize(); diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_session.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_session.hpp index d90f7cde3..010bd41c6 100644 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_session.hpp +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_session.hpp @@ -30,7 +30,7 @@ namespace ams::i2c::driver::impl { u64 retry_wait_time = 0; bool open = false; public: - Session() { /* ... */ } + Session() : bus_accessor_mutex(false) { /* ... */ } public: void Open(Bus bus, u32 slave_address, AddressingMode addr_mode, SpeedMode speed_mode, BusAccessor *bus_accessor, u32 max_retries, u64 retry_wait_time); void Start(); diff --git a/stratosphere/creport/source/creport_scoped_file.cpp b/stratosphere/creport/source/creport_scoped_file.cpp index 0ee09604f..d2c067801 100644 --- a/stratosphere/creport/source/creport_scoped_file.cpp +++ b/stratosphere/creport/source/creport_scoped_file.cpp @@ -22,7 +22,7 @@ namespace ams::creport { /* Convenience definitions. */ constexpr size_t MaximumLineLength = 0x20; - os::Mutex g_format_lock; + os::Mutex g_format_lock(false); char g_format_buffer[2 * os::MemoryPageSize]; } diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp index 43bd4cd8f..c6ca489de 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp @@ -29,18 +29,18 @@ namespace ams::dmnt::cheat::impl { class CheatProcessManager { private: static constexpr size_t ThreadStackSize = 0x4000; - static constexpr int DetectThreadPriority = 39; - static constexpr int VirtualMachineThreadPriority = 48; - static constexpr int DebugEventsThreadPriority = 24; + static constexpr s32 DetectThreadPriority = 11; + static constexpr s32 VirtualMachineThreadPriority = 20; + static constexpr s32 DebugEventsThreadPriority = -1; private: os::Mutex cheat_lock; os::Event debug_events_event; /* Autoclear. */ - os::Thread detect_thread, debug_events_thread; + os::ThreadType detect_thread, debug_events_thread; os::SystemEvent cheat_process_event; - Handle cheat_process_debug_handle = INVALID_HANDLE; + Handle cheat_process_debug_handle = svc::InvalidHandle; CheatProcessMetadata cheat_process_metadata = {}; - os::Thread vm_thread; + os::ThreadType vm_thread; bool needs_reload_vm = false; CheatVirtualMachine cheat_vm; @@ -121,13 +121,13 @@ namespace ams::dmnt::cheat::impl { } void CloseActiveCheatProcess() { - if (this->cheat_process_debug_handle != INVALID_HANDLE) { + if (this->cheat_process_debug_handle != svc::InvalidHandle) { /* Knock out the debug events thread. */ - R_ABORT_UNLESS(this->debug_events_thread.CancelSynchronization()); + os::CancelThreadSynchronization(std::addressof(this->debug_events_thread)); /* Close resources. */ - R_ABORT_UNLESS(svcCloseHandle(this->cheat_process_debug_handle)); - this->cheat_process_debug_handle = INVALID_HANDLE; + R_ABORT_UNLESS(svc::CloseHandle(this->cheat_process_debug_handle)); + this->cheat_process_debug_handle = svc::InvalidHandle; /* Save cheat toggles. */ if (this->always_save_cheat_toggles || this->should_save_cheat_toggles) { @@ -153,7 +153,7 @@ namespace ams::dmnt::cheat::impl { bool HasActiveCheatProcess() { /* Note: This function *MUST* be called only with the cheat lock held. */ os::ProcessId pid; - bool has_cheat_process = this->cheat_process_debug_handle != INVALID_HANDLE; + bool has_cheat_process = this->cheat_process_debug_handle != svc::InvalidHandle; has_cheat_process &= R_SUCCEEDED(os::TryGetProcessId(&pid, this->cheat_process_debug_handle)); has_cheat_process &= R_SUCCEEDED(pm::dmnt::GetApplicationProcessId(&pid)); has_cheat_process &= (pid == this->cheat_process_metadata.process_id); @@ -175,7 +175,7 @@ namespace ams::dmnt::cheat::impl { } Handle HookToCreateApplicationProcess() const { - Handle h = INVALID_HANDLE; + Handle h = svc::InvalidHandle; R_ABORT_UNLESS(pm::dmnt::HookToCreateApplicationProcess(&h)); return h; } @@ -185,10 +185,7 @@ namespace ams::dmnt::cheat::impl { } public: - CheatProcessManager() { - /* Create cheat process detection event. */ - R_ABORT_UNLESS(this->cheat_process_event.InitializeAsInterProcessEvent()); - + CheatProcessManager() : cheat_lock(false), debug_events_event(os::EventClearMode_AutoClear), cheat_process_event(os::EventClearMode_AutoClear, true) { /* Learn whether we should enable cheats by default. */ { u8 en = 0; @@ -203,14 +200,14 @@ namespace ams::dmnt::cheat::impl { } /* Spawn application detection thread, spawn cheat vm thread. */ - R_ABORT_UNLESS(this->detect_thread.Initialize(&CheatProcessManager::DetectLaunchThread, this, this->detect_thread_stack, ThreadStackSize, DetectThreadPriority)); - R_ABORT_UNLESS(this->vm_thread.Initialize(&CheatProcessManager::VirtualMachineThread, this, this->vm_thread_stack, ThreadStackSize, VirtualMachineThreadPriority)); - R_ABORT_UNLESS(this->debug_events_thread.Initialize(&CheatProcessManager::DebugEventsThread, this, this->debug_events_thread_stack, ThreadStackSize, DebugEventsThreadPriority)); + R_ABORT_UNLESS(os::CreateThread(std::addressof(this->detect_thread), DetectLaunchThread, this, this->detect_thread_stack, ThreadStackSize, DetectThreadPriority)); + R_ABORT_UNLESS(os::CreateThread(std::addressof(this->vm_thread), VirtualMachineThread, this, this->vm_thread_stack, ThreadStackSize, VirtualMachineThreadPriority)); + R_ABORT_UNLESS(os::CreateThread(std::addressof(this->debug_events_thread), DebugEventsThread, this, this->debug_events_thread_stack, ThreadStackSize, DebugEventsThreadPriority)); /* Start threads. */ - R_ABORT_UNLESS(this->detect_thread.Start()); - R_ABORT_UNLESS(this->vm_thread.Start()); - R_ABORT_UNLESS(this->debug_events_thread.Start()); + os::StartThread(std::addressof(this->detect_thread)); + os::StartThread(std::addressof(this->vm_thread)); + os::StartThread(std::addressof(this->debug_events_thread)); } bool GetHasActiveCheatProcess() { @@ -620,10 +617,10 @@ namespace ams::dmnt::cheat::impl { /* Get process handle, use it to learn memory extents. */ { - Handle proc_h = INVALID_HANDLE; + Handle proc_h = svc::InvalidHandle; ncm::ProgramLocation loc = {}; cfg::OverrideStatus status = {}; - ON_SCOPE_EXIT { if (proc_h != INVALID_HANDLE) { R_ABORT_UNLESS(svcCloseHandle(proc_h)); } }; + ON_SCOPE_EXIT { if (proc_h != svc::InvalidHandle) { R_ABORT_UNLESS(svcCloseHandle(proc_h)); } }; R_ABORT_UNLESS_IF_NEW_PROCESS(pm::dmnt::AtmosphereGetProcessInfo(&proc_h, &loc, &status, this->cheat_process_metadata.process_id)); this->cheat_process_metadata.program_id = loc.program_id; @@ -988,96 +985,104 @@ namespace ams::dmnt::cheat::impl { /* Manager global. */ - CheatProcessManager g_cheat_process_manager; + TYPED_STORAGE(CheatProcessManager) g_cheat_process_manager; } + void InitializeCheatManager() { + /* Initialize the debug events manager (spawning its threads). */ + InitializeDebugEventsManager(); + + /* Create the cheat process manager (spawning its threads). */ + new (GetPointer(g_cheat_process_manager)) CheatProcessManager; + } + bool GetHasActiveCheatProcess() { - return g_cheat_process_manager.GetHasActiveCheatProcess(); + return GetReference(g_cheat_process_manager).GetHasActiveCheatProcess(); } Handle GetCheatProcessEventHandle() { - return g_cheat_process_manager.GetCheatProcessEventHandle(); + return GetReference(g_cheat_process_manager).GetCheatProcessEventHandle(); } Result GetCheatProcessMetadata(CheatProcessMetadata *out) { - return g_cheat_process_manager.GetCheatProcessMetadata(out); + return GetReference(g_cheat_process_manager).GetCheatProcessMetadata(out); } Result ForceOpenCheatProcess() { - return g_cheat_process_manager.ForceOpenCheatProcess(); + return GetReference(g_cheat_process_manager).ForceOpenCheatProcess(); } Result ReadCheatProcessMemoryUnsafe(u64 process_addr, void *out_data, size_t size) { - return g_cheat_process_manager.ReadCheatProcessMemoryUnsafe(process_addr, out_data, size); + return GetReference(g_cheat_process_manager).ReadCheatProcessMemoryUnsafe(process_addr, out_data, size); } Result WriteCheatProcessMemoryUnsafe(u64 process_addr, void *data, size_t size) { - return g_cheat_process_manager.WriteCheatProcessMemoryUnsafe(process_addr, data, size); + return GetReference(g_cheat_process_manager).WriteCheatProcessMemoryUnsafe(process_addr, data, size); } Result GetCheatProcessMappingCount(u64 *out_count) { - return g_cheat_process_manager.GetCheatProcessMappingCount(out_count); + return GetReference(g_cheat_process_manager).GetCheatProcessMappingCount(out_count); } Result GetCheatProcessMappings(MemoryInfo *mappings, size_t max_count, u64 *out_count, u64 offset) { - return g_cheat_process_manager.GetCheatProcessMappings(mappings, max_count, out_count, offset); + return GetReference(g_cheat_process_manager).GetCheatProcessMappings(mappings, max_count, out_count, offset); } Result ReadCheatProcessMemory(u64 proc_addr, void *out_data, size_t size) { - return g_cheat_process_manager.ReadCheatProcessMemory(proc_addr, out_data, size); + return GetReference(g_cheat_process_manager).ReadCheatProcessMemory(proc_addr, out_data, size); } Result WriteCheatProcessMemory(u64 proc_addr, const void *data, size_t size) { - return g_cheat_process_manager.WriteCheatProcessMemory(proc_addr, data, size); + return GetReference(g_cheat_process_manager).WriteCheatProcessMemory(proc_addr, data, size); } Result QueryCheatProcessMemory(MemoryInfo *mapping, u64 address) { - return g_cheat_process_manager.QueryCheatProcessMemory(mapping, address); + return GetReference(g_cheat_process_manager).QueryCheatProcessMemory(mapping, address); } Result GetCheatCount(u64 *out_count) { - return g_cheat_process_manager.GetCheatCount(out_count); + return GetReference(g_cheat_process_manager).GetCheatCount(out_count); } Result GetCheats(CheatEntry *cheats, size_t max_count, u64 *out_count, u64 offset) { - return g_cheat_process_manager.GetCheats(cheats, max_count, out_count, offset); + return GetReference(g_cheat_process_manager).GetCheats(cheats, max_count, out_count, offset); } Result GetCheatById(CheatEntry *out_cheat, u32 cheat_id) { - return g_cheat_process_manager.GetCheatById(out_cheat, cheat_id); + return GetReference(g_cheat_process_manager).GetCheatById(out_cheat, cheat_id); } Result ToggleCheat(u32 cheat_id) { - return g_cheat_process_manager.ToggleCheat(cheat_id); + return GetReference(g_cheat_process_manager).ToggleCheat(cheat_id); } Result AddCheat(u32 *out_id, const CheatDefinition &def, bool enabled) { - return g_cheat_process_manager.AddCheat(out_id, def, enabled); + return GetReference(g_cheat_process_manager).AddCheat(out_id, def, enabled); } Result RemoveCheat(u32 cheat_id) { - return g_cheat_process_manager.RemoveCheat(cheat_id); + return GetReference(g_cheat_process_manager).RemoveCheat(cheat_id); } Result GetFrozenAddressCount(u64 *out_count) { - return g_cheat_process_manager.GetFrozenAddressCount(out_count); + return GetReference(g_cheat_process_manager).GetFrozenAddressCount(out_count); } Result GetFrozenAddresses(FrozenAddressEntry *frz_addrs, size_t max_count, u64 *out_count, u64 offset) { - return g_cheat_process_manager.GetFrozenAddresses(frz_addrs, max_count, out_count, offset); + return GetReference(g_cheat_process_manager).GetFrozenAddresses(frz_addrs, max_count, out_count, offset); } Result GetFrozenAddress(FrozenAddressEntry *frz_addr, u64 address) { - return g_cheat_process_manager.GetFrozenAddress(frz_addr, address); + return GetReference(g_cheat_process_manager).GetFrozenAddress(frz_addr, address); } Result EnableFrozenAddress(u64 *out_value, u64 address, u64 width) { - return g_cheat_process_manager.EnableFrozenAddress(out_value, address, width); + return GetReference(g_cheat_process_manager).EnableFrozenAddress(out_value, address, width); } Result DisableFrozenAddress(u64 address) { - return g_cheat_process_manager.DisableFrozenAddress(address); + return GetReference(g_cheat_process_manager).DisableFrozenAddress(address); } } diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.hpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.hpp index 8f7b18d5f..6580790de 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.hpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.hpp @@ -18,6 +18,8 @@ namespace ams::dmnt::cheat::impl { + void InitializeCheatManager(); + bool GetHasActiveCheatProcess(); Handle GetCheatProcessEventHandle(); Result GetCheatProcessMetadata(CheatProcessMetadata *out); diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp index 6abb50762..f97ba23e6 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp @@ -25,10 +25,11 @@ namespace ams::dmnt::cheat::impl { public: static constexpr size_t NumCores = 4; static constexpr size_t ThreadStackSize = os::MemoryPageSize; - static constexpr size_t ThreadPriority = 24; + static constexpr s32 ThreadPriority = -3; private: + std::array message_queue_buffers; std::array message_queues; - std::array threads; + std::array threads; os::Event continued_event; alignas(os::MemoryPageSize) u8 thread_stacks[NumCores][ThreadStackSize]; @@ -91,16 +92,24 @@ namespace ams::dmnt::cheat::impl { } public: - DebugEventsManager() : message_queues{os::MessageQueue(1), os::MessageQueue(1), os::MessageQueue(1), os::MessageQueue(1)}, thread_stacks{} { + DebugEventsManager() + : message_queues{ + os::MessageQueue(std::addressof(message_queue_buffers[0]), 1), + os::MessageQueue(std::addressof(message_queue_buffers[1]), 1), + os::MessageQueue(std::addressof(message_queue_buffers[2]), 1), + os::MessageQueue(std::addressof(message_queue_buffers[3]), 1)}, + continued_event(os::EventClearMode_AutoClear), + thread_stacks{} + { for (size_t i = 0; i < NumCores; i++) { /* Create thread. */ - R_ABORT_UNLESS(this->threads[i].Initialize(&DebugEventsManager::PerCoreThreadFunction, reinterpret_cast(this), this->thread_stacks[i], ThreadStackSize, ThreadPriority, i)); + R_ABORT_UNLESS(os::CreateThread(std::addressof(this->threads[i]), PerCoreThreadFunction, this, this->thread_stacks[i], ThreadStackSize, ThreadPriority, i)); /* Set core mask. */ - R_ABORT_UNLESS(svcSetThreadCoreMask(this->threads[i].GetHandle(), i, (1u << i))); + os::SetThreadCoreMask(std::addressof(this->threads[i]), i, (1u << i)); /* Start thread. */ - R_ABORT_UNLESS(this->threads[i].Start()); + os::StartThread(std::addressof(this->threads[i])); } } @@ -108,7 +117,7 @@ namespace ams::dmnt::cheat::impl { /* Loop getting all debug events. */ svc::DebugEventInfo d; size_t target_core = NumCores - 1; - while (R_SUCCEEDED(svcGetDebugEvent(reinterpret_cast(&d), cheat_dbg_hnd))) { + while (R_SUCCEEDED(svc::GetDebugEvent(std::addressof(d), cheat_dbg_hnd))) { if (d.type == svc::DebugEvent_AttachThread) { target_core = GetTargetCore(d, cheat_dbg_hnd); } @@ -121,12 +130,16 @@ namespace ams::dmnt::cheat::impl { }; /* Manager global. */ - DebugEventsManager g_events_manager; + TYPED_STORAGE(DebugEventsManager) g_events_manager; } + void InitializeDebugEventsManager() { + new (GetPointer(g_events_manager)) DebugEventsManager; + } + void ContinueCheatProcess(Handle cheat_dbg_hnd) { - g_events_manager.ContinueCheatProcess(cheat_dbg_hnd); + GetReference(g_events_manager).ContinueCheatProcess(cheat_dbg_hnd); } } diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.hpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.hpp index 12ae86249..604183c60 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.hpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.hpp @@ -18,6 +18,8 @@ namespace ams::dmnt::cheat::impl { + void InitializeDebugEventsManager(); + void ContinueCheatProcess(Handle cheat_dbg_hnd); } diff --git a/stratosphere/dmnt/source/dmnt_main.cpp b/stratosphere/dmnt/source/dmnt_main.cpp index 47f6b4a92..b181e306b 100644 --- a/stratosphere/dmnt/source/dmnt_main.cpp +++ b/stratosphere/dmnt/source/dmnt_main.cpp @@ -15,6 +15,7 @@ */ #include "dmnt_service.hpp" #include "cheat/dmnt_cheat_service.hpp" +#include "cheat/impl/dmnt_cheat_api.hpp" extern "C" { extern u32 __start__; @@ -122,12 +123,15 @@ namespace { constexpr size_t ThreadStackSize = 0x4000; alignas(os::MemoryPageSize) u8 g_extra_thread_stacks[NumExtraThreads][ThreadStackSize]; - os::Thread g_extra_threads[NumExtraThreads]; + os::ThreadType g_extra_threads[NumExtraThreads]; } int main(int argc, char **argv) { + /* Initialize the cheat manager. */ + ams::dmnt::cheat::impl::InitializeCheatManager(); + /* Create services. */ /* TODO: Implement rest of dmnt:- in ams.tma development branch. */ /* R_ABORT_UNLESS((g_server_manager.RegisterServer(DebugMonitorServiceName, DebugMonitorMaxSessions))); */ @@ -139,16 +143,16 @@ int main(int argc, char **argv) /* Initialize threads. */ if constexpr (NumExtraThreads > 0) { - const s32 priority = os::GetCurrentThreadPriority(); + const s32 priority = os::GetThreadCurrentPriority(os::GetCurrentThread()); for (size_t i = 0; i < NumExtraThreads; i++) { - R_ABORT_UNLESS(g_extra_threads[i].Initialize(LoopServerThread, nullptr, g_extra_thread_stacks[i], ThreadStackSize, priority)); + R_ABORT_UNLESS(os::CreateThread(std::addressof(g_extra_threads[i]), LoopServerThread, nullptr, g_extra_thread_stacks[i], ThreadStackSize, priority)); } } /* Start extra threads. */ if constexpr (NumExtraThreads > 0) { for (size_t i = 0; i < NumExtraThreads; i++) { - R_ABORT_UNLESS(g_extra_threads[i].Start()); + os::StartThread(std::addressof(g_extra_threads[i])); } } @@ -158,7 +162,7 @@ int main(int argc, char **argv) /* Wait for extra threads to finish. */ if constexpr (NumExtraThreads > 0) { for (size_t i = 0; i < NumExtraThreads; i++) { - R_ABORT_UNLESS(g_extra_threads[i].Join()); + os::WaitThread(std::addressof(g_extra_threads[i])); } } } diff --git a/stratosphere/dmnt/source/dmnt_service_target_io.cpp b/stratosphere/dmnt/source/dmnt_service_target_io.cpp index 41ac24abf..8274596dc 100644 --- a/stratosphere/dmnt/source/dmnt_service_target_io.cpp +++ b/stratosphere/dmnt/source/dmnt_service_target_io.cpp @@ -40,10 +40,10 @@ namespace ams::dmnt { /* Nintendo uses actual pointers as file handles. We'll add a layer of indirection... */ bool g_sd_initialized = false; - os::Mutex g_sd_lock; + os::Mutex g_sd_lock(false); FsFileSystem g_sd_fs; - os::Mutex g_file_handle_lock; + os::Mutex g_file_handle_lock(false); u64 g_cur_fd; std::unordered_map g_file_handles; diff --git a/stratosphere/fatal/source/fatal_config.cpp b/stratosphere/fatal/source/fatal_config.cpp index 0ee0edef9..47f936f94 100644 --- a/stratosphere/fatal/source/fatal_config.cpp +++ b/stratosphere/fatal/source/fatal_config.cpp @@ -30,17 +30,24 @@ namespace ams::fatal::srv { } /* Global event. */ - os::SystemEvent g_fatal_dirty_event(GetFatalDirtyEventReadableHandle(), true, false); - os::WaitableHolder g_fatal_dirty_waitable_holder(&g_fatal_dirty_event); + os::SystemEventType g_fatal_dirty_event; + os::WaitableHolderType g_fatal_dirty_waitable_holder; + bool g_initialized; } - os::WaitableHolder *GetFatalDirtyWaitableHolder() { - return &g_fatal_dirty_waitable_holder; + os::WaitableHolderType *GetFatalDirtyWaitableHolder() { + if (AMS_UNLIKELY(!g_initialized)) { + os::AttachReadableHandleToSystemEvent(std::addressof(g_fatal_dirty_event), GetFatalDirtyEventReadableHandle(), true, os::EventClearMode_ManualClear); + os::InitializeWaitableHolder(std::addressof(g_fatal_dirty_waitable_holder), std::addressof(g_fatal_dirty_event)); + os::SetWaitableHolderUserData(std::addressof(g_fatal_dirty_waitable_holder), reinterpret_cast(std::addressof(g_fatal_dirty_waitable_holder))); + g_initialized = true; + } + return std::addressof(g_fatal_dirty_waitable_holder); } void OnFatalDirtyEvent() { - g_fatal_dirty_event.Reset(); + os::ClearSystemEvent(std::addressof(g_fatal_dirty_event)); u64 flags_0, flags_1; if (R_SUCCEEDED(setsysGetFatalDirtyFlags(&flags_0, &flags_1)) && (flags_0 & 1)) { diff --git a/stratosphere/fatal/source/fatal_config.hpp b/stratosphere/fatal/source/fatal_config.hpp index 2b217e839..91f190fbb 100644 --- a/stratosphere/fatal/source/fatal_config.hpp +++ b/stratosphere/fatal/source/fatal_config.hpp @@ -88,7 +88,7 @@ namespace ams::fatal::srv { } }; - os::WaitableHolder *GetFatalDirtyWaitableHolder(); + os::WaitableHolderType *GetFatalDirtyWaitableHolder(); void OnFatalDirtyEvent(); const FatalConfig &GetFatalConfig(); diff --git a/stratosphere/fatal/source/fatal_event_manager.cpp b/stratosphere/fatal/source/fatal_event_manager.cpp index eabbe1e69..b5e02602f 100644 --- a/stratosphere/fatal/source/fatal_event_manager.cpp +++ b/stratosphere/fatal/source/fatal_event_manager.cpp @@ -17,26 +17,26 @@ namespace ams::fatal::srv { - FatalEventManager::FatalEventManager() { + FatalEventManager::FatalEventManager() : lock(false) { /* Just create all the events. */ for (size_t i = 0; i < FatalEventManager::NumFatalEvents; i++) { - R_ABORT_UNLESS(eventCreate(&this->events[i], true)); + R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(this->events[i]), os::EventClearMode_AutoClear, true)); } } - Result FatalEventManager::GetEvent(Handle *out) { + Result FatalEventManager::GetEvent(const os::SystemEventType **out) { std::scoped_lock lk{this->lock}; /* Only allow GetEvent to succeed NumFatalEvents times. */ R_UNLESS(this->num_events_gotten < FatalEventManager::NumFatalEvents, ResultTooManyEvents()); - *out = this->events[this->num_events_gotten++].revent; + *out = std::addressof(this->events[this->num_events_gotten++]); return ResultSuccess(); } void FatalEventManager::SignalEvents() { for (size_t i = 0; i < FatalEventManager::NumFatalEvents; i++) { - eventFire(&this->events[i]); + os::SignalSystemEvent(std::addressof(this->events[i])); } } diff --git a/stratosphere/fatal/source/fatal_event_manager.hpp b/stratosphere/fatal/source/fatal_event_manager.hpp index ab7aea155..9225aa599 100644 --- a/stratosphere/fatal/source/fatal_event_manager.hpp +++ b/stratosphere/fatal/source/fatal_event_manager.hpp @@ -19,15 +19,17 @@ namespace ams::fatal::srv { class FatalEventManager { - private: + NON_COPYABLE(FatalEventManager); + NON_MOVEABLE(FatalEventManager); + public: static constexpr size_t NumFatalEvents = 3; - + private: os::Mutex lock; size_t num_events_gotten = 0; - Event events[NumFatalEvents]; + os::SystemEventType events[NumFatalEvents]; public: FatalEventManager(); - Result GetEvent(Handle *out); + Result GetEvent(const os::SystemEventType **out); void SignalEvents(); }; diff --git a/stratosphere/fatal/source/fatal_repair.cpp b/stratosphere/fatal/source/fatal_repair.cpp index 18812b0f0..280b7dbf1 100644 --- a/stratosphere/fatal/source/fatal_repair.cpp +++ b/stratosphere/fatal/source/fatal_repair.cpp @@ -44,16 +44,16 @@ namespace ams::fatal::srv { gpioPadSetDirection(&vol_btn, GpioDirection_Input); /* Ensure that we're holding the volume button for a full second. */ - os::TimeoutHelper timeout_helper(1'000'000'000ul); - while (!timeout_helper.TimedOut()) { + auto start = os::GetSystemTick(); + do { GpioValue val; if (R_FAILED(gpioPadGetValue(&vol_btn, &val)) || val != GpioValue_Low) { return true; } /* Sleep for 100 ms. */ - svcSleepThread(100'000'000ul); - } + os::SleepThread(TimeSpan::FromMilliSeconds(100)); + } while (os::ConvertToTimeSpan(os::GetSystemTick() - start) < TimeSpan::FromSeconds(1)); } return false; diff --git a/stratosphere/fatal/source/fatal_scoped_file.cpp b/stratosphere/fatal/source/fatal_scoped_file.cpp index 27aef822f..4b47a2813 100644 --- a/stratosphere/fatal/source/fatal_scoped_file.cpp +++ b/stratosphere/fatal/source/fatal_scoped_file.cpp @@ -22,7 +22,7 @@ namespace ams::fatal::srv { /* Convenience definitions. */ constexpr size_t MaximumLineLength = 0x20; - os::Mutex g_format_lock; + os::Mutex g_format_lock(false); char g_format_buffer[2 * os::MemoryPageSize]; } diff --git a/stratosphere/fatal/source/fatal_service.cpp b/stratosphere/fatal/source/fatal_service.cpp index f5c5fa198..2ecc8f220 100644 --- a/stratosphere/fatal/source/fatal_service.cpp +++ b/stratosphere/fatal/source/fatal_service.cpp @@ -27,6 +27,8 @@ namespace ams::fatal::srv { /* Service Context. */ class ServiceContext { private: + os::Event erpt_event; + os::Event battery_event; ThrowContext context; FatalEventManager event_manager; bool has_thrown; @@ -37,14 +39,14 @@ namespace ams::fatal::srv { return ResultSuccess(); } public: - ServiceContext() { - this->context.ClearState(); - R_ABORT_UNLESS(eventCreate(&this->context.erpt_event, false)); - R_ABORT_UNLESS(eventCreate(&this->context.battery_event, false)); - this->has_thrown = false; + ServiceContext() + : erpt_event(os::EventClearMode_ManualClear), battery_event(os::EventClearMode_ManualClear), + context(std::addressof(erpt_event), std::addressof(battery_event)), has_thrown(false) + { + /* ... */ } - Result GetEvent(Handle *out) { + Result GetEvent(const os::SystemEventType **out) { return this->event_manager.GetEvent(out); } @@ -143,7 +145,10 @@ namespace ams::fatal::srv { } Result PrivateService::GetFatalEvent(sf::OutCopyHandle out_h) { - return g_context.GetEvent(out_h.GetHandlePointer()); + const os::SystemEventType *event; + R_TRY(g_context.GetEvent(std::addressof(event))); + out_h.SetValue(os::GetReadableHandleOfSystemEvent(event)); + return ResultSuccess(); } } \ No newline at end of file diff --git a/stratosphere/fatal/source/fatal_task.cpp b/stratosphere/fatal/source/fatal_task.cpp index 725d14b36..a98d87118 100644 --- a/stratosphere/fatal/source/fatal_task.cpp +++ b/stratosphere/fatal/source/fatal_task.cpp @@ -28,9 +28,9 @@ namespace ams::fatal::srv { class TaskThread { NON_COPYABLE(TaskThread); private: - static constexpr int TaskThreadPriority = 15; + static constexpr s32 TaskThreadPriority = -13; private: - os::Thread thread; + os::ThreadType thread; private: static void RunTaskImpl(void *arg) { ITask *task = reinterpret_cast(arg); @@ -42,8 +42,8 @@ namespace ams::fatal::srv { public: TaskThread() { /* ... */ } void StartTask(ITask *task) { - R_ABORT_UNLESS(this->thread.Initialize(&RunTaskImpl, task, task->GetStack(), task->GetStackSize(), TaskThreadPriority)); - R_ABORT_UNLESS(this->thread.Start()); + R_ABORT_UNLESS(os::CreateThread(std::addressof(this->thread), RunTaskImpl, task, task->GetStack(), task->GetStackSize(), TaskThreadPriority, 3)); + os::StartThread(std::addressof(this->thread)); } }; diff --git a/stratosphere/fatal/source/fatal_task_error_report.cpp b/stratosphere/fatal/source/fatal_task_error_report.cpp index 876c76267..6744be8ec 100644 --- a/stratosphere/fatal/source/fatal_task_error_report.cpp +++ b/stratosphere/fatal/source/fatal_task_error_report.cpp @@ -160,7 +160,7 @@ namespace ams::fatal::srv { } /* Signal we're done with our job. */ - eventFire(const_cast(&this->context->erpt_event)); + this->context->erpt_event->Signal(); return ResultSuccess(); } diff --git a/stratosphere/fatal/source/fatal_task_power.cpp b/stratosphere/fatal/source/fatal_task_power.cpp index 087945de8..b543a89df 100644 --- a/stratosphere/fatal/source/fatal_task_power.cpp +++ b/stratosphere/fatal/source/fatal_task_power.cpp @@ -50,6 +50,22 @@ namespace ams::fatal::srv { } }; + class RebootTimingObserver { + private: + os::Tick start_tick; + bool flag; + s32 interval; + public: + RebootTimingObserver(bool flag, s32 interval) : start_tick(os::GetSystemTick()), flag(flag), interval(interval) { + /* ... */ + } + + bool IsRebootTiming() const { + auto current_tick = os::GetSystemTick(); + return this->flag && (current_tick - this->start_tick).ToTimeSpan().GetSeconds() >= this->interval; + } + }; + /* Task globals. */ PowerControlTask g_power_control_task; PowerButtonObserveTask g_power_button_observe_task; @@ -58,13 +74,16 @@ namespace ams::fatal::srv { /* Task Implementations. */ bool PowerControlTask::TryShutdown() { /* Set a timeout of 30 seconds. */ - os::TimeoutHelper timeout_helper(30'000'000'000ul); + constexpr s32 MaxShutdownWaitSeconds = 30; + + auto start_tick = os::GetSystemTick(); bool perform_shutdown = true; PsmBatteryVoltageState bv_state = PsmBatteryVoltageState_Normal; while (true) { - if (timeout_helper.TimedOut()) { + auto cur_tick = os::GetSystemTick(); + if ((cur_tick - start_tick).ToTimeSpan().GetSeconds() > MaxShutdownWaitSeconds) { break; } @@ -77,8 +96,8 @@ namespace ams::fatal::srv { break; } - /* Query voltage state every 5 seconds, for 30 seconds. */ - svcSleepThread(5'000'000'000ul); + /* Query voltage state every 1 seconds, for 30 seconds. */ + os::SleepThread(TimeSpan::FromSeconds(1)); } if (perform_shutdown) { @@ -94,13 +113,13 @@ namespace ams::fatal::srv { /* Check the battery state, and shutdown on low voltage. */ if (R_FAILED(psmGetBatteryVoltageState(&bv_state)) || bv_state == PsmBatteryVoltageState_NeedsShutdown) { /* Wait a second for the error report task to finish. */ - eventWait(const_cast(&this->context->erpt_event), os::TimeoutHelper::NsToTick(1'000'000'000ul)); + this->context->erpt_event->TimedWait(TimeSpan::FromSeconds(1)); this->TryShutdown(); return; } /* Signal we've checked the battery at least once. */ - eventFire(const_cast(&this->context->battery_event)); + this->context->battery_event->Signal(); /* Loop querying voltage state every 5 seconds. */ while (true) { @@ -122,18 +141,18 @@ namespace ams::fatal::srv { break; } - svcSleepThread(5'000'000'000ul); + os::SleepThread(TimeSpan::FromSeconds(5)); } } void PowerButtonObserveTask::WaitForPowerButton() { /* Wait up to a second for error report generation to finish. */ - eventWait(const_cast(&this->context->erpt_event), os::TimeoutHelper::NsToTick(1'000'000'000ul)); + this->context->erpt_event->TimedWait(TimeSpan::FromSeconds(1)); /* Force a reboot after some time if kiosk unit. */ const auto &config = GetFatalConfig(); - os::TimeoutHelper quest_reboot_helper(config.GetQuestRebootTimeoutInterval()); - os::TimeoutHelper fatal_reboot_helper(config.GetFatalRebootTimeoutInterval()); + RebootTimingObserver quest_reboot_helper(config.IsQuest(), config.GetQuestRebootTimeoutInterval()); + RebootTimingObserver fatal_reboot_helper(config.IsFatalRebootEnabled(), config.GetFatalRebootTimeoutInterval()); bool check_vol_up = true, check_vol_down = true; GpioPadSession vol_up_btn, vol_down_btn; @@ -159,11 +178,10 @@ namespace ams::fatal::srv { BpcSleepButtonState state; GpioValue val; while (true) { - if ((config.IsFatalRebootEnabled() && fatal_reboot_helper.TimedOut()) || + if (fatal_reboot_helper.IsRebootTiming() || (quest_reboot_helper.IsRebootTiming()) || (check_vol_up && R_SUCCEEDED(gpioPadGetValue(&vol_up_btn, &val)) && val == GpioValue_Low) || (check_vol_down && R_SUCCEEDED(gpioPadGetValue(&vol_down_btn, &val)) && val == GpioValue_Low) || - (R_SUCCEEDED(bpcGetSleepButtonState(&state)) && state == BpcSleepButtonState_Held) || - (config.IsQuest() && quest_reboot_helper.TimedOut())) { + (R_SUCCEEDED(bpcGetSleepButtonState(&state)) && state == BpcSleepButtonState_Held)) { /* If any of the above conditions succeeded, we should reboot. */ bpcRebootSystem(); return; @@ -171,7 +189,7 @@ namespace ams::fatal::srv { /* Wait 100 ms between button checks. */ - svcSleepThread(100'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(100)); } } diff --git a/stratosphere/fatal/source/fatal_task_screen.cpp b/stratosphere/fatal/source/fatal_task_screen.cpp index 9d6262ca3..819174aaa 100644 --- a/stratosphere/fatal/source/fatal_task_screen.cpp +++ b/stratosphere/fatal/source/fatal_task_screen.cpp @@ -418,7 +418,7 @@ namespace ams::fatal::srv { Result ShowFatalTask::Run() { /* Don't show the fatal error screen until we've verified the battery is okay. */ - eventWait(const_cast(&this->context->battery_event), std::numeric_limits::max()); + this->context->battery_event->Wait(); return ShowFatal(); } diff --git a/stratosphere/loader/source/ldr_content_management.cpp b/stratosphere/loader/source/ldr_content_management.cpp index 343cd8040..8409bf7e4 100644 --- a/stratosphere/loader/source/ldr_content_management.cpp +++ b/stratosphere/loader/source/ldr_content_management.cpp @@ -19,7 +19,7 @@ namespace ams::ldr { namespace { - os::Mutex g_scoped_code_mount_lock; + os::Mutex g_scoped_code_mount_lock(false); } diff --git a/stratosphere/loader/source/ldr_patcher.cpp b/stratosphere/loader/source/ldr_patcher.cpp index bf711f549..2209b111b 100644 --- a/stratosphere/loader/source/ldr_patcher.cpp +++ b/stratosphere/loader/source/ldr_patcher.cpp @@ -29,7 +29,7 @@ namespace ams::ldr { constexpr const char * const LoaderSdMountName = "#amsldr-sdpatch"; static_assert(sizeof(LoaderSdMountName) <= fs::MountNameLengthMax); - os::Mutex g_ldr_sd_lock; + os::Mutex g_ldr_sd_lock(false); bool g_mounted_sd; bool EnsureSdCardMounted() { diff --git a/stratosphere/ncm/source/ncm_main.cpp b/stratosphere/ncm/source/ncm_main.cpp index fe07ef4d2..debd92326 100644 --- a/stratosphere/ncm/source/ncm_main.cpp +++ b/stratosphere/ncm/source/ncm_main.cpp @@ -144,14 +144,14 @@ namespace { constexpr inline sm::ServiceName ContentManagerServiceName = sm::ServiceName::Encode("ncm"); + alignas(os::ThreadStackAlignment) u8 g_content_manager_thread_stack[16_KB]; + alignas(os::ThreadStackAlignment) u8 g_location_resolver_thread_stack[16_KB]; + class ContentManagerServerManager : public sf::hipc::ServerManager { private: - static constexpr size_t ThreadStackSize = 0x4000; - static constexpr int ThreadPriority = 0x15; - using ServiceType = ncm::ContentManagerImpl; private: - os::StaticThread thread; + os::ThreadType thread; std::shared_ptr ncm_manager; private: static void ThreadFunction(void *_this) { @@ -159,7 +159,7 @@ namespace { } public: ContentManagerServerManager(ServiceType *m) - : thread(ThreadFunction, this, ThreadPriority), ncm_manager() + : ncm_manager() { /* ... */ } @@ -170,11 +170,13 @@ namespace { } ams::Result StartThreads() { - return this->thread.Start(); + R_TRY(os::CreateThread(std::addressof(this->thread), ThreadFunction, this, g_content_manager_thread_stack, sizeof(g_content_manager_thread_stack), 21)); + os::StartThread(std::addressof(this->thread)); + return ResultSuccess(); } void Wait() { - this->thread.Join(); + os::WaitThread(std::addressof(this->thread)); } }; @@ -193,12 +195,9 @@ namespace { class LocationResolverServerManager : public sf::hipc::ServerManager { private: - static constexpr size_t ThreadStackSize = 0x4000; - static constexpr int ThreadPriority = 0x15; - using ServiceType = lr::LocationResolverManagerImpl; private: - os::StaticThread thread; + os::ThreadType thread; std::shared_ptr lr_manager; private: static void ThreadFunction(void *_this) { @@ -206,7 +205,7 @@ namespace { } public: LocationResolverServerManager(ServiceType *m) - : thread(ThreadFunction, this, ThreadPriority), lr_manager(sf::ServiceObjectTraits::SharedPointerHelper::GetEmptyDeleteSharedPointer(m)) + : lr_manager(sf::ServiceObjectTraits::SharedPointerHelper::GetEmptyDeleteSharedPointer(m)) { /* ... */ } @@ -216,11 +215,13 @@ namespace { } ams::Result StartThreads() { - return this->thread.Start(); + R_TRY(os::CreateThread(std::addressof(this->thread), ThreadFunction, this, g_location_resolver_thread_stack, sizeof(g_location_resolver_thread_stack), 21)); + os::StartThread(std::addressof(this->thread)); + return ResultSuccess(); } void Wait() { - this->thread.Join(); + os::WaitThread(std::addressof(this->thread)); } }; diff --git a/stratosphere/pm/source/impl/pm_process_info.cpp b/stratosphere/pm/source/impl/pm_process_info.cpp index 564b3cfac..7965a9384 100644 --- a/stratosphere/pm/source/impl/pm_process_info.cpp +++ b/stratosphere/pm/source/impl/pm_process_info.cpp @@ -17,8 +17,9 @@ namespace ams::pm::impl { - ProcessInfo::ProcessInfo(Handle h, os::ProcessId pid, ldr::PinId pin, const ncm::ProgramLocation &l, const cfg::OverrideStatus &s) : process_id(pid), pin_id(pin), loc(l), status(s), handle(h), state(svc::ProcessState_Created), flags(0), waitable_holder(h) { - this->waitable_holder.SetUserData(reinterpret_cast(this)); + ProcessInfo::ProcessInfo(Handle h, os::ProcessId pid, ldr::PinId pin, const ncm::ProgramLocation &l, const cfg::OverrideStatus &s) : process_id(pid), pin_id(pin), loc(l), status(s), handle(h), state(svc::ProcessState_Created), flags(0) { + os::InitializeWaitableHolder(std::addressof(this->waitable_holder), this->handle); + os::SetWaitableHolderUserData(std::addressof(this->waitable_holder), reinterpret_cast(this)); } ProcessInfo::~ProcessInfo() { @@ -37,7 +38,7 @@ namespace ams::pm::impl { this->handle = INVALID_HANDLE; /* Unlink the process from its waitable manager. */ - this->waitable_holder.UnlinkFromWaitableManager(); + os::UnlinkWaitableHolder(std::addressof(this->waitable_holder)); } } diff --git a/stratosphere/pm/source/impl/pm_process_info.hpp b/stratosphere/pm/source/impl/pm_process_info.hpp index bcdbd2139..ba7998c8d 100644 --- a/stratosphere/pm/source/impl/pm_process_info.hpp +++ b/stratosphere/pm/source/impl/pm_process_info.hpp @@ -45,7 +45,7 @@ namespace ams::pm::impl { Handle handle; svc::ProcessState state; u32 flags; - os::WaitableHolder waitable_holder; + os::WaitableHolderType waitable_holder; private: void SetFlag(Flag flag) { this->flags |= flag; @@ -63,8 +63,8 @@ namespace ams::pm::impl { ~ProcessInfo(); void Cleanup(); - void LinkToWaitableManager(os::WaitableManager &manager) { - manager.LinkWaitableHolder(&this->waitable_holder); + void LinkToWaitableManager(os::WaitableManagerType &manager) { + os::LinkWaitableHolder(std::addressof(manager), std::addressof(this->waitable_holder)); } Handle GetHandle() const { @@ -163,6 +163,8 @@ namespace ams::pm::impl { private: os::Mutex lock; public: + constexpr ProcessList() : lock(false) { /* ... */ } + void Lock() { this->lock.Lock(); } diff --git a/stratosphere/pm/source/impl/pm_process_manager.cpp b/stratosphere/pm/source/impl/pm_process_manager.cpp index 0a7f7e397..d6d27f225 100644 --- a/stratosphere/pm/source/impl/pm_process_manager.cpp +++ b/stratosphere/pm/source/impl/pm_process_manager.cpp @@ -144,7 +144,7 @@ namespace ams::pm::impl { return process_info - GetPointer(this->process_info_storages[0]); } public: - constexpr ProcessInfoAllocator() { + constexpr ProcessInfoAllocator() : lock(false) { std::memset(this->process_info_storages, 0, sizeof(this->process_info_storages)); std::memset(this->process_info_allocated, 0, sizeof(this->process_info_allocated)); } @@ -176,9 +176,9 @@ namespace ams::pm::impl { /* Process Tracking globals. */ void ProcessTrackingMain(void *arg); - constexpr size_t ProcessTrackThreadStackSize = 0x4000; - constexpr int ProcessTrackThreadPriority = 0x15; - os::StaticThread g_process_track_thread(&ProcessTrackingMain, nullptr, ProcessTrackThreadPriority); + constexpr int ProcessTrackThreadPriority = 21; + os::ThreadType g_process_track_thread; + alignas(os::ThreadStackAlignment) u8 g_process_track_thread_stack[16_KB]; /* Process lists. */ ProcessList g_process_list; @@ -190,15 +190,15 @@ namespace ams::pm::impl { ProcessInfoAllocator g_process_info_allocator; /* Global events. */ - os::SystemEvent g_process_event; - os::SystemEvent g_hook_to_create_process_event; - os::SystemEvent g_hook_to_create_application_process_event; - os::SystemEvent g_boot_finished_event; + os::SystemEventType g_process_event; + os::SystemEventType g_hook_to_create_process_event; + os::SystemEventType g_hook_to_create_application_process_event; + os::SystemEventType g_boot_finished_event; /* Process Launch synchronization globals. */ - os::Mutex g_launch_program_lock; - os::Event g_process_launch_start_event; - os::Event g_process_launch_finish_event; + os::Mutex g_launch_program_lock(false); + os::Event g_process_launch_start_event(os::EventClearMode_AutoClear); + os::Event g_process_launch_finish_event(os::EventClearMode_AutoClear); Result g_process_launch_result = ResultSuccess(); LaunchProcessArgs g_process_launch_args = {}; @@ -207,7 +207,7 @@ namespace ams::pm::impl { std::atomic g_application_hook; /* Forward declarations. */ - Result LaunchProcess(os::WaitableManager &waitable_manager, const LaunchProcessArgs &args); + Result LaunchProcess(os::WaitableManagerType &waitable_manager, const LaunchProcessArgs &args); void OnProcessSignaled(ProcessListAccessor &list, ProcessInfo *process_info); /* Helpers. */ @@ -215,12 +215,14 @@ namespace ams::pm::impl { /* This is the main loop of the process tracking thread. */ /* Setup waitable manager. */ - os::WaitableManager process_waitable_manager; - os::WaitableHolder start_event_holder(&g_process_launch_start_event); - process_waitable_manager.LinkWaitableHolder(&start_event_holder); + os::WaitableManagerType process_waitable_manager; + os::WaitableHolderType start_event_holder; + os::InitializeWaitableManager(std::addressof(process_waitable_manager)); + os::InitializeWaitableHolder(std::addressof(start_event_holder), g_process_launch_start_event.GetBase()); + os::LinkWaitableHolder(std::addressof(process_waitable_manager), std::addressof(start_event_holder)); while (true) { - auto signaled_holder = process_waitable_manager.WaitAny(); + auto signaled_holder = os::WaitAny(std::addressof(process_waitable_manager)); if (signaled_holder == &start_event_holder) { /* Launch start event signaled. */ /* TryWait will clear signaled, preventing duplicate notifications. */ @@ -231,7 +233,7 @@ namespace ams::pm::impl { } else { /* Some process was signaled. */ ProcessListAccessor list(g_process_list); - OnProcessSignaled(list, reinterpret_cast(signaled_holder->GetUserData())); + OnProcessSignaled(list, reinterpret_cast(os::GetWaitableHolderUserData(signaled_holder))); } } } @@ -275,7 +277,7 @@ namespace ams::pm::impl { g_process_info_allocator.FreeProcessInfo(process_info); } - Result LaunchProcess(os::WaitableManager &waitable_manager, const LaunchProcessArgs &args) { + Result LaunchProcess(os::WaitableManagerType &waitable_manager, const LaunchProcessArgs &args) { /* Get Program Info. */ ldr::ProgramInfo program_info; cfg::OverrideStatus override_status; @@ -351,10 +353,10 @@ namespace ams::pm::impl { /* Process hooks/signaling. */ if (location.program_id == g_program_id_hook) { - g_hook_to_create_process_event.Signal(); + os::SignalSystemEvent(std::addressof(g_hook_to_create_process_event)); g_program_id_hook = ncm::InvalidProgramId; } else if (is_application && g_application_hook) { - g_hook_to_create_application_process_event.Signal(); + os::SignalSystemEvent(std::addressof(g_hook_to_create_application_process_event)); g_application_hook = false; } else if (!ShouldStartSuspended(args.flags)) { R_TRY(StartProcess(process_info, &program_info)); @@ -394,22 +396,22 @@ namespace ams::pm::impl { if (process_info->ShouldSignalOnDebugEvent()) { process_info->ClearSuspended(); process_info->SetSuspendedStateChanged(); - g_process_event.Signal(); + os::SignalSystemEvent(std::addressof(g_process_event)); } else if (hos::GetVersion() >= hos::Version_200 && process_info->ShouldSignalOnStart()) { process_info->SetStartedStateChanged(); process_info->ClearSignalOnStart(); - g_process_event.Signal(); + os::SignalSystemEvent(std::addressof(g_process_event)); } break; case svc::ProcessState_Crashed: process_info->SetExceptionOccurred(); - g_process_event.Signal(); + os::SignalSystemEvent(std::addressof(g_process_event)); break; case svc::ProcessState_RunningAttached: if (process_info->ShouldSignalOnDebugEvent()) { process_info->ClearSuspended(); process_info->SetSuspendedStateChanged(); - g_process_event.Signal(); + os::SignalSystemEvent(std::addressof(g_process_event)); } break; case svc::ProcessState_Terminated: @@ -417,7 +419,7 @@ namespace ams::pm::impl { process_info->Cleanup(); if (hos::GetVersion() < hos::Version_500 && process_info->ShouldSignalOnExit()) { - g_process_event.Signal(); + os::SignalSystemEvent(std::addressof(g_process_event)); } else { /* Handle the case where we need to keep the process alive some time longer. */ if (hos::GetVersion() >= hos::Version_500 && process_info->ShouldSignalOnExit()) { @@ -431,7 +433,7 @@ namespace ams::pm::impl { } /* Signal. */ - g_process_event.Signal(); + os::SignalSystemEvent(std::addressof(g_process_event)); } else { /* Actually delete process. */ CleanupProcessInfo(list, process_info); @@ -442,7 +444,7 @@ namespace ams::pm::impl { if (process_info->ShouldSignalOnDebugEvent()) { process_info->SetSuspended(); process_info->SetSuspendedStateChanged(); - g_process_event.Signal(); + os::SignalSystemEvent(std::addressof(g_process_event)); } break; } @@ -453,16 +455,19 @@ namespace ams::pm::impl { /* Initialization. */ Result InitializeProcessManager() { /* Create events. */ - R_ABORT_UNLESS(g_process_event.InitializeAsInterProcessEvent()); - R_ABORT_UNLESS(g_hook_to_create_process_event.InitializeAsInterProcessEvent()); - R_ABORT_UNLESS(g_hook_to_create_application_process_event.InitializeAsInterProcessEvent()); - R_ABORT_UNLESS(g_boot_finished_event.InitializeAsInterProcessEvent()); + R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_process_event), os::EventClearMode_AutoClear, true)); + R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_process_event), os::EventClearMode_AutoClear, true)); + R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_application_process_event), os::EventClearMode_AutoClear, true)); + R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_boot_finished_event), os::EventClearMode_AutoClear, true)); /* Initialize resource limits. */ R_TRY(resource::InitializeResourceManager()); + /* Create thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(g_process_track_thread), ProcessTrackingMain, nullptr, g_process_track_thread_stack, sizeof(g_process_track_thread_stack), ProcessTrackThreadPriority)); + /* Start thread. */ - R_ABORT_UNLESS(g_process_track_thread.Start()); + os::StartThread(std::addressof(g_process_track_thread)); return ResultSuccess(); } @@ -515,7 +520,7 @@ namespace ams::pm::impl { } Result GetProcessEventHandle(Handle *out) { - *out = g_process_event.GetReadableHandle(); + *out = os::GetReadableHandleOfSystemEvent(std::addressof(g_process_event)); return ResultSuccess(); } @@ -670,7 +675,7 @@ namespace ams::pm::impl { R_UNLESS(g_program_id_hook.compare_exchange_strong(old_value, program_id), pm::ResultDebugHookInUse()); } - *out_hook = g_hook_to_create_process_event.GetReadableHandle(); + *out_hook = os::GetReadableHandleOfSystemEvent(std::addressof(g_hook_to_create_process_event)); return ResultSuccess(); } @@ -682,7 +687,7 @@ namespace ams::pm::impl { R_UNLESS(g_application_hook.compare_exchange_strong(old_value, true), pm::ResultDebugHookInUse()); } - *out_hook = g_hook_to_create_application_process_event.GetReadableHandle(); + *out_hook = os::GetReadableHandleOfSystemEvent(std::addressof(g_hook_to_create_application_process_event)); return ResultSuccess(); } @@ -702,7 +707,7 @@ namespace ams::pm::impl { if (!g_has_boot_finished) { boot2::LaunchPreSdCardBootProgramsAndBoot2(); g_has_boot_finished = true; - g_boot_finished_event.Signal(); + os::SignalSystemEvent(std::addressof(g_boot_finished_event)); } return ResultSuccess(); } @@ -712,7 +717,7 @@ namespace ams::pm::impl { /* Nintendo only signals it in safe mode FIRM, and this function aborts on normal FIRM. */ /* We will signal it always, but only allow this function to succeed on safe mode. */ AMS_ABORT_UNLESS(spl::IsRecoveryBoot()); - *out = g_boot_finished_event.GetReadableHandle(); + *out = os::GetReadableHandleOfSystemEvent(std::addressof(g_boot_finished_event)); return ResultSuccess(); } diff --git a/stratosphere/pm/source/impl/pm_resource_manager.cpp b/stratosphere/pm/source/impl/pm_resource_manager.cpp index 9e7413c29..18ea37f37 100644 --- a/stratosphere/pm/source/impl/pm_resource_manager.cpp +++ b/stratosphere/pm/source/impl/pm_resource_manager.cpp @@ -41,7 +41,7 @@ namespace ams::pm::resource { constexpr size_t ExtraSystemMemorySizeAtmosphere500 = 33_MB; /* Applet pool is 0x20100000 */ /* Globals. */ - os::Mutex g_resource_limit_lock; + os::Mutex g_resource_limit_lock(false); Handle g_resource_limit_handles[ResourceLimitGroup_Count]; spl::MemoryArrangement g_memory_arrangement = spl::MemoryArrangement_Standard; u64 g_system_memory_boost_size = 0; diff --git a/stratosphere/spl/source/spl_api_impl.cpp b/stratosphere/spl/source/spl_api_impl.cpp index 13a56540e..3c6336e9f 100644 --- a/stratosphere/spl/source/spl_api_impl.cpp +++ b/stratosphere/spl/source/spl_api_impl.cpp @@ -97,14 +97,14 @@ namespace ams::spl::impl { /* Global variables. */ CtrDrbg g_drbg; - os::InterruptEvent g_se_event; - os::SystemEvent g_se_keyslot_available_event; + os::InterruptEventType g_se_event; + os::SystemEventType g_se_keyslot_available_event; Handle g_se_das_hnd; u32 g_se_mapped_work_buffer_addr; alignas(os::MemoryPageSize) u8 g_work_buffer[2 * WorkBufferSizeMax]; - os::Mutex g_async_op_lock; + os::Mutex g_async_op_lock(false); const void *g_keyslot_owners[MaxAesKeyslots]; BootReasonValue g_boot_reason; @@ -130,10 +130,10 @@ namespace ams::spl::impl { void InitializeSeEvents() { u64 irq_num; AMS_ABORT_UNLESS(smc::GetConfig(&irq_num, 1, SplConfigItem_SecurityEngineIrqNumber) == smc::Result::Success); - R_ABORT_UNLESS(g_se_event.Initialize(irq_num)); + os::InitializeInterruptEvent(std::addressof(g_se_event), irq_num, os::EventClearMode_AutoClear); - R_ABORT_UNLESS(g_se_keyslot_available_event.InitializeAsInterProcessEvent()); - g_se_keyslot_available_event.Signal(); + R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_se_keyslot_available_event), os::EventClearMode_AutoClear, true)); + os::SignalSystemEvent(std::addressof(g_se_keyslot_available_event)); } void InitializeDeviceAddressSpace() { @@ -173,7 +173,7 @@ namespace ams::spl::impl { /* Internal async implementation functionality. */ void WaitSeOperationComplete() { - g_se_event.Wait(); + os::WaitInterruptEvent(std::addressof(g_se_event)); } smc::Result WaitCheckStatus(smc::AsyncOperationKey op_key) { @@ -596,7 +596,7 @@ namespace ams::spl::impl { } } - g_se_keyslot_available_event.Reset(); + os::ClearSystemEvent(std::addressof(g_se_keyslot_available_event)); return spl::ResultOutOfKeyslots(); } @@ -616,7 +616,7 @@ namespace ams::spl::impl { smc::LoadAesKey(keyslot, access_key, key_source); } g_keyslot_owners[keyslot] = nullptr; - g_se_keyslot_available_event.Signal(); + os::SignalSystemEvent(std::addressof(g_se_keyslot_available_event)); return ResultSuccess(); } @@ -792,7 +792,7 @@ namespace ams::spl::impl { } Handle GetAesKeyslotAvailableEventHandle() { - return g_se_keyslot_available_event.GetReadableHandle(); + return os::GetReadableHandleOfSystemEvent(std::addressof(g_se_keyslot_available_event)); } }