From cbb0a084a6bfe423e8ec88296e594d5352611b71 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 18 Apr 2018 11:41:17 -0600 Subject: [PATCH] Stratosphere: Add IWaitable, WaitableManager --- stratosphere/loader/source/iserviceobject.hpp | 2 +- stratosphere/loader/source/iwaitable.hpp | 32 +++++++++ stratosphere/loader/source/serviceserver.cpp | 70 +++++++++++++++++- stratosphere/loader/source/serviceserver.hpp | 13 +++- stratosphere/loader/source/servicesession.cpp | 26 +++++++ stratosphere/loader/source/servicesession.hpp | 12 +++- .../loader/source/waitablemanager.cpp | 72 +++++++++++++++++++ .../loader/source/waitablemanager.hpp | 26 +++++++ 8 files changed, 244 insertions(+), 9 deletions(-) create mode 100644 stratosphere/loader/source/iwaitable.hpp create mode 100644 stratosphere/loader/source/waitablemanager.cpp create mode 100644 stratosphere/loader/source/waitablemanager.hpp diff --git a/stratosphere/loader/source/iserviceobject.hpp b/stratosphere/loader/source/iserviceobject.hpp index 61f7d65f0..053d19bda 100644 --- a/stratosphere/loader/source/iserviceobject.hpp +++ b/stratosphere/loader/source/iserviceobject.hpp @@ -3,5 +3,5 @@ class IServiceObject { public: - virtual Result dispatch(IpcParsedCommand *r, u32 *cmd_buf, u32 cmd_id, u32 *in_rawdata, u32 in_rawdata_size, u32 *out_rawdata, u32 *out_raw_data_count); + virtual Result dispatch(IpcParsedCommand *r, u32 *cmd_buf, u32 cmd_id, u32 *in_rawdata, u32 in_rawdata_size, u32 *out_rawdata, u32 *out_raw_data_count) = 0; }; \ No newline at end of file diff --git a/stratosphere/loader/source/iwaitable.hpp b/stratosphere/loader/source/iwaitable.hpp new file mode 100644 index 000000000..e090298fc --- /dev/null +++ b/stratosphere/loader/source/iwaitable.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +class IWaitable { + u64 wait_priority = 0; + IWaitable *parent_waitable; + public: + virtual ~IWaitable(); + virtual unsigned int get_num_waitables() = 0; + virtual void get_waitables(IWaitable **dst) = 0; + virtual void delete_child(IWaitable *child) = 0; + virtual Handle get_handle() = 0; + virtual Result handle_signaled() = 0; + + bool has_parent() { + return this->parent_waitable != NULL; + } + + IWaitable *get_parent() { + return this->parent_waitable; + } + + void update_priority() { + static u64 g_cur_priority = 0; + this->wait_priority = g_cur_priority++; + } + + static bool compare(IWaitable *a, IWaitable *b) { + return (a->wait_priority < b->wait_priority); + } +}; \ No newline at end of file diff --git a/stratosphere/loader/source/serviceserver.cpp b/stratosphere/loader/source/serviceserver.cpp index 9e428b262..7199f7cfd 100644 --- a/stratosphere/loader/source/serviceserver.cpp +++ b/stratosphere/loader/source/serviceserver.cpp @@ -11,6 +11,7 @@ ServiceServer::ServiceServer(const char *service_name, unsigned int max_s) : for (unsigned int i = 0; i < this->max_sessions; i++) { this->sessions[i] = NULL; } + this->num_sessions = 0; } template @@ -28,8 +29,71 @@ ServiceServer::~ServiceServer() { } } +/* IWaitable functions. */ template -Result ServiceServer::process() { - /* TODO */ - return 0; +unsigned int ServiceServer::get_num_waitables() { + unsigned int n = 1; + for (unsigned int i = 0; i < this->max_sessions; i++) { + if (this->sessions[i]) { + n += this->sessions[i]->get_num_waitables(); + } + } + return n; +} + +template +void ServiceServer::get_waitables(IWaitable **dst) { + dst[0] = this; + unsigned int n = 0; + for (unsigned int i = 0; i < this->max_sessions; i++) { + if (this->sessions[i]) { + this->sessions[i]->get_waitables(&dst[1 + n]); + n += this->sessions[i]->get_num_waitables(); + } + } +} + +template +void ServiceSession::delete_child(IWaitable *child) { + unsigned int i; + for (i = 0; i < this->max_sessions; i++) { + if (this->sessions[i] == child) { + break; + } + } + + if (i == this->max_sessions) { + /* TODO: Panic, because this isn't our child. */ + } else { + delete this->sessions[i]; + this->sessions[i] = NULL; + this->num_sessions--; + } +} + +template +Handle ServiceServer::get_handle() { + return this->port_handle; +} + +template +Result ServiceServer::handle_signaled() { + /* If this server's port was signaled, accept a new session. */ + Handle session_h; + svcAcceptSession(&session_h, this->port_handle); + + if (this->num_sessions >= this->max_sessions) { + svcCloseHandle(session_h); + return 0x10601; + } + + unsigned int i; + for (i = 0; i < this->max_sessions; i++) { + if (this->sessions[i] = NULL) { + break; + } + } + + this->sessions[i] = new ServiceSession(this, session_h, 0); + this->num_sessions++; } \ No newline at end of file diff --git a/stratosphere/loader/source/serviceserver.hpp b/stratosphere/loader/source/serviceserver.hpp index 817e2b127..d0469e1d7 100644 --- a/stratosphere/loader/source/serviceserver.hpp +++ b/stratosphere/loader/source/serviceserver.hpp @@ -3,22 +3,29 @@ #include #include "iserviceobject.hpp" +#include "iwaitable.hpp" #include "servicesession.hpp" template class ServiceSession; template -class ServiceServer { +class ServiceServer : IWaitable { static_assert(std::is_base_of::value, "Service Objects must derive from IServiceObject"); Handle port_handle; unsigned int max_sessions; + unsigned int num_sessions; ServiceSession **sessions; public: ServiceServer(const char *service_name, unsigned int max_s); - ~ServiceServer(); - Result process(); + virtual ~ServiceServer(); + /* IWaitable */ + virtual unsigned int get_num_waitables(); + virtual void get_waitables(IWaitable **dst); + virtual void delete_child(IWaitable *child); + virtual Handle get_handle(); + virtual Result handle_signaled(); }; \ No newline at end of file diff --git a/stratosphere/loader/source/servicesession.cpp b/stratosphere/loader/source/servicesession.cpp index 550eafa8f..36e0c4abf 100644 --- a/stratosphere/loader/source/servicesession.cpp +++ b/stratosphere/loader/source/servicesession.cpp @@ -2,3 +2,29 @@ #include "servicesession.hpp" +/* IWaitable functions. */ +template +unsigned int ServiceSession::get_num_waitables() { + return 1; +} + +template +void ServiceSession::get_waitables(IWaitable **dst) { + dst[0] = this; +} + +template +void ServiceSession::delete_child(IWaitable *child) { + /* TODO: Panic, because we can never have any children. */ +} + +template +Handle ServiceSession::get_handle() { + return this->server_handle; +} + +template +Result ServiceSession::handle_signaled() { + /* TODO */ + return 0; +} \ No newline at end of file diff --git a/stratosphere/loader/source/servicesession.hpp b/stratosphere/loader/source/servicesession.hpp index 76cbc508e..84e6beead 100644 --- a/stratosphere/loader/source/servicesession.hpp +++ b/stratosphere/loader/source/servicesession.hpp @@ -3,13 +3,14 @@ #include #include "iserviceobject.hpp" +#include "iwaitable.hpp" #include "serviceserver.hpp" template class ServiceServer; template -class ServiceSession { +class ServiceSession : IWaitable { static_assert(std::is_base_of::value, "Service Objects must derive from IServiceObject"); T *service_object; @@ -21,7 +22,7 @@ class ServiceSession { this->service_object = new T(); } - ~ServiceSession() { + virtual ~ServiceSession() { delete this->service_object; if (server_handle) { svcCloseHandle(server_handle); @@ -34,4 +35,11 @@ class ServiceSession { T *get_service_object() { return this->service_object; } Handle get_server_handle() { return this->server_handle; } Handle get_client_handle() { return this->client_handle; } + + /* IWaitable */ + virtual unsigned int get_num_waitables(); + virtual void get_waitables(IWaitable **dst); + virtual void delete_child(IWaitable *child); + virtual Handle get_handle(); + virtual Result handle_signaled(); }; \ No newline at end of file diff --git a/stratosphere/loader/source/waitablemanager.cpp b/stratosphere/loader/source/waitablemanager.cpp new file mode 100644 index 000000000..386ea6ec9 --- /dev/null +++ b/stratosphere/loader/source/waitablemanager.cpp @@ -0,0 +1,72 @@ +#include + +#include + +#include "waitablemanager.hpp" + + +unsigned int WaitableManager::get_num_signalable() { + unsigned int n = 0; + for (auto & waitable : this->waitables) { + n += waitable->get_num_waitables(); + } + return n; +} + +void WaitableManager::add_waitable(IWaitable *waitable) { + this->waitables.push_back(waitable); +} + +void WaitableManager::process() { + std::vector signalables; + std::vector handles; + + int handle_index = 0; + Result rc; + + while (1) { + /* Create vector of signalable items. */ + signalables.resize(this->get_num_signalable(), NULL); + unsigned int n = 0; + for (auto & waitable : this->waitables) { + waitable->get_waitables(signalables.data() + n); + n += waitable->get_num_waitables(); + } + + /* Sort signalables by priority. */ + std::sort(signalables.begin(), signalables.end(), IWaitable::compare); + + /* Copy out handles. */ + handles.resize(signalables.size()); + std::transform(signalables.begin(), signalables.end(), handles.begin(), [](IWaitable *w) { return w->get_handle(); }); + + rc = svcWaitSynchronization(&handle_index, handles.data(), handles.size(), this->timeout); + if (R_SUCCEEDED(rc)) { + /* Handle a signaled waitable. */ + /* TODO: What should be done with the result here? */ + signalables[handle_index]->handle_signaled(); + } else if (rc == 0xEA01) { + /* Timeout. */ + for (auto & waitable : signalables) { + waitable->update_priority(); + } + } else if (rc == 0xF601) { + /* handles[handle_index] was closed! */ + + /* Close the handle. */ + svcCloseHandle(handles[handle_index]); + + /* If relevant, remove from waitables. */ + this->waitables.erase(std::remove(this->waitables.begin(), this->waitables.end(), signalables[handle_index]), this->waitables.end()); + + /* Delete it. */ + if (signalables[handle_index]->has_parent()) { + signalables[handle_index]->get_parent()->delete_child(signalables[handle_index]); + } else { + delete signalables[handle_index]; + } + } else { + /* TODO: Panic. When can this happen? */ + } + } +} \ No newline at end of file diff --git a/stratosphere/loader/source/waitablemanager.hpp b/stratosphere/loader/source/waitablemanager.hpp new file mode 100644 index 000000000..f71ff0669 --- /dev/null +++ b/stratosphere/loader/source/waitablemanager.hpp @@ -0,0 +1,26 @@ +#pragma once +#include +#include + +#include "iwaitable.hpp" + +class WaitableManager { + std::vector waitables; + + u64 timeout; + + public: + WaitableManager(u64 t) : waitables(0), timeout(t) { } + ~WaitableManager() { + /* This should call the destructor for every waitable. */ + for (auto & waitable : waitables) { + delete waitable; + } + waitables.clear(); + } + + unsigned int get_num_signalable(); + void add_waitable(IWaitable *waitable); + void delete_waitable(IWaitable *waitable); + void process(); +}; \ No newline at end of file