Stratosphere: Implement support for deferred commands. Finish sm GetService()'s deferred path.

This commit is contained in:
Michael Scire 2018-04-22 03:02:08 -06:00
parent 1e35599dbe
commit d772b823c6
6 changed files with 73 additions and 6 deletions

View File

@ -387,6 +387,26 @@ struct Encoder<std::tuple<Args...>> {
}; };
template<auto IpcCommandImpl, typename Class, typename... Args>
Result WrapDeferredIpcCommandImpl(Class *this_ptr, Args... args) {
using InArgs = typename boost::callable_traits::args_t<decltype(IpcCommandImpl)>;
using InArgsWithoutThis = typename pop_front<InArgs>::type;
using OutArgs = typename boost::callable_traits::return_type_t<decltype(IpcCommandImpl)>;
static_assert(is_specialization_of<OutArgs, std::tuple>::value, "IpcCommandImpls must return std::tuple<Result, ...>");
static_assert(std::is_same_v<std::tuple_element_t<0, OutArgs>, Result>, "IpcCommandImpls must return std::tuple<Result, ...>");
static_assert(std::is_same_v<InArgsWithoutThis, std::tuple<Args...>>, "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<OutArgs>{out_command}, result);
}
template<auto IpcCommandImpl, typename Class> template<auto IpcCommandImpl, typename Class>
Result WrapIpcCommandImpl(Class *this_ptr, IpcParsedCommand& r, IpcCommand &out_command, u8 *pointer_buffer, size_t pointer_buffer_size) { 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<decltype(IpcCommandImpl)>; using InArgs = typename boost::callable_traits::args_t<decltype(IpcCommandImpl)>;

View File

@ -90,6 +90,11 @@ class IServer : public IWaitable {
return this->port_handle; return this->port_handle;
} }
virtual void handle_deferred() {
/* TODO: Panic, because we can never defer a server. */
}
virtual Result handle_signaled(u64 timeout) { virtual Result handle_signaled(u64 timeout) {
/* If this server's port was signaled, accept a new session. */ /* If this server's port was signaled, accept a new session. */
Handle session_h; Handle session_h;

View File

@ -7,4 +7,5 @@ class IServiceObject {
public: public:
virtual ~IServiceObject() { } virtual ~IServiceObject() { }
virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) = 0; 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;
}; };

View File

@ -4,6 +4,7 @@
class IWaitable { class IWaitable {
u64 wait_priority = 0; u64 wait_priority = 0;
bool is_deferred = false;
IWaitable *parent_waitable; IWaitable *parent_waitable;
public: public:
virtual ~IWaitable() { } virtual ~IWaitable() { }
@ -11,6 +12,7 @@ class IWaitable {
virtual unsigned int get_num_waitables() = 0; virtual unsigned int get_num_waitables() = 0;
virtual void get_waitables(IWaitable **dst) = 0; virtual void get_waitables(IWaitable **dst) = 0;
virtual void delete_child(IWaitable *child) = 0; virtual void delete_child(IWaitable *child) = 0;
virtual void handle_deferred() = 0;
virtual Handle get_handle() = 0; virtual Handle get_handle() = 0;
virtual Result handle_signaled(u64 timeout) = 0; virtual Result handle_signaled(u64 timeout) = 0;
@ -34,7 +36,15 @@ class IWaitable {
this->wait_priority = g_cur_priority++; 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) { static bool compare(IWaitable *a, IWaitable *b) {
return (a->wait_priority < b->wait_priority); return (a->wait_priority < b->wait_priority) && !a->is_deferred;
} }
}; };

View File

@ -16,6 +16,8 @@ enum IpcControlCommand {
}; };
#define POINTER_BUFFER_SIZE_MAX 0xFFFF #define POINTER_BUFFER_SIZE_MAX 0xFFFF
#define RESULT_DEFER_SESSION (0x6580A)
template <typename T> template <typename T>
class IServer; class IServer;
@ -68,6 +70,20 @@ class ServiceSession : public IWaitable {
return this->server_handle; 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) { virtual Result handle_signaled(u64 timeout) {
Result rc; Result rc;
int handle_index; int handle_index;
@ -124,10 +140,15 @@ class ServiceSession : public IWaitable {
} }
if (retval != 0xF601) { if (retval == RESULT_DEFER_SESSION) {
rc = svcReplyAndReceive(&handle_index, &this->server_handle, 1, this->server_handle, 0); /* Session defer. */
} else { this->set_deferred(true);
rc = retval; rc = retval;
} else if (retval == 0xF601) {
/* Session close. */
rc = retval;
} else {
rc = svcReplyAndReceive(&handle_index, &this->server_handle, 0, this->server_handle, 0);
} }
} }

View File

@ -41,7 +41,14 @@ void WaitableManager::process() {
handles.resize(signalables.size()); handles.resize(signalables.size());
std::transform(signalables.begin(), signalables.end(), handles.begin(), [](IWaitable *w) { return w->get_handle(); }); 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)) { if (R_SUCCEEDED(rc)) {
/* Handle a signaled waitable. */ /* Handle a signaled waitable. */
/* TODO: What timeout should be passed here? */ /* TODO: What timeout should be passed here? */
@ -80,5 +87,8 @@ void WaitableManager::process() {
signalables[i]->update_priority(); 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(); });
} }
} }