From a0cddf7c06ab6dacc0684fe210a80e0612d456fc Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 8 Apr 2020 02:21:35 -0700 Subject: [PATCH] os: refactor/rewrite entire namespace. --- .../stratosphere/fatal/fatal_types.hpp | 24 +- .../stratosphere/fs/fs_file_storage.hpp | 2 +- .../kvdb/kvdb_file_key_value_store.hpp | 2 +- .../lmem/impl/lmem_impl_common.hpp | 2 +- .../lr/lr_location_resolver_manager_impl.hpp | 2 +- .../ncm/ncm_content_manager_impl.hpp | 4 +- .../ncm/ncm_install_task_base.hpp | 2 +- .../stratosphere/ncm/ncm_rights_id_cache.hpp | 2 +- libstratosphere/include/stratosphere/os.hpp | 6 +- .../impl/os_internal_condition_variable.hpp | 58 +++ ...nal_condition_variable_impl.os.horizon.hpp | 43 ++ .../os/impl/os_internal_critical_section.hpp | 62 +++ ...ernal_critical_section_impl.os.horizon.hpp | 52 ++ .../stratosphere/os/os_condition_variable.hpp | 65 +++ .../os/os_condition_variable_api.hpp | 35 ++ .../os/os_condition_variable_common.hpp | 26 + .../os/os_condition_variable_types.hpp | 37 ++ .../include/stratosphere/os/os_condvar.hpp | 68 --- .../include/stratosphere/os/os_event.hpp | 67 ++- .../include/stratosphere/os/os_event_api.hpp | 37 ++ .../stratosphere/os/os_event_common.hpp | 26 + .../stratosphere/os/os_event_types.hpp | 49 ++ .../stratosphere/os/os_interrupt_event.hpp | 59 ++- .../os/os_interrupt_event_api.hpp | 36 ++ .../os/os_interrupt_event_common.hpp | 23 + .../os/os_interrupt_event_types.hpp | 46 ++ .../stratosphere/os/os_message_queue.hpp | 116 +++-- .../stratosphere/os/os_message_queue_api.hpp | 51 ++ .../os/os_message_queue_common.hpp | 26 + .../os/os_message_queue_types.hpp | 50 ++ .../include/stratosphere/os/os_mutex.hpp | 70 +-- .../include/stratosphere/os/os_mutex_api.hpp | 34 ++ .../stratosphere/os/os_mutex_common.hpp | 27 + .../stratosphere/os/os_mutex_types.hpp | 43 ++ .../include/stratosphere/os/os_semaphore.hpp | 65 ++- .../stratosphere/os/os_semaphore_api.hpp | 39 ++ .../stratosphere/os/os_semaphore_types.hpp | 46 ++ .../stratosphere/os/os_system_event.hpp | 133 +++-- .../stratosphere/os/os_system_event_api.hpp | 47 ++ .../stratosphere/os/os_system_event_types.hpp | 60 +++ .../include/stratosphere/os/os_thread.hpp | 106 +--- .../include/stratosphere/os/os_thread_api.hpp | 67 +++ .../stratosphere/os/os_thread_common.hpp | 38 ++ .../stratosphere/os/os_thread_types.hpp | 72 +++ .../stratosphere/os/os_timeout_helper.hpp | 63 --- .../include/stratosphere/os/os_waitable.hpp | 19 + .../stratosphere/os/os_waitable_api.hpp | 45 ++ .../stratosphere/os/os_waitable_holder.hpp | 67 --- .../stratosphere/os/os_waitable_manager.hpp | 51 -- .../stratosphere/os/os_waitable_types.hpp | 47 ++ .../stratosphere/os/os_waitable_utils.hpp | 22 + .../sf/cmif/sf_cmif_domain_manager.hpp | 2 +- .../stratosphere/sf/hipc/sf_hipc_api.hpp | 3 + .../sf/hipc/sf_hipc_server_manager.hpp | 50 +- .../hipc/sf_hipc_server_session_manager.hpp | 8 +- libstratosphere/source/ams/ams_emummc_api.cpp | 2 +- .../source/cfg/cfg_privileged_process.cpp | 2 +- libstratosphere/source/cfg/cfg_sd_card.cpp | 2 +- .../source/fs/fs_memory_management.cpp | 2 +- .../source/fs/fsa/fs_filesystem_accessor.cpp | 2 +- .../source/fs/fsa/fs_mount_table.hpp | 2 +- .../fssystem/fssystem_aes_xts_storage.cpp | 2 +- ...fssystem_directory_savedata_filesystem.cpp | 4 +- .../fssystem/fssystem_pooled_buffer.cpp | 4 +- libstratosphere/source/hid/hid_api.cpp | 2 +- .../source/hos/hos_version_api.cpp | 2 +- .../lmem/impl/lmem_impl_common_heap.cpp | 6 - .../lmem/impl/lmem_impl_common_heap.hpp | 4 +- libstratosphere/source/lmem/lmem_exp_heap.cpp | 9 +- .../source/lmem/lmem_unit_heap.cpp | 21 +- .../heap/mem_impl_heap_tls_heap_central.hpp | 4 +- .../mem/impl/mem_impl_platform.os.horizon.cpp | 2 +- .../source/ncm/ncm_install_task_base.cpp | 5 +- .../source/ncm/ncm_placeholder_accessor.hpp | 2 +- .../source/os/impl/os_inter_process_event.cpp | 232 ++++----- .../source/os/impl/os_inter_process_event.hpp | 45 +- .../os/impl/os_inter_process_event_impl.hpp | 23 + ...os_inter_process_event_impl.os.horizon.cpp | 123 +++++ ...os_inter_process_event_impl.os.horizon.hpp | 32 ++ ...nal_condition_variable_impl.os.horizon.cpp | 56 ++ ...ernal_critical_section_impl.os.horizon.cpp | 160 ++++++ .../os/impl/os_interrupt_event_impl.hpp | 58 +++ ...interrupt_event_target_impl.os.horizon.cpp | 114 ++++ ...interrupt_event_target_impl.os.horizon.hpp | 45 ++ .../source/os/impl/os_mutex_impl.hpp | 24 + .../source/os/impl/os_resource_manager.hpp | 6 +- .../source/os/impl/os_rng_manager.hpp | 1 + .../source/os/impl/os_rng_manager_impl.hpp | 2 +- .../source/os/impl/os_thread_manager.cpp | 226 ++++++++ .../source/os/impl/os_thread_manager.hpp | 40 ++ .../os_thread_manager_impl.os.horizon.cpp | 214 ++++++++ .../os_thread_manager_impl.os.horizon.hpp | 79 +++ .../os/impl/os_thread_manager_types.hpp | 151 ++++++ .../source/os/impl/os_timeout_helper.cpp | 37 ++ .../source/os/impl/os_timeout_helper.hpp | 64 +++ .../os_timeout_helper_impl.os.horizon.cpp | 30 ++ .../os_timeout_helper_impl.os.horizon.hpp | 33 ++ .../os/impl/os_waitable_holder_base.hpp | 12 +- .../os/impl/os_waitable_holder_impl.hpp | 4 +- .../os/impl/os_waitable_holder_of_event.hpp | 10 +- ...waitable_holder_of_inter_process_event.hpp | 7 +- .../os_waitable_holder_of_interrupt_event.cpp | 26 + .../os_waitable_holder_of_interrupt_event.hpp | 9 +- .../os_waitable_holder_of_message_queue.hpp | 38 +- .../impl/os_waitable_holder_of_semaphore.hpp | 10 +- .../os/impl/os_waitable_holder_of_thread.hpp | 30 +- .../os/impl/os_waitable_manager_impl.cpp | 76 ++- .../os/impl/os_waitable_manager_impl.hpp | 33 +- ...aitable_manager_target_impl.os.horizon.cpp | 43 ++ ...aitable_manager_target_impl.os.horizon.hpp | 59 +++ .../source/os/os_condition_variable.cpp | 107 ++++ libstratosphere/source/os/os_event.cpp | 159 ++++-- .../source/os/os_interrupt_event.cpp | 106 ++-- .../source/os/os_message_queue.cpp | 486 ++++++++++++------ libstratosphere/source/os/os_mutex.cpp | 160 ++++++ libstratosphere/source/os/os_random.cpp | 2 +- libstratosphere/source/os/os_semaphore.cpp | 138 +++-- libstratosphere/source/os/os_system_event.cpp | 234 ++++----- libstratosphere/source/os/os_thread.cpp | 209 ++++++++ libstratosphere/source/os/os_waitable.cpp | 151 ++++++ .../source/os/os_waitable_holder.cpp | 116 ----- .../source/os/os_waitable_manager.cpp | 88 ---- .../source/patcher/patcher_api.cpp | 2 +- libstratosphere/source/pm/pm_info_api.cpp | 2 +- .../source/sf/cmif/sf_cmif_domain_manager.cpp | 2 +- .../source/sf/hipc/sf_hipc_api.cpp | 12 +- .../source/sf/hipc/sf_hipc_mitm_query_api.cpp | 15 +- .../source/sf/hipc/sf_hipc_server_manager.cpp | 38 +- .../hipc/sf_hipc_server_session_manager.cpp | 1 + libstratosphere/source/sm/sm_utils.cpp | 12 +- libstratosphere/source/sm/sm_utils.hpp | 12 +- libvapours/include/vapours/svc/svc_common.hpp | 6 + .../include/vapours/svc/svc_types_common.hpp | 10 +- libvapours/include/vapours/timespan.hpp | 88 +++- 134 files changed, 5012 insertions(+), 1677 deletions(-) create mode 100644 libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable.hpp create mode 100644 libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable_impl.os.horizon.hpp create mode 100644 libstratosphere/include/stratosphere/os/impl/os_internal_critical_section.hpp create mode 100644 libstratosphere/include/stratosphere/os/impl/os_internal_critical_section_impl.os.horizon.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_condition_variable.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_condition_variable_api.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_condition_variable_common.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_condition_variable_types.hpp delete mode 100644 libstratosphere/include/stratosphere/os/os_condvar.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_event_api.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_event_common.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_event_types.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_interrupt_event_api.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_interrupt_event_common.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_interrupt_event_types.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_message_queue_api.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_message_queue_common.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_message_queue_types.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_mutex_api.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_mutex_common.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_mutex_types.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_semaphore_api.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_semaphore_types.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_system_event_api.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_system_event_types.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_thread_api.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_thread_common.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_thread_types.hpp delete mode 100644 libstratosphere/include/stratosphere/os/os_timeout_helper.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_waitable.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_waitable_api.hpp delete mode 100644 libstratosphere/include/stratosphere/os/os_waitable_holder.hpp delete mode 100644 libstratosphere/include/stratosphere/os/os_waitable_manager.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_waitable_types.hpp create mode 100644 libstratosphere/include/stratosphere/os/os_waitable_utils.hpp create mode 100644 libstratosphere/source/os/impl/os_inter_process_event_impl.hpp create mode 100644 libstratosphere/source/os/impl/os_inter_process_event_impl.os.horizon.cpp create mode 100644 libstratosphere/source/os/impl/os_inter_process_event_impl.os.horizon.hpp create mode 100644 libstratosphere/source/os/impl/os_internal_condition_variable_impl.os.horizon.cpp create mode 100644 libstratosphere/source/os/impl/os_internal_critical_section_impl.os.horizon.cpp create mode 100644 libstratosphere/source/os/impl/os_interrupt_event_impl.hpp create mode 100644 libstratosphere/source/os/impl/os_interrupt_event_target_impl.os.horizon.cpp create mode 100644 libstratosphere/source/os/impl/os_interrupt_event_target_impl.os.horizon.hpp create mode 100644 libstratosphere/source/os/impl/os_mutex_impl.hpp create mode 100644 libstratosphere/source/os/impl/os_thread_manager.cpp create mode 100644 libstratosphere/source/os/impl/os_thread_manager.hpp create mode 100644 libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.cpp create mode 100644 libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.hpp create mode 100644 libstratosphere/source/os/impl/os_thread_manager_types.hpp create mode 100644 libstratosphere/source/os/impl/os_timeout_helper.cpp create mode 100644 libstratosphere/source/os/impl/os_timeout_helper.hpp create mode 100644 libstratosphere/source/os/impl/os_timeout_helper_impl.os.horizon.cpp create mode 100644 libstratosphere/source/os/impl/os_timeout_helper_impl.os.horizon.hpp create mode 100644 libstratosphere/source/os/impl/os_waitable_holder_of_interrupt_event.cpp create mode 100644 libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.cpp create mode 100644 libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.hpp create mode 100644 libstratosphere/source/os/os_condition_variable.cpp create mode 100644 libstratosphere/source/os/os_mutex.cpp create mode 100644 libstratosphere/source/os/os_thread.cpp create mode 100644 libstratosphere/source/os/os_waitable.cpp delete mode 100644 libstratosphere/source/os/os_waitable_holder.cpp delete mode 100644 libstratosphere/source/os/os_waitable_manager.cpp diff --git a/libstratosphere/include/stratosphere/fatal/fatal_types.hpp b/libstratosphere/include/stratosphere/fatal/fatal_types.hpp index 59283488..f2a9dc91 100644 --- a/libstratosphere/include/stratosphere/fatal/fatal_types.hpp +++ b/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/libstratosphere/include/stratosphere/fs/fs_file_storage.hpp b/libstratosphere/include/stratosphere/fs/fs_file_storage.hpp index 2ef9cdf1..a6b3855a 100644 --- a/libstratosphere/include/stratosphere/fs/fs_file_storage.hpp +++ b/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/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp b/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp index ac3d5ae9..ce204dfb 100644 --- a/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp +++ b/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/libstratosphere/include/stratosphere/lmem/impl/lmem_impl_common.hpp b/libstratosphere/include/stratosphere/lmem/impl/lmem_impl_common.hpp index 11158801..4cc2240a 100644 --- a/libstratosphere/include/stratosphere/lmem/impl/lmem_impl_common.hpp +++ b/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/libstratosphere/include/stratosphere/lr/lr_location_resolver_manager_impl.hpp b/libstratosphere/include/stratosphere/lr/lr_location_resolver_manager_impl.hpp index daa198e0..c9da61cb 100644 --- a/libstratosphere/include/stratosphere/lr/lr_location_resolver_manager_impl.hpp +++ b/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/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp b/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp index a6b75b88..94cdd431 100644 --- a/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp +++ b/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/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp b/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp index 7f93a7da..4959f42f 100644 --- a/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp +++ b/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/libstratosphere/include/stratosphere/ncm/ncm_rights_id_cache.hpp b/libstratosphere/include/stratosphere/ncm/ncm_rights_id_cache.hpp index f29d01a8..93dadc0f 100644 --- a/libstratosphere/include/stratosphere/ncm/ncm_rights_id_cache.hpp +++ b/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/libstratosphere/include/stratosphere/os.hpp b/libstratosphere/include/stratosphere/os.hpp index 061da55e..57578afd 100644 --- a/libstratosphere/include/stratosphere/os.hpp +++ b/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/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable.hpp b/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable.hpp new file mode 100644 index 00000000..33a34a70 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable_impl.os.horizon.hpp b/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable_impl.os.horizon.hpp new file mode 100644 index 00000000..1b3031b0 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section.hpp b/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section.hpp new file mode 100644 index 00000000..d1b0871f --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section_impl.os.horizon.hpp b/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section_impl.os.horizon.hpp new file mode 100644 index 00000000..1b8dabfd --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_condition_variable.hpp b/libstratosphere/include/stratosphere/os/os_condition_variable.hpp new file mode 100644 index 00000000..d4438384 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_condition_variable_api.hpp b/libstratosphere/include/stratosphere/os/os_condition_variable_api.hpp new file mode 100644 index 00000000..83f1dd2d --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_condition_variable_common.hpp b/libstratosphere/include/stratosphere/os/os_condition_variable_common.hpp new file mode 100644 index 00000000..65fc8467 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_condition_variable_types.hpp b/libstratosphere/include/stratosphere/os/os_condition_variable_types.hpp new file mode 100644 index 00000000..7eeba689 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_condvar.hpp b/libstratosphere/include/stratosphere/os/os_condvar.hpp deleted file mode 100644 index d2b05bd9..00000000 --- a/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/libstratosphere/include/stratosphere/os/os_event.hpp b/libstratosphere/include/stratosphere/os/os_event.hpp index 5c825446..4bad3879 100644 --- a/libstratosphere/include/stratosphere/os/os_event.hpp +++ b/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/libstratosphere/include/stratosphere/os/os_event_api.hpp b/libstratosphere/include/stratosphere/os/os_event_api.hpp new file mode 100644 index 00000000..0f7b7501 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_event_common.hpp b/libstratosphere/include/stratosphere/os/os_event_common.hpp new file mode 100644 index 00000000..625c435c --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_event_types.hpp b/libstratosphere/include/stratosphere/os/os_event_types.hpp new file mode 100644 index 00000000..7b61a5ce --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_interrupt_event.hpp b/libstratosphere/include/stratosphere/os/os_interrupt_event.hpp index 94a970f6..8e1b6c0d 100644 --- a/libstratosphere/include/stratosphere/os/os_interrupt_event.hpp +++ b/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/libstratosphere/include/stratosphere/os/os_interrupt_event_api.hpp b/libstratosphere/include/stratosphere/os/os_interrupt_event_api.hpp new file mode 100644 index 00000000..b2dcf794 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_interrupt_event_common.hpp b/libstratosphere/include/stratosphere/os/os_interrupt_event_common.hpp new file mode 100644 index 00000000..99e71097 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_interrupt_event_types.hpp b/libstratosphere/include/stratosphere/os/os_interrupt_event_types.hpp new file mode 100644 index 00000000..68583209 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_message_queue.hpp b/libstratosphere/include/stratosphere/os/os_message_queue.hpp index 06ea9be7..d7f6205e 100644 --- a/libstratosphere/include/stratosphere/os/os_message_queue.hpp +++ b/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/libstratosphere/include/stratosphere/os/os_message_queue_api.hpp b/libstratosphere/include/stratosphere/os/os_message_queue_api.hpp new file mode 100644 index 00000000..51fc0a5c --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_message_queue_common.hpp b/libstratosphere/include/stratosphere/os/os_message_queue_common.hpp new file mode 100644 index 00000000..4d135c0a --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_message_queue_types.hpp b/libstratosphere/include/stratosphere/os/os_message_queue_types.hpp new file mode 100644 index 00000000..7c7789c6 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_mutex.hpp b/libstratosphere/include/stratosphere/os/os_mutex.hpp index 5fdec6d4..a5c4265a 100644 --- a/libstratosphere/include/stratosphere/os/os_mutex.hpp +++ b/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/libstratosphere/include/stratosphere/os/os_mutex_api.hpp b/libstratosphere/include/stratosphere/os/os_mutex_api.hpp new file mode 100644 index 00000000..84ce0dd9 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_mutex_common.hpp b/libstratosphere/include/stratosphere/os/os_mutex_common.hpp new file mode 100644 index 00000000..b6ef8962 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_mutex_types.hpp b/libstratosphere/include/stratosphere/os/os_mutex_types.hpp new file mode 100644 index 00000000..5af5549b --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_semaphore.hpp b/libstratosphere/include/stratosphere/os/os_semaphore.hpp index eb43aada..a00d620c 100644 --- a/libstratosphere/include/stratosphere/os/os_semaphore.hpp +++ b/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/libstratosphere/include/stratosphere/os/os_semaphore_api.hpp b/libstratosphere/include/stratosphere/os/os_semaphore_api.hpp new file mode 100644 index 00000000..bd761911 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_semaphore_types.hpp b/libstratosphere/include/stratosphere/os/os_semaphore_types.hpp new file mode 100644 index 00000000..37d4b4d2 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_system_event.hpp b/libstratosphere/include/stratosphere/os/os_system_event.hpp index 7ceca197..e46d9ca8 100644 --- a/libstratosphere/include/stratosphere/os/os_system_event.hpp +++ b/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/libstratosphere/include/stratosphere/os/os_system_event_api.hpp b/libstratosphere/include/stratosphere/os/os_system_event_api.hpp new file mode 100644 index 00000000..d31c0e04 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_system_event_types.hpp b/libstratosphere/include/stratosphere/os/os_system_event_types.hpp new file mode 100644 index 00000000..cff31bc0 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_thread.hpp b/libstratosphere/include/stratosphere/os/os_thread.hpp index 2e24d57b..909a9851 100644 --- a/libstratosphere/include/stratosphere/os/os_thread.hpp +++ b/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/libstratosphere/include/stratosphere/os/os_thread_api.hpp b/libstratosphere/include/stratosphere/os/os_thread_api.hpp new file mode 100644 index 00000000..3d7c4216 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_thread_common.hpp b/libstratosphere/include/stratosphere/os/os_thread_common.hpp new file mode 100644 index 00000000..8e634dc9 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_thread_types.hpp b/libstratosphere/include/stratosphere/os/os_thread_types.hpp new file mode 100644 index 00000000..c687add1 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_timeout_helper.hpp b/libstratosphere/include/stratosphere/os/os_timeout_helper.hpp deleted file mode 100644 index b69d475e..00000000 --- a/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/libstratosphere/include/stratosphere/os/os_waitable.hpp b/libstratosphere/include/stratosphere/os/os_waitable.hpp new file mode 100644 index 00000000..13fa5c9f --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_waitable_api.hpp b/libstratosphere/include/stratosphere/os/os_waitable_api.hpp new file mode 100644 index 00000000..2ede0d93 --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_waitable_holder.hpp b/libstratosphere/include/stratosphere/os/os_waitable_holder.hpp deleted file mode 100644 index 959a8486..00000000 --- a/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/libstratosphere/include/stratosphere/os/os_waitable_manager.hpp b/libstratosphere/include/stratosphere/os/os_waitable_manager.hpp deleted file mode 100644 index 8da05fdc..00000000 --- a/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/libstratosphere/include/stratosphere/os/os_waitable_types.hpp b/libstratosphere/include/stratosphere/os/os_waitable_types.hpp new file mode 100644 index 00000000..d4aa47fd --- /dev/null +++ b/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/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp b/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp new file mode 100644 index 00000000..7119d4ec --- /dev/null +++ b/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/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp b/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp index 4878a350..673df1e0 100644 --- a/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp +++ b/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/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_api.hpp b/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_api.hpp index 7a80c9f5..0b7c501e 100644 --- a/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_api.hpp +++ b/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/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp b/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp index 11af973c..fabff0f9 100644 --- a/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp +++ b/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/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp b/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp index 23278f8f..117af73c 100644 --- a/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp +++ b/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/libstratosphere/source/ams/ams_emummc_api.cpp b/libstratosphere/source/ams/ams_emummc_api.cpp index df18d5b3..f00f8653 100644 --- a/libstratosphere/source/ams/ams_emummc_api.cpp +++ b/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/libstratosphere/source/cfg/cfg_privileged_process.cpp b/libstratosphere/source/cfg/cfg_privileged_process.cpp index 87d9b378..b941208c 100644 --- a/libstratosphere/source/cfg/cfg_privileged_process.cpp +++ b/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/libstratosphere/source/cfg/cfg_sd_card.cpp b/libstratosphere/source/cfg/cfg_sd_card.cpp index 3c644a35..a79a23fa 100644 --- a/libstratosphere/source/cfg/cfg_sd_card.cpp +++ b/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/libstratosphere/source/fs/fs_memory_management.cpp b/libstratosphere/source/fs/fs_memory_management.cpp index ccf3f439..da4ba270 100644 --- a/libstratosphere/source/fs/fs_memory_management.cpp +++ b/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/libstratosphere/source/fs/fsa/fs_filesystem_accessor.cpp b/libstratosphere/source/fs/fsa/fs_filesystem_accessor.cpp index c8bf1199..10b2e3ec 100644 --- a/libstratosphere/source/fs/fsa/fs_filesystem_accessor.cpp +++ b/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/libstratosphere/source/fs/fsa/fs_mount_table.hpp b/libstratosphere/source/fs/fsa/fs_mount_table.hpp index 23145d84..e0034588 100644 --- a/libstratosphere/source/fs/fsa/fs_mount_table.hpp +++ b/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/libstratosphere/source/fssystem/fssystem_aes_xts_storage.cpp b/libstratosphere/source/fssystem/fssystem_aes_xts_storage.cpp index db19eceb..959725bd 100644 --- a/libstratosphere/source/fssystem/fssystem_aes_xts_storage.cpp +++ b/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/libstratosphere/source/fssystem/fssystem_directory_savedata_filesystem.cpp b/libstratosphere/source/fssystem/fssystem_directory_savedata_filesystem.cpp index f867917c..723cfd1c 100644 --- a/libstratosphere/source/fssystem/fssystem_directory_savedata_filesystem.cpp +++ b/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/libstratosphere/source/fssystem/fssystem_pooled_buffer.cpp b/libstratosphere/source/fssystem/fssystem_pooled_buffer.cpp index 7b224aa0..315d24e5 100644 --- a/libstratosphere/source/fssystem/fssystem_pooled_buffer.cpp +++ b/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/libstratosphere/source/hid/hid_api.cpp b/libstratosphere/source/hid/hid_api.cpp index e5d3bfc7..da4bc259 100644 --- a/libstratosphere/source/hid/hid_api.cpp +++ b/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/libstratosphere/source/hos/hos_version_api.cpp b/libstratosphere/source/hos/hos_version_api.cpp index c1bfc314..113ae1ca 100644 --- a/libstratosphere/source/hos/hos_version_api.cpp +++ b/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/libstratosphere/source/lmem/impl/lmem_impl_common_heap.cpp b/libstratosphere/source/lmem/impl/lmem_impl_common_heap.cpp index 3b58dd4a..7afc2d78 100644 --- a/libstratosphere/source/lmem/impl/lmem_impl_common_heap.cpp +++ b/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/libstratosphere/source/lmem/impl/lmem_impl_common_heap.hpp b/libstratosphere/source/lmem/impl/lmem_impl_common_heap.hpp index 0e254f4f..99018b64 100644 --- a/libstratosphere/source/lmem/impl/lmem_impl_common_heap.hpp +++ b/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/libstratosphere/source/lmem/lmem_exp_heap.cpp b/libstratosphere/source/lmem/lmem_exp_heap.cpp index 5d8e8352..60604043 100644 --- a/libstratosphere/source/lmem/lmem_exp_heap.cpp +++ b/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/libstratosphere/source/lmem/lmem_unit_heap.cpp b/libstratosphere/source/lmem/lmem_unit_heap.cpp index 3b613bb7..edc7c8cd 100644 --- a/libstratosphere/source/lmem/lmem_unit_heap.cpp +++ b/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/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_central.hpp b/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_central.hpp index c67bf839..327e029b 100644 --- a/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_central.hpp +++ b/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/libstratosphere/source/mem/impl/mem_impl_platform.os.horizon.cpp b/libstratosphere/source/mem/impl/mem_impl_platform.os.horizon.cpp index d1f7e663..11370ec5 100644 --- a/libstratosphere/source/mem/impl/mem_impl_platform.os.horizon.cpp +++ b/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/libstratosphere/source/ncm/ncm_install_task_base.cpp b/libstratosphere/source/ncm/ncm_install_task_base.cpp index 5032cf02..506e89fa 100644 --- a/libstratosphere/source/ncm/ncm_install_task_base.cpp +++ b/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/libstratosphere/source/ncm/ncm_placeholder_accessor.hpp b/libstratosphere/source/ncm/ncm_placeholder_accessor.hpp index 487e81f4..1d808445 100644 --- a/libstratosphere/source/ncm/ncm_placeholder_accessor.hpp +++ b/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/libstratosphere/source/os/impl/os_inter_process_event.cpp b/libstratosphere/source/os/impl/os_inter_process_event.cpp index 4db3e0b5..8ae89918 100644 --- a/libstratosphere/source/os/impl/os_inter_process_event.cpp +++ b/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/libstratosphere/source/os/impl/os_inter_process_event.hpp b/libstratosphere/source/os/impl/os_inter_process_event.hpp index 92f7c38a..0b5ee902 100644 --- a/libstratosphere/source/os/impl/os_inter_process_event.hpp +++ b/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/libstratosphere/source/os/impl/os_inter_process_event_impl.hpp b/libstratosphere/source/os/impl/os_inter_process_event_impl.hpp new file mode 100644 index 00000000..0a98a7a4 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_inter_process_event_impl.os.horizon.cpp b/libstratosphere/source/os/impl/os_inter_process_event_impl.os.horizon.cpp new file mode 100644 index 00000000..db40e5f0 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_inter_process_event_impl.os.horizon.hpp b/libstratosphere/source/os/impl/os_inter_process_event_impl.os.horizon.hpp new file mode 100644 index 00000000..1149e03d --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_internal_condition_variable_impl.os.horizon.cpp b/libstratosphere/source/os/impl/os_internal_condition_variable_impl.os.horizon.cpp new file mode 100644 index 00000000..c26f5756 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_internal_critical_section_impl.os.horizon.cpp b/libstratosphere/source/os/impl/os_internal_critical_section_impl.os.horizon.cpp new file mode 100644 index 00000000..effa13f5 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_interrupt_event_impl.hpp b/libstratosphere/source/os/impl/os_interrupt_event_impl.hpp new file mode 100644 index 00000000..842affff --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_interrupt_event_target_impl.os.horizon.cpp b/libstratosphere/source/os/impl/os_interrupt_event_target_impl.os.horizon.cpp new file mode 100644 index 00000000..f68187fb --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_interrupt_event_target_impl.os.horizon.hpp b/libstratosphere/source/os/impl/os_interrupt_event_target_impl.os.horizon.hpp new file mode 100644 index 00000000..c70f3522 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_mutex_impl.hpp b/libstratosphere/source/os/impl/os_mutex_impl.hpp new file mode 100644 index 00000000..d905e72e --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_resource_manager.hpp b/libstratosphere/source/os/impl/os_resource_manager.hpp index 61525e90..11e5209b 100644 --- a/libstratosphere/source/os/impl/os_resource_manager.hpp +++ b/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/libstratosphere/source/os/impl/os_rng_manager.hpp b/libstratosphere/source/os/impl/os_rng_manager.hpp index 7abc003f..387dcbe4 100644 --- a/libstratosphere/source/os/impl/os_rng_manager.hpp +++ b/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/libstratosphere/source/os/impl/os_rng_manager_impl.hpp b/libstratosphere/source/os/impl/os_rng_manager_impl.hpp index 25406633..b782edde 100644 --- a/libstratosphere/source/os/impl/os_rng_manager_impl.hpp +++ b/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/libstratosphere/source/os/impl/os_thread_manager.cpp b/libstratosphere/source/os/impl/os_thread_manager.cpp new file mode 100644 index 00000000..8fee951a --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_thread_manager.hpp b/libstratosphere/source/os/impl/os_thread_manager.hpp new file mode 100644 index 00000000..ae6c28e7 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.cpp b/libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.cpp new file mode 100644 index 00000000..3f3c9553 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.hpp b/libstratosphere/source/os/impl/os_thread_manager_impl.os.horizon.hpp new file mode 100644 index 00000000..b0e4f853 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_thread_manager_types.hpp b/libstratosphere/source/os/impl/os_thread_manager_types.hpp new file mode 100644 index 00000000..3209d1d7 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_timeout_helper.cpp b/libstratosphere/source/os/impl/os_timeout_helper.cpp new file mode 100644 index 00000000..41468ad9 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_timeout_helper.hpp b/libstratosphere/source/os/impl/os_timeout_helper.hpp new file mode 100644 index 00000000..7d128c8b --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_timeout_helper_impl.os.horizon.cpp b/libstratosphere/source/os/impl/os_timeout_helper_impl.os.horizon.cpp new file mode 100644 index 00000000..47ea439e --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_timeout_helper_impl.os.horizon.hpp b/libstratosphere/source/os/impl/os_timeout_helper_impl.os.horizon.hpp new file mode 100644 index 00000000..42ec0dd4 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_waitable_holder_base.hpp b/libstratosphere/source/os/impl/os_waitable_holder_base.hpp index d64fc1b1..c6c098f9 100644 --- a/libstratosphere/source/os/impl/os_waitable_holder_base.hpp +++ b/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/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp b/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp index 162acb13..25b3c02a 100644 --- a/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp +++ b/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/libstratosphere/source/os/impl/os_waitable_holder_of_event.hpp b/libstratosphere/source/os/impl/os_waitable_holder_of_event.hpp index 2e356e30..8b203c4a 100644 --- a/libstratosphere/source/os/impl/os_waitable_holder_of_event.hpp +++ b/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/libstratosphere/source/os/impl/os_waitable_holder_of_inter_process_event.hpp b/libstratosphere/source/os/impl/os_waitable_holder_of_inter_process_event.hpp index 99adfdde..3012da3e 100644 --- a/libstratosphere/source/os/impl/os_waitable_holder_of_inter_process_event.hpp +++ b/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/libstratosphere/source/os/impl/os_waitable_holder_of_interrupt_event.cpp b/libstratosphere/source/os/impl/os_waitable_holder_of_interrupt_event.cpp new file mode 100644 index 00000000..e553f131 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_waitable_holder_of_interrupt_event.hpp b/libstratosphere/source/os/impl/os_waitable_holder_of_interrupt_event.hpp index 66ae893a..10dcea93 100644 --- a/libstratosphere/source/os/impl/os_waitable_holder_of_interrupt_event.hpp +++ b/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/libstratosphere/source/os/impl/os_waitable_holder_of_message_queue.hpp b/libstratosphere/source/os/impl/os_waitable_holder_of_message_queue.hpp index 0beb9293..bc546839 100644 --- a/libstratosphere/source/os/impl/os_waitable_holder_of_message_queue.hpp +++ b/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/libstratosphere/source/os/impl/os_waitable_holder_of_semaphore.hpp b/libstratosphere/source/os/impl/os_waitable_holder_of_semaphore.hpp index 846b328e..bc42b421 100644 --- a/libstratosphere/source/os/impl/os_waitable_holder_of_semaphore.hpp +++ b/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/libstratosphere/source/os/impl/os_waitable_holder_of_thread.hpp b/libstratosphere/source/os/impl/os_waitable_holder_of_thread.hpp index 6212a0b7..19a10a89 100644 --- a/libstratosphere/source/os/impl/os_waitable_holder_of_thread.hpp +++ b/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/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp b/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp index 73691df5..4da762eb 100644 --- a/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp +++ b/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/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp b/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp index 0c068dd4..a014c4ff 100644 --- a/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp +++ b/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/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.cpp b/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.cpp new file mode 100644 index 00000000..2f4b9513 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.hpp b/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.hpp new file mode 100644 index 00000000..03918c95 --- /dev/null +++ b/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/libstratosphere/source/os/os_condition_variable.cpp b/libstratosphere/source/os/os_condition_variable.cpp new file mode 100644 index 00000000..4feec65c --- /dev/null +++ b/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/libstratosphere/source/os/os_event.cpp b/libstratosphere/source/os/os_event.cpp index bc1965d4..372e6fd6 100644 --- a/libstratosphere/source/os/os_event.cpp +++ b/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/libstratosphere/source/os/os_interrupt_event.cpp b/libstratosphere/source/os/os_interrupt_event.cpp index 87e4f77f..e9823193 100644 --- a/libstratosphere/source/os/os_interrupt_event.cpp +++ b/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/libstratosphere/source/os/os_message_queue.cpp b/libstratosphere/source/os/os_message_queue.cpp index 9421bcca..795ecf4a 100644 --- a/libstratosphere/source/os/os_message_queue.cpp +++ b/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/libstratosphere/source/os/os_mutex.cpp b/libstratosphere/source/os/os_mutex.cpp new file mode 100644 index 00000000..5b33bda1 --- /dev/null +++ b/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/libstratosphere/source/os/os_random.cpp b/libstratosphere/source/os/os_random.cpp index cd3962d2..b0bc60c5 100644 --- a/libstratosphere/source/os/os_random.cpp +++ b/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/libstratosphere/source/os/os_semaphore.cpp b/libstratosphere/source/os/os_semaphore.cpp index 64415467..e5569109 100644 --- a/libstratosphere/source/os/os_semaphore.cpp +++ b/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/libstratosphere/source/os/os_system_event.cpp b/libstratosphere/source/os/os_system_event.cpp index 40300196..829b98d7 100644 --- a/libstratosphere/source/os/os_system_event.cpp +++ b/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/libstratosphere/source/os/os_thread.cpp b/libstratosphere/source/os/os_thread.cpp new file mode 100644 index 00000000..1dcbb2de --- /dev/null +++ b/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/libstratosphere/source/os/os_waitable.cpp b/libstratosphere/source/os/os_waitable.cpp new file mode 100644 index 00000000..20870881 --- /dev/null +++ b/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/libstratosphere/source/os/os_waitable_holder.cpp b/libstratosphere/source/os/os_waitable_holder.cpp deleted file mode 100644 index c986b2e8..00000000 --- a/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/libstratosphere/source/os/os_waitable_manager.cpp b/libstratosphere/source/os/os_waitable_manager.cpp deleted file mode 100644 index 56f67fb8..00000000 --- a/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/libstratosphere/source/patcher/patcher_api.cpp b/libstratosphere/source/patcher/patcher_api.cpp index 74c7637f..c7ec0ab9 100644 --- a/libstratosphere/source/patcher/patcher_api.cpp +++ b/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/libstratosphere/source/pm/pm_info_api.cpp b/libstratosphere/source/pm/pm_info_api.cpp index 94f3da32..3c550d0f 100644 --- a/libstratosphere/source/pm/pm_info_api.cpp +++ b/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/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp b/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp index 7b9d1dcf..db84b5ab 100644 --- a/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp +++ b/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/libstratosphere/source/sf/hipc/sf_hipc_api.cpp b/libstratosphere/source/sf/hipc/sf_hipc_api.cpp index 3f6658a6..113c47fe 100644 --- a/libstratosphere/source/sf/hipc/sf_hipc_api.cpp +++ b/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/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp b/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp index 8d5ae412..85023c02 100644 --- a/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp +++ b/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/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp b/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp index a3de70ba..c763dbae 100644 --- a/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp +++ b/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/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp b/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp index 43ebb0c8..edec2ba3 100644 --- a/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp +++ b/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/libstratosphere/source/sm/sm_utils.cpp b/libstratosphere/source/sm/sm_utils.cpp index 3fe0475a..62d79f3c 100644 --- a/libstratosphere/source/sm/sm_utils.cpp +++ b/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/libstratosphere/source/sm/sm_utils.hpp b/libstratosphere/source/sm/sm_utils.hpp index 1db2bb6b..d3b4b153 100644 --- a/libstratosphere/source/sm/sm_utils.hpp +++ b/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/libvapours/include/vapours/svc/svc_common.hpp b/libvapours/include/vapours/svc/svc_common.hpp index 3e3850a0..9e4fc430 100644 --- a/libvapours/include/vapours/svc/svc_common.hpp +++ b/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/libvapours/include/vapours/svc/svc_types_common.hpp b/libvapours/include/vapours/svc/svc_types_common.hpp index 1fe27194..9ecb7501 100644 --- a/libvapours/include/vapours/svc/svc_types_common.hpp +++ b/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/libvapours/include/vapours/timespan.hpp b/libvapours/include/vapours/timespan.hpp index 51d755c3..201b0c21 100644 --- a/libvapours/include/vapours/timespan.hpp +++ b/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