Stratosphere: Add IWaitable, WaitableManager

This commit is contained in:
Michael Scire 2018-04-18 11:41:17 -06:00
parent 8e25534912
commit cbb0a084a6
8 changed files with 244 additions and 9 deletions

View File

@ -3,5 +3,5 @@
class IServiceObject { class IServiceObject {
public: 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;
}; };

View File

@ -0,0 +1,32 @@
#pragma once
#include <switch.h>
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);
}
};

View File

@ -11,6 +11,7 @@ ServiceServer<T>::ServiceServer(const char *service_name, unsigned int max_s) :
for (unsigned int i = 0; i < this->max_sessions; i++) { for (unsigned int i = 0; i < this->max_sessions; i++) {
this->sessions[i] = NULL; this->sessions[i] = NULL;
} }
this->num_sessions = 0;
} }
template <typename T> template <typename T>
@ -28,8 +29,71 @@ ServiceServer<T>::~ServiceServer() {
} }
} }
/* IWaitable functions. */
template <typename T> template <typename T>
Result ServiceServer<T>::process() { unsigned int ServiceServer<T>::get_num_waitables() {
/* TODO */ unsigned int n = 1;
return 0; for (unsigned int i = 0; i < this->max_sessions; i++) {
if (this->sessions[i]) {
n += this->sessions[i]->get_num_waitables();
}
}
return n;
}
template <typename T>
void ServiceServer<T>::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 <typename T>
void ServiceSession<T>::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 <typename T>
Handle ServiceServer<T>::get_handle() {
return this->port_handle;
}
template <typename T>
Result ServiceServer<T>::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<T>(this, session_h, 0);
this->num_sessions++;
} }

View File

@ -3,22 +3,29 @@
#include <type_traits> #include <type_traits>
#include "iserviceobject.hpp" #include "iserviceobject.hpp"
#include "iwaitable.hpp"
#include "servicesession.hpp" #include "servicesession.hpp"
template <typename T> template <typename T>
class ServiceSession; class ServiceSession;
template <typename T> template <typename T>
class ServiceServer { class ServiceServer : IWaitable {
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject"); static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
Handle port_handle; Handle port_handle;
unsigned int max_sessions; unsigned int max_sessions;
unsigned int num_sessions;
ServiceSession<T> **sessions; ServiceSession<T> **sessions;
public: public:
ServiceServer(const char *service_name, unsigned int max_s); ServiceServer(const char *service_name, unsigned int max_s);
~ServiceServer(); virtual ~ServiceServer();
Result process();
/* 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();
}; };

View File

@ -2,3 +2,29 @@
#include "servicesession.hpp" #include "servicesession.hpp"
/* IWaitable functions. */
template <typename T>
unsigned int ServiceSession<T>::get_num_waitables() {
return 1;
}
template <typename T>
void ServiceSession<T>::get_waitables(IWaitable **dst) {
dst[0] = this;
}
template <typename T>
void ServiceSession<T>::delete_child(IWaitable *child) {
/* TODO: Panic, because we can never have any children. */
}
template <typename T>
Handle ServiceSession<T>::get_handle() {
return this->server_handle;
}
template <typename T>
Result ServiceSession<T>::handle_signaled() {
/* TODO */
return 0;
}

View File

@ -3,13 +3,14 @@
#include <type_traits> #include <type_traits>
#include "iserviceobject.hpp" #include "iserviceobject.hpp"
#include "iwaitable.hpp"
#include "serviceserver.hpp" #include "serviceserver.hpp"
template <typename T> template <typename T>
class ServiceServer; class ServiceServer;
template <typename T> template <typename T>
class ServiceSession { class ServiceSession : IWaitable {
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject"); static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
T *service_object; T *service_object;
@ -21,7 +22,7 @@ class ServiceSession {
this->service_object = new T(); this->service_object = new T();
} }
~ServiceSession() { virtual ~ServiceSession() {
delete this->service_object; delete this->service_object;
if (server_handle) { if (server_handle) {
svcCloseHandle(server_handle); svcCloseHandle(server_handle);
@ -34,4 +35,11 @@ class ServiceSession {
T *get_service_object() { return this->service_object; } T *get_service_object() { return this->service_object; }
Handle get_server_handle() { return this->server_handle; } Handle get_server_handle() { return this->server_handle; }
Handle get_client_handle() { return this->client_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();
}; };

View File

@ -0,0 +1,72 @@
#include <switch.h>
#include <algorithm>
#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<IWaitable *> signalables;
std::vector<Handle> 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? */
}
}
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <switch.h>
#include <vector>
#include "iwaitable.hpp"
class WaitableManager {
std::vector<IWaitable *> 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();
};