diff --git a/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_serialization.hpp b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_serialization.hpp index 66225fe6e..7ce9bd39e 100644 --- a/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_serialization.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_serialization.hpp @@ -612,14 +612,16 @@ constexpr Result WrapIpcCommandImpl(IpcResponseContext *ctx) { ON_SCOPE_EXIT { /* Clean up objects as necessary. */ - if (IsDomainObject(ctx->obj_holder) && R_FAILED(rc)) { - for (unsigned int i = 0; i < num_out_objects; i++) { - ctx->obj_holder->GetServiceObject()->FreeObject(ctx->out_object_ids[i]); - } - } else { - for (unsigned int i = 0; i < num_out_objects; i++) { - svcCloseHandle(ctx->out_object_server_handles[i]); - svcCloseHandle(ctx->out_handles[CommandMetaData::NumOutHandles + i].handle); + if (R_FAILED(rc)) { + if (IsDomainObject(ctx->obj_holder)) { + for (unsigned int i = 0; i < num_out_objects; i++) { + ctx->obj_holder->GetServiceObject()->FreeObject(ctx->out_object_ids[i]); + } + } else { + for (unsigned int i = 0; i < num_out_objects; i++) { + svcCloseHandle(ctx->out_object_server_handles[i]); + svcCloseHandle(ctx->out_handles[CommandMetaData::NumOutHandles + i].handle); + } } } diff --git a/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_service_session.hpp b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_service_session.hpp index 3b25e8721..752c533e5 100644 --- a/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_service_session.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_service_session.hpp @@ -234,16 +234,14 @@ class ServiceSession : public IWaitable return ctx.rc; } - virtual void HandleDeferred() override { + virtual Result HandleDeferred() override { memcpy(armGetTls(), this->backup_tls, sizeof(this->backup_tls)); Result rc = this->HandleReceived(); if (rc != RESULT_DEFER_SESSION) { this->SetDeferred(false); - if (rc == 0xF601) { - svcCloseHandle(this->GetHandle()); - } } + return rc; } virtual Result HandleSignaled(u64 timeout) { diff --git a/stratosphere/libstratosphere/include/stratosphere/iwaitable.hpp b/stratosphere/libstratosphere/include/stratosphere/iwaitable.hpp index 175996067..d5721f849 100644 --- a/stratosphere/libstratosphere/include/stratosphere/iwaitable.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/iwaitable.hpp @@ -32,8 +32,10 @@ class IWaitable { public: virtual ~IWaitable() = default; - virtual void HandleDeferred() { - /* ... */ + virtual Result HandleDeferred() { + /* By default, HandleDeferred panics, because object shouldn't be deferrable. */ + std::abort(); + return 0; } bool IsSignaled() { diff --git a/stratosphere/libstratosphere/include/stratosphere/waitable_manager.hpp b/stratosphere/libstratosphere/include/stratosphere/waitable_manager.hpp index 2f48f42f1..569817c6b 100644 --- a/stratosphere/libstratosphere/include/stratosphere/waitable_manager.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/waitable_manager.hpp @@ -55,12 +55,14 @@ class WaitableManager : public SessionManagerBase { /* Waitable Manager */ std::vector to_add_waitables; std::vector waitables; + std::vector deferred_waitables; u32 num_threads; Thread *threads; HosMutex process_lock; HosMutex signal_lock; HosMutex add_lock; HosMutex cur_thread_lock; + HosMutex deferred_lock; bool has_new_waitables = false; IWaitable *next_signaled = nullptr; @@ -84,7 +86,9 @@ class WaitableManager : public SessionManagerBase { ~WaitableManager() override { /* This should call the destructor for every waitable. */ + std::for_each(to_add_waitables.begin(), to_add_waitables.end(), std::default_delete{}); std::for_each(waitables.begin(), waitables.end(), std::default_delete{}); + std::for_each(deferred_waitables.begin(), deferred_waitables.end(), std::default_delete{}); /* TODO: Exit the threads? */ } @@ -143,10 +147,37 @@ class WaitableManager : public SessionManagerBase { /* Close! */ delete w; } else { - this_ptr->AddWaitable(w); + if (w->IsDeferred()) { + std::scoped_lock lk{this_ptr->deferred_lock}; + this_ptr->deferred_waitables.push_back(w); + } else { + this_ptr->AddWaitable(w); + } + } + } + + /* We finished processing, and maybe that means we can stop deferring an object. */ + { + std::scoped_lock lk{this_ptr->deferred_lock}; + + for (size_t i = 0; i < this_ptr->deferred_waitables.size(); i++) { + auto w = this_ptr->deferred_waitables[i]; + Result rc = w->HandleDeferred(); + if (rc == 0xF601 || !w->IsDeferred()) { + /* Remove from the deferred list. */ + this_ptr->deferred_waitables.erase(this_ptr->deferred_waitables.begin() + i); + if (rc == 0xF601) { + /* Delete the closed waitable. */ + delete w; + } else { + /* Add to the waitables list. */ + this_ptr->AddWaitable(w); + } + /* Subtract one from i, to avoid skipping a deferred session. */ + i--; + } } } - } } @@ -193,6 +224,8 @@ class WaitableManager : public SessionManagerBase { handles.resize(this->waitables.size()); wait_list.resize(this->waitables.size()); unsigned int num_handles = 0; + + /* Try to add waitables to wait list. */ for (unsigned int i = 0; i < this->waitables.size(); i++) { Handle h = this->waitables[i]->GetHandle(); if (h != INVALID_HANDLE) { @@ -201,21 +234,13 @@ class WaitableManager : public SessionManagerBase { } } - - /* Do deferred callback for each waitable. This has to happen before we wait on anything else. */ - for (auto & waitable : this->waitables) { - if (waitable->IsDeferred()) { - waitable->HandleDeferred(); - } - } - /* Wait forever. */ rc = svcWaitSynchronization(&handle_index, handles.data(), num_handles, U64_MAX); if (R_SUCCEEDED(rc)) { IWaitable *w = wait_list[handle_index]; size_t w_ind = std::distance(this->waitables.begin(), std::find(this->waitables.begin(), this->waitables.end(), w)); - std::for_each(waitables.begin(), waitables.begin() + w_ind, std::mem_fn(&IWaitable::UpdatePriority)); + std::for_each(waitables.begin(), waitables.begin() + w_ind + 1, std::mem_fn(&IWaitable::UpdatePriority)); result = w; } else if (rc == 0xEA01) { /* Timeout: Just update priorities. */ @@ -229,14 +254,13 @@ class WaitableManager : public SessionManagerBase { result = this->next_signaled; } } - } else if (rc != 0xF601 && rc != 0xE401) { + } else { + /* TODO: Consider the following cases that this covers: */ + /* 7601: Thread termination requested. */ + /* E401: Handle is dead. */ + /* E601: Handle list address invalid. */ + /* EE01: Too many handles. */ std::abort(); - } else { - IWaitable *w = wait_list[handle_index]; - size_t w_ind = std::distance(this->waitables.begin(), std::find(this->waitables.begin(), this->waitables.end(), w)); - this->waitables.erase(this->waitables.begin() + w_ind); - std::for_each(waitables.begin(), waitables.begin() + w_ind - 1, std::mem_fn(&IWaitable::UpdatePriority)); - delete w; } } }