mc.mitm: add basic functionality to run async code in a worker thread and receive a response

This commit is contained in:
ndeadly 2022-03-30 16:36:45 +11:00
parent 18c7453530
commit 1bb3594e24
3 changed files with 168 additions and 0 deletions

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<AsyncFunction>(reinterpret_cast<AsyncFunction *>(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<uintptr_t>(function));
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include <functional>
namespace ams::async {
using AsyncFunction = std::function<Result(void)>;
Result Initialize(void);
void Finalize(void);
void QueueWork(AsyncFunction *function);
#define MC_RUN_ASYNC(code) async::QueueWork(new async::AsyncFunction ([&]() -> ams::Result { code }));
}

View file

@ -0,0 +1,57 @@
#pragma once
#include <stratosphere.hpp>
namespace ams {
template <class T1, class T2, class T3>
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;
};
}