/* * 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 . */ #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)); } } }