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;
+ };
+
+}