libstrat: Significantly cleanup waitable manager result handling.

This commit is contained in:
Michael Scire 2018-11-07 22:40:19 -08:00
parent bac81f4ccc
commit 147f3c690a
4 changed files with 58 additions and 32 deletions

View file

@ -612,14 +612,16 @@ constexpr Result WrapIpcCommandImpl(IpcResponseContext *ctx) {
ON_SCOPE_EXIT { ON_SCOPE_EXIT {
/* Clean up objects as necessary. */ /* Clean up objects as necessary. */
if (IsDomainObject(ctx->obj_holder) && R_FAILED(rc)) { if (R_FAILED(rc)) {
for (unsigned int i = 0; i < num_out_objects; i++) { if (IsDomainObject(ctx->obj_holder)) {
ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->out_object_ids[i]); for (unsigned int i = 0; i < num_out_objects; i++) {
} ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->out_object_ids[i]);
} else { }
for (unsigned int i = 0; i < num_out_objects; i++) { } else {
svcCloseHandle(ctx->out_object_server_handles[i]); for (unsigned int i = 0; i < num_out_objects; i++) {
svcCloseHandle(ctx->out_handles[CommandMetaData::NumOutHandles + i].handle); svcCloseHandle(ctx->out_object_server_handles[i]);
svcCloseHandle(ctx->out_handles[CommandMetaData::NumOutHandles + i].handle);
}
} }
} }

View file

@ -234,16 +234,14 @@ class ServiceSession : public IWaitable
return ctx.rc; return ctx.rc;
} }
virtual void HandleDeferred() override { virtual Result HandleDeferred() override {
memcpy(armGetTls(), this->backup_tls, sizeof(this->backup_tls)); memcpy(armGetTls(), this->backup_tls, sizeof(this->backup_tls));
Result rc = this->HandleReceived(); Result rc = this->HandleReceived();
if (rc != RESULT_DEFER_SESSION) { if (rc != RESULT_DEFER_SESSION) {
this->SetDeferred(false); this->SetDeferred(false);
if (rc == 0xF601) {
svcCloseHandle(this->GetHandle());
}
} }
return rc;
} }
virtual Result HandleSignaled(u64 timeout) { virtual Result HandleSignaled(u64 timeout) {

View file

@ -32,8 +32,10 @@ class IWaitable {
public: public:
virtual ~IWaitable() = default; 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() { bool IsSignaled() {

View file

@ -55,12 +55,14 @@ class WaitableManager : public SessionManagerBase {
/* Waitable Manager */ /* Waitable Manager */
std::vector<IWaitable *> to_add_waitables; std::vector<IWaitable *> to_add_waitables;
std::vector<IWaitable *> waitables; std::vector<IWaitable *> waitables;
std::vector<IWaitable *> deferred_waitables;
u32 num_threads; u32 num_threads;
Thread *threads; Thread *threads;
HosMutex process_lock; HosMutex process_lock;
HosMutex signal_lock; HosMutex signal_lock;
HosMutex add_lock; HosMutex add_lock;
HosMutex cur_thread_lock; HosMutex cur_thread_lock;
HosMutex deferred_lock;
bool has_new_waitables = false; bool has_new_waitables = false;
IWaitable *next_signaled = nullptr; IWaitable *next_signaled = nullptr;
@ -84,7 +86,9 @@ class WaitableManager : public SessionManagerBase {
~WaitableManager() override { ~WaitableManager() override {
/* This should call the destructor for every waitable. */ /* This should call the destructor for every waitable. */
std::for_each(to_add_waitables.begin(), to_add_waitables.end(), std::default_delete<IWaitable>{});
std::for_each(waitables.begin(), waitables.end(), std::default_delete<IWaitable>{}); std::for_each(waitables.begin(), waitables.end(), std::default_delete<IWaitable>{});
std::for_each(deferred_waitables.begin(), deferred_waitables.end(), std::default_delete<IWaitable>{});
/* TODO: Exit the threads? */ /* TODO: Exit the threads? */
} }
@ -143,10 +147,37 @@ class WaitableManager : public SessionManagerBase {
/* Close! */ /* Close! */
delete w; delete w;
} else { } 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()); handles.resize(this->waitables.size());
wait_list.resize(this->waitables.size()); wait_list.resize(this->waitables.size());
unsigned int num_handles = 0; unsigned int num_handles = 0;
/* Try to add waitables to wait list. */
for (unsigned int i = 0; i < this->waitables.size(); i++) { for (unsigned int i = 0; i < this->waitables.size(); i++) {
Handle h = this->waitables[i]->GetHandle(); Handle h = this->waitables[i]->GetHandle();
if (h != INVALID_HANDLE) { 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. */ /* Wait forever. */
rc = svcWaitSynchronization(&handle_index, handles.data(), num_handles, U64_MAX); rc = svcWaitSynchronization(&handle_index, handles.data(), num_handles, U64_MAX);
if (R_SUCCEEDED(rc)) { if (R_SUCCEEDED(rc)) {
IWaitable *w = wait_list[handle_index]; IWaitable *w = wait_list[handle_index];
size_t w_ind = std::distance(this->waitables.begin(), std::find(this->waitables.begin(), this->waitables.end(), w)); 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; result = w;
} else if (rc == 0xEA01) { } else if (rc == 0xEA01) {
/* Timeout: Just update priorities. */ /* Timeout: Just update priorities. */
@ -229,14 +254,13 @@ class WaitableManager : public SessionManagerBase {
result = this->next_signaled; 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(); 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;
} }
} }
} }