From 26e676424d8921e1fcd184590b18ec22367a28d8 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 14 Jun 2018 17:50:01 -0600 Subject: [PATCH] fs.mitm: WIP LayeredFS impl (NOTE: UNUSABLE ATM) Also greatly refactors libstratosphere, and does a lot of other things. There is a lot of code in this one. --- include/stratosphere.hpp | 1 + include/stratosphere/domainowner.hpp | 29 +++-- include/stratosphere/ievent.hpp | 8 +- include/stratosphere/ipcsession.hpp | 4 +- include/stratosphere/iserviceobject.hpp | 1 - include/stratosphere/isession.hpp | 39 ++++--- .../multithreadedwaitablemanager.hpp | 42 +++++++ include/stratosphere/systemevent.hpp | 2 +- include/stratosphere/waitablemanager.hpp | 19 +-- include/stratosphere/waitablemanagerbase.hpp | 1 + source/multithreadedwaitablemanager.cpp | 110 ++++++++++++++++++ 11 files changed, 206 insertions(+), 50 deletions(-) create mode 100644 include/stratosphere/multithreadedwaitablemanager.hpp create mode 100644 source/multithreadedwaitablemanager.cpp diff --git a/include/stratosphere.hpp b/include/stratosphere.hpp index 7d861a8a..d37ec97f 100644 --- a/include/stratosphere.hpp +++ b/include/stratosphere.hpp @@ -16,3 +16,4 @@ #include "stratosphere/hossynch.hpp" #include "stratosphere/waitablemanager.hpp" +#include "stratosphere/multithreadedwaitablemanager.hpp" diff --git a/include/stratosphere/domainowner.hpp b/include/stratosphere/domainowner.hpp index bf7b560f..09dde791 100644 --- a/include/stratosphere/domainowner.hpp +++ b/include/stratosphere/domainowner.hpp @@ -1,37 +1,36 @@ #pragma once #include +#include #include #include "iserviceobject.hpp" -#define DOMAIN_ID_MAX 0x200 +#define DOMAIN_ID_MAX 0x1000 class IServiceObject; class DomainOwner { private: - IServiceObject *domain_objects[DOMAIN_ID_MAX]; + std::shared_ptr domain_objects[DOMAIN_ID_MAX]; public: DomainOwner() { for (unsigned int i = 0; i < DOMAIN_ID_MAX; i++) { - domain_objects[i] = NULL; + domain_objects[i].reset(); } } virtual ~DomainOwner() { - for (unsigned int i = 0; i < DOMAIN_ID_MAX; i++) { - this->delete_object(i); - } + /* Shared ptrs should auto delete here. */ } - IServiceObject *get_domain_object(unsigned int i) { + std::shared_ptr get_domain_object(unsigned int i) { if (i < DOMAIN_ID_MAX) { return domain_objects[i]; } - return NULL; + return nullptr; } - Result reserve_object(IServiceObject *object, unsigned int *out_i) { + Result reserve_object(std::shared_ptr object, unsigned int *out_i) { for (unsigned int i = 4; i < DOMAIN_ID_MAX; i++) { if (domain_objects[i] == NULL) { domain_objects[i] = object; @@ -43,7 +42,7 @@ class DomainOwner { return 0x1900B; } - Result set_object(IServiceObject *object, unsigned int i) { + Result set_object(std::shared_ptr object, unsigned int i) { if (domain_objects[i] == NULL) { domain_objects[i] = object; object->set_owner(this); @@ -52,7 +51,7 @@ class DomainOwner { return 0x1900B; } - unsigned int get_object_id(IServiceObject *object) { + unsigned int get_object_id(std::shared_ptr object) { for (unsigned int i = 0; i < DOMAIN_ID_MAX; i++) { if (domain_objects[i] == object) { return i; @@ -63,16 +62,14 @@ class DomainOwner { void delete_object(unsigned int i) { if (domain_objects[i]) { - delete domain_objects[i]; - domain_objects[i] = NULL; + domain_objects[i].reset(); } } - void delete_object(IServiceObject *object) { + void delete_object(std::shared_ptr object) { for (unsigned int i = 0; i < DOMAIN_ID_MAX; i++) { if (domain_objects[i] == object) { - delete domain_objects[i]; - domain_objects[i] = NULL; + domain_objects[i].reset(); break; } } diff --git a/include/stratosphere/ievent.hpp b/include/stratosphere/ievent.hpp index ae41a912..5d30ce1e 100644 --- a/include/stratosphere/ievent.hpp +++ b/include/stratosphere/ievent.hpp @@ -4,18 +4,20 @@ #include "iwaitable.hpp" -typedef Result (*EventCallback)(Handle *handles, size_t num_handles, u64 timeout); +typedef Result (*EventCallback)(void *arg, Handle *handles, size_t num_handles, u64 timeout); class IEvent : public IWaitable { protected: std::vector handles; EventCallback callback; + void *arg; public: - IEvent(Handle wait_h, EventCallback callback) { + IEvent(Handle wait_h, void *a, EventCallback callback) { if (wait_h) { this->handles.push_back(wait_h); } + this->arg = a; this->callback = callback; } @@ -41,7 +43,7 @@ class IEvent : public IWaitable { } virtual Result handle_signaled(u64 timeout) { - return this->callback(this->handles.data(), this->handles.size(), timeout); + return this->callback(this->arg, this->handles.data(), this->handles.size(), timeout); } static Result PanicCallback(Handle *handles, size_t num_handles, u64 timeout) { diff --git a/include/stratosphere/ipcsession.hpp b/include/stratosphere/ipcsession.hpp index 5cabd6a1..e3967723 100644 --- a/include/stratosphere/ipcsession.hpp +++ b/include/stratosphere/ipcsession.hpp @@ -17,13 +17,13 @@ class IPCSession final : public ISession { if (R_FAILED((rc = svcCreateSession(&this->server_handle, &this->client_handle, 0, 0)))) { fatalSimple(rc); } - this->service_object = new T(); + this->service_object = std::make_shared(); this->pointer_buffer_size = pbs; this->pointer_buffer = new char[this->pointer_buffer_size]; this->is_domain = false; } - IPCSession(T *so, size_t pbs = 0x400) : ISession(NULL, 0, 0, so, 0) { + IPCSession(std::shared_ptr so, size_t pbs = 0x400) : ISession(NULL, 0, 0, so, 0) { Result rc; if (R_FAILED((rc = svcCreateSession(&this->server_handle, &this->client_handle, 0, 0)))) { fatalSimple(rc); diff --git a/include/stratosphere/iserviceobject.hpp b/include/stratosphere/iserviceobject.hpp index 965fc59c..9433d062 100644 --- a/include/stratosphere/iserviceobject.hpp +++ b/include/stratosphere/iserviceobject.hpp @@ -18,7 +18,6 @@ class IServiceObject { DomainOwner *get_owner() { return this->owner; } void set_owner(DomainOwner *owner) { this->owner = owner; } virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) = 0; - protected: virtual Result handle_deferred() = 0; }; diff --git a/include/stratosphere/isession.hpp b/include/stratosphere/isession.hpp index de33692e..52df6c42 100644 --- a/include/stratosphere/isession.hpp +++ b/include/stratosphere/isession.hpp @@ -27,10 +27,10 @@ class IServer; class IServiceObject; template -class ISession : public IWaitable, public DomainOwner { +class ISession : public IWaitable { static_assert(std::is_base_of::value, "Service Objects must derive from IServiceObject"); protected: - T *service_object; + std::shared_ptr service_object; IServer *server; Handle server_handle; Handle client_handle; @@ -38,35 +38,35 @@ class ISession : public IWaitable, public DomainOwner { size_t pointer_buffer_size; bool is_domain; + std::shared_ptr domain; - IServiceObject *active_object; + std::shared_ptr active_object; static_assert(sizeof(pointer_buffer) <= POINTER_BUFFER_SIZE_MAX, "Incorrect Size for PointerBuffer!"); public: ISession(IServer *s, Handle s_h, Handle c_h, size_t pbs = 0x400) : server(s), server_handle(s_h), client_handle(c_h), pointer_buffer_size(pbs) { - this->service_object = new T(); + this->service_object = std::make_shared(); if (this->pointer_buffer_size) { this->pointer_buffer = new char[this->pointer_buffer_size]; } this->is_domain = false; - this->active_object = NULL; + this->domain.reset(); + this->active_object.reset(); } - ISession(IServer *s, Handle s_h, Handle c_h, T *so, size_t pbs = 0x400) : service_object(so), server(s), server_handle(s_h), client_handle(c_h), pointer_buffer_size(pbs) { + ISession(IServer *s, Handle s_h, Handle c_h, std::shared_ptr so, size_t pbs = 0x400) : service_object(so), server(s), server_handle(s_h), client_handle(c_h), pointer_buffer_size(pbs) { if (this->pointer_buffer_size) { this->pointer_buffer = new char[this->pointer_buffer_size]; } this->is_domain = false; - this->active_object = NULL; + this->domain.reset(); + this->active_object.reset(); } ~ISession() override { delete this->pointer_buffer; - if (this->service_object && !this->is_domain) { - //delete this->service_object; - } if (server_handle) { svcCloseHandle(server_handle); } @@ -86,12 +86,12 @@ class ISession : public IWaitable, public DomainOwner { } } - T *get_service_object() { return this->service_object; } + std::shared_ptr get_service_object() { return this->service_object; } Handle get_server_handle() { return this->server_handle; } Handle get_client_handle() { return this->client_handle; } - DomainOwner *get_owner() { return is_domain ? this : NULL; } + DomainOwner *get_owner() { return this->is_domain ? this->domain.get() : NULL; } /* IWaitable */ Handle get_handle() override { @@ -125,7 +125,7 @@ class ISession : public IWaitable, public DomainOwner { if (r.IsDomainMessage && r.MessageType == DomainMessageType_Close) { - this->delete_object(this->active_object); + this->domain->delete_object(this->active_object); this->active_object = NULL; struct { u64 magic; @@ -188,7 +188,7 @@ class ISession : public IWaitable, public DomainOwner { ipcAddRecvStatic(&c_for_reply, this->pointer_buffer, this->pointer_buffer_size, 0); ipcPrepareHeader(&c_for_reply, 0); - if (R_SUCCEEDED(rc = svcReplyAndReceive(&handle_index, &this->server_handle, 1, 0, timeout))) { + if (R_SUCCEEDED(rc = svcReplyAndReceive(&handle_index, &this->server_handle, 1, 0, U64_MAX))) { if (handle_index != 0) { /* TODO: Panic? */ } @@ -203,7 +203,7 @@ class ISession : public IWaitable, public DomainOwner { if (!r.IsDomainMessage || r.ThisObjectId >= DOMAIN_ID_MAX) { retval = 0xF601; } else { - this->active_object = this->get_domain_object(r.ThisObjectId); + this->active_object = this->domain->get_domain_object(r.ThisObjectId); } } else { this->active_object = this->service_object; @@ -218,19 +218,22 @@ class ISession : public IWaitable, public DomainOwner { if (retval == RESULT_DEFER_SESSION) { /* Session defer. */ - this->active_object = NULL; + this->active_object.reset(); this->set_deferred(true); rc = retval; } else if (retval == 0xF601) { /* Session close. */ - this->active_object = NULL; + this->active_object.reset(); rc = retval; } else { if (R_SUCCEEDED(retval)) { this->postprocess(r, cmd_id); } - this->active_object = NULL; + this->active_object.reset(); rc = svcReplyAndReceive(&handle_index, &this->server_handle, 0, this->server_handle, 0); + if (rc == 0xEA01) { + rc = 0x0; + } this->cleanup(); } } diff --git a/include/stratosphere/multithreadedwaitablemanager.hpp b/include/stratosphere/multithreadedwaitablemanager.hpp new file mode 100644 index 00000000..44806093 --- /dev/null +++ b/include/stratosphere/multithreadedwaitablemanager.hpp @@ -0,0 +1,42 @@ +#pragma once +#include +#include + +#include "waitablemanager.hpp" +#include "systemevent.hpp" + +class MultiThreadedWaitableManager : public WaitableManager { + protected: + u32 num_threads; + Thread *threads; + HosMutex get_waitable_lock; + SystemEvent *new_waitable_event; + public: + MultiThreadedWaitableManager(u32 n, u64 t, u32 ss = 0x8000) : WaitableManager(t), num_threads(n-1) { + u32 prio; + u32 cpuid = svcGetCurrentProcessorNumber(); + Result rc; + threads = new Thread[num_threads]; + if (R_FAILED((rc = svcGetThreadPriority(&prio, CUR_THREAD_HANDLE)))) { + fatalSimple(rc); + } + for (unsigned int i = 0; i < num_threads; i++) { + threads[i] = {0}; + threadCreate(&threads[i], &MultiThreadedWaitableManager::thread_func, this, ss, prio, cpuid); + } + new_waitable_event = new SystemEvent(this, &MultiThreadedWaitableManager::add_waitable_callback); + this->waitables.push_back(new_waitable_event); + } + ~MultiThreadedWaitableManager() override { + /* TODO: Exit the threads? */ + } + + IWaitable *get_waitable(); + + void add_waitable(IWaitable *waitable) override; + void process() override; + void process_until_timeout() override; + + static Result add_waitable_callback(void *this_ptr, Handle *handles, size_t num_handles, u64 timeout); + static void thread_func(void *this_ptr); +}; \ No newline at end of file diff --git a/include/stratosphere/systemevent.hpp b/include/stratosphere/systemevent.hpp index 69cce0a6..ccd5a5eb 100644 --- a/include/stratosphere/systemevent.hpp +++ b/include/stratosphere/systemevent.hpp @@ -9,7 +9,7 @@ class SystemEvent final : public IEvent { public: - SystemEvent(EventCallback callback) : IEvent(0, callback) { + SystemEvent(void *a, EventCallback callback) : IEvent(0, a, callback) { Handle wait_h; Handle sig_h; if (R_FAILED(svcCreateEvent(&sig_h, &wait_h))) { diff --git a/include/stratosphere/waitablemanager.hpp b/include/stratosphere/waitablemanager.hpp index ebefa548..5c0006e4 100644 --- a/include/stratosphere/waitablemanager.hpp +++ b/include/stratosphere/waitablemanager.hpp @@ -9,16 +9,17 @@ class IWaitable; class WaitableManager : public WaitableManagerBase { - std::vector to_add_waitables; - std::vector waitables; - u64 timeout; - HosMutex lock; - std::atomic_bool has_new_items; + protected: + std::vector to_add_waitables; + std::vector waitables; + u64 timeout; + HosMutex lock; + std::atomic_bool has_new_items; private: void process_internal(bool break_on_timeout); public: WaitableManager(u64 t) : waitables(0), timeout(t), has_new_items(false) { } - ~WaitableManager() { + ~WaitableManager() override { /* This should call the destructor for every waitable. */ for (auto & waitable : waitables) { delete waitable; @@ -26,7 +27,7 @@ class WaitableManager : public WaitableManagerBase { waitables.clear(); } - void add_waitable(IWaitable *waitable); - void process(); - void process_until_timeout(); + virtual void add_waitable(IWaitable *waitable); + virtual void process(); + virtual void process_until_timeout(); }; \ No newline at end of file diff --git a/include/stratosphere/waitablemanagerbase.hpp b/include/stratosphere/waitablemanagerbase.hpp index f294b8fd..97271006 100644 --- a/include/stratosphere/waitablemanagerbase.hpp +++ b/include/stratosphere/waitablemanagerbase.hpp @@ -7,6 +7,7 @@ class WaitableManagerBase { std::atomic cur_priority; public: WaitableManagerBase() : cur_priority(0) { } + virtual ~WaitableManagerBase() { } u64 get_priority() { return std::atomic_fetch_add(&cur_priority, (u64)1); diff --git a/source/multithreadedwaitablemanager.cpp b/source/multithreadedwaitablemanager.cpp new file mode 100644 index 00000000..02c2714e --- /dev/null +++ b/source/multithreadedwaitablemanager.cpp @@ -0,0 +1,110 @@ +#include + +#include + +#include + +void MultiThreadedWaitableManager::process() { + Result rc; + for (unsigned int i = 0; i < num_threads; i++) { + if (R_FAILED((rc = threadStart(&threads[i])))) { + fatalSimple(rc); + } + } + MultiThreadedWaitableManager::thread_func(this); +} + +void MultiThreadedWaitableManager::process_until_timeout() { + /* TODO: Panic. */ +} + +void MultiThreadedWaitableManager::add_waitable(IWaitable *waitable) { + this->lock.Lock(); + this->to_add_waitables.push_back(waitable); + waitable->set_manager(this); + this->new_waitable_event->signal_event(); + this->lock.Unlock(); +} + + +IWaitable *MultiThreadedWaitableManager::get_waitable() { + std::vector handles; + + int handle_index = 0; + Result rc; + this->get_waitable_lock.Lock(); + while (1) { + /* Sort waitables by priority. */ + std::sort(this->waitables.begin(), this->waitables.end(), IWaitable::compare); + + /* Copy out handles. */ + handles.resize(this->waitables.size()); + std::transform(this->waitables.begin(), this->waitables.end(), handles.begin(), [](IWaitable *w) { return w->get_handle(); }); + + rc = svcWaitSynchronization(&handle_index, handles.data(), this->waitables.size(), this->timeout); + IWaitable *w = this->waitables[handle_index]; + if (R_SUCCEEDED(rc)) { + for (int i = 0; i < handle_index; i++) { + this->waitables[i]->update_priority(); + } + this->waitables.erase(this->waitables.begin() + handle_index); + } else if (rc == 0xEA01) { + /* Timeout. */ + for (auto & waitable : this->waitables) { + waitable->update_priority(); + } + } else if (rc != 0xF601) { + /* TODO: Panic. When can this happen? */ + } else { + for (int i = 0; i < handle_index; i++) { + this->waitables[i]->update_priority(); + } + this->waitables.erase(this->waitables.begin() + handle_index); + delete w; + } + + /* Do deferred callback for each waitable. */ + for (auto & waitable : this->waitables) { + if (waitable->get_deferred()) { + waitable->handle_deferred(); + } + } + + /* Return waitable. */ + if (R_SUCCEEDED(rc)) { + if (w == this->new_waitable_event) { + w->handle_signaled(0); + this->waitables.push_back(w); + } else { + this->get_waitable_lock.Unlock(); + return w; + } + } + } +} + +Result MultiThreadedWaitableManager::add_waitable_callback(void *arg, Handle *handles, size_t num_handles, u64 timeout) { + MultiThreadedWaitableManager *this_ptr = (MultiThreadedWaitableManager *)arg; + svcClearEvent(handles[0]); + this_ptr->lock.Lock(); + this_ptr->waitables.insert(this_ptr->waitables.end(), this_ptr->to_add_waitables.begin(), this_ptr->to_add_waitables.end()); + this_ptr->to_add_waitables.clear(); + this_ptr->lock.Unlock(); + return 0; +} + +void MultiThreadedWaitableManager::thread_func(void *t) { + MultiThreadedWaitableManager *this_ptr = (MultiThreadedWaitableManager *)t; + while (1) { + IWaitable *w = this_ptr->get_waitable(); + if (w) { + Result rc = w->handle_signaled(0); + if (rc == 0xF601) { + /* Close! */ + delete w; + } else { + this_ptr->add_waitable(w); + } + } + } +} \ No newline at end of file