From 1e7a327a251648cb49a05cd3d0377afd137f6238 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 29 Sep 2021 13:24:03 -0700 Subject: [PATCH] os: implement LightSemaphore --- .../include/stratosphere/os.hpp | 2 +- .../stratosphere/os/os_light_semaphore.hpp | 72 +++++++ .../os/os_light_semaphore_api.hpp | 36 ++++ .../os/os_light_semaphore_types.hpp | 47 +++++ .../stratosphere/os/os_semaphore_types.hpp | 4 +- .../source/os/os_light_semaphore.cpp | 179 ++++++++++++++++++ .../source/os/os_semaphore.cpp | 11 +- 7 files changed, 346 insertions(+), 5 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/os/os_light_semaphore.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/os/os_light_semaphore_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/os/os_light_semaphore_types.hpp create mode 100644 libraries/libstratosphere/source/os/os_light_semaphore.cpp diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp index 516c52f62..48f33f894 100644 --- a/libraries/libstratosphere/include/stratosphere/os.hpp +++ b/libraries/libstratosphere/include/stratosphere/os.hpp @@ -46,5 +46,5 @@ #include #include #include -//#include +#include #include diff --git a/libraries/libstratosphere/include/stratosphere/os/os_light_semaphore.hpp b/libraries/libstratosphere/include/stratosphere/os/os_light_semaphore.hpp new file mode 100644 index 000000000..23f170db1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_light_semaphore.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 + +namespace ams::os { + + class LightSemaphore { + NON_COPYABLE(LightSemaphore); + NON_MOVEABLE(LightSemaphore); + private: + LightSemaphoreType m_sema; + public: + explicit LightSemaphore(s32 count, s32 max_count) { + InitializeLightSemaphore(std::addressof(m_sema), count, max_count); + } + + ~LightSemaphore() { FinalizeLightSemaphore(std::addressof(m_sema)); } + + void Acquire() { + return os::AcquireLightSemaphore(std::addressof(m_sema)); + } + + bool TryAcquire() { + return os::TryAcquireLightSemaphore(std::addressof(m_sema)); + } + + bool TimedAcquire(TimeSpan timeout) { + return os::TimedAcquireLightSemaphore(std::addressof(m_sema), timeout); + } + + void Release() { + return os::ReleaseLightSemaphore(std::addressof(m_sema)); + } + + void Release(s32 count) { + return os::ReleaseLightSemaphore(std::addressof(m_sema), count); + } + + s32 GetCurrentCount() const { + return os::GetCurrentLightSemaphoreCount(std::addressof(m_sema)); + } + + operator LightSemaphoreType &() { + return m_sema; + } + + operator const LightSemaphoreType &() const { + return m_sema; + } + + LightSemaphoreType *GetBase() { + return std::addressof(m_sema); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_light_semaphore_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_light_semaphore_api.hpp new file mode 100644 index 000000000..e20d6caac --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_light_semaphore_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 + +namespace ams::os { + + struct LightSemaphoreType; + + void InitializeLightSemaphore(LightSemaphoreType *sema, s32 count, s32 max_count); + void FinalizeLightSemaphore(LightSemaphoreType *sema); + + void AcquireLightSemaphore(LightSemaphoreType *sema); + bool TryAcquireLightSemaphore(LightSemaphoreType *sema); + bool TimedAcquireLightSemaphore(LightSemaphoreType *sema, TimeSpan timeout); + + void ReleaseLightSemaphore(LightSemaphoreType *sema); + void ReleaseLightSemaphore(LightSemaphoreType *sema, s32 count); + + s32 GetCurrentLightSemaphoreCount(const LightSemaphoreType *sema); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_light_semaphore_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_light_semaphore_types.hpp new file mode 100644 index 000000000..a21bb2e0d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_light_semaphore_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 +#include +#include + +namespace ams::os { + + namespace impl { + + using LightSemaphoreMutex = InternalBusyMutex; + using LightSemaphoreMutexStorage = InternalBusyMutexStorage; + + } + + struct LightSemaphoreType { + enum State { + State_NotInitialized = 0, + State_Initialized = 1, + }; + + u8 state; + s32 count; + s32 max_count; + + mutable impl::LightSemaphoreMutexStorage mutex; + impl::InternalLightEventStorage ev_not_zero; + }; + static_assert(std::is_trivial::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_semaphore_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_semaphore_types.hpp index 37d4b4d21..128736679 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_semaphore_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_semaphore_types.hpp @@ -35,8 +35,8 @@ namespace ams::os { util::TypedStorage waitlist; u8 state; - int count; - int max_count; + s32 count; + s32 max_count; impl::InternalCriticalSectionStorage cs_sema; impl::InternalConditionVariableStorage cv_not_zero; diff --git a/libraries/libstratosphere/source/os/os_light_semaphore.cpp b/libraries/libstratosphere/source/os/os_light_semaphore.cpp new file mode 100644 index 000000000..5f0ff642f --- /dev/null +++ b/libraries/libstratosphere/source/os/os_light_semaphore.cpp @@ -0,0 +1,179 @@ +/* + * 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_timeout_helper.hpp" + +namespace ams::os { + + void InitializeLightSemaphore(LightSemaphoreType *sema, s32 count, s32 max_count) { + /* Check pre-conditions. */ + AMS_ASSERT(max_count >= 1); + AMS_ASSERT(0 <= count && count <= max_count); + + /* Setup objects. */ + util::ConstructAt(sema->mutex); + util::ConstructAt(sema->ev_not_zero, count > 0); + + /* Set member variables. */ + sema->count = count; + sema->max_count = max_count; + + /* Mark initialized. */ + sema->state = LightSemaphoreType::State_Initialized; + } + + void FinalizeLightSemaphore(LightSemaphoreType *sema) { + /* Check pre-conditions. */ + AMS_ASSERT(sema->state == LightSemaphoreType::State_Initialized); + + /* Mark uninitialized. */ + sema->state = LightSemaphoreType::State_NotInitialized; + + /* Destroy objects. */ + util::DestroyAt(sema->mutex); + util::DestroyAt(sema->ev_not_zero); + } + + void AcquireLightSemaphore(LightSemaphoreType *sema) { + /* Check pre-conditions. */ + AMS_ASSERT(sema->state == LightSemaphoreType::State_Initialized); + + /* Repeatedly try to acquire the semaphore. */ + while (true) { + /* Try to acquire the semaphore. */ + { + /* Acquire exclusive access to the semaphore. */ + std::scoped_lock lk(util::GetReference(sema->mutex)); + + /* If we can, acquire. */ + if (sema->count > 0) { + --sema->count; + return; + } + + /* Ensure that we can wait once we're unlocked. */ + util::GetReference(sema->ev_not_zero).Clear(); + } + + /* Wait until we can try again. */ + util::GetReference(sema->ev_not_zero).WaitWithManualClear(); + } + } + + bool TryAcquireLightSemaphore(LightSemaphoreType *sema) { + /* Check pre-conditions. */ + AMS_ASSERT(sema->state == LightSemaphoreType::State_Initialized); + + /* Acquire exclusive access to the semaphore. */ + std::scoped_lock lk(util::GetReference(sema->mutex)); + + /* Try to acquire. */ + if (sema->count > 0) { + --sema->count; + return true; + } else { + return false; + } + } + + bool TimedAcquireLightSemaphore(LightSemaphoreType *sema, TimeSpan timeout) { + /* Check pre-conditions. */ + AMS_ASSERT(sema->state == LightSemaphoreType::State_Initialized); + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); + + /* Create timeout helper. */ + impl::TimeoutHelper timeout_helper(timeout); + + /* Repeatedly try to acquire the semaphore. */ + while (true) { + /* Try to acquire the semaphore. */ + { + /* Acquire exclusive access to the semaphore. */ + std::scoped_lock lk(util::GetReference(sema->mutex)); + + /* If we can, acquire. */ + if (sema->count > 0) { + --sema->count; + return true; + } + + /* Ensure that we can wait once we're unlocked. */ + util::GetReference(sema->ev_not_zero).Clear(); + } + + /* Check if we're timed out. */ + if (timeout_helper.TimedOut()) { + return false; + } + + /* Wait until we can try again. */ + util::GetReference(sema->ev_not_zero).TimedWaitWithManualClear(timeout_helper); + } + } + + void ReleaseLightSemaphore(LightSemaphoreType *sema) { + /* Check pre-conditions. */ + AMS_ASSERT(sema->state == LightSemaphoreType::State_Initialized); + + /* Release the semaphore. */ + { + /* Acquire exclusive access to the semaphore. */ + std::scoped_lock lk(util::GetReference(sema->mutex)); + + /* Check that we can release. */ + AMS_ASSERT(sema->count + 1 <= sema->max_count); + + /* Release. */ + ++sema->count; + } + + /* Signal that we released. */ + util::GetReference(sema->ev_not_zero).SignalWithManualClear(); + } + + void ReleaseLightSemaphore(LightSemaphoreType *sema, s32 count) { + /* Check pre-conditions. */ + AMS_ASSERT(sema->state == LightSemaphoreType::State_Initialized); + AMS_ASSERT(count >= 1); + + /* Release the semaphore. */ + { + /* Acquire exclusive access to the semaphore. */ + std::scoped_lock lk(util::GetReference(sema->mutex)); + + /* Check that we can release. */ + AMS_ASSERT(sema->count + count <= sema->max_count); + + /* Release. */ + sema->count += count; + } + + /* Signal that we released. */ + util::GetReference(sema->ev_not_zero).SignalWithManualClear(); + } + + s32 GetCurrentLightSemaphoreCount(const LightSemaphoreType *sema) { + /* Check pre-conditions. */ + AMS_ASSERT(sema->state == LightSemaphoreType::State_Initialized); + + /* Acquire exclusive access to the semaphore. */ + std::scoped_lock lk(util::GetReference(sema->mutex)); + + /* Get the count. */ + return sema->count; + } + +} diff --git a/libraries/libstratosphere/source/os/os_semaphore.cpp b/libraries/libstratosphere/source/os/os_semaphore.cpp index 3234c3e2e..3c99c755a 100644 --- a/libraries/libstratosphere/source/os/os_semaphore.cpp +++ b/libraries/libstratosphere/source/os/os_semaphore.cpp @@ -15,13 +15,14 @@ */ #include #include "impl/os_waitable_object_list.hpp" +#include "impl/os_waitable_holder_impl.hpp" #include "impl/os_timeout_helper.hpp" namespace ams::os { void InitializeSemaphore(SemaphoreType *sema, s32 count, s32 max_count) { AMS_ASSERT(max_count >= 1); - AMS_ASSERT(count >= 0); + AMS_ASSERT(0 <= count && count <= max_count); /* Setup objects. */ util::ConstructAt(sema->cs_sema); @@ -141,6 +142,12 @@ namespace ams::os { return sema->count; } - // void InitializeWaitableHolder(WaitableHolderType *waitable_holder, SemaphoreType *sema); + void InitializeWaitableHolder(WaitableHolderType *waitable_holder, SemaphoreType *sema) { + AMS_ASSERT(sema->state == SemaphoreType::State_Initialized); + + util::ConstructAt(GetReference(waitable_holder->impl_storage).holder_of_semaphore_storage, sema); + + waitable_holder->user_data = 0; + } }