mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-23 10:52:13 +00:00
libstrat: Significantly cleanup waitable manager result handling.
This commit is contained in:
parent
bac81f4ccc
commit
147f3c690a
4 changed files with 58 additions and 32 deletions
|
@ -612,7 +612,8 @@ 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)) {
|
||||||
|
if (IsDomainObject(ctx->obj_holder)) {
|
||||||
for (unsigned int i = 0; i < num_out_objects; i++) {
|
for (unsigned int i = 0; i < num_out_objects; i++) {
|
||||||
ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->out_object_ids[i]);
|
ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->out_object_ids[i]);
|
||||||
}
|
}
|
||||||
|
@ -622,6 +623,7 @@ constexpr Result WrapIpcCommandImpl(IpcResponseContext *ctx) {
|
||||||
svcCloseHandle(ctx->out_handles[CommandMetaData::NumOutHandles + i].handle);
|
svcCloseHandle(ctx->out_handles[CommandMetaData::NumOutHandles + i].handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < num_out_objects; i++) {
|
for (unsigned int i = 0; i < num_out_objects; i++) {
|
||||||
ctx->out_objs[i] = nullptr;
|
ctx->out_objs[i] = nullptr;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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? */
|
||||||
}
|
}
|
||||||
|
@ -142,11 +146,38 @@ class WaitableManager : public SessionManagerBase {
|
||||||
if (rc == 0xF601) {
|
if (rc == 0xF601) {
|
||||||
/* Close! */
|
/* Close! */
|
||||||
delete w;
|
delete w;
|
||||||
|
} else {
|
||||||
|
if (w->IsDeferred()) {
|
||||||
|
std::scoped_lock lk{this_ptr->deferred_lock};
|
||||||
|
this_ptr->deferred_waitables.push_back(w);
|
||||||
} else {
|
} else {
|
||||||
this_ptr->AddWaitable(w);
|
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) {
|
|
||||||
std::abort();
|
|
||||||
} else {
|
} else {
|
||||||
IWaitable *w = wait_list[handle_index];
|
/* TODO: Consider the following cases that this covers: */
|
||||||
size_t w_ind = std::distance(this->waitables.begin(), std::find(this->waitables.begin(), this->waitables.end(), w));
|
/* 7601: Thread termination requested. */
|
||||||
this->waitables.erase(this->waitables.begin() + w_ind);
|
/* E401: Handle is dead. */
|
||||||
std::for_each(waitables.begin(), waitables.begin() + w_ind - 1, std::mem_fn(&IWaitable::UpdatePriority));
|
/* E601: Handle list address invalid. */
|
||||||
delete w;
|
/* EE01: Too many handles. */
|
||||||
|
std::abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue