From d772b823c6bfa9e4997413a49f97952a4ac7c2c7 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 22 Apr 2018 03:02:08 -0600 Subject: [PATCH] Stratosphere: Implement support for deferred commands. Finish sm GetService()'s deferred path. --- include/stratosphere/ipc_templating.hpp | 20 ++++++++++++++++++ include/stratosphere/iserver.hpp | 5 +++++ include/stratosphere/iserviceobject.hpp | 1 + include/stratosphere/iwaitable.hpp | 12 ++++++++++- include/stratosphere/servicesession.hpp | 27 ++++++++++++++++++++++--- source/waitablemanager.cpp | 14 +++++++++++-- 6 files changed, 73 insertions(+), 6 deletions(-) diff --git a/include/stratosphere/ipc_templating.hpp b/include/stratosphere/ipc_templating.hpp index f1d44ae6..f9e00fdf 100644 --- a/include/stratosphere/ipc_templating.hpp +++ b/include/stratosphere/ipc_templating.hpp @@ -387,6 +387,26 @@ struct Encoder> { }; +template +Result WrapDeferredIpcCommandImpl(Class *this_ptr, Args... args) { + using InArgs = typename boost::callable_traits::args_t; + using InArgsWithoutThis = typename pop_front::type; + using OutArgs = typename boost::callable_traits::return_type_t; + + static_assert(is_specialization_of::value, "IpcCommandImpls must return std::tuple"); + static_assert(std::is_same_v, Result>, "IpcCommandImpls must return std::tuple"); + static_assert(std::is_same_v>, "Invalid Deferred Wrapped IpcCommandImpl arguments!"); + + IpcCommand out_command; + + ipcInitialize(&out_command); + + auto tuple_args = std::make_tuple(args...); + auto result = std::apply( [=](auto&&... a) { return (this_ptr->*IpcCommandImpl)(a...); }, tuple_args); + + return std::apply(Encoder{out_command}, result); +} + template Result WrapIpcCommandImpl(Class *this_ptr, IpcParsedCommand& r, IpcCommand &out_command, u8 *pointer_buffer, size_t pointer_buffer_size) { using InArgs = typename boost::callable_traits::args_t; diff --git a/include/stratosphere/iserver.hpp b/include/stratosphere/iserver.hpp index 0188000c..ed6cb57a 100644 --- a/include/stratosphere/iserver.hpp +++ b/include/stratosphere/iserver.hpp @@ -90,6 +90,11 @@ class IServer : public IWaitable { return this->port_handle; } + + virtual void handle_deferred() { + /* TODO: Panic, because we can never defer a server. */ + } + virtual Result handle_signaled(u64 timeout) { /* If this server's port was signaled, accept a new session. */ Handle session_h; diff --git a/include/stratosphere/iserviceobject.hpp b/include/stratosphere/iserviceobject.hpp index a34e6075..cf4e7d67 100644 --- a/include/stratosphere/iserviceobject.hpp +++ b/include/stratosphere/iserviceobject.hpp @@ -7,4 +7,5 @@ class IServiceObject { public: virtual ~IServiceObject() { } virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) = 0; + virtual Result handle_deferred() = 0; }; \ No newline at end of file diff --git a/include/stratosphere/iwaitable.hpp b/include/stratosphere/iwaitable.hpp index f124ae69..ded31396 100644 --- a/include/stratosphere/iwaitable.hpp +++ b/include/stratosphere/iwaitable.hpp @@ -4,6 +4,7 @@ class IWaitable { u64 wait_priority = 0; + bool is_deferred = false; IWaitable *parent_waitable; public: virtual ~IWaitable() { } @@ -11,6 +12,7 @@ class IWaitable { virtual unsigned int get_num_waitables() = 0; virtual void get_waitables(IWaitable **dst) = 0; virtual void delete_child(IWaitable *child) = 0; + virtual void handle_deferred() = 0; virtual Handle get_handle() = 0; virtual Result handle_signaled(u64 timeout) = 0; @@ -34,7 +36,15 @@ class IWaitable { this->wait_priority = g_cur_priority++; } + bool get_deferred() { + return this->is_deferred; + } + + void set_deferred(bool d) { + this->is_deferred = d; + } + static bool compare(IWaitable *a, IWaitable *b) { - return (a->wait_priority < b->wait_priority); + return (a->wait_priority < b->wait_priority) && !a->is_deferred; } }; \ No newline at end of file diff --git a/include/stratosphere/servicesession.hpp b/include/stratosphere/servicesession.hpp index 996a228f..e730849d 100644 --- a/include/stratosphere/servicesession.hpp +++ b/include/stratosphere/servicesession.hpp @@ -16,6 +16,8 @@ enum IpcControlCommand { }; #define POINTER_BUFFER_SIZE_MAX 0xFFFF +#define RESULT_DEFER_SESSION (0x6580A) + template class IServer; @@ -68,6 +70,20 @@ class ServiceSession : public IWaitable { return this->server_handle; } + virtual void handle_deferred() { + Result rc = this->service_object->handle_deferred(); + int handle_index; + + if (rc != RESULT_DEFER_SESSION) { + this->set_deferred(false); + if (rc == 0xF601) { + svcCloseHandle(this->get_handle()); + } else { + rc = svcReplyAndReceive(&handle_index, &this->server_handle, 0, this->server_handle, 0); + } + } + } + virtual Result handle_signaled(u64 timeout) { Result rc; int handle_index; @@ -124,10 +140,15 @@ class ServiceSession : public IWaitable { } - if (retval != 0xF601) { - rc = svcReplyAndReceive(&handle_index, &this->server_handle, 1, this->server_handle, 0); - } else { + if (retval == RESULT_DEFER_SESSION) { + /* Session defer. */ + this->set_deferred(true); rc = retval; + } else if (retval == 0xF601) { + /* Session close. */ + rc = retval; + } else { + rc = svcReplyAndReceive(&handle_index, &this->server_handle, 0, this->server_handle, 0); } } diff --git a/source/waitablemanager.cpp b/source/waitablemanager.cpp index 5eee835c..ea2edee8 100644 --- a/source/waitablemanager.cpp +++ b/source/waitablemanager.cpp @@ -41,7 +41,14 @@ void WaitableManager::process() { 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); + unsigned int num_not_deferred = 0; + for (auto & signalable : signalables) { + if (!signalable->get_deferred()) { + num_not_deferred++; + } + } + + rc = svcWaitSynchronization(&handle_index, handles.data(), num_not_deferred, this->timeout); if (R_SUCCEEDED(rc)) { /* Handle a signaled waitable. */ /* TODO: What timeout should be passed here? */ @@ -79,6 +86,9 @@ void WaitableManager::process() { for (int i = 0; i < handle_index; i++) { signalables[i]->update_priority(); } - } + } + + /* Do deferred callback for each waitable. */ + std::for_each(signalables.begin() + num_not_deferred, signalables.end(), [](IWaitable *w) { w->handle_deferred(); }); } } \ No newline at end of file