From 1bb3594e24d8e25dbb43af4f39e4f680e56c9f94 Mon Sep 17 00:00:00 2001 From: ndeadly <24677491+ndeadly@users.noreply.github.com> Date: Wed, 30 Mar 2022 16:36:45 +1100 Subject: [PATCH] mc.mitm: add basic functionality to run async code in a worker thread and receive a response --- mc_mitm/source/async/async.cpp | 80 ++++++++++++++++++++++++ mc_mitm/source/async/async.hpp | 31 +++++++++ mc_mitm/source/async/future_response.hpp | 57 +++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 mc_mitm/source/async/async.cpp create mode 100644 mc_mitm/source/async/async.hpp create mode 100644 mc_mitm/source/async/future_response.hpp diff --git a/mc_mitm/source/async/async.cpp b/mc_mitm/source/async/async.cpp new file mode 100644 index 0000000..d785671 --- /dev/null +++ b/mc_mitm/source/async/async.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2020-2022 ndeadly + * + * 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 "async.hpp" + +namespace ams::async { + + namespace { + + const size_t ThreadCount = 1; + const size_t ThreadStackSize = 0x2000; + const s32 ThreadPriority = 10; + + alignas(os::MemoryPageSize) uint8_t g_thread_stacks[ThreadCount][ThreadStackSize]; + os::ThreadType g_thread_pool[ThreadCount]; + + const size_t MessageBufferSize = 10; + uintptr_t g_message_buffer[MessageBufferSize]; + os::MessageQueueType g_work_queue; + + void WorkerThreadFunc(void *) { + uintptr_t ptr; + for (;;) { + os::ReceiveMessageQueue(&ptr, &g_work_queue); + + // Convert pointer to work function back to correct type and claim ownership + auto work_func = std::unique_ptr(reinterpret_cast(ptr)); + + // Execute the work function + Result rc = (*work_func)(); + } + } + + } + + Result Initialize(void) { + os::InitializeMessageQueue(&g_work_queue, g_message_buffer, MessageBufferSize); + + for (unsigned int i = 0; i < ThreadCount; ++i) { + R_TRY(os::CreateThread(&g_thread_pool[i], + WorkerThreadFunc, + nullptr, + g_thread_stacks[i], + ThreadStackSize, + ThreadPriority + )); + + os::SetThreadNamePointer(&g_thread_pool[i], "mc::AsyncWorker"); + os::StartThread(&g_thread_pool[i]); + } + + return ams::ResultSuccess(); + } + + void Finalize(void) { + os::FinalizeMessageQueue(&g_work_queue); + + for (unsigned int i = 0; i < ThreadCount; ++i) { + os::DestroyThread(&g_thread_pool[i]); + } + + } + + void QueueWork(AsyncFunction *function) { + os::SendMessageQueue(&g_work_queue, reinterpret_cast(function)); + } + +} diff --git a/mc_mitm/source/async/async.hpp b/mc_mitm/source/async/async.hpp new file mode 100644 index 0000000..1e9d13a --- /dev/null +++ b/mc_mitm/source/async/async.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020-2022 ndeadly + * + * 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::async { + + using AsyncFunction = std::function; + + Result Initialize(void); + void Finalize(void); + + void QueueWork(AsyncFunction *function); + + #define MC_RUN_ASYNC(code) async::QueueWork(new async::AsyncFunction ([&]() -> ams::Result { code })); + +} diff --git a/mc_mitm/source/async/future_response.hpp b/mc_mitm/source/async/future_response.hpp new file mode 100644 index 0000000..c3341d6 --- /dev/null +++ b/mc_mitm/source/async/future_response.hpp @@ -0,0 +1,57 @@ +#pragma once +#include + +namespace ams { + + template + class FutureResponse { + public: + FutureResponse(T1 type) : m_type(type) { + os::InitializeEvent(&m_ready_event, false, os::EventClearMode_AutoClear); + } + + ~FutureResponse() { + os::FinalizeEvent(&m_ready_event); + } + + const T1& GetType() { + return m_type; + } + + void SetData(const T2& data) { + m_data = data; + os::SignalEvent(&m_ready_event); + } + + const T2& GetData() { + return m_data; + } + + void SetUserData(const T3& user_data) { + m_user_data = user_data; + } + + const T3& GetUserData() { + return m_user_data; + } + + void Wait(void) { + os::WaitEvent(&m_ready_event); + } + + bool TryWait(void) { + return os::TryWaitEvent(&m_ready_event); + } + + bool TimedWait(TimeSpan timeout) { + return os::TimedWaitEvent(&m_ready_event, timeout); + } + + private: + T1 m_type; + T2 m_data; + T3 m_user_data; + os::EventType m_ready_event; + }; + +}