mirror of
				https://github.com/Atmosphere-NX/Atmosphere.git
				synced 2025-10-31 11:15:51 +01:00 
			
		
		
		
	This implements waitable management for Events (and implements Events). It also refactors PM to use new Event/Waitable semantics, and also adds STS_ASSERT as a macro for asserting a boolean expression. The rest of stratosphere has been refactored to use STS_ASSERT whenever possible.
		
			
				
	
	
		
			178 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2018-2019 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 sts::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);
 | |
|                 STS_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(ResultKernelTimedOut) { return WaitTimedOut; }
 | |
|             R_CATCH(ResultKernelCancelled) { return WaitCancelled; }
 | |
|             /* All other results are critical errors. */
 | |
|             /* 7601: Thread termination requested. */
 | |
|             /* E401: Handle is dead. */
 | |
|             /* E601: Handle list address invalid. */
 | |
|             /* EE01: Too many handles. */
 | |
|         } 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) {
 | |
|                 STS_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));
 | |
|         }
 | |
|     }
 | |
| 
 | |
| }
 |