From dd6c9e1de1e09ebf1d37c8e0f895b048bbf5933c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 25 Nov 2020 22:09:41 -0800 Subject: [PATCH] pf2: implement critical sections --- .../libvapours/include/vapours/prfile2.hpp | 1 + .../prfile2/pf/prfile2_pf_api_common.hpp | 1 + .../prfile2/prfile2_critical_section.hpp | 51 +++++++ .../vapours/prfile2/prfile2_system.hpp | 25 +++ .../prfile2/prfile2_critical_section.cpp | 143 ++++++++++++++++++ .../source/prfile2/prfile2_system.cpp | 48 ++++++ 6 files changed, 269 insertions(+) create mode 100644 libraries/libvapours/include/vapours/prfile2/prfile2_critical_section.hpp create mode 100644 libraries/libvapours/include/vapours/prfile2/prfile2_system.hpp create mode 100644 libraries/libvapours/source/prfile2/prfile2_critical_section.cpp create mode 100644 libraries/libvapours/source/prfile2/prfile2_system.cpp diff --git a/libraries/libvapours/include/vapours/prfile2.hpp b/libraries/libvapours/include/vapours/prfile2.hpp index e01bcd523..708804fe3 100644 --- a/libraries/libvapours/include/vapours/prfile2.hpp +++ b/libraries/libvapours/include/vapours/prfile2.hpp @@ -22,3 +22,4 @@ #include #include +#include diff --git a/libraries/libvapours/include/vapours/prfile2/pf/prfile2_pf_api_common.hpp b/libraries/libvapours/include/vapours/prfile2/pf/prfile2_pf_api_common.hpp index 41d54424e..c8f13b2b6 100644 --- a/libraries/libvapours/include/vapours/prfile2/pf/prfile2_pf_api_common.hpp +++ b/libraries/libvapours/include/vapours/prfile2/pf/prfile2_pf_api_common.hpp @@ -16,6 +16,7 @@ #pragma once #include #include +#include namespace ams::prfile2::pf { diff --git a/libraries/libvapours/include/vapours/prfile2/prfile2_critical_section.hpp b/libraries/libvapours/include/vapours/prfile2/prfile2_critical_section.hpp new file mode 100644 index 000000000..f73e4942f --- /dev/null +++ b/libraries/libvapours/include/vapours/prfile2/prfile2_critical_section.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 + +namespace ams::prfile2 { + + struct CriticalSection { + enum State { + State_NotInitialized = 0, + State_Initialized = 1, + }; + + struct Resource; + + u8 state; + int lock_count; + Resource *resource; + u64 owner; + }; + + void InitializeCriticalSection(CriticalSection *cs); + void FinalizeCriticalSection(CriticalSection *cs); + + void EnterCriticalSection(CriticalSection *cs); + void ExitCriticalSection(CriticalSection *cs); + + class ScopedCriticalSection { + private: + CriticalSection *cs; + public: + ALWAYS_INLINE ScopedCriticalSection(CriticalSection *c) : cs(c) { EnterCriticalSection(this->cs); } + ALWAYS_INLINE ScopedCriticalSection(CriticalSection &c) : ScopedCriticalSection(std::addressof(c)) { /* ... */ } + + ALWAYS_INLINE ~ScopedCriticalSection() { ExitCriticalSection(this->cs); } + }; + +} diff --git a/libraries/libvapours/include/vapours/prfile2/prfile2_system.hpp b/libraries/libvapours/include/vapours/prfile2/prfile2_system.hpp new file mode 100644 index 000000000..3f4798097 --- /dev/null +++ b/libraries/libvapours/include/vapours/prfile2/prfile2_system.hpp @@ -0,0 +1,25 @@ +/* + * 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::prfile2::system { + + void Initialize(); + + pf::Error GetCurrentContextId(u64 *out); + +} diff --git a/libraries/libvapours/source/prfile2/prfile2_critical_section.cpp b/libraries/libvapours/source/prfile2/prfile2_critical_section.cpp new file mode 100644 index 000000000..7ee99221b --- /dev/null +++ b/libraries/libvapours/source/prfile2/prfile2_critical_section.cpp @@ -0,0 +1,143 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif + +namespace ams::prfile2 { + + #if defined(AMS_PRFILE2_THREAD_SAFE) + + struct CriticalSection::Resource { + os::MutexType mutex; + bool in_use; + }; + + namespace { + + constexpr inline const auto NumCriticalSectionResources = 5 /* TODO: NumVolumes */; + + constinit os::SdkMutex g_crit_resource_mutex; + constinit CriticalSection::Resource g_crit_resources[NumCriticalSectionResources]; + + CriticalSection::Resource *AllocateCriticalSectionResource() { + std::scoped_lock lk(g_crit_resource_mutex); + + for (auto &resource : g_crit_resources) { + if (!resource.in_use) { + resource.in_use = true; + return std::addressof(resource); + } + } + + AMS_ABORT("Failed to allocate critical section resource"); + } + + void AcquireCriticalSectionResource(CriticalSection::Resource *resource) { + return os::LockMutex(std::addressof(resource->mutex)); + } + + void ReleaseCriticalSectionResource(CriticalSection::Resource *resource) { + return os::UnlockMutex(std::addressof(resource->mutex)); + } + + } + + void InitializeCriticalSection(CriticalSection *cs) { + /* Check pre-condition. */ + AMS_ASSERT(cs->state == CriticalSection::State_NotInitialized); + + /* Perform initialization. */ + if (cs->state == CriticalSection::State_NotInitialized) { + cs->resource = AllocateCriticalSectionResource(); + cs->owner = 0; + cs->state = CriticalSection::State_Initialized; + } + } + + void FinalizeCriticalSection(CriticalSection *cs) { + /* Check pre-condition. */ + AMS_ASSERT(cs->state == CriticalSection::State_Initialized); + + /* TODO */ + AMS_UNUSED(cs); + AMS_ABORT("prfile2::FinalizeCriticalSection"); + } + + void EnterCriticalSection(CriticalSection *cs) { + /* Check pre-condition. */ + AMS_ASSERT(cs->state == CriticalSection::State_Initialized); + + if (AMS_LIKELY(cs->lock_count == 0)) { + /* Acquire the lock .*/ + AcquireCriticalSectionResource(cs->resource); + system::GetCurrentContextId(std::addressof(cs->owner)); + } else { + /* Get the current context id. */ + u64 current_context_id; + system::GetCurrentContextId(std::addressof(current_context_id)); + + /* If the lock isn't already held, acquire it. */ + if (cs->owner != current_context_id) { + AcquireCriticalSectionResource(cs->resource); + cs->owner = current_context_id; + } + } + + /* Increment the lock count. */ + ++cs->lock_count; + } + + void ExitCriticalSection(CriticalSection *cs) { + /* Check pre-condition. */ + AMS_ASSERT(cs->state == CriticalSection::State_Initialized); + + /* Unlock. */ + if ((--cs->lock_count) == 0) { + cs->owner = 0; + ReleaseCriticalSectionResource(cs->resource); + } + } + + #else + + void InitializeCriticalSection(CriticalSection *cs) { + AMS_UNUSED(cs); + } + + void FinalizeCriticalSection(CriticalSection *cs) { + AMS_UNUSED(cs); + } + + void EnterCriticalSection(CriticalSection *cs) { + AMS_UNUSED(cs); + } + + void ExitCriticalSection(CriticalSection *cs) { + AMS_UNUSED(cs); + } + + #endif + + + +} diff --git a/libraries/libvapours/source/prfile2/prfile2_system.cpp b/libraries/libvapours/source/prfile2/prfile2_system.cpp new file mode 100644 index 000000000..26cfffa13 --- /dev/null +++ b/libraries/libvapours/source/prfile2/prfile2_system.cpp @@ -0,0 +1,48 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif + +namespace ams::prfile2::system { + + void Initialize() { + /* ... */ + } + + pf::Error GetCurrentContextId(u64 *out) { + /* Check that out isn't null. */ + if (out == nullptr) { + return static_cast(-2); + } + + /* Set the output. */ + #if defined(AMS_PRFILE2_THREAD_SAFE) + *out = reinterpret_cast(os::GetCurrentThread()); + #else + *out = 0; + #endif + + return pf::Error_Ok; + } + +}