mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-10 07:06:34 +00:00
Stratosphere: Add IWaitable, WaitableManager
This commit is contained in:
parent
8e25534912
commit
cbb0a084a6
8 changed files with 244 additions and 9 deletions
|
@ -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;
|
||||||
};
|
};
|
32
stratosphere/loader/source/iwaitable.hpp
Normal file
32
stratosphere/loader/source/iwaitable.hpp
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
|
@ -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++;
|
||||||
}
|
}
|
|
@ -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();
|
||||||
};
|
};
|
|
@ -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;
|
||||||
|
}
|
|
@ -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();
|
||||||
};
|
};
|
72
stratosphere/loader/source/waitablemanager.cpp
Normal file
72
stratosphere/loader/source/waitablemanager.cpp
Normal 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? */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
stratosphere/loader/source/waitablemanager.hpp
Normal file
26
stratosphere/loader/source/waitablemanager.hpp
Normal 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();
|
||||||
|
};
|
Loading…
Reference in a new issue