mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-14 00:56:35 +00:00
177 lines
6.3 KiB
C++
177 lines
6.3 KiB
C++
/*
|
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "os_waitable_manager_impl.hpp"
|
|
#include "os_waitable_object_list.hpp"
|
|
|
|
namespace ams::os::impl{
|
|
|
|
WaitableHolderBase *WaitableManagerImpl::WaitAnyImpl(bool infinite, u64 timeout) {
|
|
/* Set processing thread handle while in scope. */
|
|
this->waiting_thread_handle = threadGetCurHandle();
|
|
ON_SCOPE_EXIT { this->waiting_thread_handle = INVALID_HANDLE; };
|
|
|
|
/* Prepare for processing. */
|
|
this->signaled_holder = nullptr;
|
|
WaitableHolderBase *result = this->LinkHoldersToObjectList();
|
|
|
|
/* Check if we've been signaled. */
|
|
{
|
|
std::scoped_lock lk(this->lock);
|
|
if (this->signaled_holder != nullptr) {
|
|
result = this->signaled_holder;
|
|
}
|
|
}
|
|
|
|
/* Process object array. */
|
|
if (result == nullptr) {
|
|
result = this->WaitAnyHandleImpl(infinite, timeout);
|
|
}
|
|
|
|
/* Unlink holders from the current object list. */
|
|
this->UnlinkHoldersFromObjectList();
|
|
|
|
return result;
|
|
}
|
|
|
|
WaitableHolderBase *WaitableManagerImpl::WaitAnyHandleImpl(bool infinite, u64 timeout) {
|
|
Handle object_handles[MaximumHandleCount];
|
|
WaitableHolderBase *objects[MaximumHandleCount];
|
|
|
|
const size_t count = this->BuildHandleArray(object_handles, objects);
|
|
const u64 end_time = infinite ? U64_MAX : armTicksToNs(armGetSystemTick());
|
|
|
|
while (true) {
|
|
this->current_time = armTicksToNs(armGetSystemTick());
|
|
|
|
u64 min_timeout = 0;
|
|
WaitableHolderBase *min_timeout_object = this->RecalculateNextTimeout(&min_timeout, end_time);
|
|
|
|
s32 index;
|
|
if (count == 0 && min_timeout == 0) {
|
|
index = WaitTimedOut;
|
|
} else {
|
|
index = this->WaitSynchronization(object_handles, count, min_timeout);
|
|
AMS_ASSERT(index != WaitInvalid);
|
|
}
|
|
|
|
switch (index) {
|
|
case WaitTimedOut:
|
|
if (min_timeout_object) {
|
|
this->current_time = armTicksToNs(armGetSystemTick());
|
|
if (min_timeout_object->IsSignaled() == TriBool::True) {
|
|
std::scoped_lock lk(this->lock);
|
|
this->signaled_holder = min_timeout_object;
|
|
return this->signaled_holder;
|
|
}
|
|
continue;
|
|
}
|
|
return nullptr;
|
|
case WaitCancelled:
|
|
if (this->signaled_holder) {
|
|
return this->signaled_holder;
|
|
}
|
|
continue;
|
|
default: /* 0 - 0x3F, valid. */
|
|
{
|
|
std::scoped_lock lk(this->lock);
|
|
this->signaled_holder = objects[index];
|
|
return this->signaled_holder;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
s32 WaitableManagerImpl::WaitSynchronization(Handle *handles, size_t count, u64 timeout) {
|
|
s32 index = WaitInvalid;
|
|
|
|
R_TRY_CATCH(svcWaitSynchronization(&index, handles, count, timeout)) {
|
|
R_CATCH(svc::ResultTimedOut) { return WaitTimedOut; }
|
|
R_CATCH(svc::ResultCancelled) { return WaitCancelled; }
|
|
/* All other results are critical errors. */
|
|
/* svc::ResultThreadTerminating */
|
|
/* svc::ResultInvalidHandle. */
|
|
/* svc::ResultInvalidPointer */
|
|
/* svc::ResultOutOfRange */
|
|
} R_END_TRY_CATCH_WITH_ASSERT;
|
|
|
|
return index;
|
|
}
|
|
|
|
size_t WaitableManagerImpl::BuildHandleArray(Handle *out_handles, WaitableHolderBase **out_objects) {
|
|
size_t count = 0;
|
|
|
|
for (WaitableHolderBase &holder_base : this->waitable_list) {
|
|
if (Handle handle = holder_base.GetHandle(); handle != INVALID_HANDLE) {
|
|
AMS_ASSERT(count < MaximumHandleCount);
|
|
|
|
out_handles[count] = handle;
|
|
out_objects[count] = &holder_base;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
WaitableHolderBase *WaitableManagerImpl::LinkHoldersToObjectList() {
|
|
WaitableHolderBase *signaled_holder = nullptr;
|
|
|
|
for (WaitableHolderBase &holder_base : this->waitable_list) {
|
|
TriBool is_signaled = holder_base.LinkToObjectList();
|
|
|
|
if (signaled_holder == nullptr && is_signaled == TriBool::True) {
|
|
signaled_holder = &holder_base;
|
|
}
|
|
}
|
|
|
|
return signaled_holder;
|
|
}
|
|
|
|
void WaitableManagerImpl::UnlinkHoldersFromObjectList() {
|
|
for (WaitableHolderBase &holder_base : this->waitable_list) {
|
|
holder_base.UnlinkFromObjectList();
|
|
}
|
|
}
|
|
|
|
WaitableHolderBase *WaitableManagerImpl::RecalculateNextTimeout(u64 *out_min_timeout, u64 end_time) {
|
|
WaitableHolderBase *min_timeout_holder = nullptr;
|
|
u64 min_time = end_time;
|
|
|
|
for (WaitableHolderBase &holder_base : this->waitable_list) {
|
|
if (const u64 cur_time = holder_base.GetWakeupTime(); cur_time < min_time) {
|
|
min_timeout_holder = &holder_base;
|
|
min_time = cur_time;
|
|
}
|
|
}
|
|
|
|
if (min_time < this->current_time) {
|
|
*out_min_timeout = 0;
|
|
} else {
|
|
*out_min_timeout = min_time - this->current_time;
|
|
}
|
|
return min_timeout_holder;
|
|
}
|
|
|
|
void WaitableManagerImpl::SignalAndWakeupThread(WaitableHolderBase *holder_base) {
|
|
std::scoped_lock lk(this->lock);
|
|
|
|
if (this->signaled_holder == nullptr) {
|
|
this->signaled_holder = holder_base;
|
|
R_ASSERT(svcCancelSynchronization(this->waiting_thread_handle));
|
|
}
|
|
}
|
|
|
|
}
|