From b8a1ebd11a20f40f4d8205e204ac2ce83dbab4f0 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 29 Sep 2021 12:55:52 -0700 Subject: [PATCH] os: implement LightMessageQueue --- .../include/stratosphere/os.hpp | 2 +- .../os/os_light_message_queue.hpp | 100 ++++++ .../os/os_light_message_queue_api.hpp | 47 +++ .../os/os_light_message_queue_types.hpp | 50 +++ .../source/os/os_light_message_queue.cpp | 320 ++++++++++++++++++ 5 files changed, 518 insertions(+), 1 deletion(-) create mode 100644 libraries/libstratosphere/include/stratosphere/os/os_light_message_queue.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/os/os_light_message_queue_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/os/os_light_message_queue_types.hpp create mode 100644 libraries/libstratosphere/source/os/os_light_message_queue.cpp diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp index c24591d7e..516c52f62 100644 --- a/libraries/libstratosphere/include/stratosphere/os.hpp +++ b/libraries/libstratosphere/include/stratosphere/os.hpp @@ -45,6 +45,6 @@ #include #include #include -//#include +#include //#include #include diff --git a/libraries/libstratosphere/include/stratosphere/os/os_light_message_queue.hpp b/libraries/libstratosphere/include/stratosphere/os/os_light_message_queue.hpp new file mode 100644 index 000000000..df1fca21b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_light_message_queue.hpp @@ -0,0 +1,100 @@ +/* + * 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 LightMessageQueue { + NON_COPYABLE(LightMessageQueue); + NON_MOVEABLE(LightMessageQueue); + private: + LightMessageQueueType m_mq; + public: + explicit LightMessageQueue(uintptr_t *buf, size_t count) { + InitializeLightMessageQueue(std::addressof(m_mq), buf, count); + } + + ~LightMessageQueue() { FinalizeLightMessageQueue(std::addressof(m_mq)); } + + /* Sending (FIFO functionality) */ + void Send(uintptr_t data) { + return SendLightMessageQueue(std::addressof(m_mq), data); + } + + bool TrySend(uintptr_t data) { + return TrySendLightMessageQueue(std::addressof(m_mq), data); + } + + bool TimedSend(uintptr_t data, TimeSpan timeout) { + return TimedSendLightMessageQueue(std::addressof(m_mq), data, timeout); + } + + /* Jamming (LIFO functionality) */ + void Jam(uintptr_t data) { + return JamLightMessageQueue(std::addressof(m_mq), data); + } + + bool TryJam(uintptr_t data) { + return TryJamLightMessageQueue(std::addressof(m_mq), data); + } + + bool TimedJam(uintptr_t data, TimeSpan timeout) { + return TimedJamLightMessageQueue(std::addressof(m_mq), data, timeout); + } + + /* Receive functionality */ + void Receive(uintptr_t *out) { + return ReceiveLightMessageQueue(out, std::addressof(m_mq)); + } + + bool TryReceive(uintptr_t *out) { + return TryReceiveLightMessageQueue(out, std::addressof(m_mq)); + } + + bool TimedReceive(uintptr_t *out, TimeSpan timeout) { + return TimedReceiveLightMessageQueue(out, std::addressof(m_mq), timeout); + } + + /* Peek functionality */ + void Peek(uintptr_t *out) const { + return PeekLightMessageQueue(out, std::addressof(m_mq)); + } + + bool TryPeek(uintptr_t *out) const { + return TryPeekLightMessageQueue(out, std::addressof(m_mq)); + } + + bool TimedPeek(uintptr_t *out, TimeSpan timeout) const { + return TimedPeekLightMessageQueue(out, std::addressof(m_mq), timeout); + } + + operator LightMessageQueueType &() { + return m_mq; + } + + operator const LightMessageQueueType &() const { + return m_mq; + } + + LightMessageQueueType *GetBase() { + return std::addressof(m_mq); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_light_message_queue_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_light_message_queue_api.hpp new file mode 100644 index 000000000..aca768696 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_light_message_queue_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 + +namespace ams::os { + + struct LightMessageQueueType; + + void InitializeLightMessageQueue(LightMessageQueueType *mq, uintptr_t *buffer, size_t count); + void FinalizeLightMessageQueue(LightMessageQueueType *mq); + + /* Sending (FIFO functionality) */ + void SendLightMessageQueue(LightMessageQueueType *mq, uintptr_t data); + bool TrySendLightMessageQueue(LightMessageQueueType *mq, uintptr_t data); + bool TimedSendLightMessageQueue(LightMessageQueueType *mq, uintptr_t data, TimeSpan timeout); + + /* Jamming (LIFO functionality) */ + void JamLightMessageQueue(LightMessageQueueType *mq, uintptr_t data); + bool TryJamLightMessageQueue(LightMessageQueueType *mq, uintptr_t data); + bool TimedJamLightMessageQueue(LightMessageQueueType *mq, uintptr_t data, TimeSpan timeout); + + /* Receive functionality */ + void ReceiveLightMessageQueue(uintptr_t *out, LightMessageQueueType *mq); + bool TryReceiveLightMessageQueue(uintptr_t *out, LightMessageQueueType *mq); + bool TimedReceiveLightMessageQueue(uintptr_t *out, LightMessageQueueType *mq, TimeSpan timeout); + + /* Peek functionality */ + void PeekLightMessageQueue(uintptr_t *out, const LightMessageQueueType *mq); + bool TryPeekLightMessageQueue(uintptr_t *out, const LightMessageQueueType *mq); + bool TimedPeekLightMessageQueue(uintptr_t *out, const LightMessageQueueType *mq, TimeSpan timeout); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_light_message_queue_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_light_message_queue_types.hpp new file mode 100644 index 000000000..4bf955c33 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_light_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 +#include + +namespace ams::os { + + namespace impl { + + using LightMessageQueueMutex = InternalBusyMutex; + using LightMessageQueueMutexStorage = InternalBusyMutexStorage; + + } + + struct LightMessageQueueType { + enum State { + State_NotInitialized = 0, + State_Initialized = 1, + }; + + uintptr_t *buffer; + s32 capacity; + s32 count; + s32 offset; + u8 state; + + mutable impl::LightMessageQueueMutexStorage mutex_queue; + mutable impl::InternalLightEventStorage ev_not_full; + mutable impl::InternalLightEventStorage ev_not_empty; + }; + static_assert(std::is_trivial::value); + +} diff --git a/libraries/libstratosphere/source/os/os_light_message_queue.cpp b/libraries/libstratosphere/source/os/os_light_message_queue.cpp new file mode 100644 index 000000000..cecf587db --- /dev/null +++ b/libraries/libstratosphere/source/os/os_light_message_queue.cpp @@ -0,0 +1,320 @@ +/* + * 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" +#include "impl/os_message_queue_helper.hpp" + +namespace ams::os { + + namespace { + + using MessageQueueHelper = impl::MessageQueueHelper; + + template + bool TryEnqueueLightMessageQueueImpl(LightMessageQueueType *mq, uintptr_t data) { + /* Perform the enqueue. */ + { + /* Acquire exclusive access to the queue. */ + std::scoped_lock lk(util::GetReference(mq->mutex_queue)); + + /* Check that we can enqueue. */ + if (MessageQueueHelper::IsMessageQueueFull(mq)) { + /* If we should, clear the event. */ + if constexpr (ClearEvent) { + util::GetReference(mq->ev_not_full).Clear(); + } + + /* We can't enqueue because we're full. */ + return false; + } + + /* Enqueue the data. */ + EnqueueFunction(mq, data); + } + + /* Signal that we're not empty. */ + util::GetReference(mq->ev_not_empty).SignalWithManualClear(); + + return true; + } + + template + bool TrySendLightMessageQueueImpl(LightMessageQueueType *mq, uintptr_t data) { + return TryEnqueueLightMessageQueueImpl(mq, data); + } + + template + bool TryJamLightMessageQueueImpl(LightMessageQueueType *mq, uintptr_t data) { + return TryEnqueueLightMessageQueueImpl(mq, data); + } + + template + bool TryReceiveLightMessageQueueImpl(uintptr_t *out, LightMessageQueueType *mq) { + /* Perform the receive. */ + { + /* Acquire exclusive access to the queue. */ + std::scoped_lock lk(util::GetReference(mq->mutex_queue)); + + /* Check that we can receive. */ + if (MessageQueueHelper::IsMessageQueueEmpty(mq)) { + /* If we should, clear the event. */ + if constexpr (ClearEvent) { + util::GetReference(mq->ev_not_empty).Clear(); + } + + /* We can't enqueue because we're full. */ + return false; + } + + /* Receive the data. */ + *out = MessageQueueHelper::DequeueUnsafe(mq); + } + + /* Signal that we're not full. */ + util::GetReference(mq->ev_not_full).SignalWithManualClear(); + + return true; + } + + template + bool TryPeekLightMessageQueueImpl(uintptr_t *out, const LightMessageQueueType *mq) { + /* Perform the peek. */ + { + /* Acquire exclusive access to the queue. */ + std::scoped_lock lk(util::GetReference(mq->mutex_queue)); + + /* Check that we can peek. */ + if (MessageQueueHelper::IsMessageQueueEmpty(mq)) { + /* If we should, clear the event. */ + if constexpr (ClearEvent) { + util::GetReference(mq->ev_not_empty).Clear(); + } + + /* We can't enqueue because we're full. */ + return false; + } + + /* Peek the data. */ + *out = MessageQueueHelper::PeekUnsafe(mq); + } + + return true; + } + + } + + void InitializeLightMessageQueue(LightMessageQueueType *mq, uintptr_t *buffer, size_t count) { + /* Check pre-conditions. */ + AMS_ASSERT(buffer != nullptr); + AMS_ASSERT(count >= 1); + + /* Construct objects. */ + util::ConstructAt(mq->mutex_queue); + util::ConstructAt(mq->ev_not_empty, false); + util::ConstructAt(mq->ev_not_full, true); + + /* Set member variables. */ + mq->buffer = buffer; + mq->capacity = static_cast(count); + mq->count = 0; + mq->offset = 0; + + /* Mark initialized. */ + mq->state = LightMessageQueueType::State_Initialized; + } + + void FinalizeLightMessageQueue(LightMessageQueueType *mq) { + /* Check pre-conditions. */ + AMS_ASSERT(mq->state == LightMessageQueueType::State_Initialized); + + /* Mark uninitialized. */ + mq->state = LightMessageQueueType::State_NotInitialized; + + /* Destroy objects. */ + util::DestroyAt(mq->ev_not_empty); + util::DestroyAt(mq->ev_not_full); + util::DestroyAt(mq->mutex_queue); + } + + /* Sending (FIFO functionality) */ + void SendLightMessageQueue(LightMessageQueueType *mq, uintptr_t data) { + /* Check pre-conditions. */ + AMS_ASSERT(mq->state == LightMessageQueueType::State_Initialized); + + /* Repeatedly try to send. */ + while (!TrySendLightMessageQueueImpl(mq, data)) { + /* Wait until we can try to send again. */ + util::GetReference(mq->ev_not_full).WaitWithManualClear(); + } + } + + bool TrySendLightMessageQueue(LightMessageQueueType *mq, uintptr_t data) { + /* Check pre-conditions. */ + AMS_ASSERT(mq->state == LightMessageQueueType::State_Initialized); + + /* Try to send. */ + return TrySendLightMessageQueueImpl(mq, data); + } + + bool TimedSendLightMessageQueue(LightMessageQueueType *mq, uintptr_t data, TimeSpan timeout) { + /* Check pre-conditions. */ + AMS_ASSERT(mq->state == LightMessageQueueType::State_Initialized); + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); + + /* Create timeout helper. */ + impl::TimeoutHelper timeout_helper(timeout); + + /* Repeatedly try to send. */ + while (!TrySendLightMessageQueueImpl(mq, data)) { + /* Check if we're timed out. */ + if (timeout_helper.TimedOut()) { + return false; + } + + /* Wait until we can try to send again. */ + util::GetReference(mq->ev_not_full).TimedWaitWithManualClear(timeout_helper); + } + + return true; + } + + /* Jamming (LIFO functionality) */ + void JamLightMessageQueue(LightMessageQueueType *mq, uintptr_t data) { + /* Check pre-conditions. */ + AMS_ASSERT(mq->state == LightMessageQueueType::State_Initialized); + + /* Repeatedly try to jam. */ + while (!TryJamLightMessageQueueImpl(mq, data)) { + /* Wait until we can try to jam again. */ + util::GetReference(mq->ev_not_full).WaitWithManualClear(); + } + } + + bool TryJamLightMessageQueue(LightMessageQueueType *mq, uintptr_t data) { + /* Check pre-conditions. */ + AMS_ASSERT(mq->state == LightMessageQueueType::State_Initialized); + + /* Try to jam. */ + return TryJamLightMessageQueueImpl(mq, data); + } + + bool TimedJamLightMessageQueue(LightMessageQueueType *mq, uintptr_t data, TimeSpan timeout) { + /* Check pre-conditions. */ + AMS_ASSERT(mq->state == LightMessageQueueType::State_Initialized); + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); + + /* Create timeout helper. */ + impl::TimeoutHelper timeout_helper(timeout); + + /* Repeatedly try to jam. */ + while (!TryJamLightMessageQueueImpl(mq, data)) { + /* Check if we're timed out. */ + if (timeout_helper.TimedOut()) { + return false; + } + + /* Wait until we can try to jam again. */ + util::GetReference(mq->ev_not_full).TimedWaitWithManualClear(timeout_helper); + } + + return true; + } + + /* Receive functionality */ + void ReceiveLightMessageQueue(uintptr_t *out, LightMessageQueueType *mq) { + /* Check pre-conditions. */ + AMS_ASSERT(mq->state == LightMessageQueueType::State_Initialized); + + /* Repeatedly try to receive. */ + while (!TryReceiveLightMessageQueueImpl(out, mq)) { + /* Wait until we can try to receive again. */ + util::GetReference(mq->ev_not_empty).WaitWithManualClear(); + } + } + + bool TryReceiveLightMessageQueue(uintptr_t *out, LightMessageQueueType *mq) { + /* Check pre-conditions. */ + AMS_ASSERT(mq->state == LightMessageQueueType::State_Initialized); + + /* Try to receive. */ + return TryReceiveLightMessageQueueImpl(out, mq); + } + + bool TimedReceiveLightMessageQueue(uintptr_t *out, LightMessageQueueType *mq, TimeSpan timeout) { + /* Check pre-conditions. */ + AMS_ASSERT(mq->state == LightMessageQueueType::State_Initialized); + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); + + /* Create timeout helper. */ + impl::TimeoutHelper timeout_helper(timeout); + + /* Repeatedly try to receive. */ + while (!TryReceiveLightMessageQueueImpl(out, mq)) { + /* Check if we're timed out. */ + if (timeout_helper.TimedOut()) { + return false; + } + + /* Wait until we can try to receive again. */ + util::GetReference(mq->ev_not_empty).TimedWaitWithManualClear(timeout_helper); + } + + return true; + } + + /* Peek functionality */ + void PeekLightMessageQueue(uintptr_t *out, const LightMessageQueueType *mq) { + /* Check pre-conditions. */ + AMS_ASSERT(mq->state == LightMessageQueueType::State_Initialized); + + /* Repeatedly try to peek. */ + while (!TryPeekLightMessageQueueImpl(out, mq)) { + /* Wait until we can try to peek again. */ + util::GetReference(mq->ev_not_empty).WaitWithManualClear(); + } + } + + bool TryPeekLightMessageQueue(uintptr_t *out, const LightMessageQueueType *mq) { + /* Check pre-conditions. */ + AMS_ASSERT(mq->state == LightMessageQueueType::State_Initialized); + + /* Try to peek. */ + return TryPeekLightMessageQueueImpl(out, mq); + } + + bool TimedPeekLightMessageQueue(uintptr_t *out, const LightMessageQueueType *mq, TimeSpan timeout) { + /* Check pre-conditions. */ + AMS_ASSERT(mq->state == LightMessageQueueType::State_Initialized); + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); + + /* Create timeout helper. */ + impl::TimeoutHelper timeout_helper(timeout); + + /* Repeatedly try to peek. */ + while (!TryPeekLightMessageQueueImpl(out, mq)) { + /* Check if we're timed out. */ + if (timeout_helper.TimedOut()) { + return false; + } + + /* Wait until we can try to peek again. */ + util::GetReference(mq->ev_not_empty).TimedWaitWithManualClear(timeout_helper); + } + + return true; + } + +}