mirror of
https://github.com/Atmosphere-NX/Atmosphere-libs.git
synced 2025-06-21 11:02:45 +02:00
Stratosphere: Implement support for deferred commands. Finish sm GetService()'s deferred path.
This commit is contained in:
parent
1e35599dbe
commit
d772b823c6
@ -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>
|
||||
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)>;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
@ -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;
|
||||
}
|
||||
};
|
@ -16,6 +16,8 @@ enum IpcControlCommand {
|
||||
};
|
||||
|
||||
#define POINTER_BUFFER_SIZE_MAX 0xFFFF
|
||||
#define RESULT_DEFER_SESSION (0x6580A)
|
||||
|
||||
|
||||
template <typename T>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(); });
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user